From 451985612b22d0ec088ea2fc6a43704d48b4cda0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 19 Sep 2026 20:05:36 +0200 Subject: [PATCH] Various tweaks to #4218 --- Source/Editor/Windows/Assets/FontWindow.cs | 5 +++-- Source/Engine/Render2D/Font.cpp | 22 ++++++++++++++++++---- Source/Engine/Render2D/Font.h | 4 ++-- Source/Engine/Render2D/FontAsset.cpp | 3 ++- Source/Engine/Render2D/Render2D.cpp | 10 ++++------ 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Windows/Assets/FontWindow.cs b/Source/Editor/Windows/Assets/FontWindow.cs index fe05b3b53..2f27a3f1b 100644 --- a/Source/Editor/Windows/Assets/FontWindow.cs +++ b/Source/Editor/Windows/Assets/FontWindow.cs @@ -1,6 +1,5 @@ // Copyright (c) Wojciech Figat. All rights reserved. -using System; using System.ComponentModel; using FlaxEditor.Content; using FlaxEditor.CustomEditors; @@ -26,7 +25,7 @@ namespace FlaxEditor.Windows.Assets [EditorOrder(5), EditorDisplay("Properties"), Tooltip("The rasterization mode used when generating font atlases.")] public FontRasterMode RasterMode; - [DefaultValue(32.0f)] + [DefaultValue(32.0f), VisibleIf(nameof(IsMSDF))] [EditorOrder(6), Limit(4, 512), EditorDisplay("Properties"), Tooltip("The font size used when generating MSDF font atlases.")] public float MSDFSize; @@ -46,6 +45,8 @@ namespace FlaxEditor.Windows.Assets [EditorOrder(40), EditorDisplay("Properties"), Tooltip("Enables slant effect, emulating italic style.")] public bool Italic; + private bool IsMSDF => RasterMode == FontRasterMode.MSDF; + public void Get(out FontOptions options) { options = new FontOptions diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index fb88c8256..37ce0f505 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -36,7 +36,8 @@ Font::~Font() void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback) { // Try to get the character or cache it if cannot be found - const auto key = Pair(_asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _asset->GetOptions().MSDFSize : GetSize(), c); + const FontOptions& options = _asset->GetOptions(); + const auto key = Pair(options.RasterMode == FontRasterMode::MSDF ? options.MSDFSize : GetSize(), c); if (_asset->_characterCache.TryGet(key, result)) { // With MSDF font introduced, cached entry may be created by a different font (with same MSDFSize) @@ -63,7 +64,7 @@ void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback) FontAsset* fallbackFont = FallbackFonts.Get()[fallbackIndex].Get(); if (fallbackFont && fallbackFont->ContainsChar(c)) { - fallbackFont->GetRasterMode(_asset->GetOptions().RasterMode)->CreateFont(GetSize())->GetCharacter(c, result, enableFallback); + fallbackFont->GetRasterMode(options.RasterMode)->CreateFont(GetSize())->GetCharacter(c, result, enableFallback); return; } } @@ -121,6 +122,17 @@ void Font::Invalidate() { ScopeLock lock(_asset->Locker); + // Invalidate cached characters (from atlas) + for (auto i = _asset->_characterCache.Begin(); i.IsNotEnd(); ++i) + { + if (i->Value.Font == this) + { + FontManager::Invalidate(i->Value); + _asset->_characterCache.Remove(i); + } + } + + // Rebuild font metrics FlushFaceSize(); const FT_Face face = _asset->GetFTFace(); ASSERT(face != nullptr); @@ -134,7 +146,8 @@ void Font::Invalidate() float Font::GetScale(float layoutScale) const { - return layoutScale / FontManager::FontScale * (_asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _size / _asset->GetOptions().MSDFSize : 1.0f); + const FontOptions& options = _asset->GetOptions(); + return layoutScale / FontManager::FontScale * (options.RasterMode == FontRasterMode::MSDF ? _size / options.MSDFSize : 1.0f); } void Font::ProcessText(const StringView& text, Array>& outputLines, const TextLayoutOptions& layout) @@ -491,7 +504,8 @@ void Font::FlushFaceSize() const { // Set the character size const FT_Face face = _asset->GetFTFace(); - float size = _asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _asset->GetOptions().MSDFSize : _size; + const FontOptions& options = _asset->GetOptions(); + float size = options.RasterMode == FontRasterMode::MSDF ? options.MSDFSize : _size; const FT_Error error = FT_Set_Char_Size(face, 0, ConvertPixelTo26Dot6(size * FontManager::FontScale), DefaultDPI, DefaultDPI); if (error) { diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index c8fd960d0..ada57ca9d 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -211,7 +211,7 @@ public: /// /// The character. /// The output character entry. - /// True if fallback to secondary font when the primary font doesn't contains this character. + /// True if fallback to secondary font when the primary font doesn't contain this character. void GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback = true); /// @@ -229,7 +229,7 @@ public: API_FUNCTION() void CacheText(const StringView& text); /// - /// Refresh cached metrics. Can be used after changing font asset options. + /// Refresh cached metrics and invalidates all cached characters (in atlases). Can be used after changing font asset options. /// API_FUNCTION() void Invalidate(); diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index adc641f0d..1b58d472c 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -234,9 +234,10 @@ bool FontAsset::ContainsChar(Char c) const void FontAsset::Invalidate() { ScopeLock lock(Locker); + + // Invalidate cached characters (from atlas) for (auto& entry : _characterCache) FontManager::Invalidate(entry.Value); - _characterCache.Clear(); // Refresh cached metrics of all fonts created from this asset diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index de6ad9215..359190665 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1203,7 +1203,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, Float2 invAtlasSize = Float2::One; FontCharacterEntry previous; int32 kerning; - FontOptions options = font->GetAsset()->GetOptions(); + const FontOptions& options = font->GetAsset()->GetOptions(); const float scale = font->GetScale(1.0f); const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts); @@ -1231,6 +1231,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, { // Get character entry font->GetCharacter(currentChar, entry, enableFallbackFonts); + // Fallback fonts may have different MSDFSize, so we need to calculate scale per character const float entryScale = entry.Font->GetScale(1.0f); @@ -1274,9 +1275,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, // Calculate character size and atlas coordinates const float x = pointer.X + entry.OffsetX * entryScale; const float y = pointer.Y - entry.OffsetY * entryScale + (font->GetHeight() + font->GetDescender()) * scale; - Rectangle charRect(x, y, entry.UVSize.X * entryScale, entry.UVSize.Y * entryScale); - Float2 upperLeftUV = entry.UV * invAtlasSize; Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; @@ -1321,7 +1320,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, Float2 invAtlasSize = Float2::One; FontCharacterEntry previous; int32 kerning; - FontOptions options = font->GetAsset()->GetOptions(); + const FontOptions& options = font->GetAsset()->GetOptions(); const float scale = font->GetScale(layout.Scale); const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts); @@ -1358,6 +1357,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, { // Get character entry font->GetCharacter(currentChar, entry, enableFallbackFonts); + // Fallback fonts may have different MSDFSize, so we need to calculate scale per character const float entryScale = entry.Font->GetScale(layout.Scale); @@ -1399,10 +1399,8 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, // Calculate character size and atlas coordinates const float x = pointer.X + entry.OffsetX * entryScale; const float y = pointer.Y - entry.OffsetY * entryScale + Math::Ceil((font->GetHeight() + font->GetDescender()) * scale); - Rectangle charRect(x, y, entry.UVSize.X * entryScale, entry.UVSize.Y * entryScale); charRect.Offset(layout.Bounds.Location); - Float2 upperLeftUV = entry.UV * invAtlasSize; Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize;