drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx | 87 +--- drawinglayer/source/primitive2d/Tools.cxx | 2 drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx | 70 ++- drawinglayer/source/processor2d/cairopixelprocessor2d.cxx | 205 ++++------ drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx | 8 drawinglayer/source/processor2d/vclpixelprocessor2d.cxx | 8 include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx | 51 -- include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx | 3 include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx | 16 include/drawinglayer/processor2d/cairopixelprocessor2d.hxx | 22 - svx/source/sdr/primitive2d/sdrdecompositiontools.cxx | 40 - 11 files changed, 226 insertions(+), 286 deletions(-)
New commits: commit aac4974f2a50ebd34137d0384a5e21567c6ae646 Author: Armin Le Grand (Collabora) <[email protected]> AuthorDate: Thu Jul 18 14:08:48 2024 +0200 Commit: Armin Le Grand <[email protected]> CommitDate: Fri Jul 19 10:46:34 2024 +0200 CairoSDPR: Direct support for RGBA Gradients (III) I thought about this functionality again and decided to change doing it in a way that will support more alpha more directly in existing primitives - that will be better for also supporting e.g. PolyPolys with alpha, etc. Will need checking of existing usages of e.g. FillGradientPrimitive2D, but it's worth it. Note that when your primitive processor (usually a renderer) does *not* support alpha in gradients directly it is now requuired to continue using the decomposition (what renderers do automatically when calling 'BaseProcessor2D::process' in the default case. Checked that for existing processors. Change-Id: I840c9feb8c98549b790ef16285a309b42c4b1b53 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170687 Tested-by: Jenkins Reviewed-by: Armin Le Grand <[email protected]> diff --git a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx index 2e6b83ab526f..de114e2fdf53 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx @@ -39,7 +39,8 @@ Primitive2DReference PolyPolygonGradientPrimitive2D::create2DDecomposition( // create SubSequence with FillGradientPrimitive2D const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange()); rtl::Reference<FillGradientPrimitive2D> pNewGradient = new FillGradientPrimitive2D( - aPolyPolygonRange, getDefinitionRange(), getFillGradient()); + aPolyPolygonRange, getDefinitionRange(), getFillGradient(), + hasAlphaGradient() ? &getAlphaGradient() : nullptr); Primitive2DContainer aSubSequence{ pNewGradient }; // create mask primitive @@ -49,87 +50,57 @@ Primitive2DReference PolyPolygonGradientPrimitive2D::create2DDecomposition( } PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, attribute::FillGradientAttribute aFillGradient) + const basegfx::B2DPolyPolygon& rPolyPolygon, + const attribute::FillGradientAttribute& rFillGradient) : maPolyPolygon(rPolyPolygon) , maDefinitionRange(rPolyPolygon.getB2DRange()) - , maFillGradient(std::move(aFillGradient)) + , maFillGradient(rFillGradient) + , maAlphaGradient() { } PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D( basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - attribute::FillGradientAttribute aFillGradient) + const attribute::FillGradientAttribute& rFillGradient, + const attribute::FillGradientAttribute* pAlphaGradient) : maPolyPolygon(std::move(aPolyPolygon)) , maDefinitionRange(rDefinitionRange) - , maFillGradient(std::move(aFillGradient)) + , maFillGradient(rFillGradient) + , maAlphaGradient() { + // copy alpha gradient if we got one + if (nullptr != pAlphaGradient) + maAlphaGradient = *pAlphaGradient; } bool PolyPolygonGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const PolyPolygonGradientPrimitive2D& rCompare - = static_cast<const PolyPolygonGradientPrimitive2D&>(rPrimitive); + if (!(BufferedDecompositionPrimitive2D::operator==(rPrimitive))) + return false; - return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() - && getDefinitionRange() == rCompare.getDefinitionRange() - && getFillGradient() == rCompare.getFillGradient()); - } + const PolyPolygonGradientPrimitive2D& rCompare( + static_cast<const PolyPolygonGradientPrimitive2D&>(rPrimitive)); - return false; -} + if (getB2DPolyPolygon() != rCompare.getB2DPolyPolygon()) + return false; -// provide unique ID -sal_uInt32 PolyPolygonGradientPrimitive2D::getPrimitive2DID() const -{ - return PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D; -} + if (getDefinitionRange() != rCompare.getDefinitionRange()) + return false; -Primitive2DReference PolyPolygonRGBAGradientPrimitive2D::create2DDecomposition( - const geometry::ViewInformation2D& /*rViewInformation*/) const -{ - Primitive2DContainer aContent{ new PolyPolygonGradientPrimitive2D( - getB2DPolyPolygon(), getDefinitionRange(), getFillGradient()) }; + if (getFillGradient() != rCompare.getFillGradient()) + return false; - Primitive2DContainer aAlpha{ new FillGradientPrimitive2D( - basegfx::utils::getRange(getB2DPolyPolygon()), getDefinitionRange(), - getFillGradientAlpha()) }; + if (maAlphaGradient != rCompare.maAlphaGradient) + return false; - return Primitive2DReference{ new TransparencePrimitive2D(std::move(aContent), - std::move(aAlpha)) }; + return true; } -PolyPolygonRGBAGradientPrimitive2D::PolyPolygonRGBAGradientPrimitive2D( - basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - attribute::FillGradientAttribute aFillGradient, - attribute::FillGradientAttribute aFillGradientAlpha) - : PolyPolygonGradientPrimitive2D(aPolyPolygon, rDefinitionRange, aFillGradient) - , maFillGradientAlpha(aFillGradientAlpha) -{ - // assert when the definition is not allowed, it HAS to fulfil the - // requested preconditions - assert(aFillGradient.sameDefinitionThanAlpha(aFillGradientAlpha)); -} - -bool PolyPolygonRGBAGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const -{ - if (PolyPolygonGradientPrimitive2D::operator==(rPrimitive)) - { - const PolyPolygonRGBAGradientPrimitive2D& rCompare - = static_cast<const PolyPolygonRGBAGradientPrimitive2D&>(rPrimitive); - - return getFillGradientAlpha() == rCompare.getFillGradientAlpha(); - } - - return false; -} - -sal_uInt32 PolyPolygonRGBAGradientPrimitive2D::getPrimitive2DID() const +// provide unique ID +sal_uInt32 PolyPolygonGradientPrimitive2D::getPrimitive2DID() const { - return PRIMITIVE2D_ID_POLYPOLYGONRGBAGRADIENTPRIMITIVE2D; + return PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D; } - } // end drawinglayer::primitive2d namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx index 631ec63e52c1..cef1829e1567 100644 --- a/drawinglayer/source/primitive2d/Tools.cxx +++ b/drawinglayer/source/primitive2d/Tools.cxx @@ -233,8 +233,6 @@ OUString idToString(sal_uInt32 nId) return u"SINGLELINEPRIMITIVE"_ustr; case PRIMITIVE2D_ID_EXCLUSIVEEDITVIEWPRIMITIVE2D: return u"EXCLUSIVEEDITVIEWPRIMITIVE2D"_ustr; - case PRIMITIVE2D_ID_POLYPOLYGONRGBAGRADIENTPRIMITIVE2D: - return u"POLYPOLYGONRGBAGRADIENTPRIMITIVE2D"_ustr; case PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D: return u"ANIMATEDGRAPHICPRIMITIVE2D"_ustr; default: diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx index 255d4fb96a3d..19dfe8482430 100644 --- a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx @@ -24,6 +24,7 @@ #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/groupprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> #include <utility> #include <algorithm> @@ -260,51 +261,86 @@ namespace drawinglayer::primitive2d Primitive2DReference FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { + // 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, + // so all processors can work as before + if (hasAlphaGradient()) + { + Primitive2DContainer aContent{ new FillGradientPrimitive2D( + getOutputRange(), + getDefinitionRange(), + getFillGradient()) }; + + Primitive2DContainer aAlpha{ new FillGradientPrimitive2D( + getOutputRange(), + getDefinitionRange(), + getAlphaGradient()) }; + + return Primitive2DReference{ new TransparencePrimitive2D(std::move(aContent), + std::move(aAlpha)) }; + } + // default creates overlapping fill which works with AntiAliasing and without. // The non-overlapping version does not create single filled polygons, but // PolyPolygons where each one describes a 'ring' for the gradient such // that the rings will not overlap. This is useful for the old XOR-paint // 'trick' of VCL which is recorded in Metafiles; so this version may be // used from the MetafilePrimitive2D in its decomposition. - if(!getFillGradient().isDefault()) { return createFill(/*bOverlapping*/true); } + return nullptr; } FillGradientPrimitive2D::FillGradientPrimitive2D( const basegfx::B2DRange& rOutputRange, - attribute::FillGradientAttribute aFillGradient) - : maOutputRange(rOutputRange), - maDefinitionRange(rOutputRange), - maFillGradient(std::move(aFillGradient)) + const attribute::FillGradientAttribute& rFillGradient) + : maOutputRange(rOutputRange) + , maDefinitionRange(rOutputRange) + , maFillGradient(rFillGradient) + , maAlphaGradient() { } FillGradientPrimitive2D::FillGradientPrimitive2D( const basegfx::B2DRange& rOutputRange, const basegfx::B2DRange& rDefinitionRange, - attribute::FillGradientAttribute aFillGradient) - : maOutputRange(rOutputRange), - maDefinitionRange(rDefinitionRange), - maFillGradient(std::move(aFillGradient)) + const attribute::FillGradientAttribute& rFillGradient, + const attribute::FillGradientAttribute* pAlphaGradient) + : maOutputRange(rOutputRange) + , maDefinitionRange(rDefinitionRange) + , maFillGradient(rFillGradient) + , maAlphaGradient() { + // copy alpha gradient if we got one + if (nullptr != pAlphaGradient) + maAlphaGradient = *pAlphaGradient; } bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const FillGradientPrimitive2D& rCompare = static_cast<const FillGradientPrimitive2D&>(rPrimitive); + if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + return false; - return (getOutputRange() == rCompare.getOutputRange() - && getDefinitionRange() == rCompare.getDefinitionRange() - && getFillGradient() == rCompare.getFillGradient()); - } + const FillGradientPrimitive2D& rCompare(static_cast<const FillGradientPrimitive2D&>(rPrimitive)); + + if (getOutputRange() != rCompare.getOutputRange()) + return false; + + if (getDefinitionRange() != rCompare.getDefinitionRange()) + return false; + + if (getFillGradient() != rCompare.getFillGradient()) + return false; + + if (maAlphaGradient != rCompare.maAlphaGradient) + return false; - return false; + return true; } basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx index 034e59dceea4..e49ab17931ab 100644 --- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -763,7 +763,7 @@ void LuminanceToAlpha(cairo_surface_t* pMask) for (sal_uInt32 x(0); x < nWidth; ++x) { // do not forget that we have pre-multiplied alpha - const sal_uInt8 nAlpha(pMaskPixelData[SVP_CAIRO_ALPHA]); + sal_uInt8 nAlpha(pMaskPixelData[SVP_CAIRO_ALPHA]); if (0 != nAlpha) { @@ -772,17 +772,30 @@ void LuminanceToAlpha(cairo_surface_t* pMask) + pMaskPixelData[SVP_CAIRO_BLUE] * nBlueMul; if (255 != nAlpha) - pMaskPixelData[SVP_CAIRO_ALPHA] = fLuminance / nAlpha; + nAlpha = fLuminance / nAlpha; else - pMaskPixelData[SVP_CAIRO_ALPHA] = 255.0 * fLuminance; + nAlpha = 255.0 * fLuminance; } + pMaskPixelData[SVP_CAIRO_ALPHA] = 255 - nAlpha; pMaskPixelData += 4; } } cairo_surface_mark_dirty(pMask); } + +basegfx::B2DRange getDiscreteViewRange(cairo_t* pRT) +{ + double clip_x1, clip_x2, clip_y1, clip_y2; + cairo_save(pRT); + cairo_identity_matrix(pRT); + cairo_clip_extents(pRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2); + cairo_restore(pRT); + + return basegfx::B2DRange(basegfx::B2DPoint(clip_x1, clip_y1), + basegfx::B2DPoint(clip_x2, clip_y2)); +} } namespace drawinglayer::processor2d @@ -1093,11 +1106,7 @@ void CairoPixelProcessor2D::processTransparencePrimitive2D( rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); basegfx::B2DRange aVisibleRange(aDiscreteRange); - double clip_x1, clip_x2, clip_y1, clip_y2; - cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2); - const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1), - basegfx::B2DPoint(clip_x2, clip_y2)); - aVisibleRange.intersect(aViewRange); + aVisibleRange.intersect(getDiscreteViewRange(mpRT)); if (aVisibleRange.isEmpty()) { @@ -1157,11 +1166,7 @@ void CairoPixelProcessor2D::processInvertPrimitive2D( rInvertCandidate.getChildren().getB2DRange(getViewInformation2D())); aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); basegfx::B2DRange aVisibleRange(aDiscreteRange); - double clip_x1, clip_x2, clip_y1, clip_y2; - cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2); - const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1), - basegfx::B2DPoint(clip_x2, clip_y2)); - aVisibleRange.intersect(aViewRange); + aVisibleRange.intersect(getDiscreteViewRange(mpRT)); if (aVisibleRange.isEmpty()) { @@ -1265,9 +1270,7 @@ void CairoPixelProcessor2D::processInvertPrimitive2D( } void CairoPixelProcessor2D::processMaskPrimitive2D( - const primitive2d::MaskPrimitive2D& rMaskCandidate, - const primitive2d::FillGradientPrimitive2D* pFillGradientPrimitive2D, - const attribute::FillGradientAttribute* pFillGradientAlpha) + const primitive2d::MaskPrimitive2D& rMaskCandidate) { if (rMaskCandidate.getChildren().empty()) { @@ -1284,14 +1287,9 @@ void CairoPixelProcessor2D::processMaskPrimitive2D( } // calculate visible range - double clip_x1, clip_x2, clip_y1, clip_y2; - cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2); basegfx::B2DRange aMaskRange(aMask.getB2DRange()); aMaskRange.transform(getViewInformation2D().getObjectToViewTransformation()); - const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1), - basegfx::B2DPoint(clip_x2, clip_y2)); - - if (!aViewRange.overlaps(aMaskRange)) + if (!getDiscreteViewRange(mpRT).overlaps(aMaskRange)) { // not visible, done return; @@ -1317,20 +1315,12 @@ void CairoPixelProcessor2D::processMaskPrimitive2D( cairo_clip(mpRT); cairo_new_path(mpRT); - if (nullptr != pFillGradientPrimitive2D && nullptr != pFillGradientAlpha) - { - // special case: render given FillGradientPrimitive2D using - // FillGradientAlpha as RGBA gradient directly - // note that calling this method with nullptr != pFillGradientAlpha - // is only allowed internal from - // CairoPixelProcessor2D::processPolyPolygonRGBAGradientPrimitive2D - processFillGradientPrimitive2D(*pFillGradientPrimitive2D, pFillGradientAlpha); - } - else - { - // process sub-content (that shall be masked) - process(rMaskCandidate.getChildren()); - } + // reset transformation to not have it set when processing + // child content below (was only used to set clip path) + cairo_identity_matrix(mpRT); + + // process sub-content (that shall be masked) + process(rMaskCandidate.getChildren()); cairo_restore(mpRT); } @@ -1396,11 +1386,7 @@ void CairoPixelProcessor2D::processUnifiedTransparencePrimitive2D( rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); basegfx::B2DRange aVisibleRange(aDiscreteRange); - double clip_x1, clip_x2, clip_y1, clip_y2; - cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2); - const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1), - basegfx::B2DPoint(clip_x2, clip_y2)); - aVisibleRange.intersect(aViewRange); + aVisibleRange.intersect(getDiscreteViewRange(mpRT)); if (aVisibleRange.isEmpty()) { @@ -1900,7 +1886,28 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_drawOutputRange( // fill simple rect with outer color const basegfx::BColor aColor( maBColorModifierStack.getModifiedColor(rFillGradientPrimitive2D.getOuterColor())); - cairo_set_source_rgb(mpRT, aColor.getRed(), aColor.getGreen(), aColor.getBlue()); + + if (rFillGradientPrimitive2D.hasAlphaGradient()) + { + const attribute::FillGradientAttribute& rAlphaGradient( + rFillGradientPrimitive2D.getAlphaGradient()); + double fLuminance(0.0); + + if (!rAlphaGradient.getColorStops().empty()) + { + if (css::awt::GradientStyle_AXIAL == rAlphaGradient.getStyle()) + fLuminance = rAlphaGradient.getColorStops().back().getStopColor().luminance(); + else + fLuminance = rAlphaGradient.getColorStops().front().getStopColor().luminance(); + } + + cairo_set_source_rgba(mpRT, aColor.getRed(), aColor.getGreen(), aColor.getBlue(), + 1.0 - fLuminance); + } + else + { + cairo_set_source_rgb(mpRT, aColor.getRed(), aColor.getGreen(), aColor.getBlue()); + } const basegfx::B2DHomMatrix aTrans(getViewInformation2D().getObjectToViewTransformation()); cairo_matrix_t aMatrix; @@ -1937,9 +1944,12 @@ bool CairoPixelProcessor2D::processFillGradientPrimitive2D_isCompletelyBordered( } void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial( - const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D, - const attribute::FillGradientAttribute* pFillGradientAlpha) + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) { + const attribute::FillGradientAttribute& rFillGradient( + rFillGradientPrimitive2D.getFillGradient()); + assert(!rFillGradientPrimitive2D.hasAlphaGradient() + || rFillGradient.sameDefinitionThanAlpha(rFillGradientPrimitive2D.getAlphaGradient())); assert( (css::awt::GradientStyle_LINEAR == rFillGradientPrimitive2D.getFillGradient().getStyle() || css::awt::GradientStyle_AXIAL == rFillGradientPrimitive2D.getFillGradient().getStyle()) @@ -1948,8 +1958,6 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial( // need to do 'antique' stuff adaptions for rotate/transitionStart in object coordinates // (DefinitionRange) to have the right 'bending' on rotation - const attribute::FillGradientAttribute& rFillGradient( - rFillGradientPrimitive2D.getFillGradient()); basegfx::B2DRange aAdaptedRange(rFillGradientPrimitive2D.getDefinitionRange()); const double fAngle(basegfx::normalizeToRange((2 * M_PI) - rFillGradient.getAngle(), 2 * M_PI)); const bool bAngle(!basegfx::fTools::equalZero(fAngle)); @@ -1981,9 +1989,9 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial( // get color stops (make copy, might have to be changed) basegfx::BColorStops aBColorStops(rFillGradient.getColorStops()); basegfx::BColorStops aBColorStopsAlpha; - const bool bHasAlpha(nullptr != pFillGradientAlpha); + const bool bHasAlpha(rFillGradientPrimitive2D.hasAlphaGradient()); if (bHasAlpha) - aBColorStopsAlpha = pFillGradientAlpha->getColorStops(); + aBColorStopsAlpha = rFillGradientPrimitive2D.getAlphaGradient().getColorStops(); const bool bAxial(css::awt::GradientStyle_AXIAL == rFillGradient.getStyle()); // get and apply border - create soace at start in gradient @@ -2036,7 +2044,7 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial( const basegfx::BColor aAlpha(aBColorStopsAlpha[a].getStopColor()); cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(), aColor.getGreen(), aColor.getBlue(), - aAlpha.luminance()); + 1.0 - aAlpha.luminance()); } else { @@ -2066,6 +2074,13 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial( void CairoPixelProcessor2D::processFillGradientPrimitive2D_square_rect( const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) { + if (rFillGradientPrimitive2D.hasAlphaGradient()) + { + // process recursively + process(rFillGradientPrimitive2D); + return; + } + assert( (css::awt::GradientStyle_SQUARE == rFillGradientPrimitive2D.getFillGradient().getStyle() || css::awt::GradientStyle_RECT == rFillGradientPrimitive2D.getFillGradient().getStyle()) @@ -2321,9 +2336,12 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_square_rect( } void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical( - const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D, - const attribute::FillGradientAttribute* pFillGradientAlpha) + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) { + const attribute::FillGradientAttribute& rFillGradient( + rFillGradientPrimitive2D.getFillGradient()); + assert(!rFillGradientPrimitive2D.hasAlphaGradient() + || rFillGradient.sameDefinitionThanAlpha(rFillGradientPrimitive2D.getAlphaGradient())); assert((css::awt::GradientStyle_RADIAL == rFillGradientPrimitive2D.getFillGradient().getStyle() || css::awt::GradientStyle_ELLIPTICAL == rFillGradientPrimitive2D.getFillGradient().getStyle()) @@ -2332,8 +2350,6 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical( // need to do 'antique' stuff adaptions for rotate/transitionStart in object coordinates // (DefinitionRange) to have the right 'bending' on rotation - const attribute::FillGradientAttribute& rFillGradient( - rFillGradientPrimitive2D.getFillGradient()); const basegfx::B2DRange rDefRange(rFillGradientPrimitive2D.getDefinitionRange()); const basegfx::B2DPoint aCenter(rDefRange.getCenter()); double fRadius(1.0); @@ -2387,9 +2403,9 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical( // get color stops (make copy, might have to be changed) basegfx::BColorStops aBColorStops(rFillGradient.getColorStops()); basegfx::BColorStops aBColorStopsAlpha; - const bool bHasAlpha(nullptr != pFillGradientAlpha); + const bool bHasAlpha(rFillGradientPrimitive2D.hasAlphaGradient()); if (bHasAlpha) - aBColorStopsAlpha = pFillGradientAlpha->getColorStops(); + aBColorStopsAlpha = rFillGradientPrimitive2D.getAlphaGradient().getColorStops(); // get and apply border - create soace at start in gradient const double fBorder(std::max(std::min(rFillGradient.getBorder(), 1.0), 0.0)); @@ -2419,7 +2435,7 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical( const basegfx::BColor aAlpha(aBColorStopsAlpha[a].getStopColor()); cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(), aColor.getGreen(), aColor.getBlue(), - aAlpha.luminance()); + 1.0 - aAlpha.luminance()); } else { @@ -2480,6 +2496,13 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical( void CairoPixelProcessor2D::processFillGradientPrimitive2D_fallback_decompose( const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) { + if (rFillGradientPrimitive2D.hasAlphaGradient()) + { + // process recursively to elliminate alpha, cannot be used in decompose fallback + process(rFillGradientPrimitive2D); + return; + } + // this helper draws the given gradient using the decompose fallback, // maybe needed in some cases an can/will be handy cairo_save(mpRT); @@ -2515,8 +2538,7 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_fallback_decompose( } void CairoPixelProcessor2D::processFillGradientPrimitive2D( - const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D, - const attribute::FillGradientAttribute* pFillGradientAlpha) + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) { if (rFillGradientPrimitive2D.getDefinitionRange().isEmpty()) { @@ -2564,21 +2586,13 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D( return; } - // for direct RGBA gradient render support: assert when the definition - // is not allowed, it HAS to fulfil the requested preconditions. Note that - // the form to call this function using nullptr != pFillGradientAlpha is - // only allowed locally in CairoPixelProcessor2D::processMaskPrimitive2D - assert(nullptr == pFillGradientAlpha - || rFillGradient.sameDefinitionThanAlpha(*pFillGradientAlpha)); - switch (rFillGradient.getStyle()) { case css::awt::GradientStyle_LINEAR: case css::awt::GradientStyle_AXIAL: { // use specialized renderer for this cases - linear, axial - processFillGradientPrimitive2D_linear_axial(rFillGradientPrimitive2D, - pFillGradientAlpha); + processFillGradientPrimitive2D_linear_axial(rFillGradientPrimitive2D); return; } case css::awt::GradientStyle_RADIAL: @@ -2595,8 +2609,7 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D( // also rare. IF that should make problems reactivation of that case // for the default case below is possible. main reason is that speed // for direct rendering in cairo is much better. - processFillGradientPrimitive2D_radial_elliptical(rFillGradientPrimitive2D, - pFillGradientAlpha); + processFillGradientPrimitive2D_radial_elliptical(rFillGradientPrimitive2D); return; } case css::awt::GradientStyle_SQUARE: @@ -2623,53 +2636,6 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D( } } -void CairoPixelProcessor2D::processPolyPolygonRGBAGradientPrimitive2D( - const primitive2d::PolyPolygonRGBAGradientPrimitive2D& rPolyPolygonRGBAGradientPrimitive2D) -{ - const attribute::FillGradientAttribute& rFill( - rPolyPolygonRGBAGradientPrimitive2D.getFillGradient()); - const attribute::FillGradientAttribute& rAlpha( - rPolyPolygonRGBAGradientPrimitive2D.getFillGradientAlpha()); - - if (rFill.isDefault()) - { - // no gradient definition, done - return; - } - - // assert when the definition is not allowed, it HAS to fulfil the - // requested preconditions - assert(rFill.sameDefinitionThanAlpha(rAlpha)); - - // the gradient still needs to be masked to getB2DPolyPolygon() at the - // primitive, see PolyPolygonGradientPrimitive2D::create2DDecomposition. - // we could repeat here the code inside localprocessMaskPrimitive2D, but - // it is easier to just locally temporarily create the needed data structure - // and hand over the needed extra-data - const basegfx::B2DRange aPolyPolygonRange( - rPolyPolygonRGBAGradientPrimitive2D.getB2DPolyPolygon().getB2DRange()); - primitive2d::FillGradientPrimitive2D* pFillGradientPrimitive2D( - new primitive2d::FillGradientPrimitive2D( - aPolyPolygonRange, rPolyPolygonRGBAGradientPrimitive2D.getDefinitionRange(), rFill)); - primitive2d::Primitive2DContainer aContent{ pFillGradientPrimitive2D }; - - // NOTE: I had this like - // const primitive2d::MaskPrimitive2D aMaskPrimitive2D( - // rPolyPolygonRGBAGradientPrimitive2D.getB2DPolyPolygon(), std::move(aContent)); - // but I got - // error: salhelper::SimpleReferenceObject subclass being directly stack managed, should - // be managed via rtl::Reference, const primitive2d::MaskPrimitive2D [loplugin:refcounting] - // thus I have *no choice* and have to use the heap here (?) It is no problem to use the stack - // and I wanted to do this here by purpose... sigh - primitive2d::MaskPrimitive2D* pMaskPrimitive2D(new primitive2d::MaskPrimitive2D( - rPolyPolygonRGBAGradientPrimitive2D.getB2DPolyPolygon(), std::move(aContent))); - primitive2d::Primitive2DContainer aMask{ pMaskPrimitive2D }; - (void)aMask; - - // render masked RGBA gradient - processMaskPrimitive2D(*pMaskPrimitive2D, pFillGradientPrimitive2D, &rAlpha); -} - void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { switch (rCandidate.getPrimitive2DID()) @@ -2729,6 +2695,7 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimit static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); break; } + // geometry that *may* be processed due to being able to do it better // then using the decomposition case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: @@ -2785,12 +2752,6 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimit static_cast<const primitive2d::FillGradientPrimitive2D&>(rCandidate)); break; } - case PRIMITIVE2D_ID_POLYPOLYGONRGBAGRADIENTPRIMITIVE2D: - { - processPolyPolygonRGBAGradientPrimitive2D( - static_cast<const primitive2d::PolyPolygonRGBAGradientPrimitive2D&>(rCandidate)); - break; - } // continue with decompose default: diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx index e9bcc6c10423..7bc422a98f43 100644 --- a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx @@ -1926,6 +1926,14 @@ void D2DPixelProcessor2D::processFillGraphicPrimitive2D( void D2DPixelProcessor2D::processFillGradientPrimitive2D( const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) { + if (rFillGradientPrimitive2D.hasAlphaGradient()) + { + // SDPR: As long as direct alpha is not supported by this + // renderer we need to work on the decomposition, so call it + process(rFillGradientPrimitive2D); + return; + } + // draw all-covering initial BG polygon 1st bool bDone(drawPolyPolygonColorTransformed( basegfx::B2DHomMatrix(), diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx index f09131b04c30..ea0bea62b0fa 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -930,6 +930,14 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrim void VclPixelProcessor2D::processFillGradientPrimitive2D( const primitive2d::FillGradientPrimitive2D& rPrimitive) { + if (rPrimitive.hasAlphaGradient()) + { + // SDPR: As long as direct alpha is not supported by this + // renderer we need to work on the decomposition, so call it + process(rPrimitive); + return; + } + const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient(); bool useDecompose(false); diff --git a/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx b/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx index 59bbff030abf..13afc0616016 100644 --- a/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx +++ b/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx @@ -46,6 +46,9 @@ private: /// the gradient definition attribute::FillGradientAttribute maFillGradient; + /// evtl. fitting alphaGradient definition + attribute::FillGradientAttribute maAlphaGradient; + /// local decomposition. virtual Primitive2DReference create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; @@ -53,15 +56,19 @@ private: public: /// constructors. The one without definition range will use output range as definition range PolyPolygonGradientPrimitive2D(const basegfx::B2DPolyPolygon& rPolyPolygon, - attribute::FillGradientAttribute rFillGradient); + const attribute::FillGradientAttribute& rFillGradient); PolyPolygonGradientPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - attribute::FillGradientAttribute aFillGradient); + const attribute::FillGradientAttribute& rFillGradient, + const attribute::FillGradientAttribute* pAlphaGradient + = nullptr); /// 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(); } /// compare operator virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; @@ -69,46 +76,6 @@ public: /// provide unique ID virtual sal_uInt32 getPrimitive2DID() const override; }; - -// helper primitive that can be used to directly express RGBA -// gradient definitions. It will be decomposed to a combined -// TransparencePrimitive2D if not handled directly. Use the -// already existing PolyPolygonGradientPrimitive2D as base class, -// only the additional FillGradientAlpha needs to be added. -// NOTE: FillGradientAlpha *has* to fulfil the -// 'sameDefinitionThanAlpha' condition defined by the check -// method with the same name -class DRAWINGLAYER_DLLPUBLIC PolyPolygonRGBAGradientPrimitive2D final - : public PolyPolygonGradientPrimitive2D -{ -private: - /// the gradient alpha definition - attribute::FillGradientAttribute maFillGradientAlpha; - - /// local decomposition. - virtual Primitive2DReference - create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; - -public: - /// constructors. The one without definition range will use output range as definition range - PolyPolygonRGBAGradientPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, - const basegfx::B2DRange& rDefinitionRange, - attribute::FillGradientAttribute aFillGradient, - attribute::FillGradientAttribute aFillGradientAlpha); - - /// data read access - const attribute::FillGradientAttribute& getFillGradientAlpha() const - { - return maFillGradientAlpha; - } - - /// compare operator - virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; - - /// provide unique ID - virtual sal_uInt32 getPrimitive2DID() const override; -}; - } // end of namespace primitive2d::drawinglayer /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx index 71d0be9988e7..ba165bb23db2 100644 --- a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx +++ b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx @@ -108,8 +108,7 @@ #define PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 74) #define PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 75) #define PRIMITIVE2D_ID_EXCLUSIVEEDITVIEWPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 76) -#define PRIMITIVE2D_ID_POLYPOLYGONRGBAGRADIENTPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 77) -#define PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 78) +#define PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 77) // When you add a new primitive, please update the drawinglayer::primitive2d::idToString() function // in drawinglayer/source/primitive2d/Tools.cxx. diff --git a/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx b/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx index 42f4de30fd91..41b8c91f507f 100644 --- a/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx +++ b/include/drawinglayer/primitive2d/fillgradientprimitive2d.hxx @@ -47,6 +47,12 @@ namespace drawinglayer::primitive2d default one since it works with and without AntiAliasing. The non-overlapping version is used in the MetafilePrimitive2D decomposition when the old XOR paint was recorded. + + SDPR: Have now added to directly support alpha in the definition so that + all implementations of primitive renderers may use it. Be aware that if a + renderer does not support using that alpha it is advised to use the + decomposition which separates content and alpha in a TransparencePrimitive2D. + That way all stuff being used to that before will work unchanged. */ class DRAWINGLAYER_DLLPUBLIC FillGradientPrimitive2D : public BufferedDecompositionPrimitive2D { @@ -61,6 +67,9 @@ namespace drawinglayer::primitive2d /// the gradient definition attribute::FillGradientAttribute maFillGradient; + /// evtl. fitting alphaGradient definition + attribute::FillGradientAttribute maAlphaGradient; + protected: /// local helper @@ -79,16 +88,19 @@ namespace drawinglayer::primitive2d /// constructors. The one without definition range will use output range as definition range FillGradientPrimitive2D( const basegfx::B2DRange& rOutputRange, - attribute::FillGradientAttribute aFillGradient); + const attribute::FillGradientAttribute& rFillGradient); FillGradientPrimitive2D( const basegfx::B2DRange& rOutputRange, const basegfx::B2DRange& rDefinitionRange, - attribute::FillGradientAttribute aFillGradient); + const attribute::FillGradientAttribute& rFillGradient, + const attribute::FillGradientAttribute* pAlphaGradient = nullptr); /// 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(); } /// compare operator virtual bool operator==(const BasePrimitive2D& rPrimitive) const override final; diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx index 74977b704051..00d75e5f594c 100644 --- a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx +++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx @@ -39,11 +39,6 @@ class PolyPolygonRGBAGradientPrimitive2D; class FillGraphicPrimitive2D; } -namespace drawinglayer::attribute -{ -class FillGradientAttribute; -} - namespace drawinglayer::processor2d { class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : public BaseProcessor2D @@ -65,11 +60,7 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : pub void processInvertPrimitive2D(const primitive2d::InvertPrimitive2D& rTransCandidate); void processUnifiedTransparencePrimitive2D( const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate); - void processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate, - const primitive2d::FillGradientPrimitive2D* pFillGradientPrimitive2D - = nullptr, - const attribute::FillGradientAttribute* pFillGradientAlpha - = nullptr); + void processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate); void processModifiedColorPrimitive2D( const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate); void processTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate); @@ -88,12 +79,9 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : pub void processSingleLinePrimitive2D(const primitive2d::SingleLinePrimitive2D& rSingleLinePrimitive2D); void processFillGradientPrimitive2D( - const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D, - const attribute::FillGradientAttribute* pFillGradientAlpha = nullptr); + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D); void processFillGraphicPrimitive2D( const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D); - void processPolyPolygonRGBAGradientPrimitive2D( - const primitive2d::PolyPolygonRGBAGradientPrimitive2D& rPolyPolygonRGBAGradientPrimitive2D); /* the local processor for BasePrimitive2D-Implementation based primitives, called from the common process()-implementation @@ -108,11 +96,9 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : pub bool processFillGradientPrimitive2D_isCompletelyBordered( const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D); void processFillGradientPrimitive2D_linear_axial( - const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D, - const attribute::FillGradientAttribute* pFillGradientAlpha = nullptr); + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D); void processFillGradientPrimitive2D_radial_elliptical( - const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D, - const attribute::FillGradientAttribute* pFillGradientAlpha = nullptr); + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D); void processFillGradientPrimitive2D_square_rect( const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D); diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx index 79bc74bf28a5..56656722d287 100644 --- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -395,39 +395,33 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const return Primitive2DReference(); } + // prepare access to FillGradientAttribute const attribute::FillGradientAttribute& rFillGradient(rFill.getGradient()); - // SDPR: check if RGB and A definitions of gradients are both - // used and equal, so that they could be combined to a single - // RGBA one - if (!rFillGradient.isDefault() - && 0.0 == rFill.getTransparence() - && !rAlphaGradient.isDefault() - && rFillGradient.sameDefinitionThanAlpha(rAlphaGradient)) - { - // if yes, create a primitive expressing that. That primitive's - // decompose will do the same as if the code below would be executed, - // so no primitive renderer who does not want to will have to handle - // it - but SDPR renderers that can directly render that may choose to - // do so. NOTE: That helper primitive just holds references to what - // would be created anyways, so one depth step added but not really any - // additional data - return new PolyPolygonRGBAGradientPrimitive2D( - rPolyPolygon, - rDefinitionRange, - rFillGradient, - rAlphaGradient); - } - // prepare fully scaled polygon rtl::Reference<BasePrimitive2D> pNewFillPrimitive; if(!rFillGradient.isDefault()) { + // SDPR: check early if we have a radient and a alpha + // gradient that 'fits' in it's 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() + && !rAlphaGradient.isDefault() + && rFillGradient.sameDefinitionThanAlpha(rAlphaGradient)); + pNewFillPrimitive = new PolyPolygonGradientPrimitive2D( rPolyPolygon, rDefinitionRange, - rFillGradient); + rFillGradient, + bIncludeAlpha ? &rAlphaGradient : nullptr); + + if (bIncludeAlpha) + { + return pNewFillPrimitive; + } } else if(!rFill.getHatch().isDefault()) {
