diff options
Diffstat (limited to 'source/30-game/Font.hpp')
-rw-r--r-- | source/30-game/Font.hpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/source/30-game/Font.hpp b/source/30-game/Font.hpp new file mode 100644 index 0000000..cc64c0c --- /dev/null +++ b/source/30-game/Font.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include "Color.hpp" +#include "CommonVertexIndex.hpp" +#include "RcPtr.hpp" + +#include <glad/glad.h> +#include <cstddef> +#include <cstdint> +#include <glm/glm.hpp> +#include <memory> +#include <span> +#include <string_view> +#include <vector> + +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<GlyphInfo> mGlyphs; + std::vector<int> mGlyphLookup; + RcPtr<Texture> 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<LoadingCandidate> 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<T> 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; +}; |