basegfx/source/tools/gradienttools.cxx                   |  115 ++++++++++++++
 drawinglayer/qa/unit/vclpixelprocessor2d.cxx             |    5 
 drawinglayer/source/attribute/fillgradientattribute.cxx  |  116 ++-------------
 drawinglayer/source/primitive3d/textureprimitive3d.cxx   |    9 +
 drawinglayer/source/texture/texture.cxx                  |   46 ++---
 drawinglayer/source/tools/wmfemfhelper.cxx               |    8 -
 include/basegfx/utils/gradienttools.hxx                  |   31 +++-
 include/drawinglayer/attribute/fillgradientattribute.hxx |   21 +-
 svx/source/sdr/primitive2d/sdrattributecreator.cxx       |   45 ++++-
 svx/source/xoutdev/xtabgrdt.cxx                          |    8 -
 sw/source/uibase/docvw/HeaderFooterWin.cxx               |    7 
 sw/source/uibase/docvw/ShadowOverlayObject.cxx           |   20 +-
 12 files changed, 264 insertions(+), 167 deletions(-)

New commits:
commit ecae66c41d82fecddd630cfdc144055a069134b0
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Fri Mar 3 18:03:56 2023 +0100
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Sat Mar 4 16:32:16 2023 +0000

    MCGR: ColorSteps handling moved to tooling
    
    Due to the fact that handling of a vector GradientSteps
    will get used more often with ongoing changes I moved
    some handling to tooling, see sortAndCorrectColorSteps.
    That method sorts and corrects a vector of ColorSteps
    so that only valid entreis remain in a sorted order,
    for details please refer to the docu at function
    definition. I took the occasion to rearrange that to
    work on the single provided vector which is better for
    speed & ressources.
    
    Also changed the ColorStep constructor to not
    automatically correct constructed entries: While that
    is formally correct, it moves an invalid entry to 0.0
    or 1.0, thus creating additional wrong Start/EndColor
    entries. Those may then 'overlay' the correct entry
    when corrections are applied to the vector of entries
    which leads to getting the wanted Start/EndColor to be
    factically deleted, what is an error.
    
    Also rearranged FillGradientAttribute to now work
    initially with ColorSteps, no longer requires the
    Start/EndColor, also adapted all usages. This securely
    allows start/end and in-between single-color sections
    in gradients.
    
    Also needed to re-formulate GradientRect &
    GradientElliptical ::appendTransformationsAndColors
    method for correct 2D mapping(s) - always incrementing
    from the start to end conflicts with the adaption of
    the start value for the inner loop mainly because
    increment is at start of inner loop (pre-increment).
    
    Corrected errors from clang plugin, but also some
    wrong initialiations for basegfx::ColorSteps.
    
    Change-Id: I292592ed9abcfa591f68a680479f4fcdda46cbeb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148196
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/basegfx/source/tools/gradienttools.cxx 
b/basegfx/source/tools/gradienttools.cxx
index f35f91f5a79a..5743f71ad980 100644
--- a/basegfx/source/tools/gradienttools.cxx
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -263,6 +263,121 @@ namespace basegfx
 
     namespace utils
     {
+        /* Tooling method to guarantee sort and correctness for
+           the given ColorSteps vector.
+           At return, the following conditions are guaranteed:
+           - contains no ColorSteps with offset < 0.0 (will
+             be removed)
+           - contains no ColorSteps with offset > 0.0 (will
+             be removed)
+           - contains no ColorSteps with identical offset
+             (will be removed, 1st one wins)
+           - will be sorted from lowest offset to highest
+           - if all colors are the same, the content will
+             be reducved to a single entry with offset 0.0
+             (StartColor)
+
+           Some more notes:
+           - It can happen that the result is empty
+           - It is allowed to have consecutive entries with
+             the same color, this represents single-color
+             regions inside the gradient
+           - A entry with 0.0 is not required or forced, so
+             no 'StartColor' is required on this level
+           - A entry with 1.0 is not required or forced, so
+             no 'EndColor' is required on this level
+
+           All this is done in one run (sort + O(N)) without
+           creating a copy of the data in any form
+        */
+        void sortAndCorrectColorSteps(ColorSteps& rColorSteps)
+        {
+            // no content, we are done
+            if (rColorSteps.empty())
+                return;
+
+            if (1 == rColorSteps.size())
+            {
+                // no gradient at all, but preserve given color
+                // and force it to be the StartColor
+                rColorSteps[0] = ColorStep(0.0, rColorSteps[0].getColor());
+            }
+
+            // start with sorting the input data. Remember that
+            // this preserves the order of equal entries, where
+            // equal is defined here by offset (see use operator==)
+            std::sort(rColorSteps.begin(), rColorSteps.end());
+
+            // preapare status values
+            bool bSameColorInit(false);
+            bool bAllTheSameColor(true);
+            basegfx::BColor aFirstColor;
+            size_t write(0);
+
+            // use the paradigm of a band machine with two heads, read
+            // and write with write <= read all the time. Step over the
+            // data using read and check for valid entry. If valid, decide
+            // how to keep it
+            for (size_t read(0); read < rColorSteps.size(); read++)
+            {
+                // get offset of entry at read position
+                const double rOff(rColorSteps[read].getOffset());
+
+                // step over < 0 values
+                if (basegfx::fTools::less(rOff, 0.0))
+                    continue;
+
+                // step over > 1 values; even break, since all following
+                // entries will also be bigger due to being sorted, so done
+                if (basegfx::fTools::more(rOff, 1.0))
+                    break;
+
+                // entry is valid value at read position
+
+                // check/init for all-the-same color
+                if(bSameColorInit)
+                {
+                    // already initialized, compare
+                    bAllTheSameColor = bAllTheSameColor && aFirstColor == 
rColorSteps[read].getColor();
+                }
+                else
+                {
+                    // do initialize, remember 1st valid color
+                    bSameColorInit = true;
+                    aFirstColor = rColorSteps[read].getColor();
+                }
+
+                // copy if write target is empty (write at start) or when
+                // write target is different to read
+                if (0 == write || rOff != rColorSteps[write-1].getOffset())
+                {
+                    if (write != read)
+                    {
+                        // copy read to write backwards to close gaps
+                        rColorSteps[write] = rColorSteps[read];
+                    }
+
+                    // always forward write position
+                    write++;
+                }
+            }
+
+            // correct size when length is reduced. write is always at
+            // last used position + 1
+            if (rColorSteps.size() > write)
+            {
+                rColorSteps.resize(write);
+            }
+
+            if (bSameColorInit && bAllTheSameColor && rColorSteps.size() > 1)
+            {
+                // id all-the-same color is detected, reset to single
+                // entry, but also force to StartColor and preserve the color
+                rColorSteps.resize(1);
+                rColorSteps[0] = ColorStep(0.0, aFirstColor);
+            }
+        }
+
         BColor modifyBColor(
             const ColorSteps& rColorSteps,
             double fScaler,
diff --git a/drawinglayer/qa/unit/vclpixelprocessor2d.cxx 
b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx
index 573c4bdc32f0..343556aec6d1 100644
--- a/drawinglayer/qa/unit/vclpixelprocessor2d.cxx
+++ b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx
@@ -18,6 +18,7 @@
 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
 #include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <basegfx/utils/gradienttools.hxx>
 
 using namespace drawinglayer;
 
@@ -59,8 +60,10 @@ public:
 
         basegfx::B2DRange definitionRange(0, 0, 100, 200);
         basegfx::B2DRange outputRange(0, 100, 100, 200); // Paint only lower 
half of the gradient.
+        const basegfx::ColorSteps aColorSteps{ basegfx::ColorStep(0.0, 
COL_WHITE.getBColor()),
+                                               basegfx::ColorStep(1.0, 
COL_BLACK.getBColor()) };
         attribute::FillGradientAttribute 
attributes(attribute::GradientStyle::Linear, 0, 0, 0, 0,
-                                                    COL_WHITE.getBColor(), 
COL_BLACK.getBColor());
+                                                    aColorSteps);
         rtl::Reference<primitive2d::FillGradientPrimitive2D> gradientPrimitive(
             new primitive2d::FillGradientPrimitive2D(outputRange, 
definitionRange, attributes));
         primitive2d::Primitive2DContainer primitives;
diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx 
b/drawinglayer/source/attribute/fillgradientattribute.cxx
index 07bc82e4e549..3d78273a6428 100644
--- a/drawinglayer/source/attribute/fillgradientattribute.cxx
+++ b/drawinglayer/source/attribute/fillgradientattribute.cxx
@@ -40,109 +40,31 @@ namespace drawinglayer::attribute
                 double fOffsetX,
                 double fOffsetY,
                 double fAngle,
-                const basegfx::BColor& rStartColor,
-                const basegfx::BColor& rEndColor,
-                const basegfx::ColorSteps* pColorSteps,
+                const basegfx::ColorSteps& rColorSteps,
                 sal_uInt16 nSteps)
             :   mfBorder(fBorder),
                 mfOffsetX(fOffsetX),
                 mfOffsetY(fOffsetY),
                 mfAngle(fAngle),
