drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx  |   70 +++++-
 drawinglayer/source/primitive2d/textprimitive2d.cxx           |   24 ++
 drawinglayer/source/processor2d/cairopixelprocessor2d.cxx     |  102 +++++-----
 include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx |    5 
 include/drawinglayer/primitive2d/textprimitive2d.hxx          |    6 
 5 files changed, 143 insertions(+), 64 deletions(-)

New commits:
commit cd6529a0501d49e0cd6344523d509fa7186687c9
Author:     Armin Le Grand (Collabora) <armin.le.gr...@me.com>
AuthorDate: Thu Aug 15 17:30:23 2024 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Fri Aug 16 14:44:11 2024 +0200

    CairoSDPR: Add support for Relief for Text Rendering
    
    Change-Id: Idcb33e0f799b2219a4e737d0421bc21d85b5c7aa
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171915
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>
    Tested-by: Jenkins

diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx 
b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
index 2f0792fb7aa0..bfa5ebbb7eea 100644
--- a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -120,6 +120,11 @@ namespace drawinglayer::primitive2d
 
             if(bOverlineUsed)
             {
+                // for Relief we have to manipulate the OverlineColor
+                basegfx::BColor aOverlineColor(getOverlineColor());
+                if (hasTextRelief() && COL_BLACK.getBColor() == aOverlineColor)
+                    aOverlineColor = COL_WHITE.getBColor();
+
                 // create primitive geometry for overline
                 maBufferedDecorationGeometry.push_back(
                     new TextLinePrimitive2D(
@@ -128,11 +133,16 @@ namespace drawinglayer::primitive2d
                         aTextLayouter.getOverlineOffset(),
                         aTextLayouter.getOverlineHeight(),
                         getFontOverline(),
-                        getOverlineColor()));
+                        aOverlineColor));
             }
 
             if(bUnderlineUsed)
             {
+                // for Relief we have to manipulate the TextlineColor
+                basegfx::BColor aTextlineColor(getTextlineColor());
+                if (hasTextRelief() && COL_BLACK.getBColor() == aTextlineColor)
+                    aTextlineColor = COL_WHITE.getBColor();
+
                 // create primitive geometry for underline
                 maBufferedDecorationGeometry.push_back(
                     new TextLinePrimitive2D(
@@ -141,11 +151,16 @@ namespace drawinglayer::primitive2d
                         aTextLayouter.getUnderlineOffset(),
                         aTextLayouter.getUnderlineHeight(),
                         getFontUnderline(),
-                        getTextlineColor()));
+                        aTextlineColor));
             }
 
             if(bStrikeoutUsed)
             {
+                // for Relief we have to manipulate the FontColor
+                basegfx::BColor aFontColor(getFontColor());
+                if (hasTextRelief() && COL_BLACK.getBColor() == aFontColor)
+                    aFontColor = COL_WHITE.getBColor();
+
                 // create primitive geometry for strikeout
                 if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || 
TEXT_STRIKEOUT_X == getTextStrikeout())
                 {
@@ -156,7 +171,7 @@ namespace drawinglayer::primitive2d
                         new TextCharacterStrikeoutPrimitive2D(
                             rDecTrans.getB2DHomMatrix(),
                             fTextWidth,
-                            getFontColor(),
+                            aFontColor,
                             aStrikeoutChar,
                             getFontAttribute(),
                             getLocale()));
@@ -168,7 +183,7 @@ namespace drawinglayer::primitive2d
                         new TextGeometryStrikeoutPrimitive2D(
                             rDecTrans.getB2DHomMatrix(),
                             fTextWidth,
-                            getFontColor(),
+                            aFontColor,
                             aTextLayouter.getUnderlineHeight(),
                             aTextLayouter.getStrikeoutOffset(),
                             getTextStrikeout()));
