From d3dd99c38411a0f10e9b572d7eb71e040cd2dfe3 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Tue, 21 Dec 2021 00:17:42 +0100 Subject: [PATCH 1/2] Refactor fonts reloading * Remove hardcoded default font name. This means the `fonts/fonts.ini` file is now mandatory and must contain definition of all 9 font types. Old mods relying on an incomplete `fonts.ini` file might break. A separate PR creating the required `fonts/fonts.ini` file should be merged before this pull request. * Simplify `CFontLoader`. * Return `std::optional` instead of returning a default. * Remove the now unnecessary `std::map`s. * Remove the now unnecessary `GetFontType` method. * Improve Gfx::FontType. * Provide `ToString` function for the enum, which is now used for logs and by `CFontLoader`. * Provide `ToBoldFontType` and `ToItalicFontType` functions. * Replace hex literals with binary literals for readability. * Move font caching related code to a new private class `FontsCache`. * Add neccessary changes because of changes made in `CFontLoader`. * Add minor code improvements like renames and formatting. * Split the code into smaller functions for readability. * Simplify the `CText` class. * Apply the rule of 5 to the `CachedFont` structure. --- src/common/font_loader.cpp | 43 +---- src/common/font_loader.h | 18 +-- src/graphics/engine/text.cpp | 306 +++++++++++++++++++++++++---------- src/graphics/engine/text.h | 27 ++-- 4 files changed, 246 insertions(+), 148 deletions(-) diff --git a/src/common/font_loader.cpp b/src/common/font_loader.cpp index 068ec746..3d6f0f86 100644 --- a/src/common/font_loader.cpp +++ b/src/common/font_loader.cpp @@ -33,36 +33,12 @@ #include #include #include +#include #include #include namespace bp = boost::property_tree; -const std::map DEFAULT_FONT = -{ - { Gfx::FONT_COMMON, "dvu_sans.ttf" }, - { Gfx::FONT_COMMON_BOLD, "dvu_sans_bold.ttf" }, - { Gfx::FONT_COMMON_ITALIC, "dvu_sans_italic.ttf" }, - { Gfx::FONT_STUDIO, "dvu_sans_mono.ttf" }, - { Gfx::FONT_STUDIO_BOLD, "dvu_sans_mono_bold.ttf" }, - { Gfx::FONT_STUDIO_ITALIC, "dvu_sans_mono.ttf" }, //placeholder for future use, DejaVu Sans Mono doesn't have italic variant - { Gfx::FONT_SATCOM, "dvu_sans.ttf" }, - { Gfx::FONT_SATCOM_BOLD, "dvu_sans_bold.ttf" }, - { Gfx::FONT_SATCOM_ITALIC, "dvu_sans_italic.ttf" }, -}; - -const std::map FONT_TYPE = -{ - { Gfx::FONT_COMMON, "FontCommon" }, - { Gfx::FONT_COMMON_BOLD, "FontCommonBold" }, - { Gfx::FONT_COMMON_ITALIC, "FontCommonItalic" }, - { Gfx::FONT_STUDIO, "FontStudio" }, - { Gfx::FONT_STUDIO_BOLD, "FontStudioBold" }, - { Gfx::FONT_STUDIO_ITALIC, "FontStudioItalic" }, - { Gfx::FONT_SATCOM, "FontSatCom" }, - { Gfx::FONT_SATCOM_BOLD, "FontSatComBold" }, - { Gfx::FONT_SATCOM_ITALIC, "FontSatComItalic" }, -}; CFontLoader::CFontLoader() { @@ -99,17 +75,10 @@ bool CFontLoader::Init() return true; } -std::string CFontLoader::GetFont(Gfx::FontType type) +std::optional CFontLoader::GetFont(Gfx::FontType type) const { - return std::string("/fonts/") + m_propertyTree.get(GetFontType(type), GetDefaultFont(type)); -} - -std::string CFontLoader::GetDefaultFont(Gfx::FontType type) const -{ - return DEFAULT_FONT.at(type); -} - -std::string CFontLoader::GetFontType(Gfx::FontType type) const -{ - return FONT_TYPE.at(type); + auto font = m_propertyTree.get_optional(ToString(type)); + if (font) + return std::string("/fonts/") + *font; + return std::nullopt; } diff --git a/src/common/font_loader.h b/src/common/font_loader.h index 8f853a90..d2986e0e 100644 --- a/src/common/font_loader.h +++ b/src/common/font_loader.h @@ -50,22 +50,10 @@ public: */ bool Init(); - /** Reads given font from file - * \return return path to font file + /** Reads given font path from file + * \return return path to font file if font type is configured */ - std::string GetFont(Gfx::FontType type); - - /** Const type method to read filenames of fonts from defaultFont map - * used as a fallback if it wasn't possible to read font from fonts.ini - * \return return filename of default path - */ - std::string GetDefaultFont(Gfx::FontType type) const; - - /** Const type method converting Gfx::FontType to string - * \return return id of font used in fonts.ini file - */ - - std::string GetFontType(Gfx::FontType type) const; + std::optional GetFont(Gfx::FontType type) const; private: boost::property_tree::ptree m_propertyTree; diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index 315a3fbb..0f3d5c6f 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -82,6 +82,24 @@ struct CachedFont font = TTF_OpenFontRW(this->fontFile->GetHandler(), 0, pointSize); } + CachedFont(CachedFont&& other) noexcept + : fontFile{std::move(other.fontFile)}, + font{std::exchange(other.font, nullptr)}, + cache{std::move(other.cache)} + { + } + + CachedFont& operator=(CachedFont&& other) noexcept + { + fontFile = std::move(other.fontFile); + std::swap(font, other.font); + cache = std::move(other.cache); + return *this; + } + + CachedFont(const CachedFont& other) = delete; + CachedFont& operator=(const CachedFont& other) = delete; + ~CachedFont() { if (font != nullptr) @@ -89,11 +107,38 @@ struct CachedFont } }; +std::string ToString(FontType type) +{ + switch (type) + { + case FontType::FONT_COMMON: return "FontCommon"; + case FontType::FONT_COMMON_BOLD: return "FontCommonBold"; + case FontType::FONT_COMMON_ITALIC: return "FontCommonItalic"; + case FontType::FONT_STUDIO: return "FontStudio"; + case FontType::FONT_STUDIO_BOLD: return "FontStudioBold"; + case FontType::FONT_STUDIO_ITALIC: return "FontStudioItalic"; + case FontType::FONT_SATCOM: return "FontSatCom"; + case FontType::FONT_SATCOM_BOLD: return "FontSatComBold"; + case FontType::FONT_SATCOM_ITALIC: return "FontSatComItalic"; + case FontType::FONT_BUTTON: return "FontButton"; + default: throw std::invalid_argument("Unsupported value for Gfx::FontType -> std::string conversion: " + std::to_string(type)); + } +} namespace { const Math::IntPoint REFERENCE_SIZE(800, 600); const Math::IntPoint FONT_TEXTURE_SIZE(256, 256); + +Gfx::FontType ToBoldFontType(Gfx::FontType type) +{ + return static_cast(type | FONT_BOLD); +} + +Gfx::FontType ToItalicFontType(Gfx::FontType type) +{ + return static_cast(type | FONT_ITALIC); +} } // anonymous namespace /// The QuadBatch is responsible for collecting as many quad (aka rectangle) draws as possible and @@ -167,6 +212,166 @@ private: EngineRenderState m_renderState{}; }; +class FontsCache +{ +public: + using Fonts = std::map>; + + FontsCache() + { + ClearLastCachedFont(); + } + + bool Reload(const CFontLoader& fontLoader, int pointSize) + { + Flush(); + if (!PrepareCache(fontLoader)) { Flush(); return false; } + if (!LoadDefaultFonts(fontLoader, pointSize)) { Flush(); return false; } + return true; + } + + CachedFont* GetOrOpenFont(FontType type, int pointSize) + { + if (IsLastCachedFont(type, pointSize)) + return m_lastCachedFont; + + auto multisizeFontIt = m_fonts.find(type); + if (multisizeFontIt == m_fonts.end()) + { + m_error = std::string("Font type not found in cache: ") + ToString(type); + return nullptr; + } + MultisizeFont* multisizeFont = multisizeFontIt->second.get(); + + auto cachedFontIt = multisizeFont->fonts.find(pointSize); + if (cachedFontIt != multisizeFont->fonts.end()) + { + auto* cachedFont = cachedFontIt->second.get(); + SaveLastCachedFont(cachedFont, type, pointSize); + return m_lastCachedFont; + } + + auto newFont = LoadFont(multisizeFont, pointSize); + if (!newFont) return nullptr; + + SaveLastCachedFont(newFont.get(), type, pointSize); + multisizeFont->fonts[pointSize] = std::move(newFont); + return m_lastCachedFont; + } + + void Flush() + { + Clear(); + ClearLastCachedFont(); + } + + ~FontsCache() + { + Flush(); + } + + std::string GetError() const + { + return m_error; + } + +private: + bool PrepareCache(const CFontLoader& fontLoader) + { + for (auto type : {FONT_COMMON, FONT_STUDIO, FONT_SATCOM}) + { + if (!PrepareCacheForFontType(type, fontLoader)) return false; + if (!PrepareCacheForFontType(ToBoldFontType(type), fontLoader)) return false; + if (!PrepareCacheForFontType(ToItalicFontType(type), fontLoader)) return false; + } + return true; + } + + bool PrepareCacheForFontType(Gfx::FontType type, const CFontLoader& fontLoader) + { + if (auto font = fontLoader.GetFont(type)) + { + m_fonts[type] = MakeUnique(std::move(*font)); + return true; + } + m_error = "Error on loading fonts: font type " + ToString(type) + " is not configured"; + return false; + } + + bool LoadDefaultFonts(const CFontLoader& fontLoader, int pointSize) + { + for (auto& font : m_fonts) + { + auto type = font.first; + auto* cachedFont = GetOrOpenFont(type, pointSize); + if (cachedFont == nullptr || cachedFont->font == nullptr) + return false; + } + return true; + } + + std::unique_ptr LoadFont(MultisizeFont* multisizeFont, int pointSize) + { + auto file = CResourceManager::GetSDLMemoryHandler(multisizeFont->fileName); + if (!file->IsOpen()) + { + m_error = "Unable to open file '" + multisizeFont->fileName + "' (font size = " + std::to_string(pointSize) + ")"; + return nullptr; + } + GetLogger()->Debug("Loaded font file %s (font size = %d)\n", multisizeFont->fileName.c_str(), pointSize); + auto newFont = MakeUnique(std::move(file), pointSize); + if (newFont->font == nullptr) + { + m_error = std::string("TTF_OpenFont error ") + std::string(TTF_GetError()); + return nullptr; + } + return newFont; + } + + void SaveLastCachedFont(CachedFont* font, FontType type, int pointSize) + { + m_lastCachedFont = font; + m_lastFontType = type; + m_lastFontSize = pointSize; + } + + bool IsLastCachedFont(FontType font, int pointSize) + { + return + m_lastCachedFont != nullptr && + m_lastFontType == font && + m_lastFontSize == pointSize; + } + + void Clear() + { + for (auto& [fontType, multisizeFont] : m_fonts) + { + for (auto& cachedFont : multisizeFont->fonts) + { + cachedFont.second->cache.clear(); + } + } + m_fonts.clear(); + } + + void ClearLastCachedFont() + { + m_lastCachedFont = nullptr; + m_lastFontType = FONT_COMMON; + m_lastFontSize = 0; + } + +private: + Fonts m_fonts; + + CachedFont* m_lastCachedFont; + FontType m_lastFontType; + int m_lastFontSize; + + std::string m_error; +}; + CText::CText(CEngine* engine) { @@ -176,9 +381,7 @@ CText::CText(CEngine* engine) m_defaultSize = 12.0f; m_tabSize = 4; - m_lastFontType = FONT_COMMON; - m_lastFontSize = 0; - m_lastCachedFont = nullptr; + m_fontsCache = std::make_unique(); m_quadBatch = MakeUnique(*engine); } @@ -210,42 +413,24 @@ bool CText::ReloadFonts() CFontLoader fontLoader; if (!fontLoader.Init()) { - GetLogger()->Debug("Error on parsing fonts config file: failed to open file\n"); + m_error = "Error on parsing fonts config file: failed to open file"; + return false; } - // Backup previous fonts - auto fonts = std::move(m_fonts); - m_fonts.clear(); - - for (auto type : {FONT_COMMON, FONT_STUDIO, FONT_SATCOM}) + auto newCache = std::make_unique(); + if (!newCache->Reload(fontLoader, GetFontPointSize(m_defaultSize))) { - m_fonts[static_cast(type)] = MakeUnique(fontLoader.GetFont(type)); - m_fonts[static_cast(type|FONT_BOLD)] = MakeUnique(fontLoader.GetFont(static_cast(type|FONT_BOLD))); - m_fonts[static_cast(type|FONT_ITALIC)] = MakeUnique(fontLoader.GetFont(static_cast(type|FONT_ITALIC))); - } - - for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it) - { - FontType type = (*it).first; - CachedFont* cf = GetOrOpenFont(type, m_defaultSize); - if (cf == nullptr || cf->font == nullptr) - { - m_fonts = std::move(fonts); - return false; - } + m_error = newCache->GetError(); + return false; } + m_fontsCache = std::move(newCache); return true; } void CText::Destroy() { - m_fonts.clear(); - - m_lastCachedFont = nullptr; - m_lastFontType = FONT_COMMON; - m_lastFontSize = 0; - + m_fontsCache->Flush(); TTF_Quit(); } @@ -269,17 +454,7 @@ void CText::FlushCache() } m_fontTextures.clear(); - for (auto& multisizeFont : m_fonts) - { - for (auto& cachedFont : multisizeFont.second->fonts) - { - cachedFont.second->cache.clear(); - } - } - - m_lastCachedFont = nullptr; - m_lastFontType = FONT_COMMON; - m_lastFontSize = 0; + m_fontsCache->Flush(); } int CText::GetTabSize() @@ -1099,54 +1274,21 @@ void CText::DrawCharAndAdjustPos(UTF8Char ch, FontType font, float size, Math::I } } -CachedFont* CText::GetOrOpenFont(FontType font, float size) +int CText::GetFontPointSize(float size) const { Math::IntPoint windowSize = m_engine->GetWindowSize(); - int pointSize = static_cast(size * (windowSize.Length() / REFERENCE_SIZE.Length())); + return static_cast(size * (windowSize.Length() / REFERENCE_SIZE.Length())); +} - if (m_lastCachedFont != nullptr && - m_lastFontType == font && - m_lastFontSize == pointSize) +CachedFont* CText::GetOrOpenFont(FontType type, float size) +{ + auto* cachedFont = m_fontsCache->GetOrOpenFont(type, GetFontPointSize(size)); + if (!cachedFont) { - return m_lastCachedFont; - } - - auto it = m_fonts.find(font); - if (it == m_fonts.end()) - { - m_error = std::string("Invalid font type ") + StrUtils::ToString(static_cast(font)); + m_error = m_fontsCache->GetError(); return nullptr; } - - MultisizeFont* mf = it->second.get(); - - auto jt = mf->fonts.find(pointSize); - if (jt != mf->fonts.end()) - { - m_lastCachedFont = jt->second.get(); - m_lastFontType = font; - m_lastFontSize = pointSize; - return m_lastCachedFont; - } - - auto file = CResourceManager::GetSDLMemoryHandler(mf->fileName); - if (!file->IsOpen()) - { - m_error = std::string("Unable to open file '") + mf->fileName + "' (font size = " + StrUtils::ToString(size) + ")"; - return nullptr; - } - GetLogger()->Debug("Loaded font file %s (font size = %.1f)\n", mf->fileName.c_str(), size); - - auto newFont = MakeUnique(std::move(file), pointSize); - if (newFont->font == nullptr) - { - m_error = std::string("TTF_OpenFont error ") + std::string(TTF_GetError()); - return nullptr; - } - - m_lastCachedFont = newFont.get(); - mf->fonts[pointSize] = std::move(newFont); - return m_lastCachedFont; + return cachedFont; } CharTexture CText::GetCharTexture(UTF8Char ch, FontType font, float size) diff --git a/src/graphics/engine/text.h b/src/graphics/engine/text.h index b6f2122b..1c4d5465 100644 --- a/src/graphics/engine/text.h +++ b/src/graphics/engine/text.h @@ -34,7 +34,6 @@ #include #include - // Graphics module namespace namespace Gfx { @@ -69,38 +68,40 @@ typedef short FontMetaChar; * * Bitmask in lower 4 bits (mask 0x00f) */ -enum FontType +enum FontType : unsigned char { //! Flag for bold font subtype - FONT_BOLD = 0x04, + FONT_BOLD = 0b0000'01'00, //! Flag for italic font subtype - FONT_ITALIC = 0x08, + FONT_ITALIC = 0b0000'10'00, //! Default colobot font used for interface - FONT_COMMON = 0x00, + FONT_COMMON = 0b0000'00'00, //! Alias for bold colobot font FONT_COMMON_BOLD = FONT_COMMON | FONT_BOLD, //! Alias for italic colobot font FONT_COMMON_ITALIC = FONT_COMMON | FONT_ITALIC, //! Studio font used mainly in code editor - FONT_STUDIO = 0x01, + FONT_STUDIO = 0b0000'00'01, //! Alias for bold studio font FONT_STUDIO_BOLD = FONT_STUDIO | FONT_BOLD, //! Alias for italic studio font (at this point not used anywhere) FONT_STUDIO_ITALIC = FONT_STUDIO | FONT_ITALIC, //! SatCom font used for interface (currently bold and italic wariants aren't used anywhere) - FONT_SATCOM = 0x02, + FONT_SATCOM = 0b0000'00'10, //! Alias for bold satcom font FONT_SATCOM_BOLD = FONT_SATCOM | FONT_BOLD, //! Alias for italic satcom font FONT_SATCOM_ITALIC = FONT_SATCOM | FONT_ITALIC, //! Pseudo-font loaded from textures for buttons, icons, etc. - FONT_BUTTON = 0x03, + FONT_BUTTON = 0b0000'00'11, }; +std::string ToString(FontType); + /** * \enum FontTitle * \brief Size of font title @@ -204,6 +205,7 @@ struct CharTexture }; // Definition is private - in text.cpp +class FontsCache; struct CachedFont; struct MultisizeFont; struct FontTexture; @@ -323,7 +325,8 @@ public: Math::IntPoint GetFontTextureSize(); protected: - CachedFont* GetOrOpenFont(FontType font, float size); + int GetFontPointSize(float size) const; + CachedFont* GetOrOpenFont(FontType type, float size); CharTexture CreateCharTexture(UTF8Char ch, CachedFont* font); FontTexture* GetOrCreateFontTexture(Math::IntPoint tileSize); FontTexture CreateFontTexture(Math::IntPoint tileSize); @@ -349,13 +352,9 @@ protected: float m_defaultSize; int m_tabSize; - std::map> m_fonts; + std::unique_ptr m_fontsCache; std::vector m_fontTextures; - FontType m_lastFontType; - int m_lastFontSize; - CachedFont* m_lastCachedFont; - class CQuadBatch; std::unique_ptr m_quadBatch; }; From 4ecfb47f0769253415296cdfc63f0a3f54b62dfd Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Tue, 21 Dec 2021 18:01:42 +0100 Subject: [PATCH 2/2] Fix MSVC errors --- src/common/font_loader.cpp | 1 - src/common/font_loader.h | 1 + src/graphics/engine/text.cpp | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/font_loader.cpp b/src/common/font_loader.cpp index 3d6f0f86..9cb3eaeb 100644 --- a/src/common/font_loader.cpp +++ b/src/common/font_loader.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/src/common/font_loader.h b/src/common/font_loader.h index d2986e0e..644ed98e 100644 --- a/src/common/font_loader.h +++ b/src/common/font_loader.h @@ -31,6 +31,7 @@ #include #include +#include /** * \class CFontLoader diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index 0f3d5c6f..ed063129 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -291,7 +291,7 @@ private: { if (auto font = fontLoader.GetFont(type)) { - m_fonts[type] = MakeUnique(std::move(*font)); + m_fonts[type] = std::make_unique(std::move(*font)); return true; } m_error = "Error on loading fonts: font type " + ToString(type) + " is not configured"; @@ -319,7 +319,7 @@ private: return nullptr; } GetLogger()->Debug("Loaded font file %s (font size = %d)\n", multisizeFont->fileName.c_str(), pointSize); - auto newFont = MakeUnique(std::move(file), pointSize); + auto newFont = std::make_unique(std::move(file), pointSize); if (newFont->font == nullptr) { m_error = std::string("TTF_OpenFont error ") + std::string(TTF_GetError()); @@ -335,7 +335,7 @@ private: m_lastFontSize = pointSize; } - bool IsLastCachedFont(FontType font, int pointSize) + bool IsLastCachedFont(FontType font, int pointSize) const { return m_lastCachedFont != nullptr &&