-                maColorSteps(),
+                maColorSteps(rColorSteps), // copy ColorSteps
                 meStyle(eStyle),
                 mnSteps(nSteps)
             {
-                // always add start color to guarantee 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 given ColorSteps, integrate these
-                if(nullptr != pColorSteps && !pColorSteps->empty())
-                {
-                    // append early to local & sort to prepare the following 
correction(s)
-                    // and later processing
-                    maColorSteps.insert(maColorSteps.end(), 
pColorSteps->begin(), pColorSteps->end());
-
-                    // no need to sort knowingly lowest entry StartColor, also 
guarantees that
-                    // entry to stay at begin
-                    std::sort(maColorSteps.begin() + 1, maColorSteps.end());
-
-                    // use two r/w heads on the data band maColorSteps
-                    size_t curr(0), next(1);
-
-                    // during processing, check if all colors are the same. We 
know the
-                    // StartColor, so to all be the same, all also have to be 
equal to
-                    // StartColor (including EndColor, use to initialize)
-                    bool bAllTheSameColor(rStartColor == rEndColor);
-
-                    // remove entries <= 0.0, >= 1.0 and with equal offset. do
-                    // this inside the already sorted local vector by evtl.
-                    // moving entries closer to begin to keep and adapting size
-                    // at the end
-                    for(; next < maColorSteps.size(); next++)
-                    {
-                        const double 
fNextOffset(maColorSteps[next].getOffset());
-
-                        // check for < 0.0 (should not really happen, see 
::ColorStep)
-                        // also check for == 0.0 which would mean than an 
implicit
-                        // StartColor was given in ColorSteps - ignore that, 
we want
-                        // the explicitly given StartColor to always win
-                        if(basegfx::fTools::lessOrEqual(fNextOffset, 0.0))
-                            continue;
-
-                        // check for > 1.0 (should not really happen, see 
::ColorStep)
-                        // also check for == 1.0 which would mean than an 
implicit
-                        // EndColor was given in ColorSteps - ignore that, we 
want
-                        // the explicitly given EndColor to always win
-                        if(basegfx::fTools::moreOrEqual(fNextOffset, 1.0))
-                            continue;
-
-                        // check for equal current offset
-                        const double 
fCurrOffset(maColorSteps[curr].getOffset());
-                        if(basegfx::fTools::equal(fNextOffset, fCurrOffset))
-                            continue;
-
-                        // next is > 0.0, < 1.0 and != curr, so a valid entry.
-                        // take over by evtl. have to move it left
-                        curr++;
-                        if(curr != next)
-                        {
-                            maColorSteps[curr] = maColorSteps[next];
-                        }
-
-                        // new valid entry detected, check it for all the same 
color
-                        bAllTheSameColor = bAllTheSameColor && 
maColorSteps[curr].getColor() == rStartColor;
-                    }
-
-                    if(bAllTheSameColor)
-                    {
-                        // if all are the same color, reset to StartColor only
-                        maColorSteps.resize(1);
-                    }
-                    else
-                    {
-                        // adapt size to detected useful entries
-                        curr++;
-                        if(curr != maColorSteps.size())
-                        {
-                            maColorSteps.resize(curr);
-                        }
-
-                        // add EndColor if in-between colors were added
-                        // or StartColor != EndColor
-                        if(curr > 1 || rStartColor != rEndColor)
-                        {
-                            maColorSteps.emplace_back(1.0, rEndColor);
-                        }
-                    }
-                }
-                else
+                // Correct the local ColorSteps. That will guarantee that the
+                // content does contain no offsets < 0.0, > 1.0 or double
+                // ones, also secures sorted arrangement and checks for
+                // double colors, too (see there for more information).
+                // This is what the usages of this in primitives need.
+                // Since FillGradientAttribute is read-only doing this
+                // once here in the constructor is sufficient
+                basegfx::utils::sortAndCorrectColorSteps(maColorSteps);
+
+                // sortAndCorrectColorSteps is rigid and can return
+                // an empty result. To keep things simple, add a single
+                // fallback value
+                if (maColorSteps.empty())
                 {
-                    // add EndColor if different from StartColor
-                    if(rStartColor != rEndColor)
-                    {
-                        maColorSteps.emplace_back(1.0, rEndColor);
-                    }
+                    maColorSteps.emplace_back(0.0, basegfx::BColor());
                 }
             }
 
@@ -204,12 +126,10 @@ namespace drawinglayer::attribute
             double fOffsetX,
             double fOffsetY,
             double fAngle,
-            const basegfx::BColor& rStartColor,
-            const basegfx::BColor& rEndColor,
-            const basegfx::ColorSteps* pColorSteps,
+            const basegfx::ColorSteps& rColorSteps,
             sal_uInt16 nSteps)
         :   mpFillGradientAttribute(ImpFillGradientAttribute(
-                eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rStartColor, 
rEndColor, pColorSteps, nSteps))
+                eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rColorSteps, 
nSteps))
         {
         }
 
diff --git a/drawinglayer/source/primitive3d/textureprimitive3d.cxx 
b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
index ba998c7f50bb..549932e93049 100644
--- a/drawinglayer/source/primitive3d/textureprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
@@ -20,6 +20,7 @@
 #include <primitive3d/textureprimitive3d.hxx>
 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
 #include <basegfx/color/bcolor.hxx>
+#include <basegfx/utils/gradienttools.hxx>
 #include <utility>
 
 
@@ -92,7 +93,13 @@ namespace drawinglayer::primitive3d
             {
                 // create TransparenceTexturePrimitive3D with fixed 
transparence as replacement
                 const basegfx::BColor aGray(getTransparence(), 
getTransparence(), getTransparence());
-                const attribute::FillGradientAttribute 
aFillGradient(attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 0.0, aGray, 
aGray);
+
+                // create ColorSteps with StartColor == EndCoOlor == aGray
+                const basegfx::ColorSteps aColorSteps {
+                    basegfx::ColorStep(0.0, aGray),
+                    basegfx::ColorStep(1.0, aGray) };
+
+                const attribute::FillGradientAttribute 
aFillGradient(attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 0.0, 
aColorSteps);
                 const Primitive3DReference xRef(new 
TransparenceTexturePrimitive3D(aFillGradient, getChildren(), getTextureSize()));
                 return { xRef };
             }
diff --git a/drawinglayer/source/texture/texture.cxx 
b/drawinglayer/source/texture/texture.cxx
index 788594f3b8eb..375e83f9221c 100644
--- a/drawinglayer/source/texture/texture.cxx
+++ b/drawinglayer/source/texture/texture.cxx
@@ -466,11 +466,9 @@ namespace drawinglayer::texture
             if (mnColorSteps.size() < 2)
                 return;
 
-            // need to start with the offsets *outside* of all loops, these 
will
-            // get modified non-linear below, now in more than one gradient 
section,
-            // but *constantly* over the whole range
-            double fAllWidth(0.0);
-            double fAllHeight(0.0);
+            // prepare vars dependent on aspect ratio
+            const double fAR(maGradientInfo.getAspectRatio());
+            const bool bMTO(fAR > 1.0);
 
             // outer loop over ColorSteps, each is from cs_l to cs_r
             for (auto cs_l(mnColorSteps.begin()), cs_r(cs_l + 1); cs_r != 
mnColorSteps.end(); cs_l++, cs_r++)
@@ -486,26 +484,21 @@ namespace drawinglayer::texture
                 const double fOffsetEnd(cs_r->getOffset());
                 const double fStripeWidth((fOffsetEnd - fOffsetStart) / 
nSteps);
 
-                // prepare individual increments for X/Y dependent on aspect 
ratio
-                const double fAR(maGradientInfo.getAspectRatio());
-                const bool bMTO(fAR > 1.0);
-                const double fIncrementX(bMTO ? fStripeWidth / fAR : 
fStripeWidth);
-                const double fIncrementY(bMTO ? fStripeWidth : fStripeWidth * 
fAR);
-
                 // get correct start for inner loop (see above)
                 const sal_uInt32 nStartInnerLoop(cs_l == mnColorSteps.begin() 
? 1 : 0);
 
                 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < 
nSteps; innerLoop++)
                 {
-                    // next step, actively adapt outer-loop w/h values
-                    fAllWidth += fIncrementX;
-                    fAllHeight += fIncrementY;
+                    // calculate offset position for entry
+                    const double fSize(fOffsetStart + (fStripeWidth * 
innerLoop));
 
                     // set and add at target
                     B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
 
                     aB2DHomMatrixAndBColor.maB2DHomMatrix = 
maGradientInfo.getTextureTransform()
-                        * basegfx::utils::createScaleB2DHomMatrix(1.0 - 
fAllWidth, 1.0 - fAllHeight);
+                        * basegfx::utils::createScaleB2DHomMatrix(
+                            1.0 - (bMTO ? fSize / fAR : fSize),
+                            1.0 - (bMTO ? fSize : fSize * fAR));
                     aB2DHomMatrixAndBColor.maBColor = interpolate(aCStart, 
aCEnd, double(innerLoop) / double(nSteps - 1));
                     rEntries.push_back(aB2DHomMatrixAndBColor);
                 }
@@ -656,11 +649,9 @@ namespace drawinglayer::texture
             if (mnColorSteps.size() < 2)
                 return;
 
-            // need to start with the offsets *outside* of all loops, these 
will
-            // get modified non-linear below, now in more than one gradient 
section,
-            // but *constantly* over the whole range
-            double fAllWidth(0.0);
-            double fAllHeight(0.0);
+            // prepare vars dependent on aspect ratio
+            const double fAR(maGradientInfo.getAspectRatio());
+            const bool bMTO(fAR > 1.0);
 
             // outer loop over ColorSteps, each is from cs_l to cs_r
             for (auto cs_l(mnColorSteps.begin()), cs_r(cs_l + 1); cs_r != 
mnColorSteps.end(); cs_l++, cs_r++)
@@ -676,26 +667,21 @@ namespace drawinglayer::texture
                 const double fOffsetEnd(cs_r->getOffset());
                 const double fStripeWidth((fOffsetEnd - fOffsetStart) / 
nSteps);
 
-                // prepare individual increments for X/Y dependent on aspect 
ratio
-                const double fAR(maGradientInfo.getAspectRatio());
-                const bool bMTO(fAR > 1.0);
-                const double fIncrementX(bMTO ? fStripeWidth / fAR : 
fStripeWidth);
-                const double fIncrementY(bMTO ? fStripeWidth : fStripeWidth * 
fAR);
-
                 // get correct start for inner loop (see above)
                 const sal_uInt32 nStartInnerLoop(cs_l == mnColorSteps.begin() 
? 1 : 0);
 
                 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < 
nSteps; innerLoop++)
                 {
-                    // next step, actively adapt outer-loop w/h values
-                    fAllWidth += fIncrementX;
-                    fAllHeight += fIncrementY;
+                    // calculate offset position for entry
+                    const double fSize(fOffsetStart + (fStripeWidth * 
innerLoop));
 
                     // set and add at target
                     B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
 
                     aB2DHomMatrixAndBColor.maB2DHomMatrix = 
maGradientInfo.getTextureTransform()
-                        * basegfx::utils::createScaleB2DHomMatrix(1.0 - 
fAllWidth, 1.0 - fAllHeight);
+                        * basegfx::utils::createScaleB2DHomMatrix(
+                            1.0 - (bMTO ? fSize / fAR : fSize),
+                            1.0 - (bMTO ? fSize : fSize * fAR));
                     aB2DHomMatrixAndBColor.maBColor = interpolate(aCStart, 
aCEnd, double(innerLoop) / double(nSteps - 1));
                     rEntries.push_back(aB2DHomMatrixAndBColor);
                 }
diff --git a/drawinglayer/source/tools/wmfemfhelper.cxx 
b/drawinglayer/source/tools/wmfemfhelper.cxx
index 0189c621fda3..27e6a6c94e24 100644
--- a/drawinglayer/source/tools/wmfemfhelper.cxx
+++ b/drawinglayer/source/tools/wmfemfhelper.cxx
@@ -711,15 +711,17 @@ namespace wmfemfhelper
             }
         }
 
+        const basegfx::ColorSteps aColorSteps {
+            basegfx::ColorStep(0.0, aStart),
+            basegfx::ColorStep(1.0, aEnd) };
+
         return drawinglayer::attribute::FillGradientAttribute(
             aGradientStyle,
             static_cast<double>(rGradient.GetBorder()) * 0.01,
             static_cast<double>(rGradient.GetOfsX()) * 0.01,
             static_cast<double>(rGradient.GetOfsY()) * 0.01,
             toRadians(rGradient.GetAngle()),
-            aStart,
-            aEnd,
-            nullptr,
+            aColorSteps,
             rGradient.GetSteps());
     }
 
diff --git a/include/basegfx/utils/gradienttools.hxx 
b/include/basegfx/utils/gradienttools.hxx
index 793d07a75577..328a86b8335c 100644
--- a/include/basegfx/utils/gradienttools.hxx
+++ b/include/basegfx/utils/gradienttools.hxx
@@ -69,9 +69,18 @@ namespace basegfx
         // e.g. for usage in std::vector::insert
         // ensure [0.0 .. 1.0] range for mfOffset
         ColorStep(double fOffset = 0.0, const BColor& rColor = BColor())
-            : mfOffset(std::max(0.0, std::min(fOffset, 1.0)))
+            : mfOffset(fOffset)
             , maColor(rColor)
         {
+            // NOTE: I originally *corrected* mfOffset here by using
+            //   mfOffset(std::max(0.0, std::min(fOffset, 1.0)))
+            // While that is formally correct, it moves an invalid
+            // entry to 0.0 or 1.0, thus creating additional wrong
+            // Start/EndColor entries. That may then 'overlay' the
+            // cortrect entry when corrections are applied to the
+            // vector of entries (see sortAndCorrectColorSteps)
+            // which leads to getting the wanted Start/EndColor
+            // to be factically deleted, what is an error.
         }
 
         double getOffset() const { return mfOffset; }
@@ -184,6 +193,26 @@ namespace basegfx
 
     namespace utils
     {
+        /* Helper to sort and correct ColorSteps. This will
+           sort and then correct the given ColorSteps. The
+           corrected version will
+           - be sorted
+           - have no double values
+           - have no values with offset < 0.0
+           - have no values with offset > 1.0
+           thus be ready to be used in multi-color gradients.
+
+           NOTE: The returned version may be empty (!) if no
+                 valid entries were contained
+           NOTE: It does not necessarily contain values for
+                 offset == 0.0 and 1.0 if there were none
+                 given (so no Start/EndColor)
+           NOTE: If it contains only one entry that entry is
+                 set to StartColor and the Color is preserved.
+                 This is also done when all Colors are the same
+        */
+        BASEGFX_DLLPUBLIC void sortAndCorrectColorSteps(ColorSteps& 
rColorSteps);
+
         /* Helper to grep the correct ColorStep out of
            ColorSteps and interpolate as needed for given
            relative value in fScaler in the range of [0.0 .. 1.0].
diff --git a/include/drawinglayer/attribute/fillgradientattribute.hxx 
b/include/drawinglayer/attribute/fillgradientattribute.hxx
index 4b736e678181..3a62ee41e43c 100644
--- a/include/drawinglayer/attribute/fillgradientattribute.hxx
+++ b/include/drawinglayer/attribute/fillgradientattribute.hxx
@@ -55,15 +55,15 @@ private:
 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.
+       Direct Start/EndCOlor is no longer required, instead the
+       full color gradient is handed over as ColorSteps vector.
+       To add the former Start/EndColor in a compatible way, just
+       prepare an instance of basegfx::ColorSteps with the
+       StartColor at 0.0 and the EndColor at 1.0.
 
-       Start/EndColor will be added to the internal ColorSteps with
-       the according default offsets. A rigid correction/input data
-       testing is done by the constructor, including to sort the
-       ColorSteps by offset.
+       A rigid correction/input data will be done by the constructor,
+       including to sort the ColorSteps by offset and removing invalid
+       entries (see sortAndCorrectColorSteps)
 
        To access e.g. the StartColor, use getColorSteps().front(), and
        getColorSteps().back(), accordingly, for EndColor. The existence
@@ -74,9 +74,8 @@ public:
     */
     /// constructors/assignmentoperator/destructor
     FillGradientAttribute(GradientStyle eStyle, double fBorder, double 
fOffsetX, double fOffsetY,
-                          double fAngle, const basegfx::BColor& rStartColor,
-                          const basegfx::BColor& rEndColor,
-                          const basegfx::ColorSteps* pColorSteps = nullptr, 
sal_uInt16 nSteps = 0);
+                          double fAngle, const basegfx::ColorSteps& 
rColorSteps,
+                          sal_uInt16 nSteps = 0);
     FillGradientAttribute();
     FillGradientAttribute(const FillGradientAttribute&);
     FillGradientAttribute(FillGradientAttribute&&);
diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx 
b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
index 048d5430f6f8..0e0369b08a43 100644
--- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx
+++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
@@ -493,7 +493,13 @@ namespace drawinglayer::primitive2d
                                 aEnd = interpolate(aBlack, aEnd, 
static_cast<double>(nEndIntens) * 0.01);
                             }
 
-                            basegfx::ColorSteps aColorSteps;
+                            // prepare ColorSteps
+                            // currently always two (Start/EndColor) to stay 
compatible
+                            // for now, but that will change. It already gets 
manipulated
+                            // by the test cases below
+                            basegfx::ColorSteps aColorSteps {
+                                basegfx::ColorStep(0.0, aStart),
+                                basegfx::ColorStep(1.0, aEnd) };
 
                             // test code here, can/will be removed later
                             static sal_uInt32 nUseGradientSteps(0);
