drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx  |    9 +
 drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx         |   48 
++++++----
 drawinglayer/source/processor2d/cairopixelprocessor2d.cxx           |   33 
+++++-
 drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx             |    2 
 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx          |   40 
+++++++-
 drawinglayer/source/processor2d/vclpixelprocessor2d.cxx             |    2 
 include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx |   11 +-
 include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx        |    9 +
 svx/source/sdr/primitive2d/sdrdecompositiontools.cxx                |   35 
++++---
 9 files changed, 143 insertions(+), 46 deletions(-)

New commits:
commit 4461173a4d0dff82e6223bdc1376230a9cd6b1cc
Author:     Armin Le Grand (Collabora) <armin.le.gr...@me.com>
AuthorDate: Thu Jul 25 20:25:31 2024 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Fri Jul 26 10:56:33 2024 +0200

    CairoSDPR: Support direct transparency for gradients
    
    FillGradientPrimitive2D/ and PolyPolygonGradientPrimitive2D
    now support both alphas, a gradientAlpha and a unified one.
    This allows a processor to directly handle a gradient with
    a unifiedAlpha if he wants. Adapted other places accordingly.
    
    NOTE: In VclMetafileProcessor2D handling of the primitive
    PolyPolygonGradientPrimitive2D has to do a local compromize,
    see comment there.
    
    Change-Id: I536f4935dafde0369f768dbd281d547b7bb08eb4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171052
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>
    Tested-by: Jenkins

diff --git a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx 
b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
index de114e2fdf53..d37e3d71225b 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
@@ -40,7 +40,7 @@ Primitive2DReference 
PolyPolygonGradientPrimitive2D::create2DDecomposition(
         const basegfx::B2DRange 
aPolyPolygonRange(getB2DPolyPolygon().getB2DRange());
         rtl::Reference<FillGradientPrimitive2D> pNewGradient = new 
FillGradientPrimitive2D(
             aPolyPolygonRange, getDefinitionRange(), getFillGradient(),
-            hasAlphaGradient() ? &getAlphaGradient() : nullptr);
+            hasAlphaGradient() ? &getAlphaGradient() : nullptr, 
getTransparency());
         Primitive2DContainer aSubSequence{ pNewGradient };
 
         // create mask primitive
@@ -56,17 +56,19 @@ 
PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
     , maDefinitionRange(rPolyPolygon.getB2DRange())
     , maFillGradient(rFillGradient)
     , maAlphaGradient()
+    , mfTransparency(0.0)
 {
 }
 
 PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
     basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& 
rDefinitionRange,
     const attribute::FillGradientAttribute& rFillGradient,
-    const attribute::FillGradientAttribute* pAlphaGradient)
+    const attribute::FillGradientAttribute* pAlphaGradient, double 
fTransparency)
     : maPolyPolygon(std::move(aPolyPolygon))
     , maDefinitionRange(rDefinitionRange)
     , maFillGradient(rFillGradient)
     , maAlphaGradient()
+    , mfTransparency(fTransparency)
 {
     // copy alpha gradient if we got one
     if (nullptr != pAlphaGradient)
@@ -93,6 +95,9 @@ bool PolyPolygonGradientPrimitive2D::operator==(const 
BasePrimitive2D& rPrimitiv
     if (maAlphaGradient != rCompare.maAlphaGradient)
         return false;
 
+    if (!basegfx::fTools::equal(getTransparency(), rCompare.getTransparency()))
+        return false;
+
     return true;
 }
 
diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx 
b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
index 19dfe8482430..8c2e339f8fbb 100644
--- a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
@@ -25,6 +25,7 @@
 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
 #include <utility>
 #include <algorithm>
 
@@ -264,22 +265,33 @@ namespace drawinglayer::primitive2d
             // SDPR: support alpha directly now. If a primitive processor
             // cannot deal with it, use it's decomposition. For that purpose
             // this decomposition has two stages now: This 1st one will
-            // separate content and alpha into a TransparencePrimitive2D,
+            // (if needed) separate content and alpha into a 
TransparencePrimitive2D
+            // and (if needed) embed that to a UnifiedTransparencePrimitive2D,
             // so all processors can work as before
-            if (hasAlphaGradient())
+            if (hasAlphaGradient() || hasTransparency())
             {
-                Primitive2DContainer aContent{ new FillGradientPrimitive2D(
-                    getOutputRange(),
-                    getDefinitionRange(),
-                    getFillGradient()) };
-
-                Primitive2DContainer aAlpha{ new FillGradientPrimitive2D(
-                    getOutputRange(),
-                    getDefinitionRange(),
-                    getAlphaGradient()) };
-
-                return Primitive2DReference{ new 
TransparencePrimitive2D(std::move(aContent),
-                                                                        
std::move(aAlpha)) };
+                Primitive2DReference aRetval(
+                    new FillGradientPrimitive2D(
+                        getOutputRange(),
+                        getDefinitionRange(),
+                        getFillGradient()));
+
+                if (hasAlphaGradient())
+                {
+                    Primitive2DContainer aAlpha{ new FillGradientPrimitive2D(
+                        getOutputRange(),
+                        getDefinitionRange(),
+                        getAlphaGradient()) };
+
+                    aRetval = new 
TransparencePrimitive2D(Primitive2DContainer{ aRetval }, std::move(aAlpha));
+                }
+
+                if (hasTransparency())
+                {
+                    aRetval = new 
UnifiedTransparencePrimitive2D(Primitive2DContainer{ aRetval }, 
getTransparency());
+                }
+
+                return aRetval;
             }
 
             // default creates overlapping fill which works with AntiAliasing 
and without.
@@ -303,6 +315,7 @@ namespace drawinglayer::primitive2d
         , maDefinitionRange(rOutputRange)
         , maFillGradient(rFillGradient)
         , maAlphaGradient()
+        , mfTransparency(0.0)
         {
         }
 
@@ -310,11 +323,13 @@ namespace drawinglayer::primitive2d
             const basegfx::B2DRange& rOutputRange,
             const basegfx::B2DRange& rDefinitionRange,
             const attribute::FillGradientAttribute& rFillGradient,
-            const attribute::FillGradientAttribute* pAlphaGradient)
+            const attribute::FillGradientAttribute* pAlphaGradient,
+            double fTransparency)
         : maOutputRange(rOutputRange)
         , maDefinitionRange(rDefinitionRange)
         , maFillGradient(rFillGradient)
         , maAlphaGradient()
+        , mfTransparency(fTransparency)
         {
             // copy alpha gradient if we got one
             if (nullptr != pAlphaGradient)
@@ -340,6 +355,9 @@ namespace drawinglayer::primitive2d
             if (maAlphaGradient != rCompare.maAlphaGradient)
                 return false;
 
+            if (!basegfx::fTools::equal(getTransparency(), 
rCompare.getTransparency()))
+                return false;
+
             return true;
         }
 
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index 2f5fad1e2c59..21ee580c3cf4 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -2151,8 +2151,17 @@ void 
CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial(
         }
         else
         {
-            cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), 
aColor.getRed(),
-                                             aColor.getGreen(), 
aColor.getBlue());
+            if (rFillGradientPrimitive2D.hasTransparency())
+            {
+                cairo_pattern_add_color_stop_rgba(pPattern, 
rStop.getStopOffset(), aColor.getRed(),
+                                                  aColor.getGreen(), 
aColor.getBlue(),
+                                                  1.0 - 
rFillGradientPrimitive2D.getTransparency());
+            }
+            else
+            {
+                cairo_pattern_add_color_stop_rgb(pPattern, 
rStop.getStopOffset(), aColor.getRed(),
+                                                 aColor.getGreen(), 
aColor.getBlue());
+            }
         }
     }
 
@@ -2177,9 +2186,12 @@ void 
CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial(
 void CairoPixelProcessor2D::processFillGradientPrimitive2D_square_rect(
     const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
 {
-    if (rFillGradientPrimitive2D.hasAlphaGradient())
+    if (rFillGradientPrimitive2D.hasAlphaGradient() || 
rFillGradientPrimitive2D.hasTransparency())
     {
-        // process recursively
+        // Do not use direct alpha for this: It paints using four trapez that
+        // do not add up at edges due to being painted AntiAliased; that means
+        // common pixels do not add up, but blend by transparency, so leaving
+        // visual traces -> process recursively
         process(rFillGradientPrimitive2D);
         return;
     }
@@ -2542,8 +2554,17 @@ void 
CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical(
         }
         else
         {
-            cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), 
aColor.getRed(),
-                                             aColor.getGreen(), 
aColor.getBlue());
+            if (rFillGradientPrimitive2D.hasTransparency())
+            {
+                cairo_pattern_add_color_stop_rgba(pPattern, 
rStop.getStopOffset(), aColor.getRed(),
+                                                  aColor.getGreen(), 
aColor.getBlue(),
+                                                  1.0 - 
rFillGradientPrimitive2D.getTransparency());
+            }
+            else
+            {
+                cairo_pattern_add_color_stop_rgb(pPattern, 
rStop.getStopOffset(), aColor.getRed(),
+                                                 aColor.getGreen(), 
aColor.getBlue());
+            }
         }
     }
 
diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
index 986515c6e339..a192c55db7ae 100644
--- a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
@@ -1940,7 +1940,7 @@ void D2DPixelProcessor2D::processFillGraphicPrimitive2D(
 void D2DPixelProcessor2D::processFillGradientPrimitive2D(
     const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
 {
-    if (rFillGradientPrimitive2D.hasAlphaGradient())
+    if (rFillGradientPrimitive2D.hasAlphaGradient() || 
rFillGradientPrimitive2D.hasTransparency())
     {
         // SDPR: As long as direct alpha is not supported by this
         // renderer we need to work on the decomposition, so call it
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index aa54a63366ae..0f6bf4c5c740 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -1984,15 +1984,45 @@ void 
VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(
 void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
     const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate)
 {
-    bool useDecompose(false);
-
     // SDPR: Caution: metafile export cannot handle added TransparencyGradient
-    if (rGradientCandidate.hasAlphaGradient())
+    if (rGradientCandidate.hasAlphaGradient() || 
rGradientCandidate.hasTransparency())
     {
-        // SDPR: metafile cannot handle this, use decompose
-        useDecompose = true;
+        // if it has alpha added directly we need to use the decomposition.
+        // unfortunately VclMetafileProcessor2D does *not* support the
+        // primitive created in the decomposition, the FillGradientPrimitive2D.
+        // at the same time extra stuff like adding gradient info to the
+        // metafile (BGRAD_SEQ_BEGIN) only is done HERE. To solve that and to
+        // not add PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D now, create a 
temporary
+        // decomposition to again get a PolyPolygonGradientPrimitive2D, but
+        // *without* directly added alpha
+        primitive2d::Primitive2DReference aRetval(new 
primitive2d::PolyPolygonGradientPrimitive2D(
+            rGradientCandidate.getB2DPolyPolygon(), 
rGradientCandidate.getDefinitionRange(),
+            rGradientCandidate.getFillGradient()));
+
+        if (rGradientCandidate.hasAlphaGradient())
+        {
+            const basegfx::B2DRange aPolyPolygonRange(
+                rGradientCandidate.getB2DPolyPolygon().getB2DRange());
+            primitive2d::Primitive2DContainer aAlpha{ new 
primitive2d::FillGradientPrimitive2D(
+                aPolyPolygonRange, rGradientCandidate.getDefinitionRange(),
+                rGradientCandidate.getAlphaGradient()) };
+
+            aRetval = new primitive2d::TransparencePrimitive2D(
+                primitive2d::Primitive2DContainer{ aRetval }, 
std::move(aAlpha));
+        }
+
+        if (rGradientCandidate.hasTransparency())
+        {
+            aRetval = new primitive2d::UnifiedTransparencePrimitive2D(
+                primitive2d::Primitive2DContainer{ aRetval }, 
rGradientCandidate.getTransparency());
+        }
+
+        process(primitive2d::Primitive2DContainer{ aRetval });
+        return;
     }
 
+    bool useDecompose(false);
+
     if (!useDecompose)
     {
         basegfx::B2DVector aScale, aTranslate;
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index ea0bea62b0fa..f6d684c06c3f 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -930,7 +930,7 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const 
primitive2d::BasePrim
 void VclPixelProcessor2D::processFillGradientPrimitive2D(
     const primitive2d::FillGradientPrimitive2D& rPrimitive)
 {
-    if (rPrimitive.hasAlphaGradient())
+    if (rPrimitive.hasAlphaGradient() || rPrimitive.hasTransparency())
     {
         // SDPR: As long as direct alpha is not supported by this
         // renderer we need to work on the decomposition, so call it
diff --git 
a/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx 
b/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx
index 13afc0616016..31b4374dbc00 100644
--- a/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx
+++ b/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx
@@ -49,6 +49,9 @@ private:
     /// evtl. fitting alphaGradient definition
     attribute::FillGradientAttribute maAlphaGradient;
 
+    /// the transparency in range [0.0 .. 1.0]
+    double mfTransparency;
+
     /// local decomposition.
     virtual Primitive2DReference
     create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) 
const override;
@@ -60,16 +63,20 @@ public:
     PolyPolygonGradientPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon,
                                    const basegfx::B2DRange& rDefinitionRange,
                                    const attribute::FillGradientAttribute& 
rFillGradient,
-                                   const attribute::FillGradientAttribute* 
pAlphaGradient
-                                   = nullptr);
+                                   const attribute::FillGradientAttribute* 
pAlphaGradient = nullptr,
+                                   double fTransparency = 0.0);
 
     /// data read access
     const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return 
maPolyPolygon; }
     const basegfx::B2DRange& getDefinitionRange() const { return 
maDefinitionRange; }
     const attribute::FillGradientAttribute& getFillGradient() const { return 
maFillGradient; }
+
     const attribute::FillGradientAttribute& getAlphaGradient() const { return 
maAlphaGradient; }
     bool hasAlphaGradient() const { return !maAlphaGradient.isDefault(); }
 
+    double getTransparency() const { return mfTransparency; }
+    bool hasTransparency() const { return 
!basegfx::fTools::equalZero(mfTransparency); }
+
     /// compare operator
     virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
 
diff --git a/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx 
b/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx
index 41b8c91f507f..0654801e8ef8 100644
--- a/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx
@@ -70,6 +70,8 @@ namespace drawinglayer::primitive2d
             /// evtl. fitting alphaGradient definition
             attribute::FillGradientAttribute        maAlphaGradient;
 
+            /// the transparency in range [0.0 .. 1.0]
+            double mfTransparency;
 
         protected:
             /// local helper
@@ -93,15 +95,20 @@ namespace drawinglayer::primitive2d
                 const basegfx::B2DRange& rOutputRange,
                 const basegfx::B2DRange& rDefinitionRange,
                 const attribute::FillGradientAttribute& rFillGradient,
-                const attribute::FillGradientAttribute* pAlphaGradient = 
nullptr);
+                const attribute::FillGradientAttribute* pAlphaGradient = 
nullptr,
+                double fTransparency = 0.0);
 
             /// data read access
             const basegfx::B2DRange& getOutputRange() const { return 
maOutputRange; }
             const basegfx::B2DRange& getDefinitionRange() const { return 
maDefinitionRange; }
             const attribute::FillGradientAttribute& getFillGradient() const { 
return maFillGradient; }
+
             const attribute::FillGradientAttribute& getAlphaGradient() const { 
return maAlphaGradient; }
             bool hasAlphaGradient() const { return 
!maAlphaGradient.isDefault(); }
 
+            double getTransparency() const { return mfTransparency; }
+            bool hasTransparency() const { return 
!basegfx::fTools::equalZero(mfTransparency); }
+
             /// compare operator
             virtual bool operator==(const BasePrimitive2D& rPrimitive) const 
override final;
 
diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx 
b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
index 53b0ae33b711..b518dbf8ede1 100644
--- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
+++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
@@ -404,25 +404,34 @@ sal_uInt32 
SlideBackgroundFillPrimitive2D::getPrimitive2DID() const
 
             if(!rFillGradient.isDefault())
             {
-                // SDPR: check early if we have a gradient and an alpha
-                // gradient that 'fits' in its geometric definition
-                // so that it can be rendered as RGBA directly. If yes,
-                // create it and return early
-                const bool bIncludeAlpha(
-                    0.0 == rFill.getTransparence()
+                const bool 
bHasTransparency(!basegfx::fTools::equalZero(rFill.getTransparence()));
+                // note: need to use !bHasTransparency to do the same as below
+                // where embedding to transparency is done. There, simple 
transparency
+                // gets priority over gradient transparency (and none). Thus 
here only one
+                // option is used. Note that the implementation of 
FillGradientPrimitive2D
+                // and PolyPolygonGradientPrimitive2D do support both alphas 
being used
+                const bool bHasAlphaGradient(!bHasTransparency
                     && !rAlphaGradient.isDefault()
                     && rFillGradient.sameDefinitionThanAlpha(rAlphaGradient));
 
+                if(bHasTransparency || bHasAlphaGradient)
+                {
+                    // SDPR: check early if we have a gradient and an alpha
+                    // gradient that 'fits' in its geometric definition
+                    // so that it can be rendered as RGBA directly. If yes,
+                    // create it and return early
+                    return new PolyPolygonGradientPrimitive2D(
+                        rPolyPolygon,
+                        rDefinitionRange,
+                        rFillGradient,
+                        bHasAlphaGradient ? &rAlphaGradient : nullptr,
+                        bHasTransparency ? rFill.getTransparence() : 0.0);
+                }
+
                 pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(
                     rPolyPolygon,
                     rDefinitionRange,
-                    rFillGradient,
-                    bIncludeAlpha ? &rAlphaGradient : nullptr);
-
-                if (bIncludeAlpha)
-                {
-                    return pNewFillPrimitive;
-                }
+                    rFillGradient);
             }
             else if(!rFill.getHatch().isDefault())
             {

Reply via email to