From 155393116349197135763a03a0a8c4b9b9fc9c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Jan 2024 11:22:05 +0100 Subject: [PATCH 1/2] SDL: Add more fallback font names, greatly speed up fallback font handling --- Common/Render/Text/draw_text_sdl.cpp | 107 +++++++++++++++++++++------ Common/Render/Text/draw_text_sdl.h | 6 +- 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/Common/Render/Text/draw_text_sdl.cpp b/Common/Render/Text/draw_text_sdl.cpp index 5c91294f2c..562b5f85c0 100644 --- a/Common/Render/Text/draw_text_sdl.cpp +++ b/Common/Render/Text/draw_text_sdl.cpp @@ -18,6 +18,18 @@ #include "SDL2/SDL.h" #include "SDL2/SDL_ttf.h" +static std::string getlocale() { + // setlocale is not an intuitive function... + char *curlocale = setlocale(LC_CTYPE, nullptr); + std::string loc = curlocale ? std::string(curlocale) : "en_US"; + size_t ptPos = loc.find('.'); + // Remove any secondary specifier. + if (ptPos != std::string::npos) { + loc.resize(ptPos); + } + return loc; +} + TextDrawerSDL::TextDrawerSDL(Draw::DrawContext *draw): TextDrawer(draw) { if (TTF_Init() < 0) { ERROR_LOG(G3D, "Unable to initialize SDL2_ttf"); @@ -29,7 +41,7 @@ TextDrawerSDL::TextDrawerSDL(Draw::DrawContext *draw): TextDrawer(draw) { config = FcInitLoadConfigAndFonts(); #endif - PrepareFallbackFonts(); + PrepareFallbackFonts(getlocale()); } TextDrawerSDL::~TextDrawerSDL() { @@ -44,25 +56,56 @@ TextDrawerSDL::~TextDrawerSDL() { } // If a user complains about missing characters on SDL, re-visit this! -void TextDrawerSDL::PrepareFallbackFonts() { +void TextDrawerSDL::PrepareFallbackFonts(std::string_view locale) { #if defined(USE_SDL2_TTF_FONTCONFIG) FcObjectSet *os = FcObjectSetBuild (FC_FILE, FC_INDEX, (char *) 0); - // To install the fallback font in ubuntu: + // To install the recommended Droid Sans fallback font in Ubuntu: // sudo apt install fonts-droid-fallback - const char *names[] = { - "Droid Sans Fallback", + const char *hardcodedNames[] = { "Droid Sans Medium", - "Droid Sans Bold", + "Droid Sans Fallback", "Source Han Sans Medium", - "DejaVu Sans Condensed", "Noto Sans CJK Medium", "Noto Sans Hebrew Medium", "Noto Sans Lao Medium", "Noto Sans Thai Medium", + "DejaVu Sans Condensed", + "DejaVu Sans", + "Meera Regular", + "FreeSans", + "Gargi", + "KacstDigital", + "KacstFarsi", + "Khmer OS", + "Paduak", + "Paduak", + "Jamrul", }; - for (int i = 0; i < ARRAY_SIZE(names); i++) { + std::vector names; + if (locale == "zh_CN") { + names.push_back("Noto Sans CJK SC"); + } else if (locale == "zh_TW") { + names.push_back("Noto Sans CJK TC"); + names.push_back("Noto Sans CJK HK"); + } else if (locale == "ja_JP") { + names.push_back("Noto Sans CJK JP"); + } else if (locale == "ko_KR") { + names.push_back("Noto Sans CJK KR"); + } else { + // Let's just pick one. + names.push_back("Noto Sans CJK JP"); + } + + // Then push all the hardcoded ones. + for (int i = 0; i < ARRAY_SIZE(hardcodedNames); i++) { + names.push_back(hardcodedNames[i]); + } + + // First, add the region-specific Noto fonts according to the locale. + + for (int i = 0; i < names.size(); i++) { // printf("trying font name %s\n", names[i]); FcPattern *name = FcNameParse((const FcChar8 *)names[i]); FcFontSet *foundFonts = FcFontList(config, name, os); @@ -159,19 +202,25 @@ uint32_t TextDrawerSDL::CheckMissingGlyph(const std::string& text) { return missingGlyph; } -// If this returns true, the first font in fallbackFonts_ can be used as a fallback. -bool TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) { - // If we encounter a missing glyph, try to use one of the fallback fonts. +// If this returns >= 0, the nth font in fallbackFonts_ can be used as a fallback. +int TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) { + auto iter = glyphFallbackFontIndex_.find(missingGlyph); + + if (iter != glyphFallbackFontIndex_.end()) { + return iter->second; + } + + // If we encounter a missing glyph, try to use one of already loaded fallback fonts. for (int i = 0; i < fallbackFonts_.size(); i++) { TTF_Font *fallbackFont = fallbackFonts_[i]; if (TTF_GlyphIsProvided32(fallbackFont, missingGlyph)) { - fallbackFonts_.erase(fallbackFonts_.begin() + i); - fallbackFonts_.insert(fallbackFonts_.begin(), fallbackFont); - return true; + glyphFallbackFontIndex_[missingGlyph] = i; + return i; } } // If none of the loaded fonts can handle it, load more fonts. + // TODO: Don't retry already tried fonts. for (int i = 0; i < fallbackFontPaths_.size(); i++) { std::string& fontPath = fallbackFontPaths_[i].first; int faceIndex = fallbackFontPaths_[i].second; @@ -179,14 +228,16 @@ bool TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) { TTF_Font *font = TTF_OpenFontIndex(fontPath.c_str(), ptSize, faceIndex); if (TTF_GlyphIsProvided32(font, missingGlyph)) { - fallbackFonts_.insert(fallbackFonts_.begin(), font); - return true; + fallbackFonts_.push_back(font); + return fallbackFonts_.size() - 1; } else { TTF_CloseFont(font); } } - return false; + // Not found at all? Let's remember that for this glyph. + glyphFallbackFontIndex_[missingGlyph] = -1; + return -1; } uint32_t TextDrawerSDL::SetFont(const char *fontName, int size, int flags) { @@ -234,13 +285,17 @@ void TextDrawerSDL::MeasureString(const char *str, size_t len, float *w, float * if (iter != sizeCache_.end()) { entry = iter->second.get(); } else { + printf("re-measuring %s\n", str); TTF_Font *font = fontMap_.find(fontHash_)->second; int ptSize = TTF_FontHeight(font) / 1.35; uint32_t missingGlyph = CheckMissingGlyph(key.text); - if (missingGlyph && FindFallbackFonts(missingGlyph, ptSize)) { - font = fallbackFonts_[0]; + if (missingGlyph) { + int fallbackFont = FindFallbackFonts(missingGlyph, ptSize); + if (fallbackFont >= 0) { + font = fallbackFonts_[fallbackFont]; + } } int width = 0; @@ -270,8 +325,11 @@ void TextDrawerSDL::MeasureStringRect(const char *str, size_t len, const Bounds int ptSize = TTF_FontHeight(font) / 1.35; uint32_t missingGlyph = CheckMissingGlyph(toMeasure); - if (missingGlyph && FindFallbackFonts(missingGlyph, ptSize)) { - font = fallbackFonts_[0]; + if (missingGlyph) { + int fallbackFont = FindFallbackFonts(missingGlyph, ptSize); + if (fallbackFont >= 0) { + font = fallbackFonts_[fallbackFont]; + } } std::vector lines; @@ -381,8 +439,11 @@ void TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStrin uint32_t missingGlyph = CheckMissingGlyph(processedStr); - if (missingGlyph && FindFallbackFonts(missingGlyph, ptSize)) { - font = fallbackFonts_[0]; + if (missingGlyph) { + int fallbackFont = FindFallbackFonts(missingGlyph, ptSize); + if (fallbackFont >= 0) { + font = fallbackFonts_[fallbackFont]; + } } #if SDL_TTF_VERSION_ATLEAST(2, 20, 0) diff --git a/Common/Render/Text/draw_text_sdl.h b/Common/Render/Text/draw_text_sdl.h index 87c543a660..af690e1acc 100644 --- a/Common/Render/Text/draw_text_sdl.h +++ b/Common/Render/Text/draw_text_sdl.h @@ -28,9 +28,9 @@ public: protected: void ClearCache() override; - void PrepareFallbackFonts(); + void PrepareFallbackFonts(std::string_view locale); uint32_t CheckMissingGlyph(const std::string& text); - bool FindFallbackFonts(uint32_t missingGlyph, int ptSize); + int FindFallbackFonts(uint32_t missingGlyph, int ptSize); uint32_t fontHash_; std::map fontMap_; @@ -41,6 +41,8 @@ protected: std::vector<_TTF_Font *> fallbackFonts_; std::vector> fallbackFontPaths_; // path and font face index + std::map glyphFallbackFontIndex_; + #if defined(USE_SDL2_TTF_FONTCONFIG) FcConfig *config; #endif From f77adec8afb799b14614b2165ff226171476e056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Jan 2024 11:22:28 +0100 Subject: [PATCH 2/2] Unrelated, but return true to SYSPROP_HAS_KEYBOARD in SDL. --- SDL/SDLMain.cpp | 2 +- UI/GameSettingsScreen.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index cb3c51eee8..e3fb221bd6 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -563,9 +563,9 @@ bool System_GetPropertyBool(SystemProperty prop) { #if PPSSPP_PLATFORM(SWITCH) case SYSPROP_HAS_TEXT_INPUT_DIALOG: return __nx_applet_type == AppletType_Application || __nx_applet_type != AppletType_SystemApplication; +#endif case SYSPROP_HAS_KEYBOARD: return true; -#endif case SYSPROP_APP_GOLD: #ifdef GOLD return true; diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index f7cdbef4d6..6f956cfdb7 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -44,6 +44,7 @@ protected: void dialogFinished(const Screen *dialog, DialogResult result) override; void CreateTabs() override; + bool ShowSearchControls() const { return true; } private: void PreCreateViews() override;