drawinglayer/source/primitive2d/svggradientprimitive2d.cxx  |  203 +++++-----
 drawinglayer/source/processor2d/cairopixelprocessor2d.cxx   |  223 ++++++++++++
 include/drawinglayer/primitive2d/svggradientprimitive2d.hxx |   22 -
 include/drawinglayer/processor2d/cairopixelprocessor2d.hxx  |   10 
 4 files changed, 351 insertions(+), 107 deletions(-)

New commits:
commit 13366d4951bd4ed92cdeed3c8fdeb1ab0543a3db
Author:     Armin Le Grand (Collabora) <armin.le.gr...@me.com>
AuthorDate: Thu Aug 22 12:13:55 2024 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Thu Aug 22 21:11:00 2024 +0200

    CairoSDPR: Add support for SVG Gradient direct rendering
    
    Change-Id: I7fb1c885e46a23e999ea4c98a73d3d526a2923e5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172245
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx 
b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
index 69881695a22b..4b8a5cb4ab86 100644
--- a/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
@@ -104,7 +104,6 @@ namespace drawinglayer::primitive2d
 
         void SvgGradientHelper::checkPreconditions()
         {
-            mbPreconditionsChecked = true;
             const SvgGradientEntryVector& rEntries = getGradientEntries();
 
             if(rEntries.empty())
@@ -356,7 +355,6 @@ namespace drawinglayer::primitive2d
             maGradientEntries(std::move(rGradientEntries)),
             maStart(rStart),
             maSpreadMethod(aSpreadMethod),
-            mbPreconditionsChecked(false),
             mbCreatesContent(false),
             mbSingleEntry(false),
             mbFullyOpaque(true),
@@ -468,13 +466,56 @@ namespace drawinglayer::primitive2d
             }
         }
 
-        Primitive2DReference 
SvgLinearGradientPrimitive2D::create2DDecomposition(const 
geometry::ViewInformation2D& /*rViewInformation*/) const
+        basegfx::B2DHomMatrix 
SvgLinearGradientPrimitive2D::createUnitGradientToObjectTransformation() const
         {
-            if(!getPreconditionsChecked())
+            const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+            const double fPolyWidth(aPolyRange.getWidth());
+            const double fPolyHeight(aPolyRange.getHeight());
+
+            // create ObjectTransform based on polygon range
+            const basegfx::B2DHomMatrix aObjectTransform(
+                basegfx::utils::createScaleTranslateB2DHomMatrix(
+                    fPolyWidth, fPolyHeight,
+                    aPolyRange.getMinX(), aPolyRange.getMinY()));
+            basegfx::B2DHomMatrix aUnitGradientToObject;
+
+            if(getUseUnitCoordinates())
             {
-                const_cast< SvgLinearGradientPrimitive2D* 
>(this)->checkPreconditions();
+                // interpret in unit coordinate system -> object aspect ratio 
will scale result
+                // create unit transform from unit vector [0.0 .. 1.0] along 
the X-Axis to given
+                // gradient vector defined by Start,End
+                const basegfx::B2DVector aVector(getEnd() - getStart());
+                const double fVectorLength(aVector.getLength());
+
+                aUnitGradientToObject.scale(fVectorLength, 1.0);
+                aUnitGradientToObject.rotate(atan2(aVector.getY(), 
aVector.getX()));
+                aUnitGradientToObject.translate(getStart().getX(), 
getStart().getY());
+
+                aUnitGradientToObject *= getGradientTransform();
+
+                // create full transform from unit gradient coordinates to 
object coordinates
+                // including the SvgGradient transformation
+                aUnitGradientToObject *= aObjectTransform;
+            }
+            else
+            {
+                // interpret in object coordinate system -> object aspect 
ratio will not scale result
+                const basegfx::B2DPoint aStart(aObjectTransform * getStart());
+                const basegfx::B2DPoint aEnd(aObjectTransform * getEnd());
+                const basegfx::B2DVector aVector(aEnd - aStart);
+
+                aUnitGradientToObject.scale(aVector.getLength(), 1.0);
+                aUnitGradientToObject.rotate(atan2(aVector.getY(), 
aVector.getX()));
+                aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
+
+                aUnitGradientToObject *= getGradientTransform();
             }
 
+            return aUnitGradientToObject;
+        }
+
+        Primitive2DReference 
SvgLinearGradientPrimitive2D::create2DDecomposition(const 
geometry::ViewInformation2D& /*rViewInformation*/) const
+        {
             if(getSingleEntry())
             {
                 // fill with last existing color
@@ -484,48 +525,7 @@ namespace drawinglayer::primitive2d
             {
                 // at least two color stops in range [0.0 .. 1.0], sorted, 
non-null vector, not completely
                 // invisible, width and height to fill are not empty
-                const basegfx::B2DRange 
aPolyRange(getPolyPolygon().getB2DRange());
-                const double fPolyWidth(aPolyRange.getWidth());
-                const double fPolyHeight(aPolyRange.getHeight());
-
-                // create ObjectTransform based on polygon range
-                const basegfx::B2DHomMatrix aObjectTransform(
-                    basegfx::utils::createScaleTranslateB2DHomMatrix(
-                        fPolyWidth, fPolyHeight,
-                        aPolyRange.getMinX(), aPolyRange.getMinY()));
-                basegfx::B2DHomMatrix aUnitGradientToObject;
-
-                if(getUseUnitCoordinates())
-                {
-                    // interpret in unit coordinate system -> object aspect 
ratio will scale result
-                    // create unit transform from unit vector [0.0 .. 1.0] 
along the X-Axis to given
-                    // gradient vector defined by Start,End
-                    const basegfx::B2DVector aVector(getEnd() - getStart());
-                    const double fVectorLength(aVector.getLength());
-
-                    aUnitGradientToObject.scale(fVectorLength, 1.0);
-                    aUnitGradientToObject.rotate(atan2(aVector.getY(), 
aVector.getX()));
-                    aUnitGradientToObject.translate(getStart().getX(), 
getStart().getY());
-
-                    aUnitGradientToObject *= getGradientTransform();
-
-                    // create full transform from unit gradient coordinates to 
object coordinates
-                    // including the SvgGradient transformation
-                    aUnitGradientToObject *= aObjectTransform;
-                }
-                else
-                {
-                    // interpret in object coordinate system -> object aspect 
ratio will not scale result
-                    const basegfx::B2DPoint aStart(aObjectTransform * 
getStart());
-                    const basegfx::B2DPoint aEnd(aObjectTransform * getEnd());
-                    const basegfx::B2DVector aVector(aEnd - aStart);
-
-                    aUnitGradientToObject.scale(aVector.getLength(), 1.0);
-                    aUnitGradientToObject.rotate(atan2(aVector.getY(), 
aVector.getX()));
-                    aUnitGradientToObject.translate(aStart.getX(), 
aStart.getY());
-
-                    aUnitGradientToObject *= getGradientTransform();
-                }
+                basegfx::B2DHomMatrix 
aUnitGradientToObject(createUnitGradientToObjectTransformation());
 
                 // create inverse from it
                 basegfx::B2DHomMatrix 
aObjectToUnitGradient(aUnitGradientToObject);
@@ -574,6 +574,8 @@ namespace drawinglayer::primitive2d
         :   SvgGradientHelper(rGradientTransform, rPolyPolygon, 
std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod),
             maEnd(rEnd)
         {
+            // ensure Preconditions are checked
+            checkPreconditions();
         }
 
         SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
@@ -647,8 +649,9 @@ namespace drawinglayer::primitive2d
 
                 if(isFocalSet())
                 {
-                    const basegfx::B2DVector aTranslateFrom(maFocalVector * 
(maFocalLength - fScaleFrom));
-                    const basegfx::B2DVector aTranslateTo(maFocalVector * 
(maFocalLength - fScaleTo));
+                    const basegfx::B2DVector aFocalVector(getFocal() - 
getStart());
+                    const basegfx::B2DVector aTranslateFrom(aFocalVector * 
(maFocalLength - fScaleFrom));
+                    const basegfx::B2DVector aTranslateTo(aFocalVector * 
(maFocalLength - fScaleTo));
 
                     rTargetColor.push_back(
                         new SvgRadialAtomPrimitive2D(
@@ -672,8 +675,9 @@ namespace drawinglayer::primitive2d
 
                     if(isFocalSet())
                     {
-                        const basegfx::B2DVector aTranslateFrom(maFocalVector 
* (maFocalLength - fScaleFrom));
-                        const basegfx::B2DVector aTranslateTo(maFocalVector * 
(maFocalLength - fScaleTo));
+                        const basegfx::B2DVector aFocalVector(getFocal() - 
getStart());
+                        const basegfx::B2DVector aTranslateFrom(aFocalVector * 
(maFocalLength - fScaleFrom));
+                        const basegfx::B2DVector aTranslateTo(aFocalVector * 
(maFocalLength - fScaleTo));
 
                         rTargetOpacity.push_back(
                             new SvgRadialAtomPrimitive2D(
@@ -691,13 +695,54 @@ namespace drawinglayer::primitive2d
             }
         }
 
-        Primitive2DReference 
SvgRadialGradientPrimitive2D::create2DDecomposition(const 
geometry::ViewInformation2D& /*rViewInformation*/) const
+        basegfx::B2DHomMatrix 
SvgRadialGradientPrimitive2D::createUnitGradientToObjectTransformation() const
         {
-            if(!getPreconditionsChecked())
+            const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+            const double fPolyWidth(aPolyRange.getWidth());
+            const double fPolyHeight(aPolyRange.getHeight());
+
+            // create ObjectTransform based on polygon range
+            const basegfx::B2DHomMatrix aObjectTransform(
+                basegfx::utils::createScaleTranslateB2DHomMatrix(
+                    fPolyWidth, fPolyHeight,
+                    aPolyRange.getMinX(), aPolyRange.getMinY()));
+            basegfx::B2DHomMatrix aUnitGradientToObject;
+
+            if(getUseUnitCoordinates())
             {
-                const_cast< SvgRadialGradientPrimitive2D* 
>(this)->checkPreconditions();
+                // interpret in unit coordinate system -> object aspect ratio 
will scale result
+                // create unit transform from unit vector to given linear 
gradient vector
+                aUnitGradientToObject.scale(getRadius(), getRadius());
+                aUnitGradientToObject.translate(getStart().getX(), 
getStart().getY());
+
+                if(!getGradientTransform().isIdentity())
+                {
+                    aUnitGradientToObject = getGradientTransform() * 
aUnitGradientToObject;
+                }
+
+                // create full transform from unit gradient coordinates to 
object coordinates
+                // including the SvgGradient transformation
+                aUnitGradientToObject = aObjectTransform * 
aUnitGradientToObject;
             }
+            else
+            {
+                // interpret in object coordinate system -> object aspect 
ratio will not scale result
+                // use X-Axis with radius, it was already made relative to 
object width when coming from
+                // SVG import
+                const double fRadius((aObjectTransform * 
basegfx::B2DVector(getRadius(), 0.0)).getLength());
+                const basegfx::B2DPoint aStart(aObjectTransform * getStart());
+
+                aUnitGradientToObject.scale(fRadius, fRadius);
+                aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
+
+                aUnitGradientToObject *= getGradientTransform();
+            }
+
+            return aUnitGradientToObject;
+        }
 
+        Primitive2DReference 
SvgRadialGradientPrimitive2D::create2DDecomposition(const 
geometry::ViewInformation2D& /*rViewInformation*/) const
+        {
             if(getSingleEntry())
             {
                 // fill with last existing color
@@ -707,46 +752,7 @@ namespace drawinglayer::primitive2d
             {
                 // at least two color stops in range [0.0 .. 1.0], sorted, 
non-null vector, not completely
                 // invisible, width and height to fill are not empty
-                const basegfx::B2DRange 
aPolyRange(getPolyPolygon().getB2DRange());
-                const double fPolyWidth(aPolyRange.getWidth());
-                const double fPolyHeight(aPolyRange.getHeight());
-
-                // create ObjectTransform based on polygon range
-                const basegfx::B2DHomMatrix aObjectTransform(
-                    basegfx::utils::createScaleTranslateB2DHomMatrix(
-                        fPolyWidth, fPolyHeight,
-                        aPolyRange.getMinX(), aPolyRange.getMinY()));
-                basegfx::B2DHomMatrix aUnitGradientToObject;
-
-                if(getUseUnitCoordinates())
-                {
-                    // interpret in unit coordinate system -> object aspect 
ratio will scale result
-                    // create unit transform from unit vector to given linear 
gradient vector
-                    aUnitGradientToObject.scale(getRadius(), getRadius());
-                    aUnitGradientToObject.translate(getStart().getX(), 
getStart().getY());
-
-                    if(!getGradientTransform().isIdentity())
-                    {
-                        aUnitGradientToObject = getGradientTransform() * 
aUnitGradientToObject;
-                    }
-
-                    // create full transform from unit gradient coordinates to 
object coordinates
-                    // including the SvgGradient transformation
-                    aUnitGradientToObject = aObjectTransform * 
aUnitGradientToObject;
-                }
-                else
-                {
-                    // interpret in object coordinate system -> object aspect 
ratio will not scale result
-                    // use X-Axis with radius, it was already made relative to 
object width when coming from
-                    // SVG import
-                    const double fRadius((aObjectTransform * 
basegfx::B2DVector(getRadius(), 0.0)).getLength());
-                    const basegfx::B2DPoint aStart(aObjectTransform * 
getStart());
-
-                    aUnitGradientToObject.scale(fRadius, fRadius);
-                    aUnitGradientToObject.translate(aStart.getX(), 
aStart.getY());
-
-                    aUnitGradientToObject *= getGradientTransform();
-                }
+                basegfx::B2DHomMatrix 
aUnitGradientToObject(createUnitGradientToObjectTransformation());
 
                 // create inverse from it
                 basegfx::B2DHomMatrix 
aObjectToUnitGradient(aUnitGradientToObject);
@@ -802,16 +808,15 @@ namespace drawinglayer::primitive2d
         :   SvgGradientHelper(rGradientTransform, rPolyPolygon, 
std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod),
             mfRadius(fRadius),
             maFocal(rStart),
-            maFocalVector(0.0, 0.0),
-            maFocalLength(0.0),
-            mbFocalSet(false)
+            maFocalLength(0.0)
         {
             if(pFocal && !pFocal->equal(getStart()))
             {
                 maFocal = *pFocal;
-                maFocalVector = maFocal - getStart();
-                mbFocalSet = true;
             }
+
+            // ensure Preconditions are checked
+            checkPreconditions();
         }
 
         SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index c3a1c74e764a..4fbddc3cf9ca 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -43,6 +43,7 @@
 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
 #include <drawinglayer/converters.hxx>
 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
 #include <basegfx/curve/b2dcubicbezier.hxx>
@@ -3213,6 +3214,216 @@ void 
CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
     }
 }
 
+bool CairoPixelProcessor2D::handleSvgGradientHelper(
+    const primitive2d::SvgGradientHelper& rCandidate)
+{
+    // check PolyPolygon to be filled
+    const basegfx::B2DPolyPolygon& rPolyPolygon(rCandidate.getPolyPolygon());
+
+    if (!rPolyPolygon.count())
+    {
+        // no PolyPolygon, done
+        return true;
+    }
+
+    // calculate visible range
+    basegfx::B2DRange aPolyPolygonRange(rPolyPolygon.getB2DRange());
+    
aPolyPolygonRange.transform(getViewInformation2D().getObjectToViewTransformation());
+    if (!getDiscreteViewRange(mpRT).overlaps(aPolyPolygonRange))
+    {
+        // not visible, done
+        return true;
+    }
+
+    if (!rCandidate.getCreatesContent())
+    {
+        // creates no content, done
+        return true;
+    }
+
+    if (rCandidate.getSingleEntry())
+    {
+        // only one color entry, fill with last existing color, done
+        primitive2d::SvgGradientEntryVector::const_reference aEntry(
+            rCandidate.getGradientEntries().back());
+        paintPolyPoylgonRGBA(rCandidate.getPolyPolygon(), aEntry.getColor(),
+                             1.0 - aEntry.getOpacity());
+
+        return true;
+    }
+
+    return false;
+}
+
+void CairoPixelProcessor2D::processSvgLinearGradientPrimitive2D(
+    const primitive2d::SvgLinearGradientPrimitive2D& rCandidate)
+{
+    // check for simple cases, returns if all necesary is already done
+    if (handleSvgGradientHelper(rCandidate))
+    {
+        // simple case, handled, done
+        return;
+    }
+
+    cairo_save(mpRT);
+
+    // set ObjectToView as regular transformation at CairoContext
+    const basegfx::B2DHomMatrix 
aTrans(getViewInformation2D().getObjectToViewTransformation());
+    cairo_matrix_t aMatrix;
+    cairo_matrix_init(&aMatrix, aTrans.a(), aTrans.b(), aTrans.c(), 
aTrans.d(), aTrans.e(),
+                      aTrans.f());
+    cairo_set_matrix(mpRT, &aMatrix);
+
+    // create pattern using unit coordinates. Unit coordinates here means that
+    // the transformation provided by the primitive maps the linear gradient
+    // to (0,0) -> (1,0) at the unified object coordinates, along the unified
+    // X-Axis
+    cairo_pattern_t* pPattern(cairo_pattern_create_linear(0, 0, 1, 0));
+
+    // get pre-defined UnitGradientToObject transformation from primitive
+    // and invert to get ObjectToUnitGradient transform
+    basegfx::B2DHomMatrix aObjectToUnitGradient(
+        rCandidate.createUnitGradientToObjectTransformation());
+    aObjectToUnitGradient.invert();
+
+    // set ObjectToUnitGradient as transformation at gradient - patterns
+    // need the inverted transformation, see cairo documentation
+    cairo_matrix_init(&aMatrix, aObjectToUnitGradient.a(), 
aObjectToUnitGradient.b(),
+                      aObjectToUnitGradient.c(), aObjectToUnitGradient.d(),
+                      aObjectToUnitGradient.e(), aObjectToUnitGradient.f());
+    cairo_pattern_set_matrix(pPattern, &aMatrix);
+
+    // add color stops
+    const primitive2d::SvgGradientEntryVector& 
rGradientEntries(rCandidate.getGradientEntries());
+
+    for (const auto& entry : rGradientEntries)
+    {
+        const basegfx::BColor& rColor(entry.getColor());
+        cairo_pattern_add_color_stop_rgba(pPattern, entry.getOffset(), 
rColor.getRed(),
+                                          rColor.getGreen(), rColor.getBlue(), 
entry.getOpacity());
+    }
+
+    // set SpreadMethod. Note that we have no SpreadMethod::None because the
+    // source is SVG and SVG does also not have that (checked that)
+    switch (rCandidate.getSpreadMethod())
+    {
+        case primitive2d::SpreadMethod::Pad:
+            cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_PAD);
+            break;
+        case primitive2d::SpreadMethod::Reflect:
+            cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REFLECT);
+            break;
+        case primitive2d::SpreadMethod::Repeat:
+            cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REPEAT);
+            break;
+    }
+
+    // get PathGeometry & paint it filed with gradient
+    cairo_new_path(mpRT);
+    getOrCreateFillGeometry(mpRT, rCandidate.getPolyPolygon());
+    cairo_set_source(mpRT, pPattern);
+    cairo_fill(mpRT);
+
+    // cleanup
+    cairo_pattern_destroy(pPattern);
+    cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processSvgRadialGradientPrimitive2D(
+    const primitive2d::SvgRadialGradientPrimitive2D& rCandidate)
+{
+    // check for simple cases, returns if all necesary is already done
+    if (handleSvgGradientHelper(rCandidate))
+    {
+        // simple case, handled, done
+        return;
+    }
+
+    cairo_save(mpRT);
+
+    // set ObjectToView as regular transformation at CairoContext
+    const basegfx::B2DHomMatrix 
aTrans(getViewInformation2D().getObjectToViewTransformation());
+    cairo_matrix_t aMatrix;
+    cairo_matrix_init(&aMatrix, aTrans.a(), aTrans.b(), aTrans.c(), 
aTrans.d(), aTrans.e(),
+                      aTrans.f());
+    cairo_set_matrix(mpRT, &aMatrix);
+
+    // get pre-defined UnitGradientToObject transformation from primitive
+    // and invert to get ObjectToUnitGradient transform
+    basegfx::B2DHomMatrix aObjectToUnitGradient(
+        rCandidate.createUnitGradientToObjectTransformation());
+    aObjectToUnitGradient.invert();
+
+    // prepare empty FocalVector
+    basegfx::B2DVector aFocalVector(0.0, 0.0);
+
+    if (rCandidate.isFocalSet())
+    {
+        // FocalPoint is used, create ObjectTransform based on polygon range
+        const basegfx::B2DRange 
aPolyRange(rCandidate.getPolyPolygon().getB2DRange());
+        const double fPolyWidth(aPolyRange.getWidth());
+        const double fPolyHeight(aPolyRange.getHeight());
+        const basegfx::B2DHomMatrix aObjectTransform(
+            basegfx::utils::createScaleTranslateB2DHomMatrix(
+                fPolyWidth, fPolyHeight, aPolyRange.getMinX(), 
aPolyRange.getMinY()));
+
+        // get vector, then transform to object coordinates, then to
+        // UnitGradient coordinates to be in the needed coordinate system
+        aFocalVector = basegfx::B2DVector(rCandidate.getStart() - 
rCandidate.getFocal());
+        aFocalVector *= aObjectTransform;
+        aFocalVector *= aObjectToUnitGradient;
+    }
+
+    // create pattern using unit coordinates. Unit coordinates here means that
+    // the transformation provided by the primitive maps the radial gradient
+    // to (0,0) as center, 1.0 as radius - which is the unit circle. The
+    // FocalPoint (if used) has to be relative to that, so - since unified
+    // center is at (0, 0), handling as vector is sufficient
+    cairo_pattern_t* pPattern(
+        cairo_pattern_create_radial(0, 0, 0, aFocalVector.getX(), 
aFocalVector.getY(), 1));
+
+    // set ObjectToUnitGradient as transformation at gradient - patterns
+    // need the inverted transformation, see cairo documentation
+    cairo_matrix_init(&aMatrix, aObjectToUnitGradient.a(), 
aObjectToUnitGradient.b(),
+                      aObjectToUnitGradient.c(), aObjectToUnitGradient.d(),
+                      aObjectToUnitGradient.e(), aObjectToUnitGradient.f());
+    cairo_pattern_set_matrix(pPattern, &aMatrix);
+
+    // add color stops
+    const primitive2d::SvgGradientEntryVector& 
rGradientEntries(rCandidate.getGradientEntries());
+
+    for (const auto& entry : rGradientEntries)
+    {
+        const basegfx::BColor& rColor(entry.getColor());
+        cairo_pattern_add_color_stop_rgba(pPattern, entry.getOffset(), 
rColor.getRed(),
+                                          rColor.getGreen(), rColor.getBlue(), 
entry.getOpacity());
+    }
+
+    // set SpreadMethod
+    switch (rCandidate.getSpreadMethod())
+    {
+        case primitive2d::SpreadMethod::Pad:
+            cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_PAD);
+            break;
+        case primitive2d::SpreadMethod::Reflect:
+            cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REFLECT);
+            break;
+        case primitive2d::SpreadMethod::Repeat:
+            cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REPEAT);
+            break;
+    }
+
+    // get PathGeometry & paint it filed with gradient
+    cairo_new_path(mpRT);
+    getOrCreateFillGeometry(mpRT, rCandidate.getPolyPolygon());
+    cairo_set_source(mpRT, pPattern);
+    cairo_fill(mpRT);
+
+    // cleanup
+    cairo_pattern_destroy(pPattern);
+    cairo_restore(mpRT);
+}
+
 void CairoPixelProcessor2D::processBasePrimitive2D(const 
primitive2d::BasePrimitive2D& rCandidate)
 {
     switch (rCandidate.getPrimitive2DID())
@@ -3359,6 +3570,18 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const 
primitive2d::BasePrimit
                 static_cast<const 
primitive2d::TextDecoratedPortionPrimitive2D&>(rCandidate));
             break;
         }
+        case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D:
+        {
+            processSvgLinearGradientPrimitive2D(
+                static_cast<const 
primitive2d::SvgLinearGradientPrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D:
+        {
+            processSvgRadialGradientPrimitive2D(
+                static_cast<const 
primitive2d::SvgRadialGradientPrimitive2D&>(rCandidate));
+            break;
+        }
 
         // continue with decompose
         default:
diff --git a/include/drawinglayer/primitive2d/svggradientprimitive2d.hxx 
b/include/drawinglayer/primitive2d/svggradientprimitive2d.hxx
index e63e1df96507..ff5ae8df2eac 100644
--- a/include/drawinglayer/primitive2d/svggradientprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/svggradientprimitive2d.hxx
@@ -100,7 +100,6 @@ namespace drawinglayer::primitive2d
             /// how to spread
             SpreadMethod                maSpreadMethod;
 
-            bool                        mbPreconditionsChecked : 1;
             bool                        mbCreatesContent : 1;
             bool                        mbSingleEntry : 1;
             bool                        mbFullyOpaque : 1;
@@ -137,13 +136,14 @@ namespace drawinglayer::primitive2d
                 Primitive2DContainer aTargetOpacity,
                 const basegfx::B2DHomMatrix& rUnitGradientToObject,
                 bool bInvert = false) const;
-            bool getCreatesContent() const { return mbCreatesContent; }
-            bool getSingleEntry() const { return mbSingleEntry; }
+
             void setSingleEntry() { mbSingleEntry = true; }
-            bool getPreconditionsChecked() const { return 
mbPreconditionsChecked; }
             bool getFullyOpaque() const { return mbFullyOpaque; }
 
         public:
+            bool getCreatesContent() const { return mbCreatesContent; }
+            bool getSingleEntry() const { return mbSingleEntry; }
+
             /// constructor
             SvgGradientHelper(
                 basegfx::B2DHomMatrix aGradientTransform,
@@ -164,6 +164,9 @@ namespace drawinglayer::primitive2d
 
             /// compare operator
             bool operator==(const SvgGradientHelper& rSvgGradientHelper) const;
+
+            /// create transformation from UnitGrandient to ObjectTransform
+            virtual basegfx::B2DHomMatrix 
createUnitGradientToObjectTransformation() const = 0;
         };
 
         /// the basic linear gradient primitive
@@ -209,6 +212,9 @@ namespace drawinglayer::primitive2d
 
             /// provide unique ID
             virtual sal_uInt32 getPrimitive2DID() const override;
+
+            /// create transformation from UnitGrandient to ObjectTransform
+            virtual basegfx::B2DHomMatrix 
createUnitGradientToObjectTransformation() const override;
         };
 
         /// the basic radial gradient primitive
@@ -220,11 +226,8 @@ namespace drawinglayer::primitive2d
 
             /// Focal only used when focal is set at all, see constructors
             basegfx::B2DPoint                       maFocal;
-            basegfx::B2DVector                      maFocalVector;
             double                                  maFocalLength;
 
-            bool                                    mbFocalSet : 1;
-
             /// local helpers
             virtual void createAtom(
                 Primitive2DContainer& rTargetColor,
@@ -254,7 +257,7 @@ namespace drawinglayer::primitive2d
             /// data read access
             double getRadius() const { return mfRadius; }
             const basegfx::B2DPoint& getFocal() const { return maFocal; }
-            bool isFocalSet() const { return mbFocalSet; }
+            bool isFocalSet() const { return !maFocal.equal(getStart()); }
 
             /// compare operator
             virtual bool operator==(const BasePrimitive2D& rPrimitive) const 
override;
@@ -264,6 +267,9 @@ namespace drawinglayer::primitive2d
 
             /// provide unique ID
             virtual sal_uInt32 getPrimitive2DID() const override;
+
+            /// create transformation from UnitGrandient to ObjectTransform
+            virtual basegfx::B2DHomMatrix 
createUnitGradientToObjectTransformation() const override;
         };
 
         // SvgLinearAtomPrimitive2D class
diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx 
b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
index 56281bd0cd32..02a49842c512 100644
--- a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
+++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
@@ -43,6 +43,9 @@ class BitmapAlphaPrimitive2D;
 class TextSimplePortionPrimitive2D;
 class TextDecoratedPortionPrimitive2D;
 class TextLayouterDevice;
+class SvgLinearGradientPrimitive2D;
+class SvgRadialGradientPrimitive2D;
+class SvgGradientHelper;
 }
 
 namespace basegfx
@@ -138,6 +141,13 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) 
CairoPixelProcessor2D final : pub
         const basegfx::B2DHomMatrix* pOptionalObjectTransform = nullptr,
         const basegfx::BColor* pReplacementColor = nullptr);
 
+    // support for SVG gradients
+    void processSvgLinearGradientPrimitive2D(
+        const primitive2d::SvgLinearGradientPrimitive2D& rCandidate);
+    void processSvgRadialGradientPrimitive2D(
+        const primitive2d::SvgRadialGradientPrimitive2D& rCandidate);
+    bool handleSvgGradientHelper(const primitive2d::SvgGradientHelper& 
rCandidate);
+
     /*  the local processor for BasePrimitive2D-Implementation based 
primitives,
         called from the common process()-implementation
      */

Reply via email to