@@ -502,11 +508,12 @@ namespace drawinglayer::primitive2d
                                 case 1:
                                 {
                                     // just test a nice valid gradient
-                                    aStart = basegfx::BColor(1.0, 0.0, 0.0); 
// red
-                                    aEnd = basegfx::BColor(0.0, 0.0, 1.0); // 
blue
+                                    aColorSteps.clear();
+                                    aColorSteps.emplace_back(0.0, 
basegfx::BColor(1.0, 0.0, 0.0)); // red
                                     aColorSteps.emplace_back(0.25, 
basegfx::BColor(0.0, 1.0, 0.0)); // green@25%
                                     aColorSteps.emplace_back(0.50, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
                                     aColorSteps.emplace_back(0.75, 
basegfx::BColor(1.0, 0.0, 1.0)); // pink@75%
+                                    aColorSteps.emplace_back(1.0, 
basegfx::BColor(0.0, 0.0, 1.0)); // blue
                                     break;
                                 }
 
@@ -527,6 +534,7 @@ namespace drawinglayer::primitive2d
                                 case 4:
                                 {
                                     // check additional EndColor, the one 
given directly has to win
+                                    // due this one being added *after* the 
original one
                                     aColorSteps.emplace_back(1.0, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
                                     break;
                                 }
@@ -547,7 +555,7 @@ namespace drawinglayer::primitive2d
 
                                 case 7:
                                 {
-                                    // check in-between single-color part
+                                    // check in-between single-color section
                                     aColorSteps.emplace_back(0.3, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
                                     aColorSteps.emplace_back(0.7, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
                                     break;
@@ -555,10 +563,11 @@ namespace drawinglayer::primitive2d
 
                                 case 8:
                                 {
-                                    // check in-between single-color parts
+                                    // check in-between single-color sections
                                     aColorSteps.emplace_back(0.2, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
-                                    aColorSteps.emplace_back(0.4, aEnd);
-                                    aColorSteps.emplace_back(0.6, aStart);
+                                    aColorSteps.emplace_back(0.4, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+                                    aColorSteps.emplace_back(0.5, aStart);
+                                    aColorSteps.emplace_back(0.6, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
                                     aColorSteps.emplace_back(0.8, 
basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
                                     break;
                                 }
@@ -573,7 +582,16 @@ namespace drawinglayer::primitive2d
                                 case 10:
                                 {
                                     // check single-color end area
-                                    aColorSteps.emplace_back(0.6, aEnd);
+                                    aColorSteps.emplace_back(0.4, aEnd);
+                                    break;
+                                }
+
+                                case 11:
+                                {
+                                    // check case without direct Start/EndColor
+                                    aColorSteps.clear();
+                                    aColorSteps.emplace_back(0.4, aEnd);
+                                    aColorSteps.emplace_back(0.6, aStart);
                                     break;
                                 }
 
@@ -589,9 +607,7 @@ namespace drawinglayer::primitive2d
                                 static_cast<double>(aXGradient.GetXOffset()) * 
0.01,
                                 static_cast<double>(aXGradient.GetYOffset()) * 
0.01,
                                 toRadians(aXGradient.GetAngle()),
-                                aStart,
-                                aEnd,
-                                aColorSteps.empty() ? nullptr : &aColorSteps,
+                                aColorSteps,
                                 rSet.Get(XATTR_GRADIENTSTEPCOUNT).GetValue());
 
                             break;
@@ -745,14 +761,17 @@ namespace drawinglayer::primitive2d
                     const double fStartLum(nStartLuminance / 255.0);
                     const double fEndLum(nEndLuminance / 255.0);
 
+                    const basegfx::ColorSteps aColorSteps {
+                        basegfx::ColorStep(0.0, basegfx::BColor(fStartLum, 
fStartLum, fStartLum)),
+                        basegfx::ColorStep(1.0, basegfx::BColor(fEndLum, 
fEndLum, fEndLum)) };
+
                     return attribute::FillGradientAttribute(
                         
XGradientStyleToGradientStyle(rGradient.GetGradientStyle()),
                         static_cast<double>(rGradient.GetBorder()) * 0.01,
                         static_cast<double>(rGradient.GetXOffset()) * 0.01,
                         static_cast<double>(rGradient.GetYOffset()) * 0.01,
                         toRadians(rGradient.GetAngle()),
-                        basegfx::BColor(fStartLum, fStartLum, fStartLum),
-                        basegfx::BColor(fEndLum, fEndLum, fEndLum));
+                        aColorSteps);
                 }
             }
 
diff --git a/svx/source/xoutdev/xtabgrdt.cxx b/svx/source/xoutdev/xtabgrdt.cxx
index bc8f79ac0426..219ee2801d69 100644
--- a/svx/source/xoutdev/xtabgrdt.cxx
+++ b/svx/source/xoutdev/xtabgrdt.cxx
@@ -35,6 +35,7 @@
 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
 #include <drawinglayer/processor2d/processor2dtools.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/utils/gradienttools.hxx>
 #include <memory>
 
 using namespace com::sun::star;
@@ -152,14 +153,17 @@ BitmapEx XGradientList::CreateBitmap( tools::Long nIndex, 
const Size& rSize ) co
             }
         }
 
+        const basegfx::ColorSteps aColorSteps {
+            basegfx::ColorStep(0.0, aStart),
+            basegfx::ColorStep(1.0, aEnd) };
+
         drawinglayer::attribute::FillGradientAttribute aFillGradient(
             aGradientStyle,
             static_cast<double>(rGradient.GetBorder()) * 0.01,
             static_cast<double>(rGradient.GetXOffset()) * 0.01,
             static_cast<double>(rGradient.GetYOffset()) * 0.01,
             toRadians(rGradient.GetAngle()),
-            aStart,
-            aEnd);
+            aColorSteps);
 
         const drawinglayer::primitive2d::Primitive2DReference 
aGradientPrimitive(
             new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
diff --git a/sw/source/uibase/docvw/HeaderFooterWin.cxx 
b/sw/source/uibase/docvw/HeaderFooterWin.cxx
index 763d6d729e12..186deaa12ab2 100644
--- a/sw/source/uibase/docvw/HeaderFooterWin.cxx
+++ b/sw/source/uibase/docvw/HeaderFooterWin.cxx
@@ -29,6 +29,7 @@
 #include <basegfx/polygon/b2dpolygon.hxx>
 #include <basegfx/range/b2drectangle.hxx>
 #include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/utils/gradienttools.hxx>
 #include <drawinglayer/attribute/fillgradientattribute.hxx>
 #include <drawinglayer/attribute/fontattribute.hxx>
 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
@@ -150,7 +151,11 @@ void 
SwFrameButtonPainter::PaintButton(drawinglayer::primitive2d::Primitive2DCon
         double nAngle = M_PI;
         if (bOnTop)
             nAngle = 0;
-        FillGradientAttribute 
aFillAttrs(drawinglayer::attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 
nAngle, aLighterColor, aFillColor);
+
+        const basegfx::ColorSteps aColorSteps {
+            basegfx::ColorStep(0.0, aLighterColor),
+            basegfx::ColorStep(1.0, aFillColor) };
+        FillGradientAttribute 
aFillAttrs(drawinglayer::attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 
nAngle, aColorSteps );
         rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
                             new 
drawinglayer::primitive2d::FillGradientPrimitive2D(aGradientRect, 
std::move(aFillAttrs))));
     }
diff --git a/sw/source/uibase/docvw/ShadowOverlayObject.cxx 
b/sw/source/uibase/docvw/ShadowOverlayObject.cxx
index 3b7490678533..002a40d5db91 100644
--- a/sw/source/uibase/docvw/ShadowOverlayObject.cxx
+++ b/sw/source/uibase/docvw/ShadowOverlayObject.cxx
@@ -27,6 +27,7 @@
 #include <sw_primitivetypes2d.hxx>
 #include <drawinglayer/primitive2d/primitivetools2d.hxx>
 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <basegfx/utils/gradienttools.hxx>
 
 namespace sw::sidebarwindows {
 
@@ -82,14 +83,17 @@ void ShadowPrimitive::create2DDecomposition(
         case SS_NORMAL:
         {
             aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), 
getSecondPosition().getY() + (2.0 * getDiscreteUnit())));
+
+            const basegfx::ColorSteps aColorSteps {
+                basegfx::ColorStep(0.0, 
basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0)),
+                basegfx::ColorStep(1.0, 
basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0)) };
             ::drawinglayer::attribute::FillGradientAttribute 
