#ifndef SPECTER_FONTCACHE_HPP #define SPECTER_FONTCACHE_HPP #include #include FT_FREETYPE_H #include #include #include #include namespace Specter { class FontTag { friend class FontCache; uint64_t m_hash = 0; FontTag(const std::string& name, bool subpixel, float points, unsigned dpi); public: FontTag() = default; operator bool() const {return m_hash != 0;} uint64_t hash() const {return m_hash;} bool operator==(const FontTag& other) const {return m_hash == other.m_hash;} }; } namespace std { template <> struct hash { size_t operator() (const Specter::FontTag& handle) const NOEXCEPT {return size_t(handle.hash());} }; } namespace Specter { class FreeTypeGZipMemFace { FT_Library m_lib; FT_StreamRec m_comp = {}; FT_StreamRec m_decomp = {}; FT_Face m_face = nullptr; public: FreeTypeGZipMemFace(FT_Library lib, const uint8_t* data, size_t sz); FreeTypeGZipMemFace(const FreeTypeGZipMemFace& other) = delete; FreeTypeGZipMemFace& operator=(const FreeTypeGZipMemFace& other) = delete; ~FreeTypeGZipMemFace() {close();} void open(); void close(); operator FT_Face() {open(); return m_face;} }; using FCharFilter = std::pair>; class FontAtlas { friend class FontCache; FT_Face m_face; boo::ITextureSA* m_tex = nullptr; boo::GraphicsDataToken m_token; uint32_t m_dpi; FT_Fixed m_ftXscale; FT_UShort m_ftXPpem; FT_Pos m_lineHeight; bool m_subpixel; public: struct Glyph { atUint32 m_unicodePoint; atUint32 m_glyphIdx; atUint32 m_layerIdx; float m_layerFloat; float m_uv[4]; atInt32 m_leftPadding; atInt32 m_advance; atInt32 m_width; atInt32 m_height; atInt32 m_verticalOffset; }; private: std::vector m_glyphs; std::unordered_map>> m_kernAdjs; struct TT_KernHead : Athena::io::DNA { DECL_DNA Value length; Value coverage; }; struct TT_KernSubHead : Athena::io::DNA { DECL_DNA Value nPairs; Value searchRange; Value entrySelector; Value rangeShift; }; struct TT_KernPair : Athena::io::DNA { DECL_DNA Value left; Value right; Value value; }; void buildKernTable(FT_Face face); std::unordered_map m_glyphLookup; public: FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, bool subpixel, FCharFilter& filter, Athena::io::FileWriter& writer); FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, bool subpixel, FCharFilter& filter, Athena::io::FileReader& reader); FontAtlas(const FontAtlas& other) = delete; FontAtlas& operator=(const FontAtlas& other) = delete; uint32_t dpi() const {return m_dpi;} FT_Fixed FT_Xscale() const {return m_ftXscale;} FT_UShort FT_XPPem() const {return m_ftXPpem;} FT_Pos FT_LineHeight() const {return m_lineHeight;} boo::ITexture* texture() const {return m_tex;} bool subpixel() const {return m_subpixel;} const Glyph* lookupGlyph(atUint32 charcode) const { auto search = m_glyphLookup.find(charcode); if (search == m_glyphLookup.end()) return nullptr; return &m_glyphs[search->second]; } atInt16 lookupKern(atUint32 leftIdx, atUint32 rightIdx) const { auto pairSearch = m_kernAdjs.find(leftIdx); if (pairSearch == m_kernAdjs.cend()) return 0; for (const std::pair& p : pairSearch->second) if (p.first == rightIdx) return p.second; return 0; } }; static FCharFilter const AllCharFilter = std::make_pair("all-glyphs", [](uint32_t)->bool {return true;}); static FCharFilter const LatinCharFilter = std::make_pair("latin-glyphs", [](uint32_t c)->bool {return c <= 0xff || ((c - 0x2200) <= (0x23FF - 0x2200));}); static FCharFilter const LatinAndJapaneseCharFilter = std::make_pair("latin-and-jp-glyphs", [](uint32_t c)->bool {return LatinCharFilter.second(c) || ((c - 0x2E00) <= (0x30FF - 0x2E00)) || ((c - 0x4E00) <= (0x9FFF - 0x4E00)) || ((c - 0xFF00) <= (0xFFEF - 0xFF00));}); class FontCache { const HECL::Runtime::FileStoreManager& m_fileMgr; HECL::SystemString m_cacheRoot; struct Library { FT_Library m_lib; Library(); ~Library(); operator FT_Library() {return m_lib;} } m_fontLib; FreeTypeGZipMemFace m_regFace; FreeTypeGZipMemFace m_monoFace; FreeTypeGZipMemFace m_curvesFace; std::unordered_map> m_cachedAtlases; public: FontCache(const HECL::Runtime::FileStoreManager& fileMgr); FontCache(const FontCache& other) = delete; FontCache& operator=(const FontCache& other) = delete; FontTag prepCustomFont(boo::IGraphicsDataFactory* gf, const std::string& name, FT_Face face, FCharFilter filter=AllCharFilter, bool subpixel=false, float points=10.0, uint32_t dpi=72); FontTag prepMainFont(boo::IGraphicsDataFactory* gf, FCharFilter filter=AllCharFilter, bool subpixel=false, float points=10.0, uint32_t dpi=72) {return prepCustomFont(gf, "droidsans-permissive", m_regFace, filter, subpixel, points, dpi);} FontTag prepMonoFont(boo::IGraphicsDataFactory* gf, FCharFilter filter=AllCharFilter, bool subpixel=false, float points=10.0, uint32_t dpi=72) {return prepCustomFont(gf, "bmonofont", m_monoFace, filter, subpixel, points, dpi);} FontTag prepCurvesFont(boo::IGraphicsDataFactory* gf, FCharFilter filter=AllCharFilter, bool subpixel=false, float points=10.0, uint32_t dpi=72) {return prepCustomFont(gf, "spectercurves", m_curvesFace, filter, subpixel, points, dpi);} void closeBuiltinFonts() {m_regFace.close(); m_monoFace.close(); m_curvesFace.close();} const FontAtlas& lookupAtlas(FontTag tag) const; }; } #endif // SPECTER_FONTCACHE_HPP