basegfx/source/tools/bgradient.cxx | 19 ++++ drawinglayer/source/attribute/fillgradientattribute.cxx | 33 +++++++ drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx | 46 ++++++++++ drawinglayer/source/primitive2d/Tools.cxx | 2 include/basegfx/utils/bgradient.hxx | 4 include/drawinglayer/attribute/fillgradientattribute.hxx | 4 include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx | 41 ++++++++ include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx | 2 svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx | 4 svx/source/sdr/primitive2d/sdrdecompositiontools.cxx | 40 ++++++-- 10 files changed, 183 insertions(+), 12 deletions(-)
New commits: commit a073b6133960734b809c1bc93e39a76fdf1e7c15 Author: Armin Le Grand (Collabora) <armin.le.gr...@me.com> AuthorDate: Mon Jul 15 18:55:56 2024 +0200 Commit: Armin Le Grand <armin.le.gr...@me.com> CommitDate: Tue Jul 16 13:10:10 2024 +0200 CairoSDPR: Direct support for RGBA Gradients (I) Detect where created when a RGBA gradient could be used directly and create a primitive representing that, a PolyPolygonRGBAGradientPrimitive2D. That primitive decomposes to what was created before, so no primitive renderer has to be touched, all will work as before. NOTE: That helper primitive just holds references to what would be created anyways, so one depth step added but not really any additional data. This is the 1st step for direct support, the 2nd is to then detect and use that primitive in SDPR implementations directly. Change-Id: I4f247636ce58a8a1fd1e0df32dabed0d6cc10d0e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170527 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> diff --git a/basegfx/source/tools/bgradient.cxx b/basegfx/source/tools/bgradient.cxx index e27ab160463b..bac738ad3221 100644 --- a/basegfx/source/tools/bgradient.cxx +++ b/basegfx/source/tools/bgradient.cxx @@ -746,6 +746,25 @@ void BColorStops::tryToApplyBColorModifierStack(const BColorModifierStack& rBCol } } +bool BColorStops::sameSizeAndDistances(const BColorStops& rComp) const +{ + if (size() != rComp.size()) + { + return false; + } + + BColorStops::const_iterator EntryA(begin()); + BColorStops::const_iterator EntryB(rComp.begin()); + + while (EntryA != end() && fTools::equal(EntryA->getStopOffset(), EntryB->getStopOffset())) + { + EntryA++; + EntryB++; + } + + return EntryA == end(); +} + std::string BGradient::GradientStyleToString(css::awt::GradientStyle eStyle) { switch (eStyle) diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx b/drawinglayer/source/attribute/fillgradientattribute.cxx index e02fdd4a5dad..54f455f647ef 100644 --- a/drawinglayer/source/attribute/fillgradientattribute.cxx +++ b/drawinglayer/source/attribute/fillgradientattribute.cxx @@ -223,6 +223,39 @@ namespace drawinglayer::attribute return mpFillGradientAttribute->getSteps(); } + bool FillGradientAttribute::sameDefinitionThanAlpha(const FillGradientAttribute& rAlpha) const + { + // entries that are used by all gradient styles + if (getStyle() != rAlpha.getStyle() + || getBorder() != rAlpha.getBorder() + || getSteps() != rAlpha.getSteps()) + { + return false; + } + + // check for offsets if not ignored + const bool bIgnoreOffset(css::awt::GradientStyle_LINEAR == getStyle() || css::awt::GradientStyle_AXIAL == getStyle()); + if (!bIgnoreOffset && (getOffsetX() != rAlpha.getOffsetX() || getOffsetY() != rAlpha.getOffsetY())) + { + return false; + } + + // check for angle if not ignored + const bool bIgnoreAngle(css::awt::GradientStyle_RADIAL == getStyle()); + if (!bIgnoreAngle && getAngle() != rAlpha.getAngle()) + { + return false; + } + + // check for same count & offsets in the gradients (all except 'colors') + if (!getColorStops().sameSizeAndDistances(rAlpha.getColorStops())) + { + return false; + } + + return true; + } + } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx index 4fe3321e62f8..2e6b83ab526f 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx @@ -22,6 +22,8 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> #include <rtl/ref.hxx> #include <utility> @@ -84,6 +86,50 @@ sal_uInt32 PolyPolygonGradientPrimitive2D::getPrimitive2DID() const return PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D; } +Primitive2DReference PolyPolygonRGBAGradientPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + Primitive2DContainer aContent{ new PolyPolygonGradientPrimitive2D( + getB2DPolyPolygon(), getDefinitionRange(), getFillGradient()) }; + + Primitive2DContainer aAlpha{ new FillGradientPrimitive2D( + basegfx::utils::getRange(getB2DPolyPolygon()), getDefinitionRange(), + getFillGradientAlpha()) }; + + return Primitive2DReference{ new TransparencePrimitive2D(std::move(aContent), + std::move(aAlpha)) }; +} + +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 +{ + return PRIMITIVE2D_ID_POLYPOLYGONRGBAGRADIENTPRIMITIVE2D; +} + } // 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 bd74a4b5cd00..b13505bc3fd6 100644 --- a/drawinglayer/source/primitive2d/Tools.cxx +++ b/drawinglayer/source/primitive2d/Tools.cxx @@ -233,6 +233,8 @@ 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; default: return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF); } diff --git a/include/basegfx/utils/bgradient.hxx b/include/basegfx/utils/bgradient.hxx index f54b8763d87f..2ac8eba41d61 100644 --- a/include/basegfx/utils/bgradient.hxx +++ b/include/basegfx/utils/bgradient.hxx @@ -264,6 +264,10 @@ public: // Apply BColorModifierStack changes void tryToApplyBColorModifierStack(const BColorModifierStack& rBColorModifierStack); + + // check if local and given BColorStops have same count and distances, + // ignore colors + bool sameSizeAndDistances(const BColorStops& rComp) const; }; class BASEGFX_DLLPUBLIC BGradient final diff --git a/include/drawinglayer/attribute/fillgradientattribute.hxx b/include/drawinglayer/attribute/fillgradientattribute.hxx index 462d7fc8151e..62b890b5f0c1 100644 --- a/include/drawinglayer/attribute/fillgradientattribute.hxx +++ b/include/drawinglayer/attribute/fillgradientattribute.hxx @@ -83,6 +83,10 @@ public: // cannot do at all) bool cannotBeHandledByVCL() const; + // check if local and given FillGradientAttribute are identical, + // do take care of exceptions (e.g. no offset for linear/axial, ...) + bool sameDefinitionThanAlpha(const FillGradientAttribute& rAlpha) const; + // compare operator bool operator==(const FillGradientAttribute& rCandidate) const; diff --git a/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx b/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx index 1d4495806d15..07ec3460101e 100644 --- a/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx +++ b/include/drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx @@ -33,7 +33,7 @@ namespace drawinglayer::primitive2d decomosition will create a MaskPrimitive2D containing a FillGradientPrimitive2D. */ -class DRAWINGLAYER_DLLPUBLIC PolyPolygonGradientPrimitive2D final +class DRAWINGLAYER_DLLPUBLIC PolyPolygonGradientPrimitive2D : public BufferedDecompositionPrimitive2D { private: @@ -70,6 +70,45 @@ public: 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' coindition 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 f276caa8ec04..2345a4b86071 100644 --- a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx +++ b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx @@ -108,6 +108,8 @@ #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) + // When you add a new primitive, please update the drawinglayer::primitive2d::idToString() function // in drawinglayer/source/primitive2d/Tools.cxx. diff --git a/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx b/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx index ac65e0eda30f..e504acd41ab6 100644 --- a/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx +++ b/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx @@ -47,13 +47,13 @@ namespace drawinglayer::primitive2d Primitive2DReference SVXCORE_DLLPUBLIC createPolyPolygonFillPrimitive( const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::SdrFillAttribute& rFill, - const attribute::FillGradientAttribute& rFillGradient); + const attribute::FillGradientAttribute& rAlphaGradient); Primitive2DReference SVXCORE_DLLPUBLIC createPolyPolygonFillPrimitive( const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange, const attribute::SdrFillAttribute& rFill, - const attribute::FillGradientAttribute& rFillGradient); + const attribute::FillGradientAttribute& rAlphaGradient); Primitive2DReference SVXCORE_DLLPUBLIC createPolygonLinePrimitive( const basegfx::B2DPolygon& rPolygon, diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx index 2913f19b2548..10b48f851443 100644 --- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -368,12 +368,10 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const }; // end of anonymous namespace - class TransparencePrimitive2D; - Primitive2DReference createPolyPolygonFillPrimitive( const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::SdrFillAttribute& rFill, - const attribute::FillGradientAttribute& rFillGradient) + const attribute::FillGradientAttribute& rAlphaGradient) { // when we have no given definition range, use the range of the given geometry // also for definition (simplest case) @@ -383,29 +381,53 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const rPolyPolygon, aRange, rFill, - rFillGradient); + rAlphaGradient); } Primitive2DReference createPolyPolygonFillPrimitive( const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange, const attribute::SdrFillAttribute& rFill, - const attribute::FillGradientAttribute& rFillGradient) + const attribute::FillGradientAttribute& rAlphaGradient) { if(basegfx::fTools::moreOrEqual(rFill.getTransparence(), 1.0)) { return Primitive2DReference(); } + 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 + // decomnpose 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(!rFill.getGradient().isDefault()) + if(!rFillGradient.isDefault()) { pNewFillPrimitive = new PolyPolygonGradientPrimitive2D( rPolyPolygon, rDefinitionRange, - rFill.getGradient()); + rFillGradient); } else if(!rFill.getHatch().isDefault()) { @@ -442,7 +464,7 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const Primitive2DContainer aContent { pNewFillPrimitive }; return new UnifiedTransparencePrimitive2D(std::move(aContent), rFill.getTransparence()); } - else if(!rFillGradient.isDefault()) + else if(!rAlphaGradient.isDefault()) { // create sequence with created fill primitive Primitive2DContainer aContent { pNewFillPrimitive }; @@ -453,7 +475,7 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const new FillGradientPrimitive2D( basegfx::utils::getRange(rPolyPolygon), rDefinitionRange, - rFillGradient) + rAlphaGradient) }; // create TransparencePrimitive2D using alpha and content