aFillGradientAttribute(
                 drawinglayer::attribute::GradientStyle::Linear,
                 0.0,
                 0.5,
                 0.5,
                 M_PI,
-                basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0),
-                basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0));
+                aColorSteps);
 
             rContainer.push_back(
                 new drawinglayer::primitive2d::FillGradientPrimitive2D(
@@ -100,14 +104,16 @@ void ShadowPrimitive::create2DDecomposition(
         case SS_VIEW:
         {
             aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), 
getSecondPosition().getY() + (4.0 * getDiscreteUnit())));
+            const basegfx::ColorSteps aColorSteps {
+                basegfx::ColorStep(0.0, 
basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0)),
+                basegfx::ColorStep(1.0, 
basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0)) };
             drawinglayer::attribute::FillGradientAttribute 
aFillGradientAttribute(
                 drawinglayer::attribute::GradientStyle::Linear,
                 0.0,
                 0.5,
                 0.5,
                 M_PI,
-                basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0),
-                basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0));
+                aColorSteps);
 
             rContainer.push_back(
                 new drawinglayer::primitive2d::FillGradientPrimitive2D(
@@ -118,14 +124,16 @@ void ShadowPrimitive::create2DDecomposition(
         case SS_EDIT:
         {
             aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), 
getSecondPosition().getY() + (4.0 * getDiscreteUnit())));
+            const basegfx::ColorSteps aColorSteps {
+                basegfx::ColorStep(0.0, 
basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0)),
+                basegfx::ColorStep(1.0, 
basegfx::BColor(83.0/255.0,83.0/255.0,83.0/255.0)) };
             drawinglayer::attribute::FillGradientAttribute 
aFillGradientAttribute(
                 drawinglayer::attribute::GradientStyle::Linear,
                 0.0,
                 0.5,
                 0.5,
                 M_PI,
-                basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0),
-                basegfx::BColor(83.0/255.0,83.0/255.0,83.0/255.0));
+                aColorSteps);
 
             rContainer.push_back(
                 new drawinglayer::primitive2d::FillGradientPrimitive2D(

Reply via email to