@@ -182,6 +197,11 @@ namespace drawinglayer::primitive2d
 
                 if (pSalLayout)
                 {
+                    // for Relief we have to manipulate the FontColor
+                    basegfx::BColor aFontColor(getFontColor());
+                    if (hasTextRelief() && COL_BLACK.getBColor() == aFontColor)
+                        aFontColor = COL_WHITE.getBColor();
+
                     // placeholders for repeated content, only created once
                     Primitive2DReference aShape;
                     Primitive2DReference aRect1;
@@ -198,7 +218,7 @@ namespace drawinglayer::primitive2d
 
                     // the callback from OutputDevice::createEmphasisMarks 
providing the data
                     // for each EmphasisMark
-                    auto aEmphasisCallback([this, &aShape, &aRect1, &aRect2, 
&aEmphasisContent, &aObjTransformWithoutScale](
+                    auto aEmphasisCallback([&aShape, &aRect1, &aRect2, 
&aEmphasisContent, &aObjTransformWithoutScale, &aFontColor](
                         const basegfx::B2DPoint& rOutPoint, const 
basegfx::B2DPolyPolygon& rShape,
                         bool isPolyLine, const tools::Rectangle& rRect1, const 
tools::Rectangle& rRect2)
                     {
@@ -212,9 +232,9 @@ namespace drawinglayer::primitive2d
                             if (!aShape)
                             {
                                 if (isPolyLine)
-                                    aShape = new 
PolyPolygonHairlinePrimitive2D(rShape, getFontColor());
+                                    aShape = new 
PolyPolygonHairlinePrimitive2D(rShape, aFontColor);
                                 else
-                                    aShape = new 
PolyPolygonColorPrimitive2D(rShape, getFontColor());
+                                    aShape = new 
PolyPolygonColorPrimitive2D(rShape, aFontColor);
                             }
 
                             aEmphasisContent.push_back(
@@ -228,7 +248,7 @@ namespace drawinglayer::primitive2d
                             // create Rectangle1 if provided
                             if (!aRect1)
                                 aRect1 = new FilledRectanglePrimitive2D(
-                                    basegfx::B2DRange(rRect1.Left(), 
rRect1.Top(), rRect1.Right(), rRect1.Bottom()), getFontColor());
+                                    basegfx::B2DRange(rRect1.Left(), 
rRect1.Top(), rRect1.Right(), rRect1.Bottom()), aFontColor);
 
                             aEmphasisContent.push_back(
                                 new TransformPrimitive2D(
@@ -241,7 +261,7 @@ namespace drawinglayer::primitive2d
                             // create Rectangle2 if provided
                             if (!aRect2)
                                 aRect2 = new FilledRectanglePrimitive2D(
-                                    basegfx::B2DRange(rRect2.Left(), 
rRect2.Top(), rRect2.Right(), rRect2.Bottom()), getFontColor());
+                                    basegfx::B2DRange(rRect2.Left(), 
rRect2.Top(), rRect2.Right(), rRect2.Bottom()), aFontColor);
 
                             aEmphasisContent.push_back(
                                 new TransformPrimitive2D(
@@ -328,16 +348,11 @@ namespace drawinglayer::primitive2d
             if(aRetval.empty())
                 return nullptr;
 
-            // outline AND shadow depend on NO TextRelief (see dialog)
-            const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
-            const bool bHasShadow(!bHasTextRelief && getShadow());
-            const bool bHasOutline(!bHasTextRelief && 
getFontAttribute().getOutline());
-
-            if(bHasShadow || bHasTextRelief || bHasOutline)
+            if(hasShadow() || hasTextRelief() || hasOutline())
             {
                 Primitive2DReference aShadow;
 
-                if(bHasShadow)
+                if(hasShadow())
                 {
                     // create shadow with current content (in aRetval). Text 
shadow
                     // is constant, relative to font size, rotated with the 
text and has a
@@ -364,7 +379,7 @@ namespace drawinglayer::primitive2d
                         Primitive2DContainer(aRetval));
                 }
 
-                if(bHasTextRelief)
+                if(hasTextRelief())
                 {
                     // create emboss using an own helper primitive since this 
will
                     // be view-dependent
@@ -412,7 +427,7 @@ namespace drawinglayer::primitive2d
                             aTextEffectStyle2D))
                      };
                 }
-                else if(bHasOutline)
+                else if(hasOutline())
                 {
                     // create outline using an own helper primitive since this 
will
                     // be view-dependent
@@ -490,6 +505,25 @@ namespace drawinglayer::primitive2d
         {
         }
 
+        bool TextDecoratedPortionPrimitive2D::hasTextRelief() const
+        {
+            return TEXT_RELIEF_NONE != getTextRelief();
+        }
+
+        bool TextDecoratedPortionPrimitive2D::hasShadow() const
+        {
+            // not allowed with TextRelief, else defined in FontAttributes
+            return !hasTextRelief() && getShadow();
+        }
+
+        bool TextDecoratedPortionPrimitive2D::hasTextDecoration() const
+        {
+            return TEXT_LINE_NONE != getFontOverline()
+                || TEXT_LINE_NONE != getFontUnderline()
+                || TEXT_STRIKEOUT_NONE != getTextStrikeout()
+                || TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark();
+        }
+
         bool TextDecoratedPortionPrimitive2D::operator==(const 
BasePrimitive2D& rPrimitive) const
         {
             if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx 
b/drawinglayer/source/primitive2d/textprimitive2d.cxx
index ec2d18a79204..db9a6359ccf2 100644
--- a/drawinglayer/source/primitive2d/textprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx
@@ -231,6 +231,30 @@ bool LocalesAreEqual(const css::lang::Locale& rA, const 
css::lang::Locale& rB)
     return (rA.Language == rB.Language && rA.Country == rB.Country && 
rA.Variant == rB.Variant);
 }
 
+bool TextSimplePortionPrimitive2D::hasTextRelief() const
+{
+    // not possible for TextSimplePortionPrimitive2D
+    return false;
+}
+
+bool TextSimplePortionPrimitive2D::hasShadow() const
+{
+    // not possible for TextSimplePortionPrimitive2D
+    return false;
+}
+
+bool TextSimplePortionPrimitive2D::hasTextDecoration() const
+{
+    // not possible for TextSimplePortionPrimitive2D
+    return false;
+}
+
+bool TextSimplePortionPrimitive2D::hasOutline() const
+{
+    // not allowed with TextRelief, else defined in FontAttributes
+    return !hasTextRelief() && getFontAttribute().getOutline();
+}
+
 bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& 
rPrimitive) const
 {
     if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index a212ac270906..083f6ab51cde 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -3030,38 +3030,14 @@ void 
CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
                              pSalLayout->GetTextWidth());
     }
 
-    // prepare flags that are only needed if Text is Decorated
-    bool bHasTextRelief(false);
-    bool bHasShadow(false);
-    bool bHasTextDecoration(false);
-
-    if (nullptr != pDecoratedCandidate)
-    {
-        // outline AND shadow depend on NO TextRelief (see dialog)
-        bHasTextRelief = primitive2d::TEXT_RELIEF_NONE != 
pDecoratedCandidate->getTextRelief();
-        bHasShadow = !bHasTextRelief && pDecoratedCandidate->getShadow();
-
-        // check if TextDecoration is needed
-        bHasTextDecoration
-            = primitive2d::TEXT_LINE_NONE != 
pDecoratedCandidate->getFontOverline()
-              || primitive2d::TEXT_LINE_NONE != 
pDecoratedCandidate->getFontUnderline()
-              || primitive2d::TEXT_STRIKEOUT_NONE != 
pDecoratedCandidate->getTextStrikeout()
-              || primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
-                     != pDecoratedCandidate->getTextEmphasisMark();
-    }
-
-    // prepare flags for non-decorated after that, these *might* be
-    // dependent on flags above
-    bool bHasOutline(!bHasTextRelief && 
rTextCandidate.getFontAttribute().getOutline());
-
-    if (bHasShadow)
+    if (rTextCandidate.hasShadow())
     {
         // Text shadow is constant, relative to font size, *not* rotated with
         // text (always from top-left!)
         static const double fFactor(1.0 / 24.0);
         const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
 
-        // see OutputDevice::ImplDrawSpecialText -> no longer simple fixed 
color
+        // see ::ImplDrawSpecialText -> no longer simple fixed color
         const basegfx::BColor aBlack(0.0, 0.0, 0.0);
         basegfx::BColor aShadowColor(aBlack);
         if (aBlack == rTextCandidate.getFontColor()
@@ -3084,7 +3060,7 @@ void 
CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
         renderSalLayout(pSalLayout, aShadowColor, aShadowFullTextTransform,
                         getViewInformation2D().getUseAntiAliasing());
 
-        if (bHasTextDecoration)
+        if (rTextCandidate.hasTextDecoration())
         {
             const basegfx::B2DHomMatrix 
aTransform(getViewInformation2D().getObjectTransformation()
                                                    * aShadowTransform);
@@ -3092,16 +3068,13 @@ void 
CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
                                                               &aTransform, 
&aShadowColor);
         }
     }
+    // get TextColor early, may have to be modified
+    basegfx::BColor aTextColor(rTextCandidate.getFontColor());
 
-    if (bHasTextRelief)
-    {
-        // todo
-    }
-    else if (bHasOutline)
+    if (rTextCandidate.hasOutline())
     {
         // render as outline
-        basegfx::BColor aTextColor(
-            
maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
+        aTextColor = maBColorModifierStack.getModifiedColor(aTextColor);
         basegfx::B2DHomMatrix aInvViewTransform;
 
         // discrete offsets defined here to easily allow to change them,
@@ -3117,7 +3090,7 @@ void 
CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
             std::pair<double, double>{ fZero, fPlus },   std::pair<double, 
double>{ fPlus, fPlus }
         };
 
-        if (bHasTextDecoration)
+        if (rTextCandidate.hasTextDecoration())
         {
             // to use discrete offset (pixels) we will need the back-transform 
from
             // discrete view coordinates to 'world' coordinates (logic view 
coordinates),
@@ -3137,7 +3110,7 @@ void 
CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
                 basegfx::utils::createTranslateB2DHomMatrix(offset.first, 
offset.second));
             renderSalLayout(pSalLayout, aTextColor, aDiscreteOffset * 
aFullTextTransform,
                             getViewInformation2D().getUseAntiAliasing());
-            if (bHasTextDecoration)
+            if (rTextCandidate.hasTextDecoration())
             {
                 const basegfx::B2DHomMatrix aTransform(
                     aInvViewTransform * aDiscreteOffset
@@ -3151,27 +3124,64 @@ void 
CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
         aTextColor = 
maBColorModifierStack.getModifiedColor(COL_WHITE.getBColor());
         renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
                         getViewInformation2D().getUseAntiAliasing());
-        if (bHasTextDecoration)
+        if (rTextCandidate.hasTextDecoration())
         {
             
renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, 
aDecTrans,
                                                               nullptr, 
&aTextColor);
         }
+
+        // paint is complete, Outline and TextRelief cannot be combined, return
+        return;
     }
-    else
+
+    if (rTextCandidate.hasTextRelief())
     {
-        // render text
-        const basegfx::BColor aTextColor(
-            
maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
-        renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
+        // manipulate TextColor for final text paint below (see 
::ImplDrawSpecialText)
+        if (aTextColor == COL_BLACK.getBColor())
+            aTextColor = COL_WHITE.getBColor();
+
+        // relief offset defined here to easily allow to change them
+        // see ::ImplDrawSpecialText and the coment @ 'nOff += mnDPIX/300'
+        const bool bEmboss(primitive2d::TEXT_RELIEF_EMBOSSED
+                           == pDecoratedCandidate->getTextRelief());
+        constexpr double fReliefOffset(1.1);
+        const double fOffset(bEmboss ? fReliefOffset : -fReliefOffset);
+        const basegfx::B2DHomMatrix aDiscreteOffset(
+            basegfx::utils::createTranslateB2DHomMatrix(fOffset, fOffset));
+
+        // see aReliefColor in ::ImplDrawSpecialText
+        basegfx::BColor aReliefColor(COL_LIGHTGRAY.getBColor());
+        if (COL_WHITE.getBColor() == aTextColor)
+            aReliefColor = COL_BLACK.getBColor();
+        aReliefColor = maBColorModifierStack.getModifiedColor(aReliefColor);
+
+        // render relief text with offset
+        renderSalLayout(pSalLayout, aReliefColor, aDiscreteOffset * 
aFullTextTransform,
                         getViewInformation2D().getUseAntiAliasing());
 
-        if (bHasTextDecoration)
+        if (rTextCandidate.hasTextDecoration())
         {
-            // render using same geometry/primitives that a decompose would
-            // create -> safe to get the same visualization for both
-            
renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, 
aDecTrans);
+            basegfx::B2DHomMatrix 
aInvViewTransform(getViewInformation2D().getViewTransformation());
+            aInvViewTransform.invert();
+            const basegfx::B2DHomMatrix aTransform(
+                aInvViewTransform * aDiscreteOffset
+                * getViewInformation2D().getObjectToViewTransformation());
+            
renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, 
aDecTrans,
+                                                              &aTransform, 
&aReliefColor);
         }
     }
+
+    // render text
+    aTextColor = maBColorModifierStack.getModifiedColor(aTextColor);
+    renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
+                    getViewInformation2D().getUseAntiAliasing());
+
+    if (rTextCandidate.hasTextDecoration())
+    {
+        // render using same geometry/primitives that a decompose would
+        // create -> safe to get the same visualization for both
+        
renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, 
aDecTrans);
+    }
 }
 
 void CairoPixelProcessor2D::processBasePrimitive2D(const 
primitive2d::BasePrimitive2D& rCandidate)
diff --git a/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx 
b/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx
index 73cfb45349ca..07b780b24922 100644
--- a/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx
@@ -129,6 +129,11 @@ namespace drawinglayer::primitive2d
             /// helper for break-up text if needed
             const Primitive2DContainer& getOrCreateBrokenUpText() const;
 
+            /// helpers for determining various decoration states
+            virtual bool hasTextRelief() const override;
+            virtual bool hasShadow() const override;
+            virtual bool hasTextDecoration() const override;
+
             /// compare operator
             virtual bool operator==( const BasePrimitive2D& rPrimitive ) const 
override;
 
diff --git a/include/drawinglayer/primitive2d/textprimitive2d.hxx 
b/include/drawinglayer/primitive2d/textprimitive2d.hxx
index 44c59ae47828..a71c24732d45 100644
--- a/include/drawinglayer/primitive2d/textprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/textprimitive2d.hxx
@@ -175,6 +175,12 @@ public:
     const basegfx::BColor& getFontColor() const { return maFontColor; }
     const Color& getTextFillColor() const { return maTextFillColor; }
 
+    /// helpers for determining various decoration states
+    virtual bool hasTextRelief() const;
+    virtual bool hasShadow() const;
+    virtual bool hasTextDecoration() const;
+    virtual bool hasOutline() const;
+
     /// compare operator
     virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
 

Reply via email to