diff --git a/.gitignore b/.gitignore index b653b7f77..30c2caeb2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ Source/*.Gen.* Source/*.csproj /Package_*/ !Source/Engine/Debug -/Source/Platforms/Editor/Linux/Mono/etc/mono/registry PackageEditor_Cert.command PackageEditor_Cert.bat PackagePlatforms_Cert.bat @@ -157,4 +156,3 @@ obj/ .idea/ *.code-workspace omnisharp.json -Content/Editor/Fonts/NotoSansSC-Regular.flax diff --git a/Content/Editor/Fonts/NotoSansSC-Regular.flax b/Content/Editor/Fonts/NotoSansSC-Regular.flax new file mode 100644 index 000000000..39964e6c5 --- /dev/null +++ b/Content/Editor/Fonts/NotoSansSC-Regular.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19fa43eb7b31ee3936348b1f271c464c79d7020a21d33e3cdbe54f98c3b14304 +size 10560899 diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index c894abe6b..8b2049ebd 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,7 +54,10 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; - public static string CjkFont = "Editor/Fonts/NotoSansSC-Regular"; + /// + /// The secondary (fallback) font to use for missing characters rendering (CJK - Chinese/Japanese/Korean characters). + /// + public static string FallbackFont = "Editor/Fonts/NotoSansSC-Regular"; /// /// The Inconsolata Regular font. diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index f75f11922..3e2bb63bd 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -43,9 +43,9 @@ namespace FlaxEditor.GUI { Depth = -1; - var mediumHeight = Style.Current.FontMedium.GetMaxHeight(); - if (Height < mediumHeight) - Height = mediumHeight + 4; + var fontHeight = Style.Current.FontMedium.Height; + if (Height < fontHeight) + Height = fontHeight + 4; } /// diff --git a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs index 3f835a35f..bedb61a5e 100644 --- a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs +++ b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs @@ -36,16 +36,16 @@ namespace FlaxEditor.GUI.Timeline.GUI string labelText; switch (_timeline.TimeShowMode) { - case Timeline.TimeShowModes.Frames: - labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Seconds: - labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Time: - labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); - break; - default: throw new ArgumentOutOfRangeException(); + case Timeline.TimeShowModes.Frames: + labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Seconds: + labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Time: + labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); + break; + default: throw new ArgumentOutOfRangeException(); } var color = (_timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground).AlphaMultiplied(0.6f); Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1); diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index af432baa7..6180b5a66 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -172,9 +172,9 @@ namespace FlaxEditor.Options set { if (value == null) - _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + _outputLogFont = new FontReference(ConsoleFont, 10); else if (!value.Font) - _outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); + _outputLogFont.Font = ConsoleFont; else _outputLogFont = value; } @@ -237,8 +237,7 @@ namespace FlaxEditor.Options public int NumberOfGameClientsToLaunch = 1; private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); - - private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); + private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); private FontReference _titleFont = new FontReference(DefaultFont, 18); private FontReference _largeFont = new FontReference(DefaultFont, 14); @@ -247,9 +246,10 @@ namespace FlaxEditor.Options private FontReference _outputLogFont = new FontReference(ConsoleFont, 10); /// - /// The fallback fonts. + /// The list of fallback fonts to use when main text font is missing certain characters. Empty to use fonts from GraphicsSettings. /// - public FontAsset[] Fallbacks = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + [EditorDisplay("Fonts"), EditorOrder(650)] + public FontAsset[] FallbackFonts = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.FallbackFont)]; /// /// Gets or sets the title font for editor UI. diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index bf35105a5..bb90be6d9 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using FlaxEditor.Content.Settings; using FlaxEditor.Modules; using FlaxEngine; @@ -225,11 +226,11 @@ namespace FlaxEditor.Options } } - var graphicsSetttings = GameSettings.Load(); - if (graphicsSetttings.EnableFontFallback && graphicsSetttings.FallbackFonts == null) - { - Render2D.FallbackFonts = graphicsSetttings.FallbackFonts = FontFallbackList.Create(Options.Interface.Fallbacks); - } + // Set fallback fonts + var fallbackFonts = Options.Interface.FallbackFonts; + if (fallbackFonts == null || fallbackFonts.Length == 0 || fallbackFonts.All(x => x == null)) + fallbackFonts = GameSettings.Load().FallbackFonts; + Font.FallbackFonts = fallbackFonts; } /// diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index f168d8d45..6526d7c8a 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -562,12 +562,12 @@ namespace FlaxEditor.Windows { switch (match.Groups["level"].Value) { - case "error": - textBlock.Style = _output.ErrorStyle; - break; - case "warning": - textBlock.Style = _output.WarningStyle; - break; + case "error": + textBlock.Style = _output.ErrorStyle; + break; + case "warning": + textBlock.Style = _output.WarningStyle; + break; } textBlock.Tag = new TextBlockTag { diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index cba59191e..92894b537 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -258,13 +258,13 @@ void SplashScreen::OnDraw() return; // Title - const auto titleLength = _titleFont->MeasureTextInternal(GetTitle()); + const auto titleLength = _titleFont->MeasureText(GetTitle()); TextLayoutOptions layout; layout.Bounds = Rectangle(10 * s, 10 * s, width - 10 * s, 50 * s); layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Near; layout.Scale = Math::Min((width - 20 * s) / titleLength.X, 1.0f); - Render2D::DrawTextInternal(_titleFont, GetTitle(), Color::White, layout); + Render2D::DrawText(_titleFont, GetTitle(), Color::White, layout); // Subtitle String subtitle(_quote); @@ -279,14 +279,14 @@ void SplashScreen::OnDraw() layout.Scale = 1.0f; layout.HorizontalAlignment = TextAlignment::Far; layout.VerticalAlignment = TextAlignment::Far; - Render2D::DrawTextInternal(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); + Render2D::DrawText(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); // Additional info const float infoMargin = 6 * s; layout.Bounds = Rectangle(infoMargin, lightBarHeight + infoMargin, width - (2 * infoMargin), height - lightBarHeight - (2 * infoMargin)); layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Center; - Render2D::DrawTextInternal(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); + Render2D::DrawText(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); } bool SplashScreen::HasLoadedFonts() const diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 675c26bd1..54ad29a7b 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -7,8 +7,6 @@ #include "Engine/Core/Types/String.h" #include "Engine/Core/Collections/Dictionary.h" -class FontFallbackList; - /// /// The main game engine configuration service. Loads and applies game configuration. /// diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index e109a55e4..b639b9e64 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -6,7 +6,7 @@ #include "Engine/Graphics/Enums.h" #include "Engine/Graphics/PostProcessSettings.h" -class FontFallbackList; +class FontAsset; /// /// Graphics rendering settings. @@ -15,6 +15,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class { API_AUTO_SERIALIZATION(); DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings); + public: /// /// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts. @@ -121,16 +122,10 @@ public: PostProcessSettings PostProcessSettings; /// - /// Whether to enable font fallbacking globally. + /// The list of fallback fonts used for text rendering. Ignored if empty. /// - API_FIELD(Attributes = "EditorOrder(12000), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") - bool EnableFontFallback = true; - - /// - /// The fallback fonts used for text rendering, ignored if null. - /// - API_FIELD(Attributes = "EditorOrder(12005), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") - FontFallbackList* FallbackFonts; + API_FIELD(Attributes="EditorOrder(5000), EditorDisplay(\"Text\")") + Array> FallbackFonts; private: /// diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index 20f29288b..a9bfef470 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -8,7 +8,7 @@ #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Engine/EngineService.h" -#include "Engine/Render2D/Render2D.h" +#include "Engine/Render2D/Font.h" bool Graphics::UseVSync = false; Quality Graphics::AAQuality = Quality::Medium; @@ -70,9 +70,9 @@ void GraphicsSettings::Apply() Graphics::GIQuality = GIQuality; Graphics::PostProcessSettings = ::PostProcessSettings(); Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f); - - Render2D::EnableFontFallback = EnableFontFallback; - Render2D::FallbackFonts = FallbackFonts; +#if !USE_EDITOR // OptionsModule handles fallback fonts in Editor + Font::FallbackFonts = FallbackFonts; +#endif } void Graphics::DisposeDevice() diff --git a/Source/Engine/Render2D/FallbackFonts.cpp b/Source/Engine/Render2D/FallbackFonts.cpp deleted file mode 100644 index 7f81a42bb..000000000 --- a/Source/Engine/Render2D/FallbackFonts.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "FallbackFonts.h" -#include "FontManager.h" -#include "Engine/Core/Math/Math.h" - -FontFallbackList::FontFallbackList(const Array& fonts) - : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), - _fontAssets(fonts) -{ - -} - diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h deleted file mode 100644 index fb247e4e2..000000000 --- a/Source/Engine/Render2D/FallbackFonts.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -#include "Engine/Core/Collections/Array.h" -#include "Engine/Core/Collections/Dictionary.h" -#include "Font.h" -#include "FontAsset.h" - -struct TextRange; -class Font; -class FontAsset; - -/// -/// Defines a list of fonts that can be used as a fallback, ordered by priority. -/// -API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FontFallbackList : public ManagedScriptingObject -{ - DECLARE_SCRIPTING_TYPE_NO_SPAWN(FontFallbackList); -private: - Array _fontAssets; - - // Cache fallback fonts of various sizes - Dictionary*> _cache; - -public: - /// - /// Initializes a new instance of the class. - /// - /// The fallback font assets. - FontFallbackList(const Array& fonts); - - /// - /// Initializes a new instance of the class, exposed for C#. - /// - /// The fallback font assets. - /// The new instance. - API_FUNCTION() FORCE_INLINE static FontFallbackList* Create(const Array& fonts) { - return New(fonts); - } - - /// - /// Get the parent assets of fallback fonts. - /// - /// The font assets. - API_PROPERTY() FORCE_INLINE Array& GetFonts() { - return _fontAssets; - } - - /// - /// Set the fallback fonts. - /// - /// The parent assets of the new fonts. - API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { - _fontAssets = val; - } - - /// - /// Gets the fallback fonts with the given size. - /// - /// The size. - /// The generated fonts. - API_FUNCTION() FORCE_INLINE Array& GetFontList(float size) { - Array* result; - if (_cache.TryGet(size, result)) { - return *result; - } - - result = New>(_fontAssets.Count()); - auto& arr = *result; - for (int32 i = 0; i < _fontAssets.Count(); i++) - { - arr.Add(_fontAssets[i]->CreateFont(size)); - } - - _cache[size] = result; - return *result; - } - - - /// - /// Gets the index of the fallback font that should be used to render the char - /// - /// The char. - /// The primary font. - /// The number to return if none of the fonts can render. - /// -1 if char can be rendered with primary font, index if it matches a fallback font. - API_FUNCTION() FORCE_INLINE int32 GetCharFallbackIndex(Char c, Font* primaryFont = nullptr, int32 missing = -1) { - if (primaryFont && primaryFont->GetAsset()->ContainsChar(c)) { - return -1; - } - - int32 fontIndex = 0; - while (fontIndex < _fontAssets.Count() && _fontAssets[fontIndex] && !_fontAssets[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - if (fontIndex < _fontAssets.Count()) { - return fontIndex; - - } - - return missing; - } - - /// - /// Checks if every font is properly loaded. - /// - /// True if every font asset is non-null, otherwise false. - API_FUNCTION() FORCE_INLINE bool Verify() { - for (int32 i = 0; i < _fontAssets.Count(); i++) - { - if (!_fontAssets[i]) { - return false; - } - } - - return true; - } -}; diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 9a2037b3f..71eca1f59 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -6,7 +6,8 @@ #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "IncludeFreeType.h" -#include "FallbackFonts.h" + +Array, HeapAllocation> Font::FallbackFonts; Font::Font(FontAsset* parentAsset, float size) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)) @@ -33,7 +34,7 @@ Font::~Font() _asset->_fonts.Remove(this); } -void Font::GetCharacter(Char c, FontCharacterEntry& result) +void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback) { // Try to get the character or cache it if cannot be found if (!_characters.TryGet(c, result)) @@ -45,6 +46,20 @@ void Font::GetCharacter(Char c, FontCharacterEntry& result) if (_characters.TryGet(c, result)) return; + // Try to use fallback font if character is missing + if (enableFallback && !_asset->ContainsChar(c)) + { + for (int32 fallbackIndex = 0; fallbackIndex < FallbackFonts.Count(); fallbackIndex++) + { + FontAsset* fallbackFont = FallbackFonts.Get()[fallbackIndex].Get(); + if (fallbackFont && fallbackFont->ContainsChar(c)) + { + fallbackFont->CreateFont(GetSize())->GetCharacter(c, result, enableFallback); + return; + } + } + } + // Create character cache FontManager::AddNewEntry(this, c, result); @@ -88,7 +103,7 @@ void Font::CacheText(const StringView& text) FontCharacterEntry entry; for (int32 i = 0; i < text.Length(); i++) { - GetCharacter(text[i], entry); + GetCharacter(text[i], entry, false); } } @@ -103,26 +118,16 @@ void Font::Invalidate() _characters.Clear(); } -float Font::GetMaxHeight(FontFallbackList* fallbacks) const -{ - float height = GetHeight(); - auto& fallbackFonts = fallbacks->GetFontList(GetSize()); - for (int32 i = 0; i < fallbackFonts.Count(); i++) - { - height = Math::Max(height, static_cast(fallbackFonts[i]->GetHeight())); - } - - return height; -} - void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout) { + int32 textLength = text.Length(); + if (textLength == 0) + return; float cursorX = 0; int32 kerning; FontLineCache tmpLine; FontCharacterEntry entry; FontCharacterEntry previous; - int32 textLength = text.Length(); float scale = layout.Scale / FontManager::FontScale; float boundsWidth = layout.Bounds.GetWidth(); float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; @@ -131,10 +136,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = 0; tmpLine.LastCharIndex = -1; - if (textLength == 0) { - return; - } - int32 lastWrapCharIndex = INVALID_INDEX; float lastWrapCharX = 0; bool lastMoveLine = false; @@ -146,11 +147,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines float xAdvance = 0; int32 nextCharIndex = currentIndex + 1; - // Submit line if text ends - if (nextCharIndex == textLength) { - moveLine = true; - } - // Cache current character const Char currentChar = text[currentIndex]; const bool isWhitespace = StringUtils::IsWhitespace(currentChar); @@ -168,6 +164,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines { // Break line moveLine = true; + currentIndex++; tmpLine.LastCharIndex++; } else @@ -178,7 +175,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else { @@ -247,8 +244,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Reset line tmpLine.Location.Y += baseLinesDistance; - tmpLine.FirstCharIndex = nextCharIndex; - tmpLine.LastCharIndex = nextCharIndex - 1; + tmpLine.FirstCharIndex = currentIndex; + tmpLine.LastCharIndex = currentIndex - 1; cursorX = 0; lastWrapCharIndex = INVALID_INDEX; lastWrapCharX = 0; @@ -260,7 +257,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines } // Check if an additional line should be created - if (text[textLength - 1] == '\n') + if (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n') { // Add line tmpLine.Size.X = cursorX; @@ -305,262 +302,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) -{ - const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); - float cursorX = 0; - int32 kerning; - BlockedTextLineCache tmpLine; - FontBlockCache tmpBlock; - FontCharacterEntry entry; - FontCharacterEntry previous; - int32 textLength = text.Length(); - float scale = layout.Scale / FontManager::FontScale; - float boundsWidth = layout.Bounds.GetWidth(); - float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; - - tmpBlock.Location = Float2::Zero; - tmpBlock.Size = Float2::Zero; - tmpBlock.FirstCharIndex = 0; - tmpBlock.LastCharIndex = -1; - - tmpLine.Location = Float2::Zero; - tmpLine.Size = Float2::Zero; - tmpLine.Blocks = Array(); - - if (textLength == 0) { - return; - } - - int32 lastWrapCharIndex = INVALID_INDEX; - float lastWrapCharX = 0; - bool lastMoveLine = false; - // The index of the font used by the current block - int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], this); - // The maximum font height of the current line - float maxHeight = 0; - float maxAscender = 0; - float lastCursorX = 0; - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : this; - }; - - // Process each character to split text into single blocks - for (int32 currentIndex = 0; currentIndex < textLength;) - { - bool moveLine = false; - bool moveBlock = false; - float xAdvance = 0; - int32 nextCharIndex = currentIndex + 1; - - // Submit line and block if text ends - if (nextCharIndex == textLength) { - moveLine = moveBlock = true; - } - - // Cache current character - const Char currentChar = text[currentIndex]; - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); - if (isWrapChar && currentIndex != 0) - { - lastWrapCharIndex = currentIndex; - lastWrapCharX = cursorX; - } - - int32 nextFontIndex = currentFontIndex; - // Check if it's a newline character - if (currentChar == '\n') - { - // Break line - moveLine = moveBlock = true; - tmpBlock.LastCharIndex++; - } - else - { - // Get character entry - if (nextCharIndex < textLength) { - nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], this, currentFontIndex); - } - - // Get character entry - getFont(currentFontIndex)->GetCharacter(currentChar, entry); - - maxHeight = Math::Max(maxHeight, - static_cast(getFont(currentFontIndex)->GetHeight())); - maxAscender = Math::Max(maxAscender, - static_cast(getFont(currentFontIndex)->GetAscender())); - - // Move block if the font changes or text ends - if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { - moveBlock = true; - } - - // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && !moveBlock) - { - kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - previous = entry; - xAdvance = (kerning + entry.AdvanceX) * scale; - - // Check if character fits the line or skip wrapping - if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) - { - // Move character - cursorX += xAdvance; - tmpBlock.LastCharIndex++; - } - else if (layout.TextWrapping == TextWrapping::WrapWords) - { - if (lastWrapCharIndex != INVALID_INDEX) - { - // Skip moving twice for the same character - int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; - if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) - { - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - continue; - } - - // Move line - const Char wrapChar = text[lastWrapCharIndex]; - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; - - cursorX = lastWrapCharX; - if (StringUtils::IsWhitespace(wrapChar)) - { - // Skip whitespaces - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex + 1; - } - else - { - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex; - } - } - } - else if (layout.TextWrapping == TextWrapping::WrapChars) - { - // Move line - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < currentChar; - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; - } - } - - if (moveBlock) { - // Add block - tmpBlock.Size.X = lastCursorX - cursorX; - tmpBlock.Size.Y = baseLinesDistanceScale * getFont(currentFontIndex)->GetHeight(); - tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); - tmpBlock.FallbackFontIndex = currentFontIndex; - tmpLine.Blocks.Add(tmpBlock); - - // Reset block - tmpBlock.Location.X = cursorX; - tmpBlock.FirstCharIndex = nextCharIndex; - tmpBlock.LastCharIndex = nextCharIndex - 1; - - currentFontIndex = nextFontIndex; - lastCursorX = cursorX; - } - - // Check if move to another line - if (moveLine) - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.MaxAscender = maxAscender; - outputLines.Add(tmpLine); - - // Reset line - tmpLine.Blocks.Clear(); - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - cursorX = 0; - tmpBlock.Location.X = cursorX; - lastWrapCharIndex = INVALID_INDEX; - lastWrapCharX = 0; - previous.IsValid = false; - - // Reset max font height - maxHeight = 0; - maxAscender = 0; - lastCursorX = 0; - } - - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - } - - // Check if an additional line should be created - if (text[textLength - 1] == '\n') - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - outputLines.Add(tmpLine); - - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - } - - // Check amount of lines - if (outputLines.IsEmpty()) - return; - - float totalHeight = tmpLine.Location.Y; - - Float2 offset = Float2::Zero; - if (layout.VerticalAlignment == TextAlignment::Center) - { - offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; - } - else if (layout.VerticalAlignment == TextAlignment::Far) - { - offset.Y += layout.Bounds.GetHeight() - totalHeight; - } - for (int32 i = 0; i < outputLines.Count(); i++) - { - BlockedTextLineCache& line = outputLines[i]; - Float2 rootPos = line.Location + offset; - - // Fix upper left line corner to match desire text alignment - if (layout.HorizontalAlignment == TextAlignment::Center) - { - rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; - } - else if (layout.HorizontalAlignment == TextAlignment::Far) - { - rootPos.X += layout.Bounds.GetWidth() - line.Size.X; - } - - line.Location = rootPos; - - // Align all blocks to center in case they have different heights - for (int32 j = 0; j < line.Blocks.Count(); j++) - { - FontBlockCache& block = line.Blocks[j]; - block.Location.Y += (line.MaxAscender - getFont(block.FallbackFontIndex)->GetAscender()) / 2; - } - } -} - -Float2 Font::MeasureTextInternal(const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -581,28 +323,7 @@ Float2 Font::MeasureTextInternal(const StringView& text, const TextLayoutOptions return max; } -Float2 Font::MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return Float2::Zero; - - // Process text - Array lines; - ProcessText(fallbacks, text, lines, layout); - - // Calculate bounds - Float2 max = Float2::Zero; - for (int32 i = 0; i < lines.Count(); i++) - { - const BlockedTextLineCache& line = lines[i]; - max = Float2::Max(max, line.Location + line.Size); - } - - return max; -} - -int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -639,7 +360,7 @@ int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, // Apply kerning if (!isWhitespace && previous.IsValid) { - x += GetKerning(previous.Character, entry.Character); + x += entry.Font->GetKerning(previous.Character, entry.Character); } previous = entry; @@ -676,107 +397,7 @@ int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, return smallestIndex; } -int32 Font::HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.Length() <= 0) - return 0; - - // Process text - const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); - Array lines; - ProcessText(fallbacks, text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - - // Offset position to match lines origin space - Float2 rootOffset = layout.Bounds.Location + lines.First().Location; - Float2 testPoint = location - rootOffset; - - // Get block which may intersect with the position (it's possible because lines have fixed height) - int32 lineIndex = 0; - while (lineIndex < lines.Count()) - { - if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { - break; - } - - lineIndex++; - } - lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); - const BlockedTextLineCache& line = lines[lineIndex]; - - int32 blockIndex = 0; - while (blockIndex < line.Blocks.Count() - 1) - { - if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { - break; - } - - blockIndex++; - } - const FontBlockCache& block = line.Blocks[blockIndex]; - float x = line.Location.X; - - // Check all characters in the line to find hit point - FontCharacterEntry previous; - FontCharacterEntry entry; - int32 smallestIndex = INVALID_INDEX; - float dst, smallestDst = MAX_float; - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : this; - }; - - for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - - getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Test - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Found closer character - smallestIndex = currentIndex; - smallestDst = dst; - } - else if (dst > smallestDst) - { - // Current char is worse so return the best result - return smallestIndex; - } - - // Move - x += entry.AdvanceX * scale; - } - - // Test line end edge - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Pointer is behind the last character in the line - smallestIndex = block.LastCharIndex; - - // Fix for last line - if (lineIndex == lines.Count() - 1) - smallestIndex++; - } - - return smallestIndex; -} - -Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -788,7 +409,7 @@ Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const ASSERT(lines.HasItems()); float scale = layout.Scale / FontManager::FontScale; float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location; + Float2 rootOffset = layout.Bounds.Location + lines.First().Location; // Find line with that position FontCharacterEntry previous; @@ -813,7 +434,7 @@ Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const // Apply kerning if (!isWhitespace && previous.IsValid) { - x += GetKerning(previous.Character, entry.Character); + x += entry.Font->GetKerning(previous.Character, entry.Character); } previous = entry; @@ -827,71 +448,7 @@ Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const } // Position after last character in the last line - return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); -} - -Float2 Font::GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return layout.Bounds.Location; - - // Process text - const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); - Array lines; - ProcessText(fallbacks, text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - float baseLinesDistance = layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location; - - // Find line with that position - FontCharacterEntry previous; - FontCharacterEntry entry; - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : this; - }; - - for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) - { - const BlockedTextLineCache& line = lines[lineIndex]; - for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) - { - const FontBlockCache& block = line.Blocks[blockIndex]; - // Check if desire position is somewhere inside characters in line range - if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) - { - float x = line.Location.X + block.Location.X; - float y = line.Location.Y + block.Location.Y; - - // Check all characters in the line - for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Move - x += entry.AdvanceX * scale; - } - - // Upper left corner of the character - return rootOffset + Float2(x, y); - } - } - } - - // Position after last character in the last line - return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); + return rootOffset + Float2(lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } void Font::FlushFaceSize() const diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 1fb41d032..cd92103cb 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,12 +8,9 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" -#include "Render2D.h" class FontAsset; -class FontFallbackList; struct FontTextureAtlasSlot; -struct BlockedTextLineCache; // The default DPI that engine is using #define DefaultDPI 96 @@ -38,7 +35,7 @@ API_STRUCT(NoDefault) struct TextRange /// /// Gets the range length. /// - int32 Length() const + FORCE_INLINE int32 Length() const { return EndIndex - StartIndex; } @@ -46,7 +43,7 @@ API_STRUCT(NoDefault) struct TextRange /// /// Gets a value indicating whether range is empty. /// - bool IsEmpty() const + FORCE_INLINE bool IsEmpty() const { return (EndIndex - StartIndex) <= 0; } @@ -56,7 +53,7 @@ API_STRUCT(NoDefault) struct TextRange /// /// The index. /// true if range contains the specified character index; otherwise, false. - bool Contains(int32 index) const + FORCE_INLINE bool Contains(int32 index) const { return index >= StartIndex && index < EndIndex; } @@ -122,74 +119,6 @@ struct TIsPODType enum { Value = true }; }; -/// -/// The font block info generated during text processing. -/// A block means a range of text that belongs to the same line and can be rendered with the same font. -/// -API_STRUCT(NoDefault) struct FontBlockCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(FontBlockCache); - - /// - /// The root position of the block (upper left corner), relative to line. - /// - API_FIELD() Float2 Location; - - /// - /// The size of the current block - /// - API_FIELD() Float2 Size; - - /// - /// The first character index (from the input text). - /// - API_FIELD() int32 FirstCharIndex; - - /// - /// The last character index (from the input text), inclusive. - /// - API_FIELD() int32 LastCharIndex; - - /// - /// Indicates the fallback font to render this block with, -1 if doesn't require fallback. - /// - API_FIELD() int32 FallbackFontIndex; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -/// -/// Line of font blocks info generated during text processing. -/// -API_STRUCT(NoDefault) struct BlockedTextLineCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(BlockedTextLineCache); - - /// - /// The root position of the line (upper left corner). - /// - API_FIELD() Float2 Location; - - /// - /// The line bounds (width and height). - /// - API_FIELD() Float2 Size; - - /// - /// The maximum ascender of the line. - /// - API_FIELD() float MaxAscender; - - /// - /// The blocks that belongs to this line - /// - API_FIELD() Array Blocks; -}; - // Font glyph metrics: // // xmin xmax @@ -281,6 +210,11 @@ API_STRUCT(NoDefault) struct FontCharacterEntry /// The slot in texture atlas, containing the pixel data of the glyph. /// API_FIELD() const FontTextureAtlasSlot* Slot; + + /// + /// The owner font. + /// + API_FIELD() const class Font* Font; }; template<> @@ -296,8 +230,8 @@ API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API Font : public ManagedScriptingOb { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font); friend FontAsset; -private: +private: FontAsset* _asset; float _size; int32 _height; @@ -309,7 +243,6 @@ private: mutable Dictionary _kerningTable; public: - /// /// Initializes a new instance of the class. /// @@ -323,6 +256,10 @@ public: ~Font(); public: + /// + /// The active fallback fonts. + /// + API_FIELD() static Array, HeapAllocation> FallbackFonts; /// /// Gets parent font asset that contains font family used by this font. @@ -373,13 +310,13 @@ public: } public: - /// /// Gets character entry. /// /// The character. /// The output character entry. - void GetCharacter(Char c, FontCharacterEntry& result); + /// True if fallback to secondary font when the primary font doesn't contains this character. + void GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback = true); /// /// Gets the kerning amount for a pair of characters. @@ -401,33 +338,8 @@ public: API_FUNCTION() void Invalidate(); public: - /// - /// Gets the maximum height among the font and the fallback fonts. - /// - /// The fallback fonts. - /// The maximum height. - API_FUNCTION() float GetMaxHeight(FontFallbackList* fallbacks) const; - - /// - /// Gets the maximum height among the font and the fallback fonts, uses the default font defined in . - /// - /// The fallback fonts. - /// The maximum height. - API_FUNCTION() FORCE_INLINE float GetMaxHeight() const - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) - { - return GetMaxHeight(Render2D::FallbackFonts); - } - else - { - return GetHeight(); - } - } - - /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The layout properties. @@ -435,12 +347,12 @@ public: void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(text, lines, layout); @@ -448,13 +360,13 @@ public: } /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(textRange.Substring(text), lines, layout); @@ -462,7 +374,7 @@ public: } /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The output lines list. @@ -472,7 +384,7 @@ public: } /// - /// Processes text to get cached lines for rendering, with font fallbacking disabled. + /// Processes text to get cached lines for rendering. /// /// The input text. /// The input text range (substring range of the input text parameter). @@ -483,349 +395,81 @@ public: } /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - void ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(fallbacks, text, lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(fallbacks, textRange.Substring(text), lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text) - { - return ProcessText(fallbacks, text, TextLayoutOptions()); - } - - /// - /// Processes text to get cached lines for rendering, using custom fallback options. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return ProcessText(fallbacks, textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// Measures minimum size of the rectangle that will be needed to draw given text. /// /// The input text to test. /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// Measures minimum size of the rectangle that will be needed to draw given text. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { - return MeasureTextInternal(textRange.Substring(text), layout); + return MeasureText(textRange.Substring(text), layout); } /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text) - { - return MeasureTextInternal(text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return MeasureTextInternal(fallbacks, textRange.Substring(text), layout); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text) - { - return MeasureTextInternal(fallbacks, text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureTextInternal(fallbacks, textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, text, layout); - } - else { - return MeasureTextInternal(text, layout); - } - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), layout); - } - else { - return MeasureTextInternal(textRange.Substring(text), layout); - } - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// Measures minimum size of the rectangle that will be needed to draw given text /// . /// The input text to test. /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, text, TextLayoutOptions()); - } - else { - return MeasureTextInternal(text, TextLayoutOptions()); - } + return MeasureText(text, TextLayoutOptions()); } /// - /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// Measures minimum size of the rectangle that will be needed to draw given text /// . /// The input text to test. /// The input text range (substring range of the input text parameter). /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), TextLayoutOptions()); - } - else { - return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); - } + return MeasureText(textRange.Substring(text), TextLayoutOptions()); } /// - /// Calculates hit character index at given location, with font fallbacking disabled. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestTextInternal(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates hit character index at given location, with font fallbacking disabled. + /// Calculates hit character index at given location. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { - return HitTestTextInternal(textRange.Substring(text), location, layout); + return HitTestText(textRange.Substring(text), location, layout); } /// - /// Calculates hit character index at given location, with font fallbacking disabled. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, const Float2& location) - { - return HitTestTextInternal(text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, with font fallbacking disabled. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, using custom fallback options. + /// Calculates hit character index at given location. /// /// The input text to test. /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates hit character index at given location, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return HitTestTextInternal(fallbacks, textRange.Substring(text), location, layout); - } - - /// - /// Calculates hit character index at given location, using custom fallback options. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location) - { - return HitTestTextInternal(fallbacks, text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestTextInternal(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, text, location, layout); - } - else { - return HitTestTextInternal(text, location, layout); - } - } - - /// - /// Calculates hit character index at given location, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, layout); - } - else { - return HitTestTextInternal(textRange.Substring(text), location, layout); - } - - } - - /// - /// Calculates hit character index at given location, follows the fallback settings defined in . + /// Calculates hit character index at given location. /// /// The input text to test. /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, text, location, TextLayoutOptions()); - } - else { - return HitTestTextInternal(text, location, TextLayoutOptions()); - } + return HitTestText(text, location, TextLayoutOptions()); } /// - /// Calculates hit character index at given location, follows the fallback settings defined in . + /// Calculates hit character index at given location. /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -833,156 +477,44 @@ public: /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, TextLayoutOptions()); - } - else { - return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); - } + return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); } /// - /// Calculates character position for given text and character index, with font fallbacking disabled. + /// Calculates character position for given text and character index. /// /// The input text to test. /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPositionInternal(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates character position for given text and character index, with font fallbacking disabled. + /// Calculates character position for given text and character index. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { - return GetCharPositionInternal(textRange.Substring(text), index, layout); + return GetCharPosition(textRange.Substring(text), index, layout); } /// - /// Calculates character position for given text and character index, with font fallbacking disabled. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, int32 index) - { - return GetCharPositionInternal(text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, with font fallbacking disabled. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, layout); - } - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index) - { - return GetCharPositionInternal(fallbacks, text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, using custom fallback options. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, text, index, layout); - } - else { - return GetCharPositionInternal(text, index, layout); - } - } - - /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, layout); - } - else { - return GetCharPositionInternal(textRange.Substring(text), index, layout); - } - } - - /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// Calculates character position for given text and character index /// /// The input text to test. /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, text, index, TextLayoutOptions()); - } - else { - return GetCharPositionInternal(text, index, TextLayoutOptions()); - } + return GetCharPosition(text, index, TextLayoutOptions()); } /// - /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// Calculates character position for given text and character index /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -990,12 +522,7 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) { - if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { - return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, TextLayoutOptions()); - } - else { - return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); - } + return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); } /// @@ -1004,7 +531,6 @@ public: void FlushFaceSize() const; public: - // [Object] String ToString() const override; }; diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index ea629367f..53fbdc930 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -199,25 +199,29 @@ bool FontAsset::Save(const StringView& path) #endif - -/// -/// Check if the font contains the glyph of a char -/// -/// The char to test. -/// True if the font contains the glyph of the char, otherwise false. - -bool FontAsset::ContainsChar(Char c) const { - return FT_Get_Char_Index(GetFTFace(), c) > 0; +bool FontAsset::ContainsChar(Char c) const +{ + return FT_Get_Char_Index(_face, c) > 0; } void FontAsset::Invalidate() { ScopeLock lock(Locker); - for (auto font : _fonts) - { font->Invalidate(); - } +} + +uint64 FontAsset::GetMemoryUsage() const +{ + Locker.Lock(); + uint64 result = BinaryAsset::GetMemoryUsage(); + result += sizeof(FontAsset) - sizeof(BinaryAsset); + result += sizeof(FT_FaceRec); + result += _fontFile.Length(); + for (auto font : _fonts) + result += sizeof(Font); + Locker.Unlock(); + return result; } bool FontAsset::init(AssetInitData& initData) diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h index f3c909911..a93276bf1 100644 --- a/Source/Engine/Render2D/FontAsset.h +++ b/Source/Engine/Render2D/FontAsset.h @@ -93,6 +93,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API FontAsset : public BinaryAsset { DECLARE_BINARY_ASSET_HEADER(FontAsset, 3); friend Font; + private: FT_Face _face; FontOptions _options; @@ -175,7 +176,7 @@ public: #endif /// - /// Check if the font contains the glyph of a char + /// Check if the font contains the glyph of a char. /// /// The char to test. /// True if the font contains the glyph of the char, otherwise false. @@ -186,6 +187,10 @@ public: /// API_FUNCTION() void Invalidate(); +public: + // [BinaryAsset] + uint64 GetMemoryUsage() const override; + protected: // [BinaryAsset] bool init(AssetInitData& initData) override; diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index 5b08c1d87..2e1ba8c6b 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -27,7 +27,6 @@ using namespace FontManagerImpl; class FontManagerService : public EngineService { public: - FontManagerService() : EngineService(TEXT("Font Manager"), -700) { @@ -155,9 +154,12 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) // Get the index to the glyph in the font face const FT_UInt glyphIndex = FT_Get_Char_Index(face, c); - if (glyphIndex == 0) { +#if !BUILD_RELEASE + if (glyphIndex == 0) + { LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c); } +#endif // Load the glyph const FT_Error error = FT_Load_Glyph(face, glyphIndex, glyphFlags); @@ -287,6 +289,7 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) entry.UVSize.X = static_cast(slot->Width - 2 * padding); entry.UVSize.Y = static_cast(slot->Height - 2 * padding); entry.Slot = slot; + entry.Font = font; return false; } diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index fa52baff0..34cb2c693 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -3,7 +3,6 @@ #include "Render2D.h" #include "Font.h" #include "FontManager.h" -#include "FallbackFonts.h" #include "FontTextureAtlas.h" #include "RotatedRectangle.h" #include "SpriteAtlas.h" @@ -55,7 +54,7 @@ const bool DownsampleForBlur = false; PACK_STRUCT(struct Data { Matrix ViewProjection; -}); + }); PACK_STRUCT(struct BlurData { Float2 InvBufferSize; @@ -63,7 +62,7 @@ PACK_STRUCT(struct BlurData { float Dummy0; Float4 Bounds; Float4 WeightAndOffsets[RENDER2D_BLUR_MAX_SAMPLES / 2]; -}); + }); enum class DrawCallType : byte { @@ -181,9 +180,7 @@ struct ClipMask Rectangle Bounds; }; -Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping; -bool Render2D::EnableFontFallback = true; -FontFallbackList* Render2D::FallbackFonts = nullptr; +Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping | RenderingFeatures::FallbackFonts; namespace { @@ -197,7 +194,6 @@ namespace // Drawing Array DrawCalls; Array Lines; - Array BlockedTextLines; Array Lines2; bool IsScissorsRectEmpty; bool IsScissorsRectEnabled; @@ -1141,12 +1137,12 @@ void DrawBatch(int32 startIndex, int32 count) } // Draw - Context->BindVB(ToSpan(&vb, 1)); // TODO: reduce bindings frequency - Context->BindIB(ib); // TODO: reduce bindings frequency + Context->BindVB(ToSpan(&vb, 1)); + Context->BindIB(ib); Context->DrawIndexed(countIb, 0, d.StartIB); } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1163,6 +1159,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& FontCharacterEntry previous; int32 kerning; float scale = 1.0f / FontManager::FontScale; + const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts); // Render all characters FontCharacterEntry entry; @@ -1178,7 +1175,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& drawCall.AsChar.Mat = nullptr; } Float2 pointer = location; - for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) + for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) { // Cache current character const Char currentChar = text[currentIndex]; @@ -1187,7 +1184,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& if (currentChar != '\n') { // Get character entry - font->GetCharacter(currentChar, entry); + font->GetCharacter(currentChar, entry, enableFallbackFonts); // 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) @@ -1214,7 +1211,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = font->GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else { @@ -1254,12 +1251,12 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& } } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawTextInternal(font, textRange.Substring(text), color, location, customMaterial); + DrawText(font, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1277,6 +1274,7 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& FontCharacterEntry previous; int32 kerning; float scale = layout.Scale / FontManager::FontScale; + const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts); // Process text to get lines Lines.Clear(); @@ -1303,10 +1301,14 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& // Render all characters from the line for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex; charIndex++) { - const Char c = text[charIndex]; - if (c != '\n') + // Cache current character + const Char currentChar = text[charIndex]; + + // Check if it isn't a newline character + if (currentChar != '\n') { - font->GetCharacter(c, entry); + // Get character entry + font->GetCharacter(currentChar, entry, enableFallbackFonts); // 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) @@ -1328,10 +1330,10 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& } // Get kerning - const bool isWhitespace = StringUtils::IsWhitespace(c); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); if (!isWhitespace && previous.IsValid) { - kerning = font->GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else { @@ -1367,317 +1369,9 @@ void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& } } -void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawTextInternal(font, textRange.Substring(text), color, layout, customMaterial); -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) -{ - RENDER2D_CHECK_RENDERING_STATE; - - // Check if there is no need to do anything - if (font == nullptr || text.Length() < 0) - return; - - // Temporary data - const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); - uint32 fontAtlasIndex = 0; - FontTextureAtlas* fontAtlas = nullptr; - Float2 invAtlasSize = Float2::One; - FontCharacterEntry previous; - int32 kerning; - float scale = 1.0f / FontManager::FontScale; - - // Process text to get lines - Array maxAscenders; - - // Render all characters - FontCharacterEntry entry; - Render2DDrawCall drawCall; - if (customMaterial) - { - drawCall.Type = DrawCallType::DrawCharMaterial; - drawCall.AsChar.Mat = customMaterial; - } - else - { - drawCall.Type = DrawCallType::DrawChar; - drawCall.AsChar.Mat = nullptr; - } - - int32 lineIndex = 0; - maxAscenders.Add(0); - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : font; - }; - - // Preprocess the text to determine vertical offset of blocks - for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) - { - const Char c = text[currentIndex]; - if (c != '\n') { - int32 fontIndex = fallbacks->GetCharFallbackIndex(c, font); - maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], - static_cast(getFont(fontIndex)->GetAscender())); - } - else { - lineIndex++; - maxAscenders.Add(0); - } - } - - lineIndex = 0; - // The following code cut the text into blocks, according to the font used to render - Float2 pointer = location; - // The starting index of the current block - int32 startIndex = 0; - // The index of the font used by the current block - int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], font); - // The maximum font height of the current line - float maxHeight = 0; - for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - int32 nextCharIndex = currentIndex + 1; - bool moveBlock = false; - bool moveLine = false; - int32 nextFontIndex = currentFontIndex; - - // Submit block if text ends - if (nextCharIndex == text.Length()) { - moveBlock = true; - } - - // Check if it isn't a newline character - if (currentChar != '\n') - { - // Get character entry - if (nextCharIndex < text.Length()) { - nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], font); - } - - if (nextFontIndex != currentFontIndex) { - moveBlock = true; - } - } - else - { - // Move - moveLine = moveBlock = true; - } - - if (moveBlock) { - // Render the pending block before beginning the new block - auto fontHeight = getFont(currentFontIndex)->GetHeight(); - maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = getFont(currentFontIndex)->GetDescender(); - for (int32 renderIndex = startIndex; renderIndex <= currentIndex; renderIndex++) - { - // Get character entry - getFont(currentFontIndex)->GetCharacter(text[renderIndex], entry); - - // 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) - { - // Get texture atlas that contains current character - fontAtlasIndex = entry.TextureIndex; - fontAtlas = FontManager::GetAtlas(fontAtlasIndex); - if (fontAtlas) - { - fontAtlas->EnsureTextureCreated(); - drawCall.AsChar.Tex = fontAtlas->GetTexture(); - invAtlasSize = 1.0f / fontAtlas->GetSize(); - } - else - { - drawCall.AsChar.Tex = nullptr; - invAtlasSize = 1.0f; - } - } - - // Check if character is a whitespace - const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); - - // Get kerning - if (!isWhitespace && previous.IsValid) - { - kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - pointer.X += kerning * scale; - 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 + (fontHeight + fontDescender - entry.OffsetY) * scale; - - Rectangle charRect(x, y + (maxAscenders[lineIndex] - getFont(currentFontIndex)->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); - - Float2 upperLeftUV = entry.UV * invAtlasSize; - Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; - - // Add draw call - drawCall.StartIB = IBIndex; - drawCall.CountIB = 6; - DrawCalls.Add(drawCall); - WriteRect(charRect, color, upperLeftUV, rightBottomUV); - } - - // Move - pointer.X += entry.AdvanceX * scale; - } - - if (moveLine) { - pointer.X = location.X; - pointer.Y += maxHeight * scale; - // Clear max height - maxHeight = 0; - lineIndex++; - } - - // Start new block - startIndex = nextCharIndex; - currentFontIndex = nextFontIndex; - } - } -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) -{ - DrawTextInternal(font, fallbacks, textRange.Substring(text), color, location, customMaterial); -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) -{ - RENDER2D_CHECK_RENDERING_STATE; - - // Check if there is no need to do anything - if (font == nullptr || text.IsEmpty() || layout.Scale <= ZeroTolerance) - return; - - // Temporary data - const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); - uint32 fontAtlasIndex = 0; - FontTextureAtlas* fontAtlas = nullptr; - Float2 invAtlasSize = Float2::One; - FontCharacterEntry previous; - int32 kerning; - float scale = layout.Scale / FontManager::FontScale; - - // Process text to get lines - BlockedTextLines.Clear(); - font->ProcessText(fallbacks, text, BlockedTextLines, layout); - - // Render all lines - FontCharacterEntry entry; - Render2DDrawCall drawCall; - if (customMaterial) - { - drawCall.Type = DrawCallType::DrawCharMaterial; - drawCall.AsChar.Mat = customMaterial; - } - else - { - drawCall.Type = DrawCallType::DrawChar; - drawCall.AsChar.Mat = nullptr; - } - - auto getFont = [&](int32 index)->Font* { - return index >= 0 ? fallbackFonts[index] : font; - }; - - for (int32 lineIndex = 0; lineIndex < BlockedTextLines.Count(); lineIndex++) - { - const BlockedTextLineCache& line = BlockedTextLines[lineIndex]; - for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) - { - const FontBlockCache& block = BlockedTextLines[lineIndex].Blocks[blockIndex]; - auto fontHeight = getFont(block.FallbackFontIndex)->GetHeight(); - auto fontDescender = getFont(block.FallbackFontIndex)->GetDescender(); - Float2 pointer = line.Location + block.Location; - - for (int32 charIndex = block.FirstCharIndex; charIndex <= block.LastCharIndex; charIndex++) - { - Char c = text[charIndex]; - if (c == '\n') - { - continue; - } - - // Get character entry - getFont(block.FallbackFontIndex)->GetCharacter(c, entry); - - // 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) - { - // Get texture atlas that contains current character - fontAtlasIndex = entry.TextureIndex; - fontAtlas = FontManager::GetAtlas(fontAtlasIndex); - if (fontAtlas) - { - fontAtlas->EnsureTextureCreated(); - invAtlasSize = 1.0f / fontAtlas->GetSize(); - drawCall.AsChar.Tex = fontAtlas->GetTexture(); - } - else - { - invAtlasSize = 1.0f; - drawCall.AsChar.Tex = nullptr; - } - } - - // Get kerning - const bool isWhitespace = StringUtils::IsWhitespace(c); - if (!isWhitespace && previous.IsValid) - { - kerning = getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - pointer.X += (float)kerning * scale; - 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((fontHeight + fontDescender) * scale); - - Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); - charRect.Offset(layout.Bounds.Location); - - Float2 upperLeftUV = entry.UV * invAtlasSize; - Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; - - // Add draw call - drawCall.StartIB = IBIndex; - drawCall.CountIB = 6; - DrawCalls.Add(drawCall); - WriteRect(charRect, color, upperLeftUV, rightBottomUV); - } - - // Move - pointer.X += entry.AdvanceX * scale; - } - } - } -} - -void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) -{ - DrawTextInternal(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); + DrawText(font, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) @@ -2181,22 +1875,22 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, { RENDER2D_CHECK_RENDERING_STATE; - // Find amount of blocks to use + // Find amount of segments to use const Float2 d1 = p2 - p1; const Float2 d2 = p3 - p2; const Float2 d3 = p4 - p3; const float len = d1.Length() + d2.Length() + d3.Length(); - const int32 blockCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); - const float blockCountInv = 1.0f / blockCount; + const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); + const float segmentCountInv = 1.0f / segmentCount; - // Draw blocked curve + // Draw segmented curve Float2 p; AnimationUtils::Bezier(p1, p2, p3, p4, 0, p); Lines2.Clear(); Lines2.Add(p); - for (int32 i = 1; i <= blockCount; i++) + for (int32 i = 1; i <= segmentCount; i++) { - const float t = i * blockCountInv; + const float t = i * segmentCountInv; AnimationUtils::Bezier(p1, p2, p3, p4, t, p); Lines2.Add(p); } diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index b36f155df..c4d9e81b4 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -1,13 +1,11 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using FlaxEngine.GUI; using System; namespace FlaxEngine { partial class Render2D { - /// /// Pushes transformation layer. /// @@ -102,7 +100,7 @@ namespace FlaxEngine } /// - /// Draws a text, follows the font fallback settings defined in . + /// Draws a text. /// /// The font to use. /// The text to render. @@ -128,7 +126,7 @@ namespace FlaxEngine } /// - /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). Follows the font fallback settings defined in . + /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). /// /// The font to use. /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 6657d8542..5e5e952dc 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,7 +15,6 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; -class FontFallbackList; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -34,7 +33,7 @@ API_CLASS(Static) class FLAXENGINE_API Render2D /// /// The rendering features and options flags. /// - API_ENUM(Attributes = "Flags") enum class RenderingFeatures + API_ENUM(Attributes="Flags") enum class RenderingFeatures { /// /// The none. @@ -45,6 +44,11 @@ API_CLASS(Static) class FLAXENGINE_API Render2D /// Enables automatic geometry vertices snapping to integer coordinates in screen space. Reduces aliasing and sampling artifacts. Might be disabled for 3D projection viewport or for complex UI transformations. /// VertexSnapping = 1, + + /// + /// Enables automatic characters usage from fallback fonts. + /// + FallbackFonts = 2, }; struct CustomData @@ -54,7 +58,6 @@ API_CLASS(Static) class FLAXENGINE_API Render2D }; public: - /// /// Checks if interface is during rendering phrase (Draw calls may be performed without failing). /// @@ -70,10 +73,6 @@ public: /// API_FIELD() static RenderingFeatures Features; - API_FIELD() static bool EnableFontFallback; - - API_FIELD() static FontFallbackList* FallbackFonts; - /// /// Called when frame rendering begins by the graphics device. /// @@ -180,17 +179,17 @@ public: public: /// - /// Draws a text, with font fallbacking disabled. + /// Draws a text. /// /// The font to use. /// The text to render. /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text, with font fallbacking disabled. + /// Draws a text. /// /// The font to use. /// The text to render. @@ -198,20 +197,20 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting, with font fallbacking disabled. + /// Draws a text with formatting. /// /// The font to use. /// The text to render. /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting, with font fallbacking disabled. + /// Draws a text with formatting. /// /// The font to use. /// The text to render. @@ -219,120 +218,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text location. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text with formatting, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text with formatting, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text with formatting, using custom fallback options. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); - - /// - /// Draws a text, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The text color. - /// The text location. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, color, location, customMaterial); - } - else { - DrawTextInternal(font, text, color, location, customMaterial); - } - } - - /// - /// Draws a text, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text location. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, textRange, color, location, customMaterial); - } - else { - DrawTextInternal(font, text, textRange, color, location, customMaterial); - } - } - - /// - /// Draws a text with formatting, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, color, layout, customMaterial); - } - else { - DrawTextInternal(font, text, color, layout, customMaterial); - } - } - - /// - /// Draws a text with formatting, follows the fallback settings defined in . - /// - /// The font to use. - /// The text to render. - /// The input text range (substring range of the input text parameter). - /// The text color. - /// The text layout properties. - /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { - if (EnableFontFallback && FallbackFonts) { - DrawTextInternal(font, FallbackFonts, text, textRange, color, layout, customMaterial); - } - else { - DrawTextInternal(font, text, textRange, color, layout, customMaterial); - } - } + API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Fills a rectangle area. @@ -571,3 +457,5 @@ public: /// The color. API_FUNCTION() static void FillTriangle(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color); }; + +DECLARE_ENUM_OPERATORS(Render2D::RenderingFeatures); diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index a4cfd76b1..188333ff1 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -294,12 +294,14 @@ namespace FlaxEngine style.DragWindow = style.BackgroundSelected * 0.7f; // Use optionally bundled default font (matches Editor) - FontAsset defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); - - style.FontTitle = new FontReference(defaultFont, 18).GetFont(); - style.FontLarge = new FontReference(defaultFont, 14).GetFont(); - style.FontMedium = new FontReference(defaultFont, 9).GetFont(); - style.FontSmall = new FontReference(defaultFont, 9).GetFont(); + var defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); + if (defaultFont) + { + style.FontTitle = defaultFont.CreateFont(18); + style.FontLarge = defaultFont.CreateFont(14); + style.FontMedium = defaultFont.CreateFont(9); + style.FontSmall = defaultFont.CreateFont(9); + } Style.Current = style; } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 8de54fda3..3c7c04fb2 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -182,12 +182,6 @@ namespace FlaxEngine.GUI set => _autoFitTextRange = value; } - /// - /// Gets or sets whether to fallback when the primary font cannot render a char. - /// - [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] - public bool EnableFontFallback { get; set; } = true; - /// /// Initializes a new instance of the class. /// @@ -239,23 +233,7 @@ namespace FlaxEngine.GUI } } - if (EnableFontFallback) - { - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); - } - else - { - var layout = new TextLayoutOptions - { - Bounds = rect, - HorizontalAlignment = hAlignment, - VerticalAlignment = wAlignment, - TextWrapping = Wrapping, - Scale = scale, - BaseLinesGapScale = BaseLinesGapScale, - }; - Render2D.DrawTextInternal(_font.GetFont(), _text, color, ref layout, Material); - } + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -276,8 +254,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = EnableFontFallback ? - font.MeasureText(_text, ref layout) : font.MeasureTextInternal(_text, ref layout); + _textSize = font.MeasureText(_text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index ab01df12b..2270136ae 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System; using System.Collections.Generic; using System.Runtime.InteropServices; using FlaxEngine.Utilities; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index df8e0be7c..b4351da75 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -213,18 +213,18 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "top": - style.Alignment = TextBlockStyle.Alignments.Top; - break; - case "bottom": - style.Alignment = TextBlockStyle.Alignments.Bottom; - break; - case "middle": - style.Alignment = TextBlockStyle.Alignments.Middle; - break; - case "baseline": - style.Alignment = TextBlockStyle.Alignments.Baseline; - break; + case "top": + style.Alignment = TextBlockStyle.Alignments.Top; + break; + case "bottom": + style.Alignment = TextBlockStyle.Alignments.Bottom; + break; + case "middle": + style.Alignment = TextBlockStyle.Alignments.Middle; + break; + case "baseline": + style.Alignment = TextBlockStyle.Alignments.Baseline; + break; } } context.StyleStack.Push(style); @@ -245,15 +245,15 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "left": - style.Alignment = TextBlockStyle.Alignments.Left; - break; - case "right": - style.Alignment = TextBlockStyle.Alignments.Right; - break; - case "center": - style.Alignment = TextBlockStyle.Alignments.Center; - break; + case "left": + style.Alignment = TextBlockStyle.Alignments.Left; + break; + case "right": + style.Alignment = TextBlockStyle.Alignments.Right; + break; + case "center": + style.Alignment = TextBlockStyle.Alignments.Center; + break; } } context.StyleStack.Push(style); diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 46f0fb1ad..438a7e3d8 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -316,7 +316,6 @@ namespace FlaxEngine.GUI color = textBlock.Style.ShadowColor; if (!enabled) color *= 0.6f; - // We don't need font fallbacks for rich text since the font is user-selected Render2D.DrawText(font, _text, ref textBlock.Range, color, textBlock.Bounds.Location + textBlock.Style.ShadowOffset, textBlock.Style.CustomMaterial); } diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 53266a1b2..ee4f744a6 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,8 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. - -using System.ComponentModel; - namespace FlaxEngine.GUI { /// @@ -67,12 +64,6 @@ namespace FlaxEngine.GUI [EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("The color of the selection (Transparent if not used).")] public Color SelectionColor { get; set; } - /// - /// Gets or sets whether to fallback when the primary font cannot render a char. - /// - [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] - public bool EnableFontFallback { get; set; } = true; - /// /// Initializes a new instance of the class. /// @@ -112,8 +103,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return EnableFontFallback ? font.MeasureText(_text, ref _layout) : - font.MeasureTextInternal(_text, ref _layout); + return font.MeasureText(_text, ref _layout); } /// @@ -126,9 +116,8 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = (EnableFontFallback ? font.GetMaxHeight() : font.Height) / DpiScale; - return EnableFontFallback ? font.GetCharPosition(_text, index, ref _layout) : - font.GetCharPositionInternal(_text, index, ref _layout); + height = font.Height / DpiScale; + return font.GetCharPosition(_text, index, ref _layout); } /// @@ -140,8 +129,7 @@ namespace FlaxEngine.GUI return 0; } - return EnableFontFallback ? font.HitTestText(_text, location, ref _layout) : - font.HitTestTextInternal(_text, location, ref _layout); + return font.HitTestText(_text, location, ref _layout); } /// @@ -180,13 +168,9 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = EnableFontFallback ? - font.GetCharPosition(_text, SelectionLeft, ref _layout) : - font.GetCharPositionInternal(_text, SelectionLeft, ref _layout); - var rightEdge = EnableFontFallback ? - font.GetCharPosition(_text, SelectionRight, ref _layout) : - font.GetCharPositionInternal(_text, SelectionRight, ref _layout); - float fontHeight = font.GetMaxHeight() / DpiScale; + var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); + var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + float fontHeight = font.Height / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); @@ -226,19 +210,11 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - if (EnableFontFallback) - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); - else - // Draw without fallback - Render2D.DrawTextInternal(font, _text, color, ref _layout, TextMaterial); + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - if (EnableFontFallback) - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); - else - // Draw without fallback - Render2D.DrawTextInternal(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 55b58634d..35819da79 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -280,13 +280,13 @@ namespace FlaxEngine.GUI /// [EditorDisplay("Border Style"), EditorOrder(2010), Tooltip("Whether to have a border."), ExpandGroups] public bool HasBorder { get; set; } = true; - + /// /// Gets or sets the border thickness. /// [EditorDisplay("Border Style"), EditorOrder(2011), Tooltip("The thickness of the border."), Limit(0)] public float BorderThickness { get; set; } = 1.0f; - + /// /// Gets or sets the color of the border (Transparent if not used). /// diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index aa49dac45..d190f1123 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -239,7 +239,7 @@ void TextRender::UpdateLayout() const bool isWhitespace = StringUtils::IsWhitespace(c); if (!isWhitespace && previous.IsValid) { - kerning = font->GetKerning(previous.Character, entry.Character); + kerning = entry.Font->GetKerning(previous.Character, entry.Character); } else {