diff options
author | Armin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de> | 2023-02-10 11:36:15 +0100 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@me.com> | 2023-02-12 10:54:06 +0000 |
commit | 43de98fb59ef3cd1c6eabd3174d57634b7d501a9 (patch) | |
tree | 2e926642ae6c4d595fa546bd02b21535c9e6a740 | |
parent | 496b1b3179ca096dea1fd91ab2408c02f16b06cf (diff) |
MCGR: Add/Provide GradientSteps to FillGradientAttribute
MCGR stands for MultiColorGRadient. This change allows/
prepares adding multiple color steps to gradient rendering.
This is preparation work to allow rendering MCGRs in the
future. All places are adapted in a way that currently
no change of behaviour will happen. It will be the base
to get MCGR rendering/decompose for Primitives and our
internal/existing gradients working.
Change-Id: I28bbd7d10b8670042343ada2a66b5909d3d31bbd
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146748
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
10 files changed, 215 insertions, 54 deletions
diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx b/drawinglayer/source/attribute/fillgradientattribute.cxx index b8b06e20bbbf..3d13c4050670 100644 --- a/drawinglayer/source/attribute/fillgradientattribute.cxx +++ b/drawinglayer/source/attribute/fillgradientattribute.cxx @@ -18,8 +18,6 @@ */ #include <drawinglayer/attribute/fillgradientattribute.hxx> -#include <basegfx/color/bcolor.hxx> - namespace drawinglayer::attribute { @@ -31,8 +29,7 @@ namespace drawinglayer::attribute double mfOffsetX; double mfOffsetY; double mfAngle; - basegfx::BColor maStartColor; - basegfx::BColor maEndColor; + FillGradientAttribute::ColorSteps maColorSteps; GradientStyle meStyle; sal_uInt16 mnSteps; @@ -44,16 +41,62 @@ namespace drawinglayer::attribute double fAngle, const basegfx::BColor& rStartColor, const basegfx::BColor& rEndColor, + const FillGradientAttribute::ColorSteps* pColorSteps, sal_uInt16 nSteps) : mfBorder(fBorder), mfOffsetX(fOffsetX), mfOffsetY(fOffsetY), mfAngle(fAngle), - maStartColor(rStartColor), - maEndColor(rEndColor), + maColorSteps(), meStyle(eStyle), mnSteps(nSteps) { + // always add start color to guarentee a color at all. It's also just safer + // to have one and not an empty vector, that spares many checks in the using code + maColorSteps.emplace_back(0.0, rStartColor); + + // if we have ColorSteps, integrate these + if(nullptr != pColorSteps) + { + for(const auto& candidate : *pColorSteps) + { + // only allow ]0.0 .. 1.0[ as offset values, *excluding* 0.0 and 1.0 + // explicitely - these are reserved for start/end color + if(basegfx::fTools::more(candidate.getOffset(), 0.0) && basegfx::fTools::less(candidate.getOffset(), 1.0)) + { + // ignore same offsets, independent from color (so 1st one wins) + // having two or more same offsets is an error (may assert evtl.) + bool bAccept(true); + + for(const auto& compare : maColorSteps) + { + if(basegfx::fTools::equal(compare.getOffset(), candidate.getOffset())) + { + bAccept = false; + break; + } + } + + if(bAccept) + { + maColorSteps.push_back(candidate); + } + } + } + + // sort by offset when colors were added + if(maColorSteps.size() > 1) + { + std::sort(maColorSteps.begin(), maColorSteps.end()); + } + } + + // add end color if different from last color - which is the start color + // when no ColorSteps are given + if(rEndColor != maColorSteps.back().getColor()) + { + maColorSteps.emplace_back(1.0, rEndColor); + } } ImpFillGradientAttribute() @@ -61,9 +104,12 @@ namespace drawinglayer::attribute mfOffsetX(0.0), mfOffsetY(0.0), mfAngle(0.0), + maColorSteps(), meStyle(GradientStyle::Linear), mnSteps(0) { + // always add a fallback color, see above + maColorSteps.emplace_back(0.0, basegfx::BColor()); } // data read access @@ -72,10 +118,25 @@ namespace drawinglayer::attribute double getOffsetX() const { return mfOffsetX; } double getOffsetY() const { return mfOffsetY; } double getAngle() const { return mfAngle; } - const basegfx::BColor& getStartColor() const { return maStartColor; } - const basegfx::BColor& getEndColor() const { return maEndColor; } + const FillGradientAttribute::ColorSteps& getColorSteps() const { return maColorSteps; } sal_uInt16 getSteps() const { return mnSteps; } + bool hasSingleColor() const + { + // no entries (should not happen, see comments for startColor) + if (0 == maColorSteps.size()) + return true; + + // check if not all colors are the same + const basegfx::BColor& rColor(maColorSteps[0].getColor()); + for (size_t a(1); a < maColorSteps.size(); a++) + if (maColorSteps[a].getColor() != rColor) + return false; + + // all colors are the same + return true; + } + bool operator==(const ImpFillGradientAttribute& rCandidate) const { return (getStyle() == rCandidate.getStyle() @@ -83,8 +144,7 @@ namespace drawinglayer::attribute && getOffsetX() == rCandidate.getOffsetX() && getOffsetY() == rCandidate.getOffsetY() && getAngle() == rCandidate.getAngle() - && getStartColor() == rCandidate.getStartColor() - && getEndColor() == rCandidate.getEndColor() + && getColorSteps() == rCandidate.getColorSteps() && getSteps() == rCandidate.getSteps()); } }; @@ -106,9 +166,10 @@ namespace drawinglayer::attribute double fAngle, const basegfx::BColor& rStartColor, const basegfx::BColor& rEndColor, + const ColorSteps* pColorSteps, sal_uInt16 nSteps) : mpFillGradientAttribute(ImpFillGradientAttribute( - eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rStartColor, rEndColor, nSteps)) + eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rStartColor, rEndColor, pColorSteps, nSteps)) { } @@ -128,6 +189,11 @@ namespace drawinglayer::attribute return mpFillGradientAttribute.same_object(theGlobalDefault()); } + bool FillGradientAttribute::hasSingleColor() const + { + return mpFillGradientAttribute->hasSingleColor(); + } + FillGradientAttribute& FillGradientAttribute::operator=(const FillGradientAttribute&) = default; FillGradientAttribute& FillGradientAttribute::operator=(FillGradientAttribute&&) = default; @@ -141,14 +207,9 @@ namespace drawinglayer::attribute return rCandidate.mpFillGradientAttribute == mpFillGradientAttribute; } - const basegfx::BColor& FillGradientAttribute::getStartColor() const - { - return mpFillGradientAttribute->getStartColor(); - } - - const basegfx::BColor& FillGradientAttribute::getEndColor() const + const FillGradientAttribute::ColorSteps& FillGradientAttribute::getColorSteps() const { - return mpFillGradientAttribute->getEndColor(); + return mpFillGradientAttribute->getColorSteps(); } double FillGradientAttribute::getBorder() const diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx index 5ca0b5852642..0f6eef0508cb 100644 --- a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx @@ -38,8 +38,8 @@ namespace drawinglayer::primitive2d rEntries.clear(); // make sure steps is not too high/low - const basegfx::BColor aStart(getFillGradient().getStartColor()); - const basegfx::BColor aEnd(getFillGradient().getEndColor()); + const basegfx::BColor aStart(getFillGradient().getColorSteps().front().getColor()); + const basegfx::BColor aEnd(getFillGradient().getColorSteps().back().getColor()); const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5)); sal_uInt32 nSteps(getFillGradient().getSteps()); diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index 33257f48175a..b5989303dd43 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -258,19 +258,20 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient( Gradient& o_rVCLGradient, const attribute::FillGradientAttribute& rFiGrAtt, bool bIsTransparenceGradient) const { + const basegfx::BColor aStartColor(rFiGrAtt.getColorSteps().front().getColor()); + const basegfx::BColor aEndColor(rFiGrAtt.getColorSteps().back().getColor()); + if (bIsTransparenceGradient) { // it's about transparence channel intensities (black/white), do not use color modifier - o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor())); - o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor())); + o_rVCLGradient.SetStartColor(Color(aStartColor)); + o_rVCLGradient.SetEndColor(Color(aEndColor)); } else { // use color modifier to influence start/end color of gradient - o_rVCLGradient.SetStartColor( - Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor()))); - o_rVCLGradient.SetEndColor( - Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor()))); + o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(aStartColor))); + o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(aEndColor))); } o_rVCLGradient.SetAngle( diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx index 7852131ee98f..7a171b915524 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -498,8 +498,10 @@ void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D( { // direct draw of gradient const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); - basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor())); - basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor())); + basegfx::BColor aStartColor( + maBColorModifierStack.getModifiedColor(rGradient.getColorSteps().front().getColor())); + basegfx::BColor aEndColor( + maBColorModifierStack.getModifiedColor(rGradient.getColorSteps().back().getColor())); basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); if (!aLocalPolyPolygon.count()) @@ -935,6 +937,14 @@ void VclPixelProcessor2D::processFillGradientPrimitive2D( { const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient(); + // MCGR: If GradientSteps are used, use decomposition since vcl is not able + // to render multi-color gradients + if (rFillGradient.getColorSteps().size() > 2) + { + process(rPrimitive); + return; + } + // VCL should be able to handle all styles, but for tdf#133477 the VCL result // is different from processing the gradient manually by drawinglayer // (and the Writer unittest for it fails). Keep using the drawinglayer code @@ -983,8 +993,8 @@ void VclPixelProcessor2D::processFillGradientPrimitive2D( GradientStyle eGradientStyle = convertGradientStyle(rFillGradient.getStyle()); - Gradient aGradient(eGradientStyle, Color(rFillGradient.getStartColor()), - Color(rFillGradient.getEndColor())); + Gradient aGradient(eGradientStyle, Color(rFillGradient.getColorSteps().front().getColor()), + Color(rFillGradient.getColorSteps().back().getColor())); aGradient.SetAngle(Degree10(static_cast<int>(basegfx::rad2deg<10>(rFillGradient.getAngle())))); aGradient.SetBorder(rFillGradient.getBorder() * 100); diff --git a/drawinglayer/source/processor3d/defaultprocessor3d.cxx b/drawinglayer/source/processor3d/defaultprocessor3d.cxx index b9159c46c73f..6be8e78d0d59 100644 --- a/drawinglayer/source/processor3d/defaultprocessor3d.cxx +++ b/drawinglayer/source/processor3d/defaultprocessor3d.cxx @@ -61,8 +61,8 @@ namespace drawinglayer::processor3d const basegfx::B2DRange aOutlineRange(0.0, 0.0, rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY()); const attribute::GradientStyle aGradientStyle(rFillGradient.getStyle()); sal_uInt32 nSteps(rFillGradient.getSteps()); - const basegfx::BColor& aStart(rFillGradient.getStartColor()); - const basegfx::BColor& aEnd(rFillGradient.getEndColor()); + const basegfx::BColor aStart(rFillGradient.getColorSteps().front().getColor()); + const basegfx::BColor aEnd(rFillGradient.getColorSteps().back().getColor()); const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5)); std::shared_ptr< texture::GeoTexSvx > pNewTex; diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx index 74e95836fdf9..65967980d110 100644 --- a/drawinglayer/source/tools/primitive2dxmldump.cxx +++ b/drawinglayer/source/tools/primitive2dxmldump.cxx @@ -299,8 +299,22 @@ void writeSdrFillAttribute(::tools::XmlWriter& rWriter, rWriter.attribute("offsetY", rGradient.getOffsetY()); rWriter.attribute("angle", rGradient.getAngle()); rWriter.attribute("steps", rGradient.getSteps()); - rWriter.attribute("startColor", convertColorToString(rGradient.getStartColor())); - rWriter.attribute("endColor", convertColorToString(rGradient.getEndColor())); + + auto const& rColorSteps(rGradient.getColorSteps()); + for (size_t a(0); a < rColorSteps.size(); a++) + { + if (0 == a) + rWriter.attribute("startColor", convertColorToString(rColorSteps[a].getColor())); + else if (rColorSteps.size() == a + 1) + rWriter.attribute("endColor", convertColorToString(rColorSteps[a].getColor())); + else + { + rWriter.startElement("colorStep"); + rWriter.attribute("offset", rColorSteps[a].getOffset()); + rWriter.attribute("color", convertColorToString(rColorSteps[a].getColor())); + rWriter.endElement(); + } + } rWriter.endElement(); } diff --git a/drawinglayer/source/tools/wmfemfhelper.cxx b/drawinglayer/source/tools/wmfemfhelper.cxx index 3a06f6423665..21a88757c954 100644 --- a/drawinglayer/source/tools/wmfemfhelper.cxx +++ b/drawinglayer/source/tools/wmfemfhelper.cxx @@ -718,6 +718,7 @@ namespace wmfemfhelper toRadians(rGradient.GetAngle()), aStart, aEnd, + nullptr, rGradient.GetSteps()); } @@ -919,10 +920,10 @@ namespace wmfemfhelper { drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if(aAttribute.hasSingleColor()) { // not really a gradient. Create filled rectangle - return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder); + return CreateColorWallpaper(rRange, aAttribute.getColorSteps().front().getColor(), rPropertyHolder); } else { @@ -2089,7 +2090,7 @@ namespace wmfemfhelper drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); basegfx::B2DPolyPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange)); - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if(aAttribute.hasSingleColor()) { // not really a gradient. Create filled rectangle createFillPrimitive( @@ -2799,13 +2800,13 @@ namespace wmfemfhelper const Gradient& rGradient = pA->GetGradient(); drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if(aAttribute.hasSingleColor()) { // not really a gradient; create UnifiedTransparencePrimitive2D rTargetHolders.Current().append( new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( std::move(xSubContent), - aAttribute.getStartColor().luminance())); + aAttribute.getColorSteps().front().getColor().luminance())); } else { @@ -2919,13 +2920,13 @@ namespace wmfemfhelper const Gradient& rGradient = pMetaGradientExAction->GetGradient(); drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if(aAttribute.hasSingleColor()) { // not really a gradient rTargetHolders.Current().append( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( std::move(aPolyPolygon), - aAttribute.getStartColor())); + aAttribute.getColorSteps().front().getColor())); } else { diff --git a/include/drawinglayer/attribute/fillgradientattribute.hxx b/include/drawinglayer/attribute/fillgradientattribute.hxx index 50a87a685f1a..11b87f1b3a32 100644 --- a/include/drawinglayer/attribute/fillgradientattribute.hxx +++ b/include/drawinglayer/attribute/fillgradientattribute.hxx @@ -21,11 +21,8 @@ #include <drawinglayer/drawinglayerdllapi.h> #include <o3tl/cow_wrapper.hxx> - -namespace basegfx -{ -class BColor; -} +#include <basegfx/color/bcolor.hxx> +#include <vector> namespace drawinglayer::attribute { @@ -46,14 +43,88 @@ class DRAWINGLAYER_DLLPUBLIC FillGradientAttribute public: typedef o3tl::cow_wrapper<ImpFillGradientAttribute> ImplType; + /* MCGR: Provide ColorSteps to the FillGradientAttribute + + This is the needed combination of offset and color: + + Offset is defined as: + - being in the range of [0.0 .. 1.0] (unit range) + - 0.0 being reserved for StartColor + - 1.0 being reserved for EndColor + - in-between offsets thus being in the range of ]0.0 .. 1.0[ + - no two equal offsets are allowed + - this is an error, but will be ignored (maybe assert?) + - missing 1.0 entry (EndColor) is allowed + - at least one value (usually 0.0, StartColor) is required + - this allows to avoid massive testing in all places where + this data has to be accessed + + Color is defined as: + - RGB with unit values [0.0 .. 1.0] + + These definitions are packed in a std::vector<ColorStep> ColorSteps, + see typedef below. This array is sorted ascending by offsets, from + lowest to highest. Since all this primitive data definition is + read-only, this can be guaranteed by forcing/checking this in the + constructor. + */ + class ColorStep + { + private: + double mfOffset; + basegfx::BColor maColor; + + public: + ColorStep(double fOffset, const basegfx::BColor& rColor) + : mfOffset(fOffset) + , maColor(rColor) + { + } + + double getOffset() const { return mfOffset; } + const basegfx::BColor& getColor() const { return maColor; } + + bool operator<(const ColorStep& rCandidate) const + { + return getOffset() < rCandidate.getOffset(); + } + + bool operator==(const ColorStep& rCandidate) const + { + return getOffset() == rCandidate.getOffset() && getColor() == rCandidate.getColor(); + } + }; + + typedef std::vector<ColorStep> ColorSteps; + private: ImplType mpFillGradientAttribute; public: + /* MCGR: Adaptions for MultiColorGradients + + To force providing start/end colors these are still part of the + constructor (see rStartColor/rEndColor). To also provide + GradientSteps these need to be handed over by ColorSteps data + if wanted/needed. + + Start/EndColor will be added to the internal ColorSteps with + the according default offsets. A rigid correction/input data + testing is done by the construtor, including to sort the + ColorSteps by offset. + + To access e.g. the StartColor, use getColorSteps().front(), and + getColorSteps().back(), accordingly, for EndColor. The existance + of at least one entry is guaranteed, so no need to check before + accessing using of front()/back() calls. If only one color entry + exists, start == end color is assumed, so not really a gradient + but (existing) fallbacks to filled polygon can trigger. + */ /// constructors/assignmentoperator/destructor FillGradientAttribute(GradientStyle eStyle, double fBorder, double fOffsetX, double fOffsetY, double fAngle, const basegfx::BColor& rStartColor, - const basegfx::BColor& rEndColor, sal_uInt16 nSteps = 0); + const basegfx::BColor& rEndColor, const ColorSteps* pColorSteps = nullptr, + sal_uInt16 nSteps = 0); FillGradientAttribute(); FillGradientAttribute(const FillGradientAttribute&); FillGradientAttribute(FillGradientAttribute&&); @@ -64,6 +135,9 @@ public: // checks if the incarnation is default constructed bool isDefault() const; + // check if it is defined by a single color, then it is no gradient at all + bool hasSingleColor() const; + // compare operator bool operator==(const FillGradientAttribute& rCandidate) const; @@ -73,8 +147,7 @@ public: double getOffsetX() const; double getOffsetY() const; double getAngle() const; - const basegfx::BColor& getStartColor() const; - const basegfx::BColor& getEndColor() const; + const ColorSteps& getColorSteps() const; sal_uInt16 getSteps() const; }; diff --git a/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx b/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx index 261eef295361..e7bd26ce4f4f 100644 --- a/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx +++ b/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx @@ -151,8 +151,8 @@ namespace drawinglayer::attribute if(!rFillTransparenceGradientAttribute.isDefault()) { - const double fTransA = rFillTransparenceGradientAttribute.getStartColor().luminance(); - const double fTransB = rFillTransparenceGradientAttribute.getEndColor().luminance(); + const double fTransA(rFillTransparenceGradientAttribute.getColorSteps().front().getColor().luminance()); + const double fTransB(rFillTransparenceGradientAttribute.getColorSteps().back().getColor().luminance()); fTransparence = (fTransA + fTransB) * 0.5; } @@ -160,10 +160,10 @@ namespace drawinglayer::attribute if(!rFillGradientAttribute.isDefault()) { // gradient fill - const basegfx::BColor& rStart = rFillGradientAttribute.getStartColor(); - const basegfx::BColor& rEnd = rFillGradientAttribute.getEndColor(); + const basegfx::BColor aStart(rFillGradientAttribute.getColorSteps().front().getColor()); + const basegfx::BColor aEnd(rFillGradientAttribute.getColorSteps().back().getColor()); - aRetval = basegfx::interpolate(rStart, rEnd, 0.5); + aRetval = basegfx::interpolate(aStart, aEnd, 0.5); } else if(!rFillHatchAttribute.isDefault()) { diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx index bafa96f95cd8..f79ea50e5a88 100644 --- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx +++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx @@ -500,6 +500,7 @@ namespace drawinglayer::primitive2d toRadians(aXGradient.GetAngle()), aStart, aEnd, + nullptr, rSet.Get(XATTR_GRADIENTSTEPCOUNT).GetValue()); break; |