From a8b6e9f23cc7f8c0835743d462fd27a20af6518e Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 25 Nov 2018 01:32:48 +0100 Subject: Codechange: Refactor the Freetype font cache into a generic part that does not depend on Freetype and into one that does. This makes it easier to add other TrueType font rendering engines. --- src/fontcache.cpp | 397 +++++++++++++++++++++++++------------------- src/settings.cpp | 4 + src/table/misc_settings.ini | 24 +-- 3 files changed, 244 insertions(+), 181 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index ac09a3e93..50600de40 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -199,16 +199,16 @@ bool SpriteFontCache::GetDrawGlyphShadow() /* static */ FontCache *FontCache::caches[FS_END] = { new SpriteFontCache(FS_NORMAL), new SpriteFontCache(FS_SMALL), new SpriteFontCache(FS_LARGE), new SpriteFontCache(FS_MONO) }; -#ifdef WITH_FREETYPE -#include -#include FT_FREETYPE_H -#include FT_GLYPH_H -#include FT_TRUETYPE_TABLES_H +#if defined(WITH_FREETYPE) -/** Font cache for fonts that are based on a freetype font. */ -class FreeTypeFontCache : public FontCache { -private: - FT_Face face; ///< The font face associated with this font. +FreeTypeSettings _freetype; + +static const byte FACE_COLOUR = 1; +static const byte SHADOW_COLOUR = 2; + +/** Font cache for fonts that are based on a TrueType font. */ +class TrueTypeFontCache : public FontCache { +protected: int req_size; ///< Requested font size. int used_size; ///< Used font size. @@ -239,31 +239,233 @@ private: GlyphEntry *GetGlyphPtr(GlyphID key); void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false); - void SetFontSize(FontSize fs, FT_Face face, int pixels); + + virtual const void *InternalGetFontTable(uint32 tag, size_t &length) = 0; + virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa) = 0; public: - FreeTypeFontCache(FontSize fs, FT_Face face, int pixels); - ~FreeTypeFontCache(); + TrueTypeFontCache(FontSize fs, int pixels); + virtual ~TrueTypeFontCache(); virtual int GetFontSize() const { return this->used_size; } virtual SpriteID GetUnicodeGlyph(WChar key) { return this->parent->GetUnicodeGlyph(key); } virtual void SetUnicodeGlyph(WChar key, SpriteID sprite) { this->parent->SetUnicodeGlyph(key, sprite); } virtual void InitializeUnicodeGlyphMap() { this->parent->InitializeUnicodeGlyphMap(); } - virtual void ClearFontCache(); virtual const Sprite *GetGlyph(GlyphID key); + virtual const void *GetFontTable(uint32 tag, size_t &length); + virtual void ClearFontCache(); virtual uint GetGlyphWidth(GlyphID key); virtual bool GetDrawGlyphShadow(); + virtual bool IsBuiltInFont() { return false; } +}; + +/** + * Create a new TrueTypeFontCache. + * @param fs The font size that is going to be cached. + * @param pixels The number of pixels this font should be high. + */ +TrueTypeFontCache::TrueTypeFontCache(FontSize fs, int pixels) : FontCache(fs), req_size(pixels), glyph_to_sprite(nullptr) +{ +} + +/** + * Free everything that was allocated for this font cache. + */ +TrueTypeFontCache::~TrueTypeFontCache() +{ + this->ClearFontCache(); + + for (auto &iter : this->font_tables) { + free(iter.second.second); + } +} + +/** + * Reset cached glyphs. + */ +void TrueTypeFontCache::ClearFontCache() +{ + if (this->glyph_to_sprite == nullptr) return; + + for (int i = 0; i < 256; i++) { + if (this->glyph_to_sprite[i] == nullptr) continue; + + for (int j = 0; j < 256; j++) { + if (this->glyph_to_sprite[i][j].duplicate) continue; + free(this->glyph_to_sprite[i][j].sprite); + } + + free(this->glyph_to_sprite[i]); + } + + free(this->glyph_to_sprite); + this->glyph_to_sprite = nullptr; + + Layouter::ResetFontCache(this->fs); +} + + +TrueTypeFontCache::GlyphEntry *TrueTypeFontCache::GetGlyphPtr(GlyphID key) +{ + if (this->glyph_to_sprite == nullptr) return nullptr; + if (this->glyph_to_sprite[GB(key, 8, 8)] == nullptr) return nullptr; + return &this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)]; +} + +void TrueTypeFontCache::SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate) +{ + if (this->glyph_to_sprite == nullptr) { + DEBUG(freetype, 3, "Allocating root glyph cache for size %u", this->fs); + this->glyph_to_sprite = CallocT(256); + } + + if (this->glyph_to_sprite[GB(key, 8, 8)] == nullptr) { + DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), this->fs); + this->glyph_to_sprite[GB(key, 8, 8)] = CallocT(256); + } + + DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, this->fs); + this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite; + this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].width = glyph->width; + this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].duplicate = duplicate; +} + +static void *AllocateFont(size_t size) +{ + return MallocT(size); +} + + +/* Check if a glyph should be rendered with anti-aliasing. */ +static bool GetFontAAState(FontSize size) +{ + /* AA is only supported for 32 bpp */ + if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 32) return false; + + switch (size) { + default: NOT_REACHED(); + case FS_NORMAL: return _freetype.medium.aa; + case FS_SMALL: return _freetype.small.aa; + case FS_LARGE: return _freetype.large.aa; + case FS_MONO: return _freetype.mono.aa; + } +} + +bool TrueTypeFontCache::GetDrawGlyphShadow() +{ + return this->fs == FS_NORMAL && GetFontAAState(FS_NORMAL); +} + +uint TrueTypeFontCache::GetGlyphWidth(GlyphID key) +{ + if ((key & SPRITE_GLYPH) != 0) return this->parent->GetGlyphWidth(key); + + GlyphEntry *glyph = this->GetGlyphPtr(key); + if (glyph == nullptr || glyph->sprite == nullptr) { + this->GetGlyph(key); + glyph = this->GetGlyphPtr(key); + } + + return glyph->width; +} + +const Sprite *TrueTypeFontCache::GetGlyph(GlyphID key) +{ + if ((key & SPRITE_GLYPH) != 0) return this->parent->GetGlyph(key); + + /* Check for the glyph in our cache */ + GlyphEntry *glyph = this->GetGlyphPtr(key); + if (glyph != nullptr && glyph->sprite != nullptr) return glyph->sprite; + + if (key == 0) { + GlyphID question_glyph = this->MapCharToGlyph('?'); + if (question_glyph == 0) { + /* The font misses the '?' character. Use built-in sprite. + * Note: We cannot use the baseset as this also has to work in the bootstrap GUI. */ +#define CPSET { 0, 0, 0, 0, 1 } +#define CP___ { 0, 0, 0, 0, 0 } + static SpriteLoader::CommonPixel builtin_questionmark_data[10 * 8] = { + CP___, CP___, CPSET, CPSET, CPSET, CPSET, CP___, CP___, + CP___, CPSET, CPSET, CP___, CP___, CPSET, CPSET, CP___, + CP___, CP___, CP___, CP___, CP___, CPSET, CPSET, CP___, + CP___, CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, + CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, + CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, + CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, + CP___, CP___, CP___, CP___, CP___, CP___, CP___, CP___, + CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, + CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, + }; +#undef CPSET +#undef CP___ + static const SpriteLoader::Sprite builtin_questionmark = { + 10, // height + 8, // width + 0, // x_offs + 0, // y_offs + ST_FONT, + builtin_questionmark_data + }; + + Sprite *spr = BlitterFactory::GetCurrentBlitter()->Encode(&builtin_questionmark, AllocateFont); + assert(spr != nullptr); + GlyphEntry new_glyph; + new_glyph.sprite = spr; + new_glyph.width = spr->width + (this->fs != FS_NORMAL); + this->SetGlyphPtr(key, &new_glyph, false); + return new_glyph.sprite; + } else { + /* Use '?' for missing characters. */ + this->GetGlyph(question_glyph); + glyph = this->GetGlyphPtr(question_glyph); + this->SetGlyphPtr(key, glyph, true); + return glyph->sprite; + } + } + + return this->InternalGetGlyph(key, GetFontAAState(this->fs)); +} + +const void *TrueTypeFontCache::GetFontTable(uint32 tag, size_t &length) +{ + const FontTable::iterator iter = this->font_tables.Find(tag); + if (iter != this->font_tables.data() + this->font_tables.size()) { + length = iter->second.first; + return iter->second.second; + } + + const void *result = this->InternalGetFontTable(tag, length); + + this->font_tables.Insert(tag, SmallPair(length, result)); + return result; +} + + +#ifdef WITH_FREETYPE +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_TRUETYPE_TABLES_H + +/** Font cache for fonts that are based on a freetype font. */ +class FreeTypeFontCache : public TrueTypeFontCache { +private: + FT_Face face; ///< The font face associated with this font. + + void SetFontSize(FontSize fs, FT_Face face, int pixels); + virtual const void *InternalGetFontTable(uint32 tag, size_t &length); + virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa); + +public: + FreeTypeFontCache(FontSize fs, FT_Face face, int pixels); + ~FreeTypeFontCache(); + virtual void ClearFontCache(); virtual GlyphID MapCharToGlyph(WChar key); - virtual const void *GetFontTable(uint32 tag, size_t &length); virtual const char *GetFontName() { return face->family_name; } virtual bool IsBuiltInFont() { return false; } }; FT_Library _library = nullptr; -FreeTypeSettings _freetype; - -static const byte FACE_COLOUR = 1; -static const byte SHADOW_COLOUR = 2; /** * Create a new FreeTypeFontCache. @@ -271,7 +473,7 @@ static const byte SHADOW_COLOUR = 2; * @param face The font that has to be loaded. * @param pixels The number of pixels this font should be high. */ -FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), req_size(pixels), glyph_to_sprite(nullptr) +FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : TrueTypeFontCache(fs, pixels), face(face) { assert(face != nullptr); @@ -410,10 +612,6 @@ FreeTypeFontCache::~FreeTypeFontCache() FT_Done_Face(this->face); this->face = nullptr; this->ClearFontCache(); - - for (auto &iter : this->font_tables) { - free(iter.second.second); - } } /** @@ -424,130 +622,14 @@ void FreeTypeFontCache::ClearFontCache() /* Font scaling might have changed, determine font size anew if it was automatically selected. */ if (this->face != nullptr) this->SetFontSize(this->fs, this->face, this->req_size); - if (this->glyph_to_sprite == nullptr) return; - - for (int i = 0; i < 256; i++) { - if (this->glyph_to_sprite[i] == nullptr) continue; - - for (int j = 0; j < 256; j++) { - if (this->glyph_to_sprite[i][j].duplicate) continue; - free(this->glyph_to_sprite[i][j].sprite); - } - - free(this->glyph_to_sprite[i]); - } - - free(this->glyph_to_sprite); - this->glyph_to_sprite = nullptr; - - Layouter::ResetFontCache(this->fs); -} - -FreeTypeFontCache::GlyphEntry *FreeTypeFontCache::GetGlyphPtr(GlyphID key) -{ - if (this->glyph_to_sprite == nullptr) return nullptr; - if (this->glyph_to_sprite[GB(key, 8, 8)] == nullptr) return nullptr; - return &this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)]; -} - - -void FreeTypeFontCache::SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate) -{ - if (this->glyph_to_sprite == nullptr) { - DEBUG(freetype, 3, "Allocating root glyph cache for size %u", this->fs); - this->glyph_to_sprite = CallocT(256); - } - - if (this->glyph_to_sprite[GB(key, 8, 8)] == nullptr) { - DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), this->fs); - this->glyph_to_sprite[GB(key, 8, 8)] = CallocT(256); - } - - DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, this->fs); - this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite; - this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].width = glyph->width; - this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].duplicate = duplicate; -} - -static void *AllocateFont(size_t size) -{ - return MallocT(size); + this->TrueTypeFontCache::ClearFontCache(); } -/* Check if a glyph should be rendered with antialiasing */ -static bool GetFontAAState(FontSize size) +const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa) { - /* AA is only supported for 32 bpp */ - if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 32) return false; - - switch (size) { - default: NOT_REACHED(); - case FS_NORMAL: return _freetype.medium.aa; - case FS_SMALL: return _freetype.small.aa; - case FS_LARGE: return _freetype.large.aa; - case FS_MONO: return _freetype.mono.aa; - } -} - - -const Sprite *FreeTypeFontCache::GetGlyph(GlyphID key) -{ - if ((key & SPRITE_GLYPH) != 0) return this->parent->GetGlyph(key); - - /* Check for the glyph in our cache */ - GlyphEntry *glyph = this->GetGlyphPtr(key); - if (glyph != nullptr && glyph->sprite != nullptr) return glyph->sprite; - FT_GlyphSlot slot = this->face->glyph; - bool aa = GetFontAAState(this->fs); - - GlyphEntry new_glyph; - if (key == 0) { - GlyphID question_glyph = this->MapCharToGlyph('?'); - if (question_glyph == 0) { - /* The font misses the '?' character. Use built-in sprite. - * Note: We cannot use the baseset as this also has to work in the bootstrap GUI. */ -#define CPSET { 0, 0, 0, 0, 1 } -#define CP___ { 0, 0, 0, 0, 0 } - static SpriteLoader::CommonPixel builtin_questionmark_data[10 * 8] = { - CP___, CP___, CPSET, CPSET, CPSET, CPSET, CP___, CP___, - CP___, CPSET, CPSET, CP___, CP___, CPSET, CPSET, CP___, - CP___, CP___, CP___, CP___, CP___, CPSET, CPSET, CP___, - CP___, CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, - CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, - CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, - CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, - CP___, CP___, CP___, CP___, CP___, CP___, CP___, CP___, - CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, - CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___, - }; -#undef CPSET -#undef CP___ - static const SpriteLoader::Sprite builtin_questionmark = { - 10, // height - 8, // width - 0, // x_offs - 0, // y_offs - ST_FONT, - builtin_questionmark_data - }; - - Sprite *spr = BlitterFactory::GetCurrentBlitter()->Encode(&builtin_questionmark, AllocateFont); - assert(spr != nullptr); - new_glyph.sprite = spr; - new_glyph.width = spr->width + (this->fs != FS_NORMAL); - this->SetGlyphPtr(key, &new_glyph, false); - return new_glyph.sprite; - } else { - /* Use '?' for missing characters. */ - this->GetGlyph(question_glyph); - glyph = this->GetGlyphPtr(question_glyph); - this->SetGlyphPtr(key, glyph, true); - return glyph->sprite; - } - } FT_Load_Glyph(this->face, key, aa ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO); FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); @@ -591,6 +673,7 @@ const Sprite *FreeTypeFontCache::GetGlyph(GlyphID key) } } + GlyphEntry new_glyph; new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, AllocateFont); new_glyph.width = slot->advance.x >> 6; @@ -600,25 +683,6 @@ const Sprite *FreeTypeFontCache::GetGlyph(GlyphID key) } -bool FreeTypeFontCache::GetDrawGlyphShadow() -{ - return this->fs == FS_NORMAL && GetFontAAState(FS_NORMAL); -} - - -uint FreeTypeFontCache::GetGlyphWidth(GlyphID key) -{ - if ((key & SPRITE_GLYPH) != 0) return this->parent->GetGlyphWidth(key); - - GlyphEntry *glyph = this->GetGlyphPtr(key); - if (glyph == nullptr || glyph->sprite == nullptr) { - this->GetGlyph(key); - glyph = this->GetGlyphPtr(key); - } - - return glyph->width; -} - GlyphID FreeTypeFontCache::MapCharToGlyph(WChar key) { assert(IsPrintable(key)); @@ -630,14 +694,8 @@ GlyphID FreeTypeFontCache::MapCharToGlyph(WChar key) return FT_Get_Char_Index(this->face, key); } -const void *FreeTypeFontCache::GetFontTable(uint32 tag, size_t &length) +const void *FreeTypeFontCache::InternalGetFontTable(uint32 tag, size_t &length) { - const FontTable::iterator iter = this->font_tables.Find(tag); - if (iter != this->font_tables.data() + this->font_tables.size()) { - length = iter->second.first; - return iter->second.second; - } - FT_ULong len = 0; FT_Byte *result = nullptr; @@ -647,14 +705,15 @@ const void *FreeTypeFontCache::GetFontTable(uint32 tag, size_t &length) result = MallocT(len); FT_Load_Sfnt_Table(this->face, tag, 0, result, &len); } - length = len; - this->font_tables.Insert(tag, SmallPair(length, result)); + length = len; return result; } #endif /* WITH_FREETYPE */ +#endif /* defined(WITH_FREETYPE) */ + /** * (Re)initialize the freetype related things, i.e. load the non-sprite fonts. * @param monospace Whether to initialise the monospace or regular fonts. diff --git a/src/settings.cpp b/src/settings.cpp index 5c0dc4a98..74a8bd8d8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -68,6 +68,10 @@ #include "void_map.h" #include "station_base.h" +#if defined(WITH_FREETYPE) +#define HAS_TRUETYPE_FONT +#endif + #include "table/strings.h" #include "table/settings.h" diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini index a1159aee0..f920f515d 100644 --- a/src/table/misc_settings.ini +++ b/src/table/misc_settings.ini @@ -144,35 +144,35 @@ var = _rightclick_emulate def = false [SDTG_STR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""small_font"" type = SLE_STRB var = _freetype.small.font def = nullptr [SDTG_STR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""medium_font"" type = SLE_STRB var = _freetype.medium.font def = nullptr [SDTG_STR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""large_font"" type = SLE_STRB var = _freetype.large.font def = nullptr [SDTG_STR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""mono_font"" type = SLE_STRB var = _freetype.mono.font def = nullptr [SDTG_VAR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""small_size"" type = SLE_UINT var = _freetype.small.size @@ -181,7 +181,7 @@ min = 0 max = 72 [SDTG_VAR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""medium_size"" type = SLE_UINT var = _freetype.medium.size @@ -190,7 +190,7 @@ min = 0 max = 72 [SDTG_VAR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""large_size"" type = SLE_UINT var = _freetype.large.size @@ -199,7 +199,7 @@ min = 0 max = 72 [SDTG_VAR] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""mono_size"" type = SLE_UINT var = _freetype.mono.size @@ -208,25 +208,25 @@ min = 0 max = 72 [SDTG_BOOL] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""small_aa"" var = _freetype.small.aa def = false [SDTG_BOOL] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""medium_aa"" var = _freetype.medium.aa def = false [SDTG_BOOL] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""large_aa"" var = _freetype.large.aa def = false [SDTG_BOOL] -ifdef = WITH_FREETYPE +ifdef = HAS_TRUETYPE_FONT name = ""mono_aa"" var = _freetype.mono.aa def = false -- cgit v1.2.3-70-g09d2