fix fallback font not scaled correctly
This commit is contained in:
@@ -37,14 +37,23 @@ 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<float, Char>(_asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _asset->GetOptions().MSDFSize : GetSize(), c);
|
||||
if (!_asset->_characterCache.TryGet(key, result))
|
||||
if (_asset->_characterCache.TryGet(key, result))
|
||||
{
|
||||
// With MSDF font introduced, cached entry may be created by a different font (with same MSDFSize)
|
||||
// This is to ensure returned entry has a reference to a font whose size matches the font being used to render
|
||||
result.Font = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This thread race condition may happen in editor but in game we usually do all stuff with fonts on main thread (chars caching)
|
||||
ScopeLock lock(_asset->Locker);
|
||||
|
||||
// Handle situation when more than one thread wants to get the same character
|
||||
if (_asset->_characterCache.TryGet(key, result))
|
||||
{
|
||||
result.Font = this;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to use fallback font if character is missing
|
||||
if (enableFallback && !_asset->ContainsChar(c))
|
||||
@@ -52,13 +61,9 @@ void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback)
|
||||
for (int32 fallbackIndex = 0; fallbackIndex < FallbackFonts.Count(); fallbackIndex++)
|
||||
{
|
||||
FontAsset* fallbackFont = FallbackFonts.Get()[fallbackIndex].Get();
|
||||
if (fallbackFont && _asset->GetOptions().RasterMode == FontRasterMode::MSDF)
|
||||
{
|
||||
fallbackFont = fallbackFont->GetMSDF();
|
||||
}
|
||||
if (fallbackFont && fallbackFont->ContainsChar(c))
|
||||
{
|
||||
fallbackFont->CreateFont(GetSize())->GetCharacter(c, result, enableFallback);
|
||||
fallbackFont->GetRasterMode(_asset->GetOptions().RasterMode)->CreateFont(GetSize())->GetCharacter(c, result, enableFallback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -127,6 +132,11 @@ void Font::Invalidate()
|
||||
_kerningTable.Clear();
|
||||
}
|
||||
|
||||
float Font::GetScale(float layoutScale) const
|
||||
{
|
||||
return layoutScale / FontManager::FontScale * (_asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _size / _asset->GetOptions().MSDFSize : 1.0f);
|
||||
}
|
||||
|
||||
void Font::ProcessText(const StringView& text, Array<FontLineCache, InlinedAllocation<8>>& outputLines, const TextLayoutOptions& layout)
|
||||
{
|
||||
int32 textLength = text.Length();
|
||||
@@ -137,7 +147,7 @@ void Font::ProcessText(const StringView& text, Array<FontLineCache, InlinedAlloc
|
||||
FontLineCache tmpLine;
|
||||
FontCharacterEntry entry;
|
||||
FontCharacterEntry previous;
|
||||
float scale = layout.Scale / FontManager::FontScale * (_asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _size / _asset->GetOptions().MSDFSize : 1.0f);
|
||||
const float scale = GetScale(layout.Scale);
|
||||
float boundsWidth = layout.Bounds.GetWidth();
|
||||
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
|
||||
tmpLine.Location = Float2::Zero;
|
||||
@@ -181,6 +191,7 @@ void Font::ProcessText(const StringView& text, Array<FontLineCache, InlinedAlloc
|
||||
{
|
||||
// Get character entry
|
||||
GetCharacter(currentChar, entry);
|
||||
const float entryScale = entry.Font->GetScale(layout.Scale);
|
||||
|
||||
// Get kerning
|
||||
if (!isWhitespace && previous.IsValid)
|
||||
@@ -192,7 +203,7 @@ void Font::ProcessText(const StringView& text, Array<FontLineCache, InlinedAlloc
|
||||
kerning = 0;
|
||||
}
|
||||
previous = entry;
|
||||
xAdvance = (kerning + entry.AdvanceX) * scale;
|
||||
xAdvance = (kerning + entry.AdvanceX) * entryScale;
|
||||
|
||||
// Check if character fits the line or skip wrapping
|
||||
if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap)
|
||||
@@ -343,7 +354,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
|
||||
Array<FontLineCache, InlinedAllocation<8>> lines;
|
||||
ProcessText(text, lines, layout);
|
||||
ASSERT(lines.HasItems());
|
||||
float scale = layout.Scale / FontManager::FontScale * (_asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _size / _asset->GetOptions().MSDFSize : 1.0f);
|
||||
const float scale = GetScale(layout.Scale);
|
||||
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
|
||||
|
||||
// Offset position to match lines origin space
|
||||
@@ -364,12 +375,13 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
|
||||
// Cache current character
|
||||
const Char currentChar = text[currentIndex];
|
||||
GetCharacter(currentChar, entry);
|
||||
const float entryScale = entry.Font->GetScale(layout.Scale);
|
||||
const bool isWhitespace = StringUtils::IsWhitespace(currentChar);
|
||||
|
||||
// Apply kerning
|
||||
if (!isWhitespace && previous.IsValid)
|
||||
{
|
||||
x += entry.Font->GetKerning(previous.Character, entry.Character);
|
||||
x += entry.Font->GetKerning(previous.Character, entry.Character) * entryScale;
|
||||
}
|
||||
previous = entry;
|
||||
|
||||
@@ -388,7 +400,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
|
||||
}
|
||||
|
||||
// Move
|
||||
x += entry.AdvanceX * scale;
|
||||
x += entry.AdvanceX * entryScale;
|
||||
}
|
||||
|
||||
// Test line end edge
|
||||
@@ -431,7 +443,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
|
||||
Array<FontLineCache, InlinedAllocation<8>> lines;
|
||||
ProcessText(text, lines, layout);
|
||||
ASSERT(lines.HasItems());
|
||||
float scale = layout.Scale / FontManager::FontScale * (_asset->GetOptions().RasterMode == FontRasterMode::MSDF ? _size / _asset->GetOptions().MSDFSize : 1.0f);
|
||||
const float scale = GetScale(layout.Scale);
|
||||
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
|
||||
|
||||
// Find line with that position
|
||||
@@ -452,17 +464,18 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
|
||||
// Cache current character
|
||||
const Char currentChar = text[currentIndex];
|
||||
GetCharacter(currentChar, entry);
|
||||
const float entryScale = entry.Font->GetScale(layout.Scale);
|
||||
const bool isWhitespace = StringUtils::IsWhitespace(currentChar);
|
||||
|
||||
// Apply kerning
|
||||
if (!isWhitespace && previous.IsValid)
|
||||
{
|
||||
charPos.X += entry.Font->GetKerning(previous.Character, entry.Character);
|
||||
charPos.X += entry.Font->GetKerning(previous.Character, entry.Character) * entryScale;
|
||||
}
|
||||
previous = entry;
|
||||
|
||||
// Move
|
||||
charPos.X += entry.AdvanceX * scale;
|
||||
charPos.X += entry.AdvanceX * entryScale;
|
||||
}
|
||||
|
||||
// Upper left corner of the character
|
||||
|
||||
@@ -234,6 +234,13 @@ public:
|
||||
API_FUNCTION() void Invalidate();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the scale factor that maps the rasterized font size to the actual rendered size.
|
||||
/// </summary>
|
||||
/// <param name="layoutScale">The layout scale.</param>
|
||||
/// <returns>The scale factor.</returns>
|
||||
float GetScale(float layoutScale) const;
|
||||
|
||||
/// <summary>
|
||||
/// Processes text to get cached lines for rendering.
|
||||
/// </summary>
|
||||
|
||||
@@ -69,7 +69,7 @@ void FontAsset::unload(bool isReloading)
|
||||
_fontFile.Release();
|
||||
_virtualBold = nullptr;
|
||||
_virtualItalic = nullptr;
|
||||
_virtualMSDF = nullptr;
|
||||
_virtualRasterMode = nullptr;
|
||||
}
|
||||
|
||||
AssetChunksFlag FontAsset::getChunksToPreload() const
|
||||
@@ -115,11 +115,11 @@ void FontAsset::SetOptions(const FontOptions& value)
|
||||
options.Flags |= FontFlags::Italic;
|
||||
_virtualItalic->SetOptions(options);
|
||||
}
|
||||
if (_virtualMSDF)
|
||||
if (_virtualRasterMode)
|
||||
{
|
||||
auto options = _options;
|
||||
options.RasterMode = FontRasterMode::MSDF;
|
||||
_virtualMSDF->SetOptions(options);
|
||||
options.RasterMode = _options.RasterMode == FontRasterMode::MSDF ? FontRasterMode::Bitmap : FontRasterMode::MSDF;
|
||||
_virtualRasterMode->SetOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,20 +176,20 @@ FontAsset* FontAsset::GetItalic()
|
||||
return _virtualItalic;
|
||||
}
|
||||
|
||||
FontAsset* FontAsset::GetMSDF()
|
||||
FontAsset* FontAsset::GetRasterMode(FontRasterMode rasterMode)
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
if (_options.RasterMode == FontRasterMode::MSDF)
|
||||
if (_options.RasterMode == rasterMode)
|
||||
return this;
|
||||
if (!_virtualMSDF)
|
||||
if (!_virtualRasterMode)
|
||||
{
|
||||
_virtualMSDF = Content::CreateVirtualAsset<FontAsset>();
|
||||
_virtualMSDF->Init(_fontFile);
|
||||
_virtualRasterMode = Content::CreateVirtualAsset<FontAsset>();
|
||||
_virtualRasterMode->Init(_fontFile);
|
||||
auto options = _options;
|
||||
options.RasterMode = FontRasterMode::MSDF;
|
||||
_virtualMSDF->SetOptions(options);
|
||||
options.RasterMode = rasterMode;
|
||||
_virtualRasterMode->SetOptions(options);
|
||||
}
|
||||
return _virtualMSDF;
|
||||
return _virtualRasterMode;
|
||||
}
|
||||
|
||||
bool FontAsset::Init(const BytesContainer& fontFile)
|
||||
|
||||
@@ -233,7 +233,7 @@ private:
|
||||
Dictionary<Pair<float, Char>, FontCharacterEntry> _characterCache;
|
||||
AssetReference<FontAsset> _virtualBold;
|
||||
AssetReference<FontAsset> _virtualItalic;
|
||||
AssetReference<FontAsset> _virtualMSDF;
|
||||
AssetReference<FontAsset> _virtualRasterMode;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -293,10 +293,10 @@ public:
|
||||
API_FUNCTION() FontAsset* GetItalic();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MSDF version of the font. Returns itself or creates a new virtual font asset using this font but rasterized with Multi-channel Signed Distance Field (MSDF).
|
||||
/// Gets the different rasterization mode of the font. Returns itself or creates a new virtual font asset using this font but rasterized with the specified mode.
|
||||
/// </summary>
|
||||
/// <returns>The virtual font or this.</returns>
|
||||
API_FUNCTION() FontAsset* GetMSDF();
|
||||
API_FUNCTION() FontAsset* GetRasterMode(FontRasterMode rasterMode);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the font with a custom font file data.
|
||||
|
||||
@@ -1204,7 +1204,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
|
||||
FontCharacterEntry previous;
|
||||
int32 kerning;
|
||||
FontOptions options = font->GetAsset()->GetOptions();
|
||||
float scale = 1.0f / FontManager::FontScale * (options.RasterMode == FontRasterMode::MSDF ? font->GetSize() / options.MSDFSize : 1.0f);
|
||||
const float scale = font->GetScale(1.0f);
|
||||
const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts);
|
||||
|
||||
// Render all characters
|
||||
@@ -1231,6 +1231,8 @@ 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);
|
||||
|
||||
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
|
||||
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
|
||||
@@ -1263,17 +1265,17 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
|
||||
{
|
||||
kerning = 0;
|
||||
}
|
||||
pointer.X += kerning * scale;
|
||||
pointer.X += kerning * entryScale;
|
||||
previous = entry;
|
||||
|
||||
// Omit whitespace characters
|
||||
if (!isWhitespace)
|
||||
{
|
||||
// Calculate character size and atlas coordinates
|
||||
const float x = pointer.X + entry.OffsetX * scale;
|
||||
const float y = pointer.Y + (font->GetHeight() + font->GetDescender() - entry.OffsetY) * scale;
|
||||
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 * scale, entry.UVSize.Y * 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;
|
||||
@@ -1286,7 +1288,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
|
||||
}
|
||||
|
||||
// Move
|
||||
pointer.X += entry.AdvanceX * scale;
|
||||
pointer.X += entry.AdvanceX * entryScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1320,7 +1322,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
|
||||
FontCharacterEntry previous;
|
||||
int32 kerning;
|
||||
FontOptions options = font->GetAsset()->GetOptions();
|
||||
float scale = layout.Scale / FontManager::FontScale * (options.RasterMode == FontRasterMode::MSDF ? font->GetSize() / options.MSDFSize : 1.0f);
|
||||
const float scale = font->GetScale(layout.Scale);
|
||||
const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts);
|
||||
|
||||
// Process text to get lines
|
||||
@@ -1356,6 +1358,8 @@ 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);
|
||||
|
||||
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
|
||||
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
|
||||
@@ -1386,17 +1390,17 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
|
||||
{
|
||||
kerning = 0;
|
||||
}
|
||||
pointer.X += (float)kerning * scale;
|
||||
pointer.X += (float)kerning * entryScale;
|
||||
previous = entry;
|
||||
|
||||
// Omit whitespace characters
|
||||
if (!isWhitespace)
|
||||
{
|
||||
// Calculate character size and atlas coordinates
|
||||
const float x = pointer.X + entry.OffsetX * scale;
|
||||
const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((font->GetHeight() + font->GetDescender()) * scale);
|
||||
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 * scale, entry.UVSize.Y * scale);
|
||||
Rectangle charRect(x, y, entry.UVSize.X * entryScale, entry.UVSize.Y * entryScale);
|
||||
charRect.Offset(layout.Bounds.Location);
|
||||
|
||||
Float2 upperLeftUV = entry.UV * invAtlasSize;
|
||||
@@ -1410,7 +1414,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
|
||||
}
|
||||
|
||||
// Move
|
||||
pointer.X += entry.AdvanceX * scale;
|
||||
pointer.X += entry.AdvanceX * entryScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ void TextRender::UpdateLayout()
|
||||
|
||||
// Pick a font (remove DPI text scale as the text is being placed in the world)
|
||||
auto font = Font->CreateFont(_size);
|
||||
float scale = _layoutOptions.Scale / FontManager::FontScale * (Font->GetOptions().RasterMode == FontRasterMode::MSDF ? _size / Font->GetOptions().MSDFSize : 1.0f);
|
||||
const float scale = font->GetScale(_layoutOptions.Scale);
|
||||
|
||||
// Prepare
|
||||
FontTextureAtlas* fontAtlas = nullptr;
|
||||
@@ -217,6 +217,7 @@ void TextRender::UpdateLayout()
|
||||
if (c != '\n')
|
||||
{
|
||||
font->GetCharacter(c, entry);
|
||||
const float entryScale = entry.Font->GetScale(_layoutOptions.Scale);
|
||||
|
||||
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
|
||||
if (fontAtlas == nullptr || entry.TextureIndex != drawChunk.FontAtlasIndex)
|
||||
@@ -273,17 +274,17 @@ void TextRender::UpdateLayout()
|
||||
{
|
||||
kerning = 0;
|
||||
}
|
||||
pointer.X += (float)kerning * scale;
|
||||
pointer.X += (float)kerning * entryScale;
|
||||
previous = entry;
|
||||
|
||||
// Omit whitespace characters
|
||||
if (!isWhitespace)
|
||||
{
|
||||
// Calculate character size and atlas coordinates
|
||||
const float x = pointer.X + (float)entry.OffsetX * scale;
|
||||
const float y = pointer.Y + (float)(font->GetHeight() + font->GetDescender() - entry.OffsetY) * scale;
|
||||
const float x = pointer.X + (float)entry.OffsetX * entryScale;
|
||||
const float y = pointer.Y - (float)entry.OffsetY * entryScale + (float)(font->GetHeight() + font->GetDescender()) * scale;
|
||||
|
||||
Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale);
|
||||
Rectangle charRect(x, y, entry.UVSize.X * entryScale, entry.UVSize.Y * entryScale);
|
||||
charRect.Offset(_layoutOptions.Bounds.Location);
|
||||
|
||||
Float2 upperLeftUV = entry.UV * invAtlasSize;
|
||||
@@ -326,7 +327,7 @@ void TextRender::UpdateLayout()
|
||||
}
|
||||
|
||||
// Move
|
||||
pointer.X += (float)entry.AdvanceX * scale;
|
||||
pointer.X += (float)entry.AdvanceX * entryScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user