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()) {