graphics: improve native system font fallback

Initialize SystemFontResolver when enabled and make its lifecycle and font-list
population thread-safe.

Use native codepoint fallback matching through Fontconfig on Linux, BSD, and
Haiku, DirectWrite on Windows, and Core Text on macOS and iOS. Preserve the
cached FreeType scan as a fallback when native matching is unavailable.

Enable system fonts by default for UIApplication and warm the font database in
the background. Allow applications to override the policy and disable the
automatic default in the unit-test runner.

Fix glyph fallback checks to respect the resolver's enabled state and add
coverage for concurrent warm-up and shaped symbol fallback.

Update efsw.
This commit is contained in:
Martín Lucas Golini
2026-08-23 16:59:23 -03:00
parent 89487fec8b
commit 301b7458d2
15 changed files with 756 additions and 1461 deletions
+8 -2
View File
@@ -110,6 +110,10 @@ class EE_API SystemFontResolver {
FontDesc getFallbackForCodepoint( Uint32 codepoint, FontWeight weight, bool italic );
/** Populate and cache the system font database without copying the resulting font list.
* Safe to call from a worker thread after enabling the resolver. */
void warmUp() const;
bool fontContainsCodepoint( const std::string& path, Uint32 codepoint );
void invalidateCache();
@@ -144,11 +148,13 @@ class EE_API SystemFontResolver {
void populateGenericFallbacks() const;
FontDesc matchFallbackForCodepoint( Uint32 codepoint, FontWeight weight, bool italic ) const;
static int scoreMatch( const FontQuery& query, const FontDesc& candidate );
mutable System::Mutex mMutex;
mutable std::vector<FontDesc> mFontList;
mutable bool mFontListPopulated{ false };
mutable std::atomic<bool> mFontListPopulated{ false };
mutable std::atomic<bool> mFontListLoading{ false };
static Uint64 makeCacheKey( const std::string& normFamily, FontWeight weight,
@@ -166,7 +172,7 @@ class EE_API SystemFontResolver {
};
mutable std::vector<GenericEntry> mGenericFallbacks;
static bool sEnabled;
static std::atomic<bool> sEnabled;
};
}} // namespace EE::Graphics
+15
View File
@@ -4,6 +4,7 @@
#include <eepp/graphics/font.hpp>
#include <eepp/window/window.hpp>
#include <memory>
#include <optional>
using namespace EE::Window;
@@ -12,6 +13,10 @@ namespace EE { namespace UI {
class UISceneNode;
namespace Private {
class UIApplicationSystemFontState;
}
class EE_API UIApplication {
public:
struct EE_API Settings {
@@ -50,6 +55,9 @@ class EE_API UIApplication {
FontHinting fontHinting{ FontHinting::Full };
//! The antialiasing policy applied to fonts owned by the default and UI resource scopes.
FontAntialiasing fontAntialiasing{ FontAntialiasing::Grayscale };
//! Enables system font fallback and warms the system font list on a background thread. If
//! not set, UIApplication::systemFontsEnabledByDefault() is used.
std::optional<bool> enableSystemFonts;
};
UIApplication( const WindowSettings& windowSettings, const Settings& appSettings = Settings(),
@@ -74,6 +82,12 @@ class EE_API UIApplication {
bool showMemoryManagerResult() const;
//! Controls the system-font fallback policy used by Settings::enableSystemFonts when unset.
//! Enabled by default. Test runners can disable it before constructing any UIApplication.
static void setSystemFontsEnabledByDefault( bool enabled );
static bool systemFontsEnabledByDefault();
String::HashType getStyleSheetDefaultMarker() const { return mStyleSheetMarker; }
protected:
@@ -82,6 +96,7 @@ class EE_API UIApplication {
String::HashType mStyleSheetMarker{ 0 };
bool mDidRun{ false };
bool mShowMemoryManagerResult{ false };
std::unique_ptr<Private::UIApplicationSystemFontState> mSystemFontState;
};
}} // namespace EE::UI