1
0
mirror of https://github.com/OpenTTD/OpenTTD synced 2025-12-10 06:52:05 +01:00

Add: Automatically load fonts for missing glyphs. (#14856)

This commit is contained in:
Peter Nelson
2025-12-06 12:22:25 +00:00
committed by GitHub
parent 064f4df6cc
commit c1d37d8699
4 changed files with 60 additions and 4 deletions

View File

@@ -2326,7 +2326,7 @@ static bool ConContent(std::span<std::string_view> argv)
*/ */
static std::string_view FontLoadReasonToName(FontLoadReason load_reason) static std::string_view FontLoadReasonToName(FontLoadReason load_reason)
{ {
static const std::string_view LOAD_REASON_TO_NAME[] = { "default", "configured", "language" }; static const std::string_view LOAD_REASON_TO_NAME[] = { "default", "configured", "language", "missing" };
static_assert(std::size(LOAD_REASON_TO_NAME) == to_underlying(FontLoadReason::End)); static_assert(std::size(LOAD_REASON_TO_NAME) == to_underlying(FontLoadReason::End));
return LOAD_REASON_TO_NAME[to_underlying(load_reason)]; return LOAD_REASON_TO_NAME[to_underlying(load_reason)];
} }

View File

@@ -75,6 +75,7 @@ int FontCache::GetDefaultFontHeight(FontSize fs)
for (const auto &fc : FontCache::caches) { for (const auto &fc : FontCache::caches) {
if (fc == nullptr || fc->fs != fs) continue; if (fc == nullptr || fc->fs != fs) continue;
if (fc->load_reason == FontLoadReason::MissingFallback) continue; // Avoid dynamically loaded fonts affecting widget sizes.
ascender = std::max(ascender, fc->ascender); ascender = std::max(ascender, fc->ascender);
descender = std::min(descender, fc->descender); descender = std::min(descender, fc->descender);
} }
@@ -269,10 +270,11 @@ std::string GetFontCacheFontName(FontSize fs)
} }
} }
/* static */ void FontCache::LoadFallbackFonts(FontSize fs) /* static */ void FontCache::LoadFallbackFonts(FontSize fs, FontLoadReason load_reason)
{ {
const FontCacheSubSetting *setting = GetFontCacheSubSetting(fs); const FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
for (auto it = setting->fallback_fonts.rbegin(); it != setting->fallback_fonts.rend(); ++it) { for (auto it = setting->fallback_fonts.rbegin(); it != setting->fallback_fonts.rend(); ++it) {
if (it->load_reason != load_reason) continue;
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType, false, it->name, it->os_handle), it->load_reason); FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType, false, it->name, it->os_handle), it->load_reason);
} }
} }
@@ -318,12 +320,15 @@ std::string GetFontCacheFontName(FontSize fs)
if (std::ranges::find(fontnames, extra_font) == std::end(fontnames)) fontnames.push_back(extra_font); if (std::ranges::find(fontnames, extra_font) == std::end(fontnames)) fontnames.push_back(extra_font);
} }
/* First load fonts for missing glyphs discovered during string formatting. */
FontCache::LoadFallbackFonts(fs, FontLoadReason::MissingFallback);
/* Load configured fonts in reverse order so that the first entry has priority. */ /* Load configured fonts in reverse order so that the first entry has priority. */
for (auto it = fontnames.rbegin(); it != fontnames.rend(); ++it) { for (auto it = fontnames.rbegin(); it != fontnames.rend(); ++it) {
if (*it == DEFAULT_FONT) { if (*it == DEFAULT_FONT) {
FontCache::LoadDefaultFonts(fs); FontCache::LoadDefaultFonts(fs);
} else if (*it == FALLBACK_FONT) { } else if (*it == FALLBACK_FONT) {
FontCache::LoadFallbackFonts(fs); FontCache::LoadFallbackFonts(fs, FontLoadReason::LanguageFallback);
} else { } else {
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType, true, std::string{*it}, {}), FontLoadReason::Configured); FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType, true, std::string{*it}, {}), FontLoadReason::Configured);
} }

View File

@@ -24,6 +24,7 @@ enum class FontLoadReason : uint8_t {
Default, Default,
Configured, Configured,
LanguageFallback, LanguageFallback,
MissingFallback,
End, End,
}; };
@@ -51,7 +52,7 @@ protected:
FontCache(FontSize fs) : fs(fs) {} FontCache(FontSize fs) : fs(fs) {}
static void Register(std::unique_ptr<FontCache> &&fc, FontLoadReason load_reason); static void Register(std::unique_ptr<FontCache> &&fc, FontLoadReason load_reason);
static void LoadDefaultFonts(FontSize fs); static void LoadDefaultFonts(FontSize fs);
static void LoadFallbackFonts(FontSize fs); static void LoadFallbackFonts(FontSize fs, FontLoadReason load_reason);
public: public:
virtual ~FontCache() = default; virtual ~FontCache() = default;

View File

@@ -10,10 +10,15 @@
#include "stdafx.h" #include "stdafx.h"
#include "core/math_func.hpp" #include "core/math_func.hpp"
#include "gfx_layout.h" #include "gfx_layout.h"
#include "gfx_func.h"
#include "string_func.h" #include "string_func.h"
#include "strings_func.h" #include "strings_func.h"
#include "core/utf8.hpp" #include "core/utf8.hpp"
#include "debug.h" #include "debug.h"
#include "timer/timer.h"
#include "timer/timer_window.h"
#include "viewport_func.h"
#include "window_func.h"
#include "table/control_codes.h" #include "table/control_codes.h"
@@ -37,6 +42,50 @@
/** Cache of ParagraphLayout lines. */ /** Cache of ParagraphLayout lines. */
std::unique_ptr<Layouter::LineCache> Layouter::linecache; std::unique_ptr<Layouter::LineCache> Layouter::linecache;
class RuntimeMissingGlyphSearcher : public MissingGlyphSearcher {
std::array<std::set<char32_t>, FS_END> glyphs{};
public:
RuntimeMissingGlyphSearcher() : MissingGlyphSearcher(FONTSIZES_ALL) {}
FontLoadReason GetLoadReason() override { return FontLoadReason::MissingFallback; }
inline void Insert(FontSize fs, char32_t c)
{
this->glyphs[fs].insert(c);
this->search_timeout.Reset();
}
std::set<char32_t> GetRequiredGlyphs(FontSizes fontsizes) override
{
std::set<char32_t> r;
for (FontSize fs : fontsizes) {
r.merge(this->glyphs[fs]);
}
return r;
}
TimeoutTimer<TimerWindow> search_timeout{std::chrono::milliseconds(250), [this]()
{
FontSizes changed_fontsizes{};
for (FontSize fs = FS_BEGIN; fs != FS_END; ++fs) {
auto &missing = this->glyphs[fs];
if (missing.empty()) continue;
if (FontProviderManager::FindFallbackFont({}, fs, this)) changed_fontsizes.Set(fs);
missing.clear();
}
if (!changed_fontsizes.Any()) return;
FontCache::LoadFontCaches(changed_fontsizes);
LoadStringWidthTable(changed_fontsizes);
UpdateAllVirtCoords();
ReInitAllWindows(true);
}};
};
static RuntimeMissingGlyphSearcher _missing_glyphs;
/** /**
* Helper for getting a ParagraphLayouter of the given type. * Helper for getting a ParagraphLayouter of the given type.
* *
@@ -98,6 +147,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
FontIndex font_index = FontCache::GetFontIndexForCharacter(state.fontsize, c); FontIndex font_index = FontCache::GetFontIndexForCharacter(state.fontsize, c);
if (font_index == INVALID_FONT_INDEX) { if (font_index == INVALID_FONT_INDEX) {
_missing_glyphs.Insert(state.fontsize, c);
font_index = FontCache::GetDefaultFontIndex(state.fontsize); font_index = FontCache::GetDefaultFontIndex(state.fontsize);
} }