#pragma once #include "Color.hpp" #include "CommonVertexIndex.hpp" #include "RcPtr.hpp" #include #include #include #include #include #include #include #include enum class FontType { Regular, Italic, Bold, BoldItalic, }; struct GlyphVariant { int horizontalAdvance; float x0, y0; float x1, y1; float u0, v0; float u1, v1; }; // TODO optimize for memory when most glyphs don't have a non-regular variant // such as CJK characters rarely use bold or italics struct GlyphInfo { char32_t codepoint; GlyphVariant regular; GlyphVariant italic; GlyphVariant bold; GlyphVariant boldItalic; }; class Font : public RefCounted { public: static const char32_t* GetGlyphRangesDefault(); static const char32_t* GetGlyphRangesKorean(); static const char32_t* GetGlyphRangesJapanese(); static const char32_t* GetGlyphRangesChineseFull(); static const char32_t* GetGlyphRangesChineseSimplifiedCommon(); static const char32_t* GetGlyphRangesCyrillic(); static const char32_t* GetGlyphRangesThai(); static const char32_t* GetGlyphRangesVietnamese(); private: std::vector mGlyphs; std::vector mGlyphLookup; RcPtr mAtlas; float mFontHeight; int mFallbackGlyphIdx; public: struct LoadingCandidate { // No std::string_view here because iostreams only support null termianted string const char* ttfPath; const char32_t* glyphRange; FontType type = FontType::Regular; }; Font() = default; Font(const Font&) = delete; Font& operator=(const Font&) = delete; Font(Font&&) = default; Font& operator=(Font&&) = default; enum ErrorCode { EC_Success, EC_FileIOFailed, EC_FontLoadingFailed, }; struct InitResult { ErrorCode errorCode; int failedItemIdx; }; /// Initialize this font object from a list of ttf files. Each ttf covers a range of unicode characters, /// and a specific font type, which are combined together into an atlas texture. /// /// \see FontType /// \see Font::LoadingCandidate InitResult Init(std::span candidates, float fontHeight, char32_t fallbackCodepoint = '?', int oversampleH = 1, int oversampleV = 1); float GetFontHeight() const; int HorizontalAdvance(char32_t c, FontType type = FontType::Regular) const; int HorizontalAdvance(std::string_view str, FontType type = FontType::Regular) const; int GetGlyphCount() const; const GlyphInfo& GetFallbackGlyph() const; /// Find the glyph corresponding to the given codepoint. If none is present, return the fallback glyph /// specified in Init(). const GlyphInfo& FindGlyphFallback(char32_t codepoint) const; /// Find the glyph corresponding to the given codepoint. If none is present, `nullptr` is returned. const GlyphInfo* FindGlyph(char32_t codepoint) const; struct DrawTargetPointer { // Draw target info Vertex_PTC* vertices; uint32_t* indices; uint32_t initialVertexIdx; // Text info glm::vec3 pos; FontType type = FontType::Regular; RgbaColor color = RgbaColor{}; // Output info, written to by DrawTo() int glyphsRendered; float horizontalAdvance; }; /// Populate the target with quads, with first character's origin at `pos` and every vertex colored `color`. /// User must allocate enough space for all characers beforehand (one way is to reserve the # of bytes). /// Suitable for raw array allocations or something like PodVector that doesn't do zero-initialization on /// resize. /// /// \see Font::DrawTargetPointer void DrawTo(std::string_view text, DrawTargetPointer& t) const; void DrawTo(std::u32string_view text, DrawTargetPointer& t) const; /// OpenGL texture containing the font atlas. Format is in GL_RED because we don't need the other channels, due to the /// font atlas being black and white (in other words, opacity only). const Texture& GetGlyphAtlas() const; };