From 6a9c2bbeeb1e7503ed82041fc199aec2b2bd7520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 5 Mar 2026 21:27:35 -0300 Subject: [PATCH] Fix background color in Text (should render over text and not the bounding rectangle. Having fixed this added background color to UIRichText and UITextSpan. --- .../eepp-text-background-color.webp | Bin 0 -> 3678 bytes include/eepp/graphics/fontstyleconfig.hpp | 6 +- include/eepp/graphics/richtext.hpp | 3 +- include/eepp/graphics/text.hpp | 1 - include/eepp/ui/uirichtext.hpp | 4 ++ include/eepp/ui/uitextspan.hpp | 6 ++ src/eepp/graphics/linewrap.cpp | 19 +++-- src/eepp/graphics/richtext.cpp | 3 +- src/eepp/graphics/text.cpp | 40 +++++++++-- src/eepp/graphics/textlayout.cpp | 11 ++- src/eepp/ui/uirichtext.cpp | 26 +++++-- src/eepp/ui/uitextspan.cpp | 29 ++++++++ src/tests/unit_tests/fontrendering.cpp | 66 ++++++++++++++++++ 13 files changed, 193 insertions(+), 21 deletions(-) create mode 100644 bin/unit_tests/assets/fontrendering/eepp-text-background-color.webp diff --git a/bin/unit_tests/assets/fontrendering/eepp-text-background-color.webp b/bin/unit_tests/assets/fontrendering/eepp-text-background-color.webp new file mode 100644 index 0000000000000000000000000000000000000000..d8eb27da990c7ca62b0952cca3fbca4a85529371 GIT binary patch literal 3678 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 literal 0 HcmV?d00001 diff --git a/include/eepp/graphics/fontstyleconfig.hpp b/include/eepp/graphics/fontstyleconfig.hpp index 7d26b7954..6ff6bd383 100644 --- a/include/eepp/graphics/fontstyleconfig.hpp +++ b/include/eepp/graphics/fontstyleconfig.hpp @@ -29,13 +29,15 @@ class FontStyleConfig { const Vector2f& getFontShadowOffset() const { return ShadowOffset; } + const Color& getBackgroundColor() const { return BackgroundColor; } + FontStyleConfig() {} bool operator==( const FontStyleConfig& other ) { return Font == other.Font && CharacterSize == other.CharacterSize && Style == other.Style && FontColor == other.FontColor && ShadowColor == other.ShadowColor && ShadowOffset == other.ShadowOffset && OutlineThickness == other.OutlineThickness && - OutlineColor == other.OutlineColor; + OutlineColor == other.OutlineColor && BackgroundColor == other.BackgroundColor; } bool operator!=( const FontStyleConfig& other ) { return !( *this == other ); } @@ -49,6 +51,7 @@ class FontStyleConfig { ShadowOffset = fontStyleConfig.ShadowOffset; OutlineThickness = fontStyleConfig.OutlineThickness; OutlineColor = fontStyleConfig.OutlineColor; + BackgroundColor = fontStyleConfig.BackgroundColor; } Graphics::Font* Font{ nullptr }; @@ -59,6 +62,7 @@ class FontStyleConfig { Vector2f ShadowOffset{ PixelDensity::dpToPx( 1 ), PixelDensity::dpToPx( 1 ) }; Float OutlineThickness{ 0 }; Color OutlineColor{ 0, 0, 0, 255 }; + Color BackgroundColor{ Color::Transparent }; }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/richtext.hpp b/include/eepp/graphics/richtext.hpp index 7ffbb8775..ffbec00cc 100644 --- a/include/eepp/graphics/richtext.hpp +++ b/include/eepp/graphics/richtext.hpp @@ -41,7 +41,8 @@ class EE_API RichText : public Drawable { * @param style The text style (optional, uses default if Regular). */ void addSpan( const String& text, Font* font = nullptr, Uint32 characterSize = 0, - Color color = Color::White, Uint32 style = Text::Regular ); + Color color = Color::White, Uint32 style = Text::Regular, + Color backgroundColor = Color::Transparent ); /** @brief Clears all text spans. */ void clear(); diff --git a/include/eepp/graphics/text.hpp b/include/eepp/graphics/text.hpp index 83209aa96..c80ba0976 100644 --- a/include/eepp/graphics/text.hpp +++ b/include/eepp/graphics/text.hpp @@ -400,7 +400,6 @@ class EE_API Text { String mString; ///< String to display FontStyleConfig mFontStyleConfig; - Color mBackgroundColor{ Color::Transparent }; mutable Rectf mBounds; ///< Bounding rectangle of the text (in local coordinates) mutable bool mGeometryNeedUpdate : 1 { false }; ///< Does the geometry need to be recomputed? diff --git a/include/eepp/ui/uirichtext.hpp b/include/eepp/ui/uirichtext.hpp index 2b4f28efd..385914cd0 100644 --- a/include/eepp/ui/uirichtext.hpp +++ b/include/eepp/ui/uirichtext.hpp @@ -63,6 +63,10 @@ class EE_API UIRichText : public UILayout { UIRichText* setFontColor( const Color& color ); + const Color& getFontBackgroundColor() const; + + UIRichText* setFontBackgroundColor( const Color& color ); + const Color& getFontShadowColor() const; UIRichText* setFontShadowColor( const Color& color ); diff --git a/include/eepp/ui/uitextspan.hpp b/include/eepp/ui/uitextspan.hpp index a8902d21f..0b2eb32a9 100644 --- a/include/eepp/ui/uitextspan.hpp +++ b/include/eepp/ui/uitextspan.hpp @@ -77,6 +77,10 @@ class EE_API UITextSpan : public UIWidget { UITextSpan* setFontColor( const Color& color ); + const Color& getFontBackgroundColor() const; + + UITextSpan* setFontBackgroundColor( const Color& color ); + const Color& getFontShadowColor() const; UITextSpan* setFontShadowColor( const Color& color ); @@ -97,6 +101,7 @@ class EE_API UITextSpan : public UIWidget { StyleStateOutlineColor = 1 << 5, StyleStateFontShadowColor = 1 << 6, StyleStateFontShadowOffset = 1 << 7, + StyleStateFontBackgroundColor = 1 << 8, StyleStateAll = 0xFFFFFFFF }; @@ -108,6 +113,7 @@ class EE_API UITextSpan : public UIWidget { bool hasOutlineColor() const; bool hasFontShadowColor() const; bool hasFontShadowOffset() const; + bool hasFontBackgroundColor() const; protected: Uint32 mStyleState{ StyleStateNone }; diff --git a/src/eepp/graphics/linewrap.cpp b/src/eepp/graphics/linewrap.cpp index 1520aa810..77d981ee1 100644 --- a/src/eepp/graphics/linewrap.cpp +++ b/src/eepp/graphics/linewrap.cpp @@ -168,18 +168,20 @@ LineWrap::computeLineBreaksInternal( const String::View& string, Font* font, Uin ? font->getGlyph( curChar, characterSize, bold, italic, outlineThickness ).advance : hspace; + Float kerning = 0; if ( curChar == '\t' ) { w = Text::tabAdvance( hspace, tabWidth, tabStops ? xoffset : std::optional{} ); prevChar = 0; } else if ( !isMonospace && curChar != '\r' ) { if ( !( textDrawHints & TextHints::NoKerning ) ) { - w += font->getKerning( prevChar, curChar, characterSize, bold, italic, - outlineThickness ); + kerning = font->getKerning( prevChar, curChar, characterSize, bold, italic, + outlineThickness ); } prevChar = curChar; } - xoffset += w; + Float xpos = xoffset + kerning; + xoffset = xpos + w; if ( hasWrap && xoffset > maxWidth ) { if ( mode == LineWrapMode::Word && lastSpace != std::string::npos ) { @@ -188,7 +190,15 @@ LineWrap::computeLineBreaksInternal( const String::View& string, Font* font, Uin } info.wraps.push_back( lastSpace + 1 ); - xoffset = info.paddingStart + ( xoffset - lastWidth ); + + Float startKerning = 0; + if ( !( textDrawHints & TextHints::NoKerning ) && !isMonospace && + lastSpace + 1 < string.size() ) { + startKerning = font->getKerning( string[lastSpace], string[lastSpace + 1], + characterSize, bold, italic, outlineThickness ); + } + + xoffset = info.paddingStart + ( xoffset - lastWidth - startKerning ); } else { // If we are about to split a word, check if we can move it to the next line if ( mode == LineWrapMode::Word && info.wraps.size() == 1 && @@ -213,6 +223,7 @@ LineWrap::computeLineBreaksInternal( const String::View& string, Font* font, Uin info.wraps.push_back( idx ); xoffset = info.paddingStart; + prevChar = 0; } } lastSpace = std::string::npos; diff --git a/src/eepp/graphics/richtext.cpp b/src/eepp/graphics/richtext.cpp index 2c7e4a515..0f575686f 100644 --- a/src/eepp/graphics/richtext.cpp +++ b/src/eepp/graphics/richtext.cpp @@ -98,7 +98,7 @@ void RichText::addCustomSize( const Sizef& size ) { } void RichText::addSpan( const String& text, Font* font, Uint32 characterSize, Color color, - Uint32 style ) { + Uint32 style, Color backgroundColor ) { FontStyleConfig config; config.Font = font ? font : mDefaultStyle.Font; config.CharacterSize = characterSize != 0 ? characterSize : mDefaultStyle.CharacterSize; @@ -108,6 +108,7 @@ void RichText::addSpan( const String& text, Font* font, Uint32 characterSize, Co config.ShadowOffset = mDefaultStyle.ShadowOffset; config.OutlineThickness = mDefaultStyle.OutlineThickness; config.OutlineColor = mDefaultStyle.OutlineColor; + config.BackgroundColor = backgroundColor; addSpan( text, config ); } diff --git a/src/eepp/graphics/text.cpp b/src/eepp/graphics/text.cpp index 528b08173..644944654 100644 --- a/src/eepp/graphics/text.cpp +++ b/src/eepp/graphics/text.cpp @@ -1575,11 +1575,11 @@ const Uint32& Text::getTabWidth() const { } Color Text::getBackgroundColor() const { - return mBackgroundColor; + return mFontStyleConfig.BackgroundColor; } void Text::setBackgroundColor( const Color& backgroundColor ) { - mBackgroundColor = backgroundColor; + mFontStyleConfig.BackgroundColor = backgroundColor; } const Vector2f& Text::getShadowOffset() const { @@ -1629,11 +1629,11 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl BlendMode effect, const OriginPoint& rotationCenter, const OriginPoint& scaleCenter, const std::vector& colors, const std::vector& outlineColors, const Color& backgroundColor ) { - unsigned int numvert = mVertices.size(); - - if ( 0 == numvert ) + if ( NULL == mFontStyleConfig.Font || mString.empty() ) return; + unsigned int numvert = mVertices.size(); + GlobalBatchRenderer::instance()->draw(); if ( rotation != 0.0f || scale != 1.0f ) { @@ -1670,7 +1670,32 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl Primitives p; p.setForceDraw( true ); p.setColor( backgroundColor ); - p.drawRectangle( getLocalBounds() ); + ensureVisualLinesUpdate(); + Float vspace = getLineSpacing(); + for ( size_t i = 0; i < mVisualLines.size(); ++i ) { + Float centerDiffX = 0; + if ( i < mLinesWidth.size() ) { + switch ( Font::getHorizontalAlign( mAlign ) ) { + case TEXT_ALIGN_CENTER: + centerDiffX = std::trunc( ( mCachedWidth - mLinesWidth[i] ) * 0.5f ); + break; + case TEXT_ALIGN_RIGHT: + centerDiffX = mCachedWidth - mLinesWidth[i]; + break; + } + } + p.drawRectangle( Rectf( centerDiffX, i * vspace, centerDiffX + mLinesWidth[i], + ( i + 1 ) * vspace ) ); + } + } + + if ( 0 == numvert ) { + if ( rotation != 0.0f || scale != 1.0f ) { + GLi->popMatrix(); + } else { + GLi->translatef( -X, -Y, 0 ); + } + return; } Texture* texture = mFontStyleConfig.Font->getTexture( mFontStyleConfig.CharacterSize ); @@ -1744,7 +1769,7 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl } draw( X, Y, scale, rotation, effect, rotationCenter, scaleCenter, mColors, mOutlineColors, - mBackgroundColor ); + mFontStyleConfig.BackgroundColor ); } void Text::ensureGeometryUpdate() { @@ -2330,6 +2355,7 @@ void Text::setStyleConfig( const FontStyleConfig& styleConfig ) { setOutlineColor( styleConfig.OutlineColor ); setShadowColor( styleConfig.ShadowColor ); setShadowOffset( styleConfig.ShadowOffset ); + setBackgroundColor( styleConfig.BackgroundColor ); } bool Text::hasSameFontStyleConfig( const FontStyleConfig& styleConfig ) { diff --git a/src/eepp/graphics/textlayout.cpp b/src/eepp/graphics/textlayout.cpp index 23ea80a14..41579a368 100644 --- a/src/eepp/graphics/textlayout.cpp +++ b/src/eepp/graphics/textlayout.cpp @@ -620,11 +620,18 @@ void TextLayout::wrapLayout( const String::View& string, TextLayout& result, breakStringIdx = string.size(); ShapedGlyph& breakGlyph = sp.shapedGlyphs[breakIndex]; + Float breakPos = breakGlyph.position.x; + Float kerning = 0; + if ( breakIndex > 0 ) { + ShapedGlyph& prevBreakGlyph = sp.shapedGlyphs[breakIndex - 1]; + kerning = ( prevBreakGlyph.position.x + prevBreakGlyph.advance.x ) - + breakGlyph.position.x; + } sp.wrapInfo.wraps.push_back( breakStringIdx ); - sp.wrapInfo.wrapsWidth.push_back( std::ceil( breakGlyph.position.x ) ); + sp.wrapInfo.wrapsWidth.push_back( std::ceil( breakPos + kerning ) ); - Vector2f adjustment( -breakGlyph.position.x + sp.wrapInfo.paddingStart, vspace ); + Vector2f adjustment( sp.wrapInfo.paddingStart - breakGlyph.position.x, vspace ); for ( std::size_t k = breakIndex; k <= idx; ++k ) sp.shapedGlyphs[k].position += adjustment; diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp index f9ab2bbc7..73cfbc9b5 100644 --- a/src/eepp/ui/uirichtext.cpp +++ b/src/eepp/ui/uirichtext.cpp @@ -98,6 +98,9 @@ bool UIRichText::applyProperty( const StyleSheetProperty& attribute ) { case PropertyId::Color: setFontColor( attribute.asColor() ); break; + case PropertyId::BackgroundColor: + setFontBackgroundColor( attribute.asColor() ); + break; case PropertyId::TextShadowColor: setFontShadowColor( attribute.asColor() ); break; @@ -141,6 +144,8 @@ std::string UIRichText::getPropertyString( const PropertyDefinition* propertyDef return Graphics::Text::styleFlagToString( getFontStyle() ); case PropertyId::Color: return getFontColor().toHexString(); + case PropertyId::BackgroundColor: + return getFontBackgroundColor().toHexString(); case PropertyId::TextShadowColor: return getFontShadowColor().toHexString(); case PropertyId::TextShadowOffset: @@ -161,10 +166,10 @@ std::string UIRichText::getPropertyString( const PropertyDefinition* propertyDef std::vector UIRichText::getPropertiesImplemented() const { auto props = UILayout::getPropertiesImplemented(); - auto local = { - PropertyId::FontFamily, PropertyId::FontSize, PropertyId::FontStyle, - PropertyId::Color, PropertyId::TextShadowColor, PropertyId::TextShadowOffset, - PropertyId::TextStrokeWidth, PropertyId::TextStrokeColor, PropertyId::TextAlign }; + auto local = { PropertyId::FontFamily, PropertyId::FontSize, PropertyId::FontStyle, + PropertyId::Color, PropertyId::BackgroundColor, PropertyId::TextShadowColor, + PropertyId::TextShadowOffset, PropertyId::TextStrokeWidth, PropertyId::TextStrokeColor, + PropertyId::TextAlign }; props.insert( props.end(), local.begin(), local.end() ); return props; } @@ -229,6 +234,19 @@ UIRichText* UIRichText::setFontColor( const Color& color ) { return this; } +const Color& UIRichText::getFontBackgroundColor() const { + return mRichText.getFontStyleConfig().BackgroundColor; +} + +UIRichText* UIRichText::setFontBackgroundColor( const Color& color ) { + if ( mRichText.getFontStyleConfig().BackgroundColor != color ) { + mRichText.getFontStyleConfig().BackgroundColor = color; + mRichText.invalidate(); + updateDefaultSpansStyle(); + } + return this; +} + const Color& UIRichText::getFontShadowColor() const { return mRichText.getFontStyleConfig().ShadowColor; } diff --git a/src/eepp/ui/uitextspan.cpp b/src/eepp/ui/uitextspan.cpp index ecf9abd1f..1870675b1 100644 --- a/src/eepp/ui/uitextspan.cpp +++ b/src/eepp/ui/uitextspan.cpp @@ -63,6 +63,9 @@ bool UITextSpan::applyProperty( const StyleSheetProperty& attribute ) { case PropertyId::Color: setFontColor( attribute.asColor() ); break; + case PropertyId::BackgroundColor: + setFontBackgroundColor( attribute.asColor() ); + break; case PropertyId::TextShadowColor: setFontShadowColor( attribute.asColor() ); break; @@ -112,6 +115,8 @@ std::string UITextSpan::getPropertyString( const PropertyDefinition* propertyDef return Graphics::Text::styleFlagToString( getFontStyle() ); case PropertyId::Color: return getFontColor().toHexString(); + case PropertyId::BackgroundColor: + return getFontBackgroundColor().toHexString(); case PropertyId::TextShadowColor: return getFontShadowColor().toHexString(); case PropertyId::TextShadowOffset: @@ -133,6 +138,7 @@ std::vector UITextSpan::getPropertiesImplemented() const { PropertyId::FontSize, PropertyId::FontStyle, PropertyId::Color, + PropertyId::BackgroundColor, PropertyId::TextShadowColor, PropertyId::TextShadowOffset, PropertyId::TextStrokeWidth, @@ -248,6 +254,19 @@ UITextSpan* UITextSpan::setFontColor( const Color& color ) { return this; } +const Color& UITextSpan::getFontBackgroundColor() const { + return mFontStyleConfig.getBackgroundColor(); +} + +UITextSpan* UITextSpan::setFontBackgroundColor( const Color& color ) { + if ( mFontStyleConfig.BackgroundColor != color ) { + mFontStyleConfig.BackgroundColor = color; + mStyleState |= StyleStateFontBackgroundColor; + onFontStyleChanged(); + } + return this; +} + const Color& UITextSpan::getFontShadowColor() const { return mFontStyleConfig.getFontShadowColor(); } @@ -404,6 +423,12 @@ void UITextSpan::setInheritedStyle( const UIFontStyleConfig& fontStyleConfig ) { fontStyleChanged = true; } + if ( !hasFontBackgroundColor() && + mFontStyleConfig.BackgroundColor != fontStyleConfig.BackgroundColor ) { + mFontStyleConfig.BackgroundColor = fontStyleConfig.BackgroundColor; + fontStyleChanged = true; + } + if ( fontChanged ) onFontChanged(); @@ -454,4 +479,8 @@ bool UITextSpan::hasFontShadowOffset() const { return 0 != ( mStyleState & StyleStateFontShadowOffset ); } +bool UITextSpan::hasFontBackgroundColor() const { + return 0 != ( mStyleState & StyleStateFontBackgroundColor ); +} + }} // namespace EE::UI diff --git a/src/tests/unit_tests/fontrendering.cpp b/src/tests/unit_tests/fontrendering.cpp index 336528058..c84096327 100644 --- a/src/tests/unit_tests/fontrendering.cpp +++ b/src/tests/unit_tests/fontrendering.cpp @@ -1434,3 +1434,69 @@ UTEST( FontRendering, TextContiguousOffset ) { runTest(); } } + +UTEST( FontRendering, TextBackgroundColor ) { + FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); + + const auto runTest = [&]() { + auto win = Engine::instance()->createWindow( + WindowSettings( 512, 400, "eepp - Text Background Color", WindowStyle::Default, + WindowBackend::Default, 32, {}, 1, false, true ) ); + + ASSERT_TRUE_MSG( win->isOpen(), "Failed to create Window" ); + + win->setClearColor( RGB( 255, 255, 255 ) ); + win->clear(); + + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); + + Vector2f pos{ 20, 20 }; + Text text; + text.setFont( font ); + text.setFontSize( 20 ); + text.setFillColor( Color::Black ); + text.setBackgroundColor( Color::Yellow ); + text.setString( "Text with background color\nand multiple lines." ); + text.draw( pos.x, pos.y ); + + pos.y += text.getTextHeight() + 20; + text.setAlign( TEXT_ALIGN_CENTER ); + text.setString( "Centered text with\nbackground color." ); + text.draw( pos.x, pos.y ); + + pos.y += text.getTextHeight() + 20; + text.setAlign( TEXT_ALIGN_LEFT ); + text.setLineWrapMode( LineWrapMode::Word ); + text.setMaxWrapWidth( 200 ); + text.setString( + "Wrapped text with background color that should only cover the text area." ); + text.draw( pos.x, pos.y ); + + pos.y += text.getTextHeight() + 20; + text.setLineWrapMode( LineWrapMode::NoWrap ); + text.setBackgroundColor( Color::cyan ); + text.setString( " " ); // Only spaces + text.draw( pos.x, pos.y ); + + compareImages( utest_state, utest_result, win, "eepp-text-background-color" ); + + Engine::destroySingleton(); + }; + + UTEST_PRINT_STEP( "Text Shaper disabled" ); + { + BoolScopedOp op( Text::TextShaperEnabled, false ); + runTest(); + } + + UTEST_PRINT_STEP( "Text Shaper enabled" ); + { + BoolScopedOp op( Text::TextShaperEnabled, true ); + runTest(); + + UTEST_PRINT_STEP( "Text Shaper enabled w/o optimizations" ); + BoolScopedOp op2( Text::TextShaperOptimizations, false ); + runTest(); + } +}