Skip to content

Instantly share code, notes, and snippets.

@JonathanCline
Created May 15, 2021 16:00
Show Gist options
  • Save JonathanCline/6e5400900605e348f4f15d7e0fed91d9 to your computer and use it in GitHub Desktop.
Save JonathanCline/6e5400900605e348f4f15d7e0fed91d9 to your computer and use it in GitHub Desktop.
#pragma once
#include <SAEEngine_Components.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <cstdint>
#include <filesystem>
#include <unordered_map>
#include <string>
#include <memory>
#include <map>
#include <concepts>
#include <type_traits>
#include <array>
#include <functional>
#include <numeric>
#include <istream>
#include <ostream>
#define SAE_ENGINE_TEXT_NAMESPACE SAE_ENGINE_NAMESPACE
namespace SAE_ENGINE_TEXT_NAMESPACE
{
/**
* @brief Default character type. Type aliases like glyph_string use this type as the CharT parameter.
*/
using default_character_t = char;
/**
* @brief concept is true if type is an integral character type (char8_t, char16_t, char32_t, char, wchar_t)
*/
template <typename T>
concept cx_character =
std::is_same<T, char8_t>::value ||
std::is_same<T, char16_t>::value ||
std::is_same<T, char32_t>::value ||
std::is_same<T, char>::value ||
std::is_same<T, wchar_t>::value;;
struct glyph_color;
struct glyph_format;
struct glyph_texture;
using glyph_texcoords = texture_sheet::texcoords;
template <cx_character CharT>
struct basic_glyph;
template <cx_character CharT>
struct basic_glyph_instance;
template <cx_character CharT>
struct basic_typeface;
template <cx_character CharT>
struct basic_font;
template <cx_character CharT>
struct basic_glyph_string;
/**
* @brief Type alias for character type allocation
* @tparam CharT character type
*/
template <cx_character CharT>
using character_allocator_type = std::allocator<CharT>;
/**
* @brief Type alias for key-value pair type allocation (used mainly by basic_typeface)
* @tparam CharT character type
*/
template <cx_character CharT>
using character_kv_pair_allocator_type = std::allocator<std::pair<const CharT, basic_glyph<CharT>>>;
/**
* @brief Type alias for signed pixel count
*/
using pixel_t = int16_t;
/**
* @brief Type alias for unsigned pixel count
*/
using upixel_t = uint16_t;
};
#pragma region TEXTURE_RELATED
#define SAE_ENGINE_TEXT_NEW_TEXTURE_TYPE
#include <IndustryPlanet_Package.h>
namespace SAE_ENGINE_TEXT_NAMESPACE
{
struct glyph_texture : public iplanet::pkg::texture
{
using iplanet::pkg::texture::texture;
using iplanet::pkg::texture::operator=;
};
}
#ifdef SAE_ENGINE_TEXT_NEW_TEXTURE_TYPE
#else
namespace SAE_ENGINE_TEXT_NAMESPACE
{
struct glyph_texture
{
public:
using value_type = uint8_t;
using pointer = value_type*;
using reference = value_type&;
using const_pointer = const value_type*;
using const_reference = const value_type&;
using dimension_type = uint16_t;
using size_type = uint32_t;
using allocator_type = std::allocator<value_type>;
private:
using ContainerT = std::vector<value_type, allocator_type>;
public:
dimension_type width() const noexcept;
dimension_type height() const noexcept;
size_type size() const noexcept;
private:
void _Update_Size();
public:
void resize(dimension_type _w, dimension_type _h);
void set_width(dimension_type _w);
void set_height(dimension_type _h);
pointer data() noexcept;
const_pointer data() const noexcept;
/**
* @brief Clears the pixel data and sets the texture's width and height to 0
*/
void clear() noexcept;
/**
* @brief Appends pixel data onto the bottom of the texture
* @param _data Pointer to the pixel data
* @param _count Maximum number of pixel values to append
* @return Number of pixel values appended to the texture
*/
size_type append(const_pointer _data, size_type _count);
glyph_texture() = default;
glyph_texture(dimension_type _width, dimension_type _height);
private:
dimension_type width_ = 0;
dimension_type height_ = 0;
ContainerT pixels_{};
};
}
#endif
#pragma endregion TEXTURE_RELATED
#pragma region FREETYPE_INTERFACING
struct FT_LibraryRec_;
typedef struct FT_LibraryRec_* FT_Library;
struct FT_FaceRec_;
typedef struct FT_FaceRec_* FT_Face;
namespace SAE_ENGINE_TEXT_NAMESPACE
{
namespace impl
{
struct FTLibWrapper
{
public:
operator FT_Library () const noexcept;
FTLibWrapper();
~FTLibWrapper();
private:
FT_Library ftlib_ = nullptr;
};
static inline FTLibWrapper freetype_wrapper_{};
};
}
#pragma endregion FREETYPE_INTERFACING
#pragma region GLYPH_IMPLEMENTATION
namespace SAE_ENGINE_TEXT_NAMESPACE
{
namespace impl
{
struct glyph_base
{
public:
struct glyph_bearing
{
pixel_t x = 0;
pixel_t y = 0;
};
struct glyph_size
{
upixel_t width = 0;
upixel_t height = 0;
};
struct glyph_advance
{
upixel_t x = 0;
upixel_t y = 0;
};
void set_size(glyph_size _size) noexcept;
constexpr glyph_size get_size() const noexcept { return this->size_; };
constexpr auto width() const noexcept { return this->size_.width; };
constexpr auto height() const noexcept { return this->size_.height; };
void set_bearing(glyph_bearing _bearing) noexcept;
constexpr glyph_bearing get_bearing() const noexcept { return this->bearing_; };
constexpr auto bearing_x() const noexcept { return this->bearing_.x; };
constexpr auto bearing_y() const noexcept { return this->bearing_.y; };
void set_advance(glyph_advance _advance) noexcept;
constexpr glyph_advance get_advance() const noexcept { return this->advance_; };
constexpr auto advance_x() const noexcept { return this->advance_.x; };
constexpr auto advance_y() const noexcept { return this->advance_.y; };
void set_texcoords(glyph_texcoords _tcoords) noexcept { this->texcoords_ = std::move(_tcoords); };
constexpr const glyph_texcoords& get_texcoords() const noexcept { return this->texcoords_; };
constexpr glyph_base() = default;
explicit constexpr glyph_base(glyph_size _s, glyph_bearing _b, glyph_advance _a, glyph_texcoords _tcoords) noexcept :
size_{ _s }, bearing_{ _b }, advance_{ _a }, texcoords_{ _tcoords }
{};
private:
glyph_texcoords texcoords_{};
glyph_bearing bearing_{};
glyph_advance advance_{};
glyph_size size_{};
};
};
/**
* @brief Holds an RGB color value used to by glyphs
*/
struct glyph_color
{
uint8_t r = 255;
uint8_t g = 255;
uint8_t b = 255;
};
struct glyph_background_color
{
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
uint8_t a = 0;
};
/**
* @brief Aggregate type for any glyph formatting options (such as color)
*/
struct glyph_format
{
glyph_color color{};
uint8_t font_size = 0;
float_t scale = 1.0f;
glyph_background_color bg_color{};
};
/**
* @brief Stores the metrics for a single font character (glyph)
* @tparam CharT character type
*/
template <cx_character CharT>
struct basic_glyph : public impl::glyph_base
{
public:
using character_t = CharT;
void set_char(character_t _char) noexcept { this->glyph_character_ = _char; };
constexpr character_t get_char() const noexcept { return glyph_character_; };
constexpr explicit operator character_t() const noexcept { return this->glyph_character_; };
friend inline constexpr bool operator==(const basic_glyph<CharT>& _lhs, CharT _rhs) noexcept
{
return (_lhs.get_char() == _rhs);
};
friend inline constexpr bool operator!=(const basic_glyph<CharT>& _lhs, CharT _rhs) noexcept
{
return (_lhs.get_char() != _rhs);
};
friend inline constexpr bool operator>(const basic_glyph<CharT>& _lhs, const basic_glyph<CharT>& _rhs) noexcept
{
return (_lhs.get_char() > _rhs.get_char());
};
friend inline constexpr bool operator<(const basic_glyph<CharT>& _lhs, const basic_glyph<CharT>& _rhs) noexcept
{
return (_lhs.get_char() < _rhs.get_char());
};
friend inline constexpr bool operator>=(const basic_glyph<CharT>& _lhs, const basic_glyph<CharT>& _rhs) noexcept
{
return (_lhs.get_char() >= _rhs.get_char());
};
friend inline constexpr bool operator<=(const basic_glyph<CharT>& _lhs, const basic_glyph<CharT>& _rhs) noexcept
{
return (_lhs.get_char() <= _rhs.get_char());
};
basic_glyph& operator=(character_t _character) noexcept
{
this->glyph_character_ = _character;
return *this;
};
explicit constexpr basic_glyph() noexcept = default;
explicit constexpr basic_glyph(character_t _character) noexcept :
impl::glyph_base{}, glyph_character_{ _character }
{};
explicit constexpr basic_glyph(character_t _char, const impl::glyph_base& _gbase) :
impl::glyph_base{ _gbase }, glyph_character_{ _char }
{};
explicit constexpr basic_glyph(character_t _char, impl::glyph_base&& _gbase) :
impl::glyph_base{ std::move(_gbase) }, glyph_character_{ _char }
{};
private:
character_t glyph_character_ = 0;
};
/**
* @brief 8-bit-character basic_glyph type alias
*/
using glyph8_t = basic_glyph<char8_t>;
/**
* @brief 16-bit-character basic_glyph type alias
*/
using glyph16_t = basic_glyph<char16_t>;
/**
* @brief 32-bit-character basic_glyph type alias
*/
using glyph32_t = basic_glyph<char32_t>;
/**
* @brief Stores the metrics for a single font character (glyph)
*/
using glyph = basic_glyph<default_character_t>;
/**
* @brief Holds a glyph and provides formatting support and a handful of helper functions
* @tparam CharT character type
*/
template <cx_character CharT>
struct basic_glyph_instance
{
public:
using character_t = CharT;
using glyph_t = basic_glyph<character_t>;
private:
constexpr float_t _scale() const noexcept { return this->format_.scale; };
glyph_t& _get_glyph() noexcept { return this->glyph_; };
constexpr const glyph_t& _get_glyph() const noexcept { return this->glyph_; };
constexpr upixel_t advance_x(float_t _scale) const noexcept
{
return (upixel_t)((this->_get_glyph().advance_x() >> 6) * _scale);
};
constexpr upixel_t advance_y(float_t _scale) const noexcept
{
return (upixel_t)((this->_get_glyph().advance_y() >> 6) * _scale);
};
constexpr pixel_t bearing_x(float_t _scale) const noexcept
{
return (pixel_t)(this->_get_glyph().bearing_x() * _scale);
};
constexpr pixel_t bearing_x() const noexcept
{
return this->bearing_x(this->_scale());
};
constexpr pixel_t bearing_y(float_t _scale) const noexcept
{
return (pixel_t)(this->_get_glyph().bearing_y() * _scale);
};
constexpr pixel_t bearing_y() const noexcept
{
return this->bearing_y(this->_scale());
};
constexpr pixel_t bbox_width(float_t _scale) const noexcept
{
return (pixel_t)(this->_get_glyph().width() * _scale);
};
constexpr pixel_t bbox_height(float_t _scale) const noexcept
{
return (pixel_t)(this->_get_glyph().height() * _scale);
};
constexpr pixel_t bbox_left(float_t _scale) const noexcept
{
return this->bearing_x(_scale);
};
constexpr pixel_t bbox_right(float_t _scale) const noexcept
{
return this->bbox_left(_scale) + this->bbox_width(_scale);
};
constexpr pixel_t bbox_top(float_t _scale) const noexcept
{
return this->bearing_y(_scale);
};
constexpr pixel_t bbox_bottom(float_t _scale) const noexcept
{
return bbox_top(_scale) - this->bbox_height(_scale);
};
constexpr upixel_t width(float_t _scale) const noexcept
{
return (upixel_t)this->advance_x(_scale);
};
constexpr upixel_t height(float_t _scale) const noexcept
{
return (upixel_t)this->advance_y(_scale);
};
public:
friend inline constexpr bool operator==(const basic_glyph_instance<character_t>& _lhs, character_t _rhs) noexcept
{
return (_lhs.get_glyph() == _rhs);
};
friend inline constexpr bool operator!=(const basic_glyph_instance<character_t>& _lhs, character_t _rhs) noexcept
{
return (_lhs.get_glyph() != _rhs);
};
/**
* @brief Returns the horizontal distance between this glyph and the next glyph's origin in pixels
*/
constexpr upixel_t advance_x() const noexcept
{
return this->advance_x(this->_scale());
};
/**
* @brief Returns the vertical distance between this glyph and the next glyph's origin in pixels
*/
constexpr upixel_t advance_y() const noexcept
{
return this->advance_y(this->_scale());
};
/**
* @brief Returns distance between the origin and the left side of the glyph's bbox. Add to cursor X to get real position.
*/
constexpr pixel_t bbox_left() const noexcept
{
return this->bbox_left(this->_scale());
};
/**
* @brief Returns distance between the origin and the right side of the glyph's bbox. Add to cursor X to get real position.
*/
constexpr pixel_t bbox_right() const noexcept
{
return this->bbox_right(this->_scale());
};
/**
* @brief Returns distance between the origin and the top side of the glyph's bbox. Add to cursor Y to get real position.
*/
constexpr pixel_t bbox_top() const noexcept
{
return this->bbox_top(this->_scale());
};
/**
* @brief Returns distance between the origin and the bottom side of the glyph's bbox. Add to cursor Y to get real position.
*/
constexpr pixel_t bbox_bottom() const noexcept
{
return this->bbox_bottom(this->_scale());
};
/**
* @brief Returns the complete width of the glyph in pixels
*/
constexpr upixel_t width() const noexcept
{
return this->width(this->_scale());
};
/**
* @brief Returns the complete height of the glyph in pixels
*/
constexpr upixel_t height() const noexcept
{
return this->height(this->_scale());
};
/**
* @brief Sets the glyph formatting to the format provided
*/
void set_format(glyph_format _fmt) noexcept
{
this->format_ = std::move(_fmt);
};
/**
* @brief Returns non-const reference to the glyph's formatting
*/
glyph_format& get_format() noexcept
{
return this->format_;
};
/**
* @brief Returns const reference to the glyph's formatting
*/
constexpr const glyph_format& get_format() const noexcept
{
return this->format_;
};
/**
* @brief Returns the RGB color value assigned to this glyph
*/
constexpr glyph_color color() const noexcept
{
return this->get_format().color;
};
/**
* @brief Sets the glyph used by the glyph instance, copy overload
*/
void set_glyph(const glyph_t& _g) noexcept
{
this->glyph_ = _g;
};
/**
* @brief Sets the glyph used by the glyph instance, move overload
*/
void set_glyph(glyph_t&& _g) noexcept
{
this->glyph_ = std::move(_g);
};
/**
* @brief Returns a non-const reference to the glyph instance's internal glyph
*/
glyph_t& get_glyph() noexcept
{
return this->_get_glyph();
};
/**
* @brief Returns a const reference to the glyph instance's internal glyph
*/
constexpr const glyph_t& get_glyph() const noexcept
{
return this->_get_glyph();
};
constexpr explicit basic_glyph_instance(const glyph_t& _g, glyph_format _fmt) noexcept(noexcept(glyph_t(glyph_t{}))) :
glyph_{ _g }, format_{ _fmt }
{};
constexpr explicit basic_glyph_instance(glyph_t&& _g, glyph_format _fmt) noexcept :
glyph_{ std::move(_g) }, format_{ _fmt }
{};
constexpr explicit basic_glyph_instance(const glyph_t& _g) noexcept(noexcept(glyph_t(glyph_t{}))) :
basic_glyph_instance{ _g, {} }
{};
constexpr explicit basic_glyph_instance(glyph_t&& _g) noexcept :
basic_glyph_instance{ std::move(_g), {} }
{};
basic_glyph_instance& operator=(const glyph_t& _g) noexcept(noexcept(glyph_t(glyph_t{})))
{
this->glyph_ = _g;
return *this;
};
basic_glyph_instance& operator=(glyph_t&& _g) noexcept
{
this->glyph_ = std::move(_g);
return *this;
};
constexpr basic_glyph_instance() noexcept = default;
constexpr basic_glyph_instance(const basic_glyph_instance& other) noexcept = default;
basic_glyph_instance& operator=(const basic_glyph_instance& other) noexcept = default;
constexpr basic_glyph_instance(basic_glyph_instance&& other) noexcept = default;
basic_glyph_instance& operator=(basic_glyph_instance&& other) noexcept = default;
private:
glyph_format format_{};
glyph_t glyph_{};
};
/**
* @brief 8-bit-character basic_glyph_instance type alias
*/
using glyph_instance8_t = basic_glyph_instance<char8_t>;
/**
* @brief 16-bit-character basic_glyph_instance type alias
*/
using glyph_instance16_t = basic_glyph_instance<char16_t>;
/**
* @brief 32-bit-character basic_glyph_instance type alias
*/
using glyph_instance32_t = basic_glyph_instance<char32_t>;
/**
* @brief Holds a glyph and provides formatting support and a handful of helper functions
*/
using glyph_instance = basic_glyph_instance<default_character_t>;
}
#pragma endregion GLYPH_IMPLEMENTATION
#pragma region FONT_LOADING
namespace SAE_ENGINE_TEXT_NAMESPACE
{
namespace impl
{
struct FontFaceLoader
{
private:
FT_Face& _get_face() noexcept;
const FT_Face& _get_face() const noexcept;
std::pair<impl::glyph_base, glyph_texture> _load_font_glyph(uint32_t _charCode);
void _set_pixel_sizes(size_t _width, size_t _height);
FT_Face _set_font_face(std::filesystem::path _fontPath);
void _free_face(FT_Face& _face);
public:
template <cx_character CharT>
std::pair<basic_glyph<CharT>, glyph_texture> load_glyph(CharT _charCode)
{
auto _gotGlyph = this->_load_font_glyph((uint32_t)_charCode);
return std::pair<basic_glyph<CharT>, glyph_texture>
{
basic_glyph<CharT>{ _charCode, std::move(_gotGlyph.first) },
std::move(_gotGlyph.second)
};
};
std::pair<size_t, size_t> max_glyph_image_size() const;
bool is_open() const noexcept;
bool is_closed() const noexcept;
bool open();
bool close();
void set_font_path(const std::filesystem::path& _fpath);
void set_font_size(size_t _pxwidth, size_t _pxheight);
size_t px_width() const noexcept;
size_t px_height() const noexcept;
int16_t linespace() const;
int16_t max_advance() const;
int16_t face_height() const;
int16_t ascender() const;
int16_t descender() const;
explicit FontFaceLoader() = default;
explicit FontFaceLoader(std::filesystem::path _fontPath);
explicit FontFaceLoader(std::filesystem::path _fontPath, size_t _pxwidth, size_t _pxheight);
~FontFaceLoader();
private:
std::filesystem::path font_path_{};
bool is_open_ = false;
FT_Face face_ = nullptr;
size_t px_width_ = 0;
size_t px_height_ = 0;
};
};
using fontface_loader_t = impl::FontFaceLoader;
}
#pragma endregion FONT_LOADING
#pragma region FONT_AND TYPEFACE
namespace SAE_ENGINE_TEXT_NAMESPACE
{
/**
* @brief Container type that stores a single font typeface alonsgide various font metrics such as linespacing
* @tparam CharT character type
*/
template <cx_character CharT>
struct basic_typeface
{
public:
using character_t = CharT;
using glyph_t = basic_glyph<character_t>;
using value_type = glyph_t;
using pointer = typename value_type*;
using reference = typename value_type&;
using const_pointer = const typename value_type*;
using const_reference = const typename value_type&;
using allocator_type = character_kv_pair_allocator_type<character_t>;
private:
using key_type = character_t;
using ContainerT = std::map<key_type, glyph_t, std::less<key_type>, allocator_type>;
public:
using iterator = typename ContainerT::iterator;
using const_iterator = typename ContainerT::const_iterator;
using reverse_iterator = typename ContainerT::reverse_iterator;
using const_reverse_iterator = typename ContainerT::const_reverse_iterator;
iterator begin() noexcept { return this->glyphs_.begin(); };
const_iterator begin() const noexcept { return this->glyphs_.cbegin(); };
const_iterator cbegin() const noexcept { return this->glyphs_.cbegin(); };
iterator end() noexcept { return this->glyphs_.end(); };
const_iterator end() const noexcept { return this->glyphs_.cend(); };
const_iterator cend() const noexcept { return this->glyphs_.cend(); };
reverse_iterator rbegin() noexcept { return this->glyphs_.rbegin(); };
const_reverse_iterator rbegin() const noexcept { return this->glyphs_.crbegin(); };
const_reverse_iterator crbegin() const noexcept { return this->glyphs_.crbegin(); };
reverse_iterator rend() noexcept { return this->glyphs_.rend(); };
const_reverse_iterator rend() const noexcept { return this->glyphs_.crend(); };
const_reverse_iterator crend() const noexcept { return this->glyphs_.crend(); };
size_t size() const noexcept { return this->glyphs_.size(); };
void push(const glyph_t& _glyph)
{
auto _glyphCharacter = _glyph.get_char();
this->glyphs_.insert({ this->size(), _glyph });
};
void push(glyph_t&& _glyph)
{
auto _glyphCharacter = _glyph.get_char();
this->glyphs_.insert({ this->size(), std::move(_glyph) });
};
void clear() noexcept { this->glyphs_.clear(); };
reference at(key_type _i) { return this->glyphs_.at(_i); };
const_reference at(key_type _i) const { return this->glyphs_.at(_i); };
reference operator[](key_type _i) { return this->at(_i); };
const_reference operator[](key_type _i) const { return this->at(_i); };
bool contains(key_type _i) const noexcept { return this->glyphs_.contains(_i); };
int16_t max_advance() const noexcept { return this->max_advance_; };
void set_max_advance(int16_t _px) noexcept { this->max_advance_ = _px; };
int16_t height() const noexcept { return this->height_; }
void set_height(int16_t _px) noexcept { this->height_ = _px; };
int16_t linespace() const noexcept { return this->linespace_; };
void set_linespace(int16_t _px) noexcept { this->linespace_ = _px; };
int16_t ascender() const noexcept { return this->ascender_; };
void set_ascender(int16_t _px) noexcept { this->ascender_ = _px; };
int16_t descender() const noexcept { return this->descender_; };
void set_descender(int16_t _px) noexcept { this->descender_ = _px; };
basic_typeface() = default;
private:
int16_t max_advance_ = 0;
int16_t height_ = 0;
int16_t linespace_ = 0;
int16_t ascender_ = 0;
int16_t descender_ = 0;
ContainerT glyphs_{};
};
/**
* @brief 8-bit-character basic_typeface type alias
*/
using typeface8_t = basic_typeface<char8_t>;
/**
* @brief 16-bit-character basic_typeface type alias
*/
using typeface16_t = basic_typeface<char16_t>;
/**
* @brief 32-bit-character basic_typeface type alias
*/
using typeface32_t = basic_typeface<char32_t>;
/**
* @brief Container type that stores a single font typeface alonsgide various font metrics such as linespacing
*/
using typeface = basic_typeface<default_character_t>;
/**
* @brief Manages a set of typefaces that follow a similar style - AKA a font.
* @tparam CharT character type
*/
template <cx_character CharT>
struct basic_font
{
public:
using character_t = CharT;
using glyph_t = basic_glyph<character_t>;
using typeface_t = basic_typeface<character_t>;
private:
uint16_t glyph_count_ = 0;
std::vector<std::vector<typeface_t>> typefaces_{};
public:
using character_t = CharT;
using glyph_t = basic_glyph<character_t>;
using typeface_t = basic_typeface<character_t>;
void clear() noexcept
{
this->typefaces_.clear();
};
const glyph_t& get_glyph(character_t _glyphCode, uint8_t _size, uint8_t _style) const
{
assert(_style < this->typefaces_.size());
return this->typefaces_[_style][_size][_glyphCode];
};
const glyph_t& get_glyph(character_t _glyphCode, uint8_t _size) const
{
return this->get_glyph(_glyphCode, _size, 0);
};
const glyph_t& get_glyph(character_t _glyphCode) const
{
return this->get_glyph(_glyphCode, 0, 0);
};
/**
* @brief Returns the total number of glyphs per typeface in the font
*/
uint16_t glyph_count() const
{
return this->glyph_count_;
};
const typeface_t& get_typeface(uint8_t _style, uint8_t _size) const
{
assert(_style < this->typefaces_.size());
return this->typefaces_.at(_style).at(_size);
};
const typeface_t& get_typeface(uint8_t _style) const
{
assert(_style < this->typefaces_.size());
return this->typefaces_.at(_style).at(0);
};
private:
bool load(const std::filesystem::path& _typefacePath, typeface_t& _tface, uint16_t _glyphHeight, fontface_loader_t _loader)
{
if (_loader.is_open())
if (!_loader.close())
abort();
_loader.set_font_path(_typefacePath);
_loader.set_font_size(0, (size_t)_glyphHeight);
if (!_loader.open())
return false;
auto _maxSize = _loader.max_glyph_image_size();
_tface.set_height(_loader.face_height() >> 6);
_tface.set_max_advance(_loader.max_advance() >> 6);
_tface.set_ascender(_loader.ascender() >> 6);
_tface.set_descender(_loader.descender() >> 6);
_tface.set_linespace(_loader.linespace() >> 6);
for (character_t c = 0; c < this->glyph_count(); ++c)
{
auto _gPair = _loader.load_glyph<character_t>(c);
auto _tcoords = this->get_texture_sheet().append_to_sheet(_gPair.second);
_gPair.first.set_texcoords(_tcoords);
_tface.push(_gPair.first);
};
assert(this->tex_.subtex_width() >= _maxSize.first);
assert(this->tex_.subtex_height() >= _maxSize.second);
if (!_loader.close())
return false;
return true;
};
public:
constexpr static inline int8_t LOAD_ERROR = -1;
/**
* @brief Loads a type face into the font
* @param _fpath Path to the type face (.ttf or .otf)
* @param _glyphCount Number of glyphs in the font face
* @return The style index of the type face or LOAD_ERROR on failure
*/
int8_t load_typeface(const std::filesystem::path& _fpath, character_t _glyphCount)
{
int8_t _style = (int8_t)this->typefaces_.size();
if (_style < LOAD_ERROR)
return LOAD_ERROR;
this->glyph_count_ = _glyphCount;
const std::array<uint8_t, 4> _loadSizes{ 12, 18, 24, 32 };
const auto _maxSize = *std::max_element(_loadSizes.begin(), _loadSizes.end());
this->tex_ = texture_sheet{ texture::TEXTURE_ENCODING::R_8, (size_t)_maxSize, (size_t)_maxSize };
this->typefaces_.push_back({});
for (auto& s : _loadSizes)
{
fontface_loader_t _fload{};
typeface_t _tface{};
if (this->load(_fpath, _tface, (uint16_t)s, _fload))
{
this->typefaces_.back().push_back(std::move(_tface));
}
else
{
this->typefaces_.back().pop_back();
return LOAD_ERROR;
};
};
return _style;
};
/**
* @brief Loads a type face into the font
* @param _fpath Path to the type face (.ttf or .otf)
* @return The style index of the type face or LOAD_ERROR on failure
*/
int8_t load_typeface(const std::filesystem::path& _fpath)
{
return this->load_typeface(_fpath, std::numeric_limits<character_t>::max());
};
texture_sheet& get_texture_sheet() noexcept { return this->tex_; };
const texture_sheet& get_texture_sheet() const noexcept { return this->tex_; };
basic_font() = default;
private:
texture_sheet tex_{};
std::vector<uint8_t> face_sizes_{};
};
/**
* @brief 8-bit-character basic_font type alias
*/
using font8_t = basic_font<char8_t>;
/**
* @brief 16-bit-character basic_font type alias
*/
using font16_t = basic_font<char16_t>;
/**
* @brief 32-bit-character basic_font type alias
*/
using font32_t = basic_font<char32_t>;
/**
* @brief Manages a set of typefaces that follow a similar style - AKA a font.
*/
using font = basic_font<default_character_t>;
/**
* @brief Stores a string of glyphs and provides an std::string-like interface
* @tparam CharT character type
*/
template <cx_character CharT>
struct basic_glyph_string
{
public:
using character_t = char;
using glyph_t = basic_glyph<character_t>;
using font_t = basic_font<character_t>;
using iglyph_t = basic_glyph_instance<character_t>;
using size_type = size_t;
private:
font_t* _get_font() const noexcept { return this->font_; };
public:
using ContainerT = std::vector<iglyph_t>;
const font_t* get_font() const noexcept { return this->_get_font(); };
public:
using iterator = typename ContainerT::iterator;
using const_iterator = typename ContainerT::const_iterator;
iterator begin() noexcept { return this->str_.begin(); };
const_iterator begin() const noexcept { return this->str_.cbegin(); };
const_iterator cbegin() const noexcept { return this->str_.cbegin(); };
iterator end() noexcept { return this->str_.end(); };
const_iterator end() const noexcept { return this->str_.cend(); };
const_iterator cend() const noexcept { return this->str_.cend(); };
private:
float_t scale() const noexcept { return this->default_formatting_.scale; };
public:
void clear() noexcept
{
this->str_.clear();
};
size_type size() const noexcept
{
return this->str_.size();
};
iglyph_t& at(size_type _i) { return this->str_.at(_i); };
const iglyph_t& at(size_type _i) const { return this->str_.at(_i); };
iglyph_t& operator[](size_type _i) { return this->at(_i); };
const iglyph_t& operator[](size_type _i) const { return this->at(_i); };
std::string string() const
{
std::string _out{};
_out.reserve(this->size());
for (const auto& c : this->str_)
{
_out += c.get_glyph().get_char();
};
return _out;
};
private:
size_type _find(character_t _c, size_type _pos) const
{
for (; _pos < this->size(); ++_pos)
{
if (this->at(_pos) == _c)
break;
};
return _pos;
};
template <typename _CharTraitsT, typename _AllocT>
size_type _find(const std::basic_string<character_t, _CharTraitsT, _AllocT>& _str, size_type _pos) const
{
auto _strIt = _str.begin();
auto _strEnd = _str.end();
for (; _pos < this->size(); ++_pos)
{
if (this->at(_pos) == *_strIt)
{
++_strIt;
}
else
{
_strIt = _strEnd;
};
if (_strIt == _str.end())
return _pos - _str.size();
};
return _pos;
};
protected:
void set_font(font_t* _font)
{
this->clear();
this->font_ = _font;
};
public:
iterator find(character_t _c, iterator _pos)
{
return this->begin() + this->_find(_c, _pos - this->begin());
};
const_iterator find(character_t _c, const_iterator _pos) const
{
return this->cbegin() + this->_find(_c, _pos - this->cbegin());
};
iterator find(character_t _c)
{
return this->find(_c, this->begin());
};
const_iterator find(character_t _c) const
{
return this->find(_c, this->cbegin());
};
template <typename _CharTraitsT, typename _AllocT>
iterator find(const std::basic_string<character_t, _CharTraitsT, _AllocT>& _str, iterator _pos)
{
return this->begin() + this->_find(_str, _pos - this->begin());
};
template <typename _CharTraitsT, typename _AllocT>
const_iterator find(const std::basic_string<character_t, _CharTraitsT, _AllocT>& _str, const_iterator _pos) const
{
return this->cbegin() + this->_find(_str, _pos - this->cbegin());
};
template <typename _CharTraitsT, typename _AllocT>
iterator find(const std::basic_string<character_t, _CharTraitsT, _AllocT>& _str)
{
return this->find(_str, this->begin());
};
template <typename _CharTraitsT, typename _AllocT>
const_iterator find(const std::basic_string<character_t, _CharTraitsT, _AllocT>& _str) const
{
return this->find(_str, this->cbegin());
};
/**
* @brief Sets the format of the glyphs within the specified range to the provided glyph formatting
* @param _begin Beginning of glyph range
* @param _end End of glyph range
* @param _fmt Format to use
* @return Iterator to final glyph that was formatted
*/
iterator format(iterator _begin, iterator _end, glyph_format _fmt)
{
glyph_texcoords _gtexcoords{};
for (; _begin != _end; ++_begin)
{
_begin->get_glyph() = this->get_font()->get_glyph(_begin->get_glyph().get_char(), _fmt.font_size);
_begin->set_format(_fmt);
};
return _begin;
};
/**
* @brief Sets the format of the glyph at the provided iterator to the provided glyph formatting
* @param _it Beginning of glyph range
* @return Iterator to the glyph that was formatted
*/
iterator format(iterator _it, const glyph_format& _fmt)
{
return this->format(_it, _it + 1, _fmt);
};
iterator insert(iterator _at, character_t _char, const glyph_format& _fmt)
{
iterator _out{};
_out = this->str_.insert(_at, iglyph_t{ this->_get_font()->get_glyph(((uint16_t)_char), _fmt.font_size), _fmt });
this->format(_out, _fmt);
return _out;
};
iterator insert(iterator _at, character_t _char)
{
return this->insert(_at, _char, this->default_formatting_);
};
template <typename _IterT>
iterator insert(iterator _at, _IterT _begin, _IterT _end, const glyph_format& _fmt)
{
auto _atcopy = _at;
for (; _begin != _end; ++_begin)
{
_at = this->insert(_at, *_begin, _fmt);
++_at;
};
return _atcopy;
};
template <typename SrcCharIterT>
iterator insert(iterator _at, SrcCharIterT _begin, SrcCharIterT _end)
{
return this->insert(_at, _begin, _end, this->default_formatting_);
};
void push_back(character_t _char, size_type _count, const glyph_format& _fmt)
{
for (size_type i = 0; i < _count; ++i)
{
this->insert(this->end(), _char, _fmt);
};
};
void push_back(character_t _char, size_type _count)
{
this->push_back(_char, _count, this->default_formatting_);
};
void push_back(character_t _char, glyph_format _fmt)
{
this->push_back(_char, 1, _fmt);
};
void push_back(character_t _char)
{
this->push_back(_char, 1);
};
template <typename CharTraits, typename Alloc>
void append(const std::basic_string<character_t, CharTraits, Alloc>& _str, glyph_format _fmt)
{
for (auto& c : _str)
{
this->push_back(c, _fmt);
};
};
template <typename CharTraits, typename Alloc>
void append(const std::basic_string<character_t, CharTraits, Alloc>& _str)
{
this->append(_str, this->default_formatting_);
};
void append(const character_t* _cstr, size_type _len, glyph_format _fmt)
{
for (const character_t* ptr = _cstr; (ptr - _cstr) != _len; ++ptr)
{
this->push_back(*ptr, _fmt);
};
};
void append(const character_t* _cstr, size_type _len)
{
this->append(_cstr, this->default_formatting_);
};
void append(const character_t* _cstr, glyph_format _fmt)
{
this->append(_cstr, std::strlen((const char*)_cstr), _fmt);
};
void append(const character_t* _cstr)
{
this->append(_cstr, std::strlen((const char*)_cstr));
};
template <typename CharTraits, typename Alloc>
void assign(const std::basic_string<character_t, CharTraits, Alloc>& _str, glyph_format _fmt)
{
this->str_.clear();
this->str_.reserve(_str.size());
for (auto& c : _str)
{
this->push_back(c, _fmt);
};
};
template <typename CharTraits, typename Alloc>
void assign(const std::basic_string<character_t, CharTraits, Alloc>& _str)
{
this->assign(_str, this->default_formatting_);
};
void assign(const character_t* _cstr, size_type _len, glyph_format _fmt)
{
this->clear();
for (const character_t* ptr = _cstr; (ptr - _cstr) != _len; ++ptr)
{
this->push_back(*ptr, _fmt);
};
};
void assign(const character_t* _cstr, size_type _len)
{
this->assign(_cstr, _len, this->default_formatting_);
};
void assign(const character_t* _cstr, glyph_format _fmt)
{
this->assign(_cstr, std::strlen(_cstr), _fmt);
};
void assign(const typename character_t* _cstr)
{
this->assign(_cstr, std::strlen((const char*)_cstr));
};
iterator erase(const_iterator _at)
{
return this->str_.erase(_at);
};
iterator erase(const_iterator _begin, const_iterator _end)
{
return this->str_.erase(_begin, _end);
};
void pop_back(size_t _count)
{
if(this->size() >= _count)
this->erase(this->end() - _count, this->end());
};
void pop_back()
{
this->pop_back(1);
};
std::string substr(const_iterator _begin, const_iterator _end) const
{
std::string _out{};
_out.reserve(_end - _begin);
for (; _begin != _end; ++_begin)
{
_out.push_back(_begin->get_glyph().get_char());
};
return _out;
};
std::string substr(size_t _offset, size_t _count) const
{
auto _at = this->begin() + _offset;
return this->substr(_at, _at + _count);
};
friend inline std::ostream& operator<<(std::ostream& _ostr, const basic_glyph_string<CharT>& _txt)
{
return _ostr << _txt.string();
};
friend inline std::istream& operator>>(std::istream& _istr, basic_glyph_string<CharT>& _txt)
{
std::basic_string<character_t> _str{};
_istr >> _str;
_txt.assign(_str);
return _istr;
};
/**
* @brief Sets the active formatting value. Any operations that can take formatting will use the active formatting value
* instead if said operation is not provided a format. For example, calling format_all() without provided a format
*/
void set_formatting(glyph_format _fmt)
{
this->default_formatting_ = _fmt;
};
/**
* @brief Returns a non-const reference to the active glyph formatting
*/
glyph_format& get_active_formatting() noexcept
{
return this->default_formatting_;
};
/**
* @brief Returns a const reference to the active glyph formatting
*/
const glyph_format& get_active_formatting() const noexcept
{
return this->default_formatting_;
};
/**
* @brief Sets all glyph formattnig to the provided formt
*/
void format_all(glyph_format _fmt)
{
this->format(this->begin(), this->end(), _fmt);
};
/**
* @brief Sets all glyph formatting to the currently set formatting (set using set_formatting())
*/
void format_all()
{
this->format_all(this->default_formatting_);
};
basic_glyph_string& operator=(const std::string& _str)
{
this->assign(_str);
return *this;
};
explicit basic_glyph_string(font_t* _font) :
font_{ _font }
{};
template <typename CharTraits, typename Alloc>
explicit basic_glyph_string(font_t* _font, const std::basic_string<character_t, CharTraits, Alloc>& _str) :
font_{ _font }
{
this->assign(_str);
};
template <typename CharTraits, typename Alloc>
explicit basic_glyph_string(font_t* _font, const std::basic_string<character_t, CharTraits, Alloc>& _str, glyph_format _fmt) :
default_formatting_{ _fmt }, font_ { _font }
{
this->assign(_str);
};
explicit basic_glyph_string(font_t* _font, const character_t* _cstr) :
font_{ _font }
{
this->assign(_cstr);
};
explicit basic_glyph_string(font_t* _font, const character_t* _cstr, glyph_format _fmt) :
default_formatting_{ _fmt }, font_{ _font }
{
this->assign(_cstr);
};
explicit basic_glyph_string(font_t* _font, const character_t* _cstr, size_t _len) :
font_{ _font }
{
this->assign(_cstr, _len);
};
explicit basic_glyph_string(font_t* _font, const character_t* _cstr, size_t _len, glyph_format _fmt) :
default_formatting_{ _fmt }, font_{ _font }
{
this->assign(_cstr, _len);
};
basic_glyph_string(const basic_glyph_string& other) = default;
basic_glyph_string& operator=(const basic_glyph_string& other) = default;
basic_glyph_string(basic_glyph_string&& other) noexcept = default;
basic_glyph_string& operator=(basic_glyph_string && other) noexcept = default;
private:
font_t* font_ = nullptr;
ContainerT str_{};
glyph_format default_formatting_{};
};
/**
* @brief 8-bit-character basic_glyph_string type alias
*/
using glyph_string8_t = basic_glyph_string<char8_t>;
/**
* @brief 16-bit-character basic_glyph_string type alias
*/
using glyph_string16_t = basic_glyph_string<char16_t>;
/**
* @brief 32-bit-character basic_glyph_string type alias
*/
using glyph_string32_t = basic_glyph_string<char32_t>;
/**
* @brief Stores a string of glyphs and provides an std::string-like interface
*/
using glyph_string = basic_glyph_string<default_character_t>;
template <cx_character CharT>
struct font_list
{
public:
using character_type = CharT;
using font_type = basic_font<character_type>;
private:
using ContainerT = std::unordered_map<std::string, std::unique_ptr<font_type>>;
public:
using iterator = typename ContainerT::iterator;
using const_iterator = typename ContainerT::const_iterator;
iterator begin() noexcept { return this->fonts_.begin(); };
const_iterator begin() const noexcept { return this->fonts_.cbegin(); };
const_iterator cbegin() const noexcept { return this->fonts_.cbegin(); };
iterator end() noexcept { return this->fonts_.end(); };
const_iterator end() const noexcept { return this->fonts_.cend(); };
const_iterator cend() const noexcept { return this->fonts_.cend(); };
void append_font(const std::string& _name, std::unique_ptr<font_type> _font)
{
this->fonts_.insert({ _name, std::move(_font) });
};
bool contains(const std::string& _name) const
{
return fonts_.contains(_name);
};
void erase(const std::string& _name)
{
this->fonts_.erase(_name);
};
void clear() noexcept
{
this->fonts_.clear();
};
font_type* at(const std::string& _name)
{
return this->fonts_.at(_name).get();
};
const font_type* at(const std::string& _name) const
{
return this->fonts_.at(_name).get();
};
font_list() = default;
private:
ContainerT fonts_{};
};
};
#pragma endregion FONT_AND TYPEFACE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment