From ea62a449d4a3b1299048a0bf7c6d4f168ccc81ff Mon Sep 17 00:00:00 2001 From: lb_ii Date: Tue, 24 Aug 2021 02:21:42 +0300 Subject: [PATCH 01/13] Add optional team parameter to produce() function --- src/script/scriptfunc.cpp | 69 +++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index f35c7a2f..a623e2cc 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -1540,7 +1540,7 @@ bool CScriptFunctions::rDeflag(CBotVar* var, CBotVar* result, int& exception, vo return WaitForForegroundTask(script, result, exception); } -// Compilation of the instruction "produce(pos, angle, type[, scriptName[, power]])" +// Compilation of the instruction "produce(pos, angle, type[, scriptName[, power[, team]]])" // or "produce(type[, power])". CBotTypResult CScriptFunctions::cProduce(CBotVar* &var, void* user) @@ -1580,6 +1580,12 @@ CBotTypResult CScriptFunctions::cProduce(CBotVar* &var, void* user) { if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); var = var->GetNext(); + + if ( var != nullptr ) + { + if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GetNext(); + } } } } @@ -1589,7 +1595,7 @@ CBotTypResult CScriptFunctions::cProduce(CBotVar* &var, void* user) return CBotTypResult(CBotTypFloat); } -// Instruction "produce(pos, angle, type[, scriptName[, power]])" +// Instruction "produce(pos, angle, type[, scriptName[, power[, team]]])" // or "produce(type[, power])". bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, void* user) @@ -1597,34 +1603,36 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v CScript* script = static_cast(user); CObject* me = script->m_object; std::string name = ""; - Math::Vector pos; - float angle = 0.0f; - ObjectType type = OBJECT_NULL; - float power = 0.0f; + + ObjectCreateParams params; + params.angle = 0.0f; + params.type = OBJECT_NULL; + params.power = 0.0f; + params.team = 0; if ( var->GetType() <= CBotTypDouble ) { - type = static_cast(var->GetValInt()); + params.type = static_cast(var->GetValInt()); var = var->GetNext(); - pos = me->GetPosition(); + params.pos = me->GetPosition(); Math::Vector rotation = me->GetRotation() + me->GetTilt(); - angle = rotation.y; + params.angle = rotation.y; if ( var != nullptr ) - power = var->GetValFloat(); + params.power = var->GetValFloat(); else - power = -1.0f; + params.power = -1.0f; } else { - if ( !GetPoint(var, exception, pos) ) return true; + if ( !GetPoint(var, exception, params.pos) ) return true; - angle = var->GetValFloat()*Math::PI/180.0f; + params.angle = var->GetValFloat()*Math::PI/180.0f; var = var->GetNext(); - type = static_cast(var->GetValInt()); + params.type = static_cast(var->GetValInt()); var = var->GetNext(); if ( var != nullptr ) @@ -1633,28 +1641,33 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v var = var->GetNext(); if ( var != nullptr ) { - power = var->GetValFloat(); + params.power = var->GetValFloat(); + var = var->GetNext(); + if ( var != nullptr ) + { + params.team = var->GetValInt(); + } } else { - power = -1.0f; + params.power = -1.0f; } } else { - power = -1.0f; + params.power = -1.0f; } } CObject* object = nullptr; - if ( type == OBJECT_ANT || - type == OBJECT_SPIDER || - type == OBJECT_BEE || - type == OBJECT_WORM ) + if ( params.type == OBJECT_ANT || + params.type == OBJECT_SPIDER || + params.type == OBJECT_BEE || + params.type == OBJECT_WORM ) { - object = CObjectManager::GetInstancePointer()->CreateObject(pos, angle, type); - CObjectManager::GetInstancePointer()->CreateObject(pos, angle, OBJECT_EGG); + object = CObjectManager::GetInstancePointer()->CreateObject(params); + CObjectManager::GetInstancePointer()->CreateObject(params.pos, params.angle, OBJECT_EGG); if (object->Implements(ObjectInterfaceType::Programmable)) { dynamic_cast(*object).SetActivity(false); @@ -1662,21 +1675,21 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v } else { - if ((type == OBJECT_POWER || type == OBJECT_ATOMIC) && power == -1.0f) + if ((params.type == OBJECT_POWER || params.type == OBJECT_ATOMIC) && params.power == -1.0f) { - power = 1.0f; + params.power = 1.0f; } - bool exists = IsValidObjectTypeId(type) && type != OBJECT_NULL && type != OBJECT_MAX && type != OBJECT_MOBILEpr; + bool exists = IsValidObjectTypeId(params.type) && params.type != OBJECT_NULL && params.type != OBJECT_MAX && params.type != OBJECT_MOBILEpr; if (exists) { - object = CObjectManager::GetInstancePointer()->CreateObject(pos, angle, type, power); + object = CObjectManager::GetInstancePointer()->CreateObject(params); } if (object == nullptr) { result->SetValInt(1); // error return true; } - if (type == OBJECT_MOBILEdr) + if (params.type == OBJECT_MOBILEdr) { assert(object->Implements(ObjectInterfaceType::Old)); // TODO: temporary hack dynamic_cast(*object).SetManual(true); From 91a290dade807f0744004d1fe413ce650c609ef1 Mon Sep 17 00:00:00 2001 From: lb_ii Date: Wed, 25 Aug 2021 01:09:58 +0300 Subject: [PATCH 02/13] Assign team id to Alien eggs created during produce() --- src/script/scriptfunc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index a623e2cc..6bc1caea 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -1667,7 +1667,8 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v params.type == OBJECT_WORM ) { object = CObjectManager::GetInstancePointer()->CreateObject(params); - CObjectManager::GetInstancePointer()->CreateObject(params.pos, params.angle, OBJECT_EGG); + params.type = OBJECT_EGG; + CObjectManager::GetInstancePointer()->CreateObject(params); if (object->Implements(ObjectInterfaceType::Programmable)) { dynamic_cast(*object).SetActivity(false); From 9269a55e8bfbcf62db15685501d103c229053f4a Mon Sep 17 00:00:00 2001 From: Evgeny Pestov Date: Tue, 14 Dec 2021 21:52:55 +0700 Subject: [PATCH 03/13] Fixed input of long UTF-8 symbols in editor (#1423) --- src/ui/controls/edit.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index 570bd518..75bb8cbf 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -492,7 +492,10 @@ bool CEdit::EventProcess(const Event &event) if ( event.type == EVENT_TEXT_INPUT && !bControl && m_bFocus ) { auto data = event.GetData(); - Insert(data->text[0]); // TODO: insert utf-8 char + for ( char c : data->text ) + { + Insert(c); + } SendModifEvent(); return true; } From cd059bd51177d146ad44bbc404254d9ed4f43d6c Mon Sep 17 00:00:00 2001 From: Evgeny Pestov Date: Thu, 16 Dec 2021 21:36:37 +0700 Subject: [PATCH 04/13] Show questions instead of crushing in case of UTF-8 errors --- src/common/stringutils.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/common/stringutils.cpp b/src/common/stringutils.cpp index 1aa97920..3e99a7b7 100644 --- a/src/common/stringutils.cpp +++ b/src/common/stringutils.cpp @@ -182,10 +182,6 @@ int StrUtils::Utf8CharSizeAt(const std::string &str, unsigned int pos) if((c & 0xE0) == 0xC0) return 2; - // Invalid char - unexpected continuation byte - if((c & 0xC0) == 0x80) - throw std::invalid_argument("Unexpected UTF-8 continuation byte"); - return 1; } From 65da4c42c4fa01162ddc3cc4a76ec51739e3177c Mon Sep 17 00:00:00 2001 From: Evgeny Pestov Date: Fri, 17 Dec 2021 00:18:08 +0700 Subject: [PATCH 05/13] CEdit::DeleteOne expands selection to delete integer number of UTF-8 symbols --- src/ui/controls/edit.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index 75bb8cbf..d9d31605 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -2787,6 +2787,11 @@ void CEdit::DeleteOne(int dir) } if ( m_cursor1 > m_cursor2 ) Math::Swap(m_cursor1, m_cursor2); + + // Expands selection to delete integer number of UTF-8 symbols + while ( m_cursor1 > 0 && (m_text[m_cursor1] & 0xC0) == 0x80 ) m_cursor1 --; + while ( m_cursor2 < m_len && (m_text[m_cursor2] & 0xC0) == 0x80 ) m_cursor2 ++; + hole = m_cursor2-m_cursor1; end = m_len-hole; for ( i=m_cursor1 ; i Date: Fri, 17 Dec 2021 00:49:16 +0700 Subject: [PATCH 06/13] CEdit::MoveChar never moves cursor between bytes of one UTF-8 symbol --- src/ui/controls/edit.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index d9d31605..037a8d06 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -2261,7 +2261,7 @@ void CEdit::MoveChar(int move, bool bWord, bool bSelect) { int character; - if ( move == -1 ) // back? + if ( move == -1 ) // back { if ( bWord ) { @@ -2306,12 +2306,15 @@ void CEdit::MoveChar(int move, bool bWord, bool bSelect) } else { - m_cursor1 --; - if ( m_cursor1 < 0 ) m_cursor1 = 0; + if ( m_cursor1 > 0 ) + { + m_cursor1 --; + while ( m_cursor1 > 0 && (m_text[m_cursor1] & 0xC0) == 0x80 ) m_cursor1 --; + } } } - if ( move == 1 ) // advance? + if ( move == 1 ) // advance { if ( bWord ) { @@ -2356,8 +2359,11 @@ void CEdit::MoveChar(int move, bool bWord, bool bSelect) } else { - m_cursor1 ++; - if ( m_cursor1 > m_len ) m_cursor1 = m_len; + if ( m_cursor1 < m_len ) + { + m_cursor1 ++; + while ( m_cursor1 < m_len && (m_text[m_cursor1] & 0xC0) == 0x80 ) m_cursor1 ++; + } } } From d3dd99c38411a0f10e9b572d7eb71e040cd2dfe3 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Tue, 21 Dec 2021 00:17:42 +0100 Subject: [PATCH 07/13] 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 08/13] 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 && From be8d37241aca64ee2328612603edef005de9404e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Sun, 23 Jan 2022 13:16:13 +0100 Subject: [PATCH 09/13] Add workaround for numpad home/end keys etc --- src/app/app.cpp | 2 +- src/common/key.cpp | 23 ++++++++++++++++++++++- src/common/key.h | 2 +- src/ui/controls/key.cpp | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 2d253aa3..83948c66 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -1425,7 +1425,7 @@ Event CApplication::CreateVirtualEvent(const Event& sourceEvent) if ((sourceEvent.type == EVENT_KEY_DOWN) || (sourceEvent.type == EVENT_KEY_UP)) { auto sourceData = sourceEvent.GetData(); - auto virtualKey = GetVirtualKey(sourceData->key); + auto virtualKey = GetVirtualKey(sourceData->key, sourceEvent.kmodState); if (virtualKey == sourceData->key) { diff --git a/src/common/key.cpp b/src/common/key.cpp index 81fd4610..e037766c 100644 --- a/src/common/key.cpp +++ b/src/common/key.cpp @@ -19,7 +19,7 @@ #include "common/key.h" -unsigned int GetVirtualKey(unsigned int key) +unsigned int GetVirtualKey(unsigned int key, unsigned int kmodState) { if(key == KEY(LCTRL) || key == KEY(RCTRL)) return VIRTUAL_KMOD(CTRL); @@ -33,5 +33,26 @@ unsigned int GetVirtualKey(unsigned int key) if(key == KEY(KP_ENTER)) return KEY(RETURN); + // Remap keypad navigation keys as a workaround for the SDL issue: https://github.com/libsdl-org/SDL/issues/1766 + if ((kmodState & KEY_MOD(NUM)) == 0) + { + if(key == KEY(KP_7)) + return KEY(HOME); + if(key == KEY(KP_1)) + return KEY(END); + if(key == KEY(KP_9)) + return KEY(PAGEUP); + if(key == KEY(KP_3)) + return KEY(PAGEDOWN); + if(key == KEY(KP_4)) + return KEY(LEFT); + if(key == KEY(KP_6)) + return KEY(RIGHT); + if(key == KEY(KP_8)) + return KEY(UP); + if(key == KEY(KP_2)) + return KEY(DOWN); + } + return key; } diff --git a/src/common/key.h b/src/common/key.h index 57bbd457..2b972321 100644 --- a/src/common/key.h +++ b/src/common/key.h @@ -62,7 +62,7 @@ enum VirtualKmod #define VIRTUAL_KMOD(x) VIRTUAL_KMOD_ ## x //! Converts individual codes to virtual keys if needed -unsigned int GetVirtualKey(unsigned int key); +unsigned int GetVirtualKey(unsigned int key, unsigned int kmodState); // Virtual key code generated on joystick button presses // num is number of joystick button diff --git a/src/ui/controls/key.cpp b/src/ui/controls/key.cpp index 89dffbff..e5751d2f 100644 --- a/src/ui/controls/key.cpp +++ b/src/ui/controls/key.cpp @@ -75,7 +75,7 @@ bool CKey::EventProcess(const Event &event) if (event.type == EVENT_KEY_DOWN && m_catch) { m_catch = false; - unsigned int key = GetVirtualKey(event.GetData()->key); + unsigned int key = GetVirtualKey(event.GetData()->key, event.kmodState); if (TestKey(key)) // impossible ? { From 05b68a4b80c4847498e89627fc47dab00f124eca Mon Sep 17 00:00:00 2001 From: Evgeny Pestov Date: Wed, 22 Dec 2021 21:52:00 +0700 Subject: [PATCH 10/13] Refactor: Create function isUtf8ContinuationByte --- src/common/stringutils.cpp | 4 ++++ src/common/stringutils.h | 3 +++ src/ui/controls/edit.cpp | 15 +++++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/common/stringutils.cpp b/src/common/stringutils.cpp index 3e99a7b7..1e854c31 100644 --- a/src/common/stringutils.cpp +++ b/src/common/stringutils.cpp @@ -197,3 +197,7 @@ std::size_t StrUtils::Utf8StringLength(const std::string &str) return result; } +bool StrUtils::isUtf8ContinuationByte(char c) +{ + return (c & 0b11'000000) == 0b10'000000; +} diff --git a/src/common/stringutils.h b/src/common/stringutils.h index bdb24049..b6eca93c 100644 --- a/src/common/stringutils.h +++ b/src/common/stringutils.h @@ -87,5 +87,8 @@ int Utf8CharSizeAt(const std::string &str, unsigned int pos); //! Returns the length in characters of UTF-8 string \a str std::size_t Utf8StringLength(const std::string &str); +//! Returns true if char is continuation UTF-8 byte +bool isUtf8ContinuationByte(char c); + } // namespace StrUtil diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index 037a8d06..168a97b8 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -27,6 +27,7 @@ #include "common/logger.h" #include "common/make_unique.h" +#include "common/stringutils.h" #include "common/resources/inputstream.h" #include "common/resources/outputstream.h" @@ -2309,7 +2310,10 @@ void CEdit::MoveChar(int move, bool bWord, bool bSelect) if ( m_cursor1 > 0 ) { m_cursor1 --; - while ( m_cursor1 > 0 && (m_text[m_cursor1] & 0xC0) == 0x80 ) m_cursor1 --; + while ( m_cursor1 > 0 && StrUtils::isUtf8ContinuationByte(m_text[m_cursor1]) ) + { + m_cursor1 --; + } } } } @@ -2362,7 +2366,10 @@ void CEdit::MoveChar(int move, bool bWord, bool bSelect) if ( m_cursor1 < m_len ) { m_cursor1 ++; - while ( m_cursor1 < m_len && (m_text[m_cursor1] & 0xC0) == 0x80 ) m_cursor1 ++; + while ( m_cursor1 < m_len && StrUtils::isUtf8ContinuationByte(m_text[m_cursor1]) ) + { + m_cursor1 ++; + } } } } @@ -2795,8 +2802,8 @@ void CEdit::DeleteOne(int dir) if ( m_cursor1 > m_cursor2 ) Math::Swap(m_cursor1, m_cursor2); // Expands selection to delete integer number of UTF-8 symbols - while ( m_cursor1 > 0 && (m_text[m_cursor1] & 0xC0) == 0x80 ) m_cursor1 --; - while ( m_cursor2 < m_len && (m_text[m_cursor2] & 0xC0) == 0x80 ) m_cursor2 ++; + while ( m_cursor1 > 0 && StrUtils::isUtf8ContinuationByte(m_text[m_cursor1]) ) m_cursor1 --; + while ( m_cursor2 < m_len && StrUtils::isUtf8ContinuationByte(m_text[m_cursor2]) ) m_cursor2 ++; hole = m_cursor2-m_cursor1; end = m_len-hole; From 4bce63e38d449085f06ca960fe38eceec649d84a Mon Sep 17 00:00:00 2001 From: Evgeny Pestov Date: Mon, 14 Feb 2022 18:09:08 +0700 Subject: [PATCH 11/13] Use 0b instead of 0x and check 1-byte prefix first in Utf8CharSizeAt 1-byte symbols is more common then 4-bytes symbols. So checking 1-byte prefix first is more efficient. --- src/common/stringutils.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/common/stringutils.cpp b/src/common/stringutils.cpp index 5c5e207a..bc11fee4 100644 --- a/src/common/stringutils.cpp +++ b/src/common/stringutils.cpp @@ -175,12 +175,14 @@ int StrUtils::Utf8CharSizeAt(const std::string &str, unsigned int pos) return 0; const char c = str[pos]; - if((c & 0xF8) == 0xF0) - return 4; - if((c & 0xF0) == 0xE0) - return 3; - if((c & 0xE0) == 0xC0) + if((c & 0b1000'0000) == 0b0000'0000) + return 1; + if((c & 0b1110'0000) == 0b1100'0000) return 2; + if((c & 0b1111'0000) == 0b1110'0000) + return 3; + if((c & 0b1111'1000) == 0b1111'0000) + return 4; return 1; } @@ -199,5 +201,5 @@ std::size_t StrUtils::Utf8StringLength(const std::string &str) bool StrUtils::isUtf8ContinuationByte(char c) { - return (c & 0b11'000000) == 0b10'000000; + return (c & 0b1100'0000) == 0b1000'0000; } From d9e26c25160746a57c5a91dcb842389a8f71f761 Mon Sep 17 00:00:00 2001 From: Evgeny Pestov Date: Mon, 14 Feb 2022 17:17:13 +0700 Subject: [PATCH 12/13] Use std::invalid_argument in Utf8CharSizeAt --- src/common/stringutils.cpp | 7 ++++++- src/graphics/engine/text.cpp | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/common/stringutils.cpp b/src/common/stringutils.cpp index bc11fee4..a7d30f6a 100644 --- a/src/common/stringutils.cpp +++ b/src/common/stringutils.cpp @@ -184,7 +184,12 @@ int StrUtils::Utf8CharSizeAt(const std::string &str, unsigned int pos) if((c & 0b1111'1000) == 0b1111'0000) return 4; - return 1; + // Invalid char - unexpected continuation byte + if (isUtf8ContinuationByte(c)) + throw std::invalid_argument("Unexpected UTF-8 continuation byte"); + + // (c & 0b1111'1000) == 0b1111'1000 is true here + throw std::invalid_argument("Byte value has no sense in UTF-8"); } std::size_t StrUtils::Utf8StringLength(const std::string &str) diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index 315a3fbb..7b6cb8a3 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -937,7 +937,14 @@ int CText::GetCharSizeAt(Gfx::FontType font, const std::string& text, unsigned i } else { - len = StrUtils::Utf8CharSizeAt(text, index); + try + { + len = StrUtils::Utf8CharSizeAt(text, index); + } + catch (std::invalid_argument &e) + { + len = 1; + } } return len; } From 550d0f915bb8a489d86501eae48d8b47b44050d1 Mon Sep 17 00:00:00 2001 From: Evgeny Pestov Date: Mon, 14 Feb 2022 18:33:41 +0700 Subject: [PATCH 13/13] Use std::out_of_range in Utf8CharSizeAt --- src/common/stringutils.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/common/stringutils.cpp b/src/common/stringutils.cpp index a7d30f6a..626c5c39 100644 --- a/src/common/stringutils.cpp +++ b/src/common/stringutils.cpp @@ -156,11 +156,17 @@ std::wstring StrUtils::Utf8StringToUnicode(const std::string &str) { std::wstring result; unsigned int pos = 0; + int len; while (pos < str.size()) { - int len = StrUtils::Utf8CharSizeAt(str, pos); - if (len == 0) + try + { + len = StrUtils::Utf8CharSizeAt(str, pos); + } + catch (std::out_of_range &e) + { break; + } std::string ch = str.substr(pos, len); result += static_cast(StrUtils::Utf8CharToUnicode(ch)); @@ -172,7 +178,7 @@ std::wstring StrUtils::Utf8StringToUnicode(const std::string &str) int StrUtils::Utf8CharSizeAt(const std::string &str, unsigned int pos) { if (pos >= str.size()) - return 0; + throw std::out_of_range("Index is greater than size"); const char c = str[pos]; if((c & 0b1000'0000) == 0b0000'0000)