diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 2e9da2223..51b2aa3ec 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -167,15 +167,14 @@ void Font::Invalidate() #endif } -void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout, Array& characterEntries, Array& characterKernings) +void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout, Array* characterEntries) { int32 textLength = text.Length(); if (textLength == 0) return; int32 cursorX = 0; - int32 kerning; FontLineCache tmpLine; - FontCharacterEntry entry; + FontLineCharacterCache characterCache; Char previous = 0; float scale = layout.Scale / FontManager::FontScale; int32 boundsWidth = layout.Bounds.GetWidth() / scale; @@ -198,8 +197,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Cache current character const Char currentChar = text[currentIndex]; - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - const bool isWrapChar = isWhitespace || StringUtils::IsUpper(currentChar) || !StringUtils::IsAlnum(currentChar); + characterCache.IsWhitespace = StringUtils::IsWhitespace(currentChar); + const bool isWrapChar = characterCache.IsWhitespace || StringUtils::IsUpper(currentChar) || !StringUtils::IsAlnum(currentChar); // Check if character can wrap words if (isWrapChar && currentIndex != 0) @@ -219,19 +218,19 @@ void Font::ProcessText(const StringView& text, Array& outputLines else { // Get character entry - GetCharacter(currentChar, entry); + GetCharacter(currentChar, characterCache.Entry); // Get kerning - if (!isWhitespace && previous != 0) + if (!characterCache.IsWhitespace && previous != 0) { - kerning = GetKerning(previous, entry.Character); + characterCache.Kerning = GetKerning(previous, currentChar); } else { - kerning = 0; + characterCache.Kerning = 0; } previous = currentChar; - xAdvance = (kerning + entry.AdvanceX); + xAdvance = (characterCache.Kerning + characterCache.Entry.AdvanceX); // Check if character fits the line or skip wrapping if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) @@ -281,8 +280,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines break; } - characterEntries.Add(entry); - characterKernings.Add(kerning); + if (characterEntries != nullptr) + characterEntries->Add(characterCache); } // Check if move to another line @@ -362,9 +361,7 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout // Process text Array lines; - Array characterEntries; - Array xAdvances; - ProcessText(text, lines, layout, characterEntries, xAdvances); + ProcessText(text, lines, layout); // Calculate bounds Float2 max = Float2::Zero; @@ -385,9 +382,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te // Process text Array lines; - Array characterEntries; - Array xAdvances; - ProcessText(text, lines, layout, characterEntries, xAdvances); + ProcessText(text, lines, layout); ASSERT(lines.HasItems()); float scale = layout.Scale / FontManager::FontScale; float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; @@ -461,9 +456,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo // Process text Array lines; - Array characterEntries; - Array xAdvances; - ProcessText(text, lines, layout, characterEntries, xAdvances); + ProcessText(text, lines, layout); ASSERT(lines.HasItems()); float scale = layout.Scale / FontManager::FontScale; float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 4ad99f07e..e190dabcc 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -225,6 +225,70 @@ struct TIsPODType enum { Value = true }; }; +/// +/// The font line info generated during text processing. +/// +API_STRUCT(NoDefault) struct FontLineCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); + + /// + /// The root position of the line (upper left corner). + /// + API_FIELD() Float2 Location; + + /// + /// The line bounds (width and height). + /// + API_FIELD() Float2 Size; + + /// + /// The first character index (from the input text). + /// + API_FIELD() int32 FirstCharIndex; + + /// + /// The last character index (from the input text). + /// + API_FIELD() int32 LastCharIndex; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +/// +/// The font line info generated during text processing. +/// +API_STRUCT(NoDefault) struct FontLineCharacterCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCharacterCache); + + /// + /// + /// + FontCharacterEntry Entry; + + /// + /// + /// + int32 Kerning; + + /// + /// + /// + bool IsWhitespace; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + + /// /// Represents font object that can be using during text rendering (it uses Font Asset but with pre-cached data for chosen font properties). /// @@ -350,7 +414,8 @@ public: /// The input text. /// The layout properties. /// The output lines list. - void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout, Array& characterEntries, Array& characterKernings); + /// Cached information about each character in lines. + void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout, Array* characterEntries = nullptr); /// /// Processes text to get cached lines for rendering. @@ -361,9 +426,7 @@ public: API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; - Array characterEntries; - Array xAdvances; - ProcessText(text, lines, layout, characterEntries, xAdvances); + ProcessText(text, lines, layout); return lines; } @@ -377,9 +440,7 @@ public: API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; - Array characterEntries; - Array xAdvances; - ProcessText(textRange.Substring(text), lines, layout, characterEntries, xAdvances); + ProcessText(textRange.Substring(text), lines, layout); return lines; } diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index c1f4f6225..f1f941519 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -195,6 +195,7 @@ namespace Array DrawCalls; Array Lines; Array Lines2; + Array CharacterCache(128); bool IsScissorsRectEmpty; bool IsScissorsRectEnabled; @@ -1355,45 +1356,24 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, uint32 fontAtlasIndex = 0; FontTextureAtlas* fontAtlas = nullptr; Float2 invAtlasSize = Float2::One; - FontCharacterEntry previous; - //int32 kerning; float scale = layout.Scale / FontManager::FontScale; const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts); float fontHeightOffset = Math::Ceil((font->GetHeight() + font->GetDescender()) * scale); + RotatedRectangle mask = ClipLayersStack.Peek().Mask; + Float2 customData = { 0.0f, (float)Render2D::Features }; + Color colorTint = color * TintLayersStack.Peek(); // Process text to get lines Lines.Clear(); - static Array characterEntries(128); - static Array characterKernings(128); - - characterEntries.Clear(); - characterKernings.Clear(); - font->ProcessText(text, Lines, layout, characterEntries, characterKernings); + CharacterCache.Clear(); + font->ProcessText(text, Lines, layout, &CharacterCache); auto drawCallCountBefore = DrawCalls.Count(); - DrawCalls.EnsureCapacity(drawCallCountBefore + characterEntries.Count()); - DrawCalls.Resize(drawCallCountBefore + characterEntries.Count()); + DrawCalls.EnsureCapacity(drawCallCountBefore + CharacterCache.Count()); + DrawCalls.Resize(drawCallCountBefore + CharacterCache.Count()); Render2DDrawCall* drawCall = &DrawCalls[drawCallCountBefore]; // 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; - }*/ - - //Matrix3x3 rotto = TransformCached; - //auto quat = Quaternion::RotationYawPitchRoll(15.0f, 25.0f, 35.0f); - //Matrix3x3 rotto = Matrix3x3::RotationQuaternion(quat); - - int32 entryIndex = 0; int32 actualDrawCallsCount = 0; for (int32 lineIndex = 0; lineIndex < Lines.Count(); lineIndex++) @@ -1401,39 +1381,21 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const FontLineCache& line = Lines[lineIndex]; Float2 pointer; ApplyTransform(line.Location, pointer); - Float2 advanceDirX; + Float2 advanceDirX, advanceDirY; Matrix3x3::Transform2DVector(Float2(1.0f, 0.0f), TransformCached, advanceDirX); - Float2 advanceDirY; Matrix3x3::Transform2DVector(Float2(0.0f, 1.0f), TransformCached, advanceDirY); // Render all characters from the line for (int32 charIndex = line.FirstCharIndex; charIndex < line.LastCharIndex; charIndex++) { const Char c = text[charIndex]; + const FontLineCharacterCache& characterCache = CharacterCache[entryIndex]; + const FontCharacterEntry& entry = characterCache.Entry; - //font->GetCharacter(c, entry); - const FontCharacterEntry& entry = characterEntries[entryIndex]; - float kerning = characterKernings[entryIndex]; - - // Get kerning - const bool isWhitespace = StringUtils::IsWhitespace(c); // kerning == inf; - /*if (!isWhitespace && previous.IsValid) - { - kerning = font->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - pointer.X += (float)kerning * scale;*/ - - //pointer.X += kerning * scale; - pointer += advanceDirX * (kerning * scale); - previous = entry; + pointer += advanceDirX * (characterCache.Kerning * scale); // Omit whitespace characters - if (!isWhitespace) - //if (xAdvance != 0.0f) + if (!characterCache.IsWhitespace) { // 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) @@ -1445,26 +1407,17 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, { fontAtlas->EnsureTextureCreated(); invAtlasSize = 1.0f / fontAtlas->GetSize(); - //drawCall->AsChar.Tex = fontAtlas->GetTexture(); } else - { invAtlasSize = 1.0f; - //drawCall->AsChar.Tex = nullptr; - } } // Calculate character size and atlas coordinates - //const float x = pointer.X + entry.OffsetX * scale; - //const float y = pointer.Y - entry.OffsetY * scale + fontHeightOffset; - // FIXME: skewed, use advanceDirY for rightBottom Float2 upperLeft(pointer.X + entry.OffsetX * scale, pointer.Y - entry.OffsetY * scale + fontHeightOffset); Float2 rightBottom(upperLeft.X + (entry.UVSize.X * scale), upperLeft.Y + (entry.UVSize.Y * scale)); upperLeft += layout.Bounds.Location; rightBottom += layout.Bounds.Location; - //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; @@ -1483,48 +1436,29 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, drawCall->AsChar.Mat = nullptr; } drawCall->AsChar.Tex = fontAtlas ? fontAtlas->GetTexture() : nullptr; - - //DrawCalls[drawCallCountBefore + actualDrawCallsCount] = drawCall; - //*drawCalls = drawCall; + drawCall++; actualDrawCallsCount++; - //DrawCalls.Add(drawCall); - //WriteRect(charRect, color, upperLeftUV, rightBottomUV); - { - Render2DVertex* quad = (Render2DVertex*)VB.WriteReserve(sizeof(Render2DVertex) * 4); - //Render2DVertex quad[4]; + + Render2DVertex* quad = (Render2DVertex*)VB.WriteReserve(sizeof(Render2DVertex) * 4); + quad[0] = MakeVertex(rightBottom, rightBottomUV, colorTint, mask, customData); + quad[1] = MakeVertex(Float2(upperLeft.X, rightBottom.Y), Float2(upperLeftUV.X, rightBottomUV.Y), colorTint, mask, customData); + quad[2] = MakeVertex(upperLeft, upperLeftUV, colorTint, mask, customData); + quad[3] = MakeVertex(Float2(rightBottom.X, upperLeft.Y), Float2(rightBottomUV.X, upperLeftUV.Y), colorTint, mask, customData); - //if (TransformIsIdentity) - { - RotatedRectangle mask = ClipLayersStack.Peek().Mask; - Float2 customData = { 0.0f, (float)Render2D::Features }; - Color colorTint = color * TintLayersStack.Peek(); - quad[0] = MakeVertex(rightBottom, rightBottomUV, colorTint, mask, customData); - quad[1] = MakeVertex(Float2(upperLeft.X, rightBottom.Y), Float2(upperLeftUV.X, rightBottomUV.Y), colorTint, mask, customData); - quad[2] = MakeVertex(upperLeft, upperLeftUV, colorTint, mask, customData); - quad[3] = MakeVertex(Float2(rightBottom.X, upperLeft.Y), Float2(rightBottomUV.X, upperLeftUV.Y), colorTint, mask, customData); - } - - //VB.Write(quad, sizeof(quad)); + uint32* indices = (uint32*)IB.WriteReserve(sizeof(uint32) * 6); + indices[0] = VBIndex + 0; + indices[1] = VBIndex + 1; + indices[2] = VBIndex + 2; + indices[3] = VBIndex + 2; + indices[4] = VBIndex + 3; + indices[5] = VBIndex + 0; - //uint32 indices[6]; - uint32* indices = (uint32*)IB.WriteReserve(sizeof(uint32) * 6); - indices[0] = VBIndex + 0; - indices[1] = VBIndex + 1; - indices[2] = VBIndex + 2; - indices[3] = VBIndex + 2; - indices[4] = VBIndex + 3; - indices[5] = VBIndex + 0; - //IB.Write(indices, sizeof(indices)); - //RENDER2D_WRITE_IB_QUAD(indices); - - VBIndex += 4; - IBIndex += 6; - } + VBIndex += 4; + IBIndex += 6; } // Move - //pointer.X += entry.AdvanceX * scale; pointer += advanceDirX * (entry.AdvanceX * scale); entryIndex++; } diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index c9c302aca..175e426c0 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -158,9 +158,7 @@ void TextRender::UpdateLayout() // Perform layout Array lines; - Array characterEntries; - Array xAdvances; - font->ProcessText(text, lines, _layoutOptions, characterEntries, xAdvances); + font->ProcessText(text, lines, _layoutOptions); // Prepare buffers capacity _ib.Data.EnsureCapacity(text.Length() * 6 * sizeof(uint16));