basctl/inc/pch/precompiled_basctl.hxx                     |    1 
 basegfx/Library_basegfx.mk                                |    1 
 basegfx/source/tools/bgradient.cxx                        |  770 ++++++++++++++
 basegfx/source/tools/gradienttools.cxx                    |  555 ----------
 chart2/inc/pch/precompiled_chartcontroller.hxx            |    1 
 chart2/qa/extras/chart2import.cxx                         |    8 
 chart2/source/controller/main/ChartController_Tools.cxx   |   11 
 cui/source/inc/cuitabarea.hxx                             |    8 
 cui/source/tabpages/tpgradnt.cxx                          |   41 
 cui/source/tabpages/tptrans.cxx                           |   18 
 drawinglayer/inc/texture/texture.hxx                      |   21 
 drawinglayer/qa/unit/vclpixelprocessor2d.cxx              |   61 -
 drawinglayer/source/attribute/fillgradientattribute.cxx   |   26 
 drawinglayer/source/primitive3d/textureprimitive3d.cxx    |    6 
 drawinglayer/source/processor3d/defaultprocessor3d.cxx    |    6 
 drawinglayer/source/texture/texture.cxx                   |  146 +-
 drawinglayer/source/tools/wmfemfhelper.cxx                |   20 
 filter/source/msfilter/msdffimp.cxx                       |    8 
 filter/source/msfilter/svdfppt.cxx                        |   22 
 include/basegfx/utils/bgradient.hxx                       |  323 +++++
 include/basegfx/utils/gradienttools.hxx                   |  242 ----
 include/drawinglayer/attribute/fillgradientattribute.hxx  |   15 
 include/svx/sidebar/AreaPropertyPanelBase.hxx             |   22 
 include/svx/sidebar/AreaTransparencyGradientPopup.hxx     |    2 
 include/svx/xflftrit.hxx                                  |    4 
 include/svx/xflgrit.hxx                                   |   13 
 include/svx/xgrad.hxx                                     |   85 -
 include/svx/xtable.hxx                                    |    8 
 oox/qa/unit/drawingml.cxx                                 |    3 
 oox/qa/unit/shape.cxx                                     |   22 
 oox/source/drawingml/fillproperties.cxx                   |   18 
 oox/source/export/chartexport.cxx                         |    5 
 oox/source/export/drawingml.cxx                           |   29 
 reportdesign/Library_rptui.mk                             |    1 
 reportdesign/inc/pch/precompiled_rptui.hxx                |    1 
 reportdesign/source/ui/misc/UITools.cxx                   |    4 
 reportdesign/source/ui/report/ReportController.cxx        |    4 
 sc/inc/pch/precompiled_sc.hxx                             |    1 
 sc/qa/unit/subsequent_filters_test3.cxx                   |    4 
 sc/source/ui/drawfunc/drawsh.cxx                          |    2 
 sd/inc/pch/precompiled_sdui.hxx                           |    1 
 sd/qa/unit/export-tests-ooxml1.cxx                        |    6 
 sd/qa/unit/export-tests-ooxml2.cxx                        |    3 
 sd/qa/unit/export-tests-ooxml3.cxx                        |    6 
 sd/qa/unit/misc-tests.cxx                                 |    3 
 sd/qa/unit/uiimpress.cxx                                  |    4 
 sd/source/core/drawdoc4.cxx                               |   18 
 sd/source/ui/sidebar/SlideBackground.cxx                  |   21 
 sd/source/ui/sidebar/SlideBackground.hxx                  |    7 
 sd/source/ui/view/drviews2.cxx                            |    2 
 sd/source/ui/view/drviews7.cxx                            |    2 
 sd/source/ui/view/drviews9.cxx                            |   21 
 solenv/clang-format/excludelist                           |    1 
 svx/inc/pch/precompiled_svx.hxx                           |    1 
 svx/source/customshapes/EnhancedCustomShape2d.cxx         |   11 
 svx/source/sdr/primitive2d/sdrattributecreator.cxx        |  317 -----
 svx/source/sidebar/area/AreaPropertyPanelBase.cxx         |   24 
 svx/source/sidebar/area/AreaTransparencyGradientPopup.cxx |   13 
 svx/source/svdraw/gradtrns.cxx                            |    8 
 svx/source/svdraw/gradtrns.hxx                            |    5 
 svx/source/svdraw/svdetc.cxx                              |    3 
 svx/source/svdraw/svdfmtf.cxx                             |   31 
 svx/source/svdraw/svdmrkv.cxx                             |    3 
 svx/source/svdraw/svdoashp.cxx                            |    4 
 svx/source/tbxctrls/fillctrl.cxx                          |    4 
 svx/source/unodraw/XPropertyTable.cxx                     |   55 -
 svx/source/unodraw/unobrushitemhelper.cxx                 |    8 
 svx/source/xoutdev/xattr.cxx                              |  301 -----
 svx/source/xoutdev/xpool.cxx                              |    8 
 svx/source/xoutdev/xtabgrdt.cxx                           |   21 
 svx/source/xoutdev/xtable.cxx                             |    2 
 sw/inc/pch/precompiled_msword.hxx                         |    1 
 sw/inc/pch/precompiled_sw.hxx                             |    1 
 sw/inc/pch/precompiled_swui.hxx                           |    1 
 sw/qa/extras/odfexport/odfexport.cxx                      |    5 
 sw/qa/extras/ooxmlexport/ooxmlexport2.cxx                 |    5 
 sw/qa/extras/rtfexport/rtfexport.cxx                      |    7 
 sw/source/core/unocore/unoframe.cxx                       |    8 
 sw/source/filter/ww8/docxattributeoutput.cxx              |    2 
 sw/source/filter/ww8/rtfattributeoutput.cxx               |    4 
 sw/source/uibase/docvw/HeaderFooterWin.cxx                |    2 
 sw/source/uibase/docvw/ShadowOverlayObject.cxx            |    6 
 sw/source/uibase/shells/drawdlg.cxx                       |    4 
 sw/source/uibase/sidebar/PageStylesPanel.cxx              |   14 
 sw/source/uibase/sidebar/PageStylesPanel.hxx              |    2 
 sw/source/uibase/uiview/viewtab.cxx                       |    4 
 86 files changed, 1713 insertions(+), 1805 deletions(-)

New commits:
commit 438f0752deaf7d6e6d9d1df381b64aca4628e944
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Fri May 12 15:32:51 2023 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Mon May 15 15:19:53 2023 +0200

    MCGR: consolidations/cleanups for changes so far
    
    Change-Id: I85cf40e4803b0485bb40349d8e81adc8123666c4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151706
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/basctl/inc/pch/precompiled_basctl.hxx 
b/basctl/inc/pch/precompiled_basctl.hxx
index 4f3566f3f6ea..257159149a3b 100644
--- a/basctl/inc/pch/precompiled_basctl.hxx
+++ b/basctl/inc/pch/precompiled_basctl.hxx
@@ -499,7 +499,6 @@
 #include <svx/svxdllapi.h>
 #include <svx/xdash.hxx>
 #include <svx/xdef.hxx>
-#include <svx/xgrad.hxx>
 #include <svx/xhatch.hxx>
 #include <svx/xpoly.hxx>
 #include <svx/xtable.hxx>
diff --git a/basegfx/Library_basegfx.mk b/basegfx/Library_basegfx.mk
index 2f9830d0dcfd..da257e2c9797 100644
--- a/basegfx/Library_basegfx.mk
+++ b/basegfx/Library_basegfx.mk
@@ -67,6 +67,7 @@ $(eval $(call gb_Library_add_exception_objects,basegfx,\
     basegfx/source/range/b3drange \
     basegfx/source/raster/rasterconvert3d \
     basegfx/source/tools/b2dclipstate \
+    basegfx/source/tools/bgradient \
     basegfx/source/tools/canvastools \
     basegfx/source/tools/gradienttools \
     basegfx/source/tools/keystoplerp \
diff --git a/basegfx/source/tools/bgradient.cxx 
b/basegfx/source/tools/bgradient.cxx
new file mode 100644
index 000000000000..7cb1ed85e859
--- /dev/null
+++ b/basegfx/source/tools/bgradient.cxx
@@ -0,0 +1,770 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <basegfx/utils/bgradient.hxx>
+#include <basegfx/utils/gradienttools.hxx>
+#include <com/sun/star/awt/Gradient2.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <map>
+
+typedef std::map<OUString, OUString> StringMap;
+
+namespace
+{
+css::awt::GradientStyle lcl_getStyleFromString(std::u16string_view rStyle)
+{
+    if (rStyle == u"LINEAR")
+        return css::awt::GradientStyle_LINEAR;
+    else if (rStyle == u"AXIAL")
+        return css::awt::GradientStyle_AXIAL;
+    else if (rStyle == u"RADIAL")
+        return css::awt::GradientStyle_RADIAL;
+    else if (rStyle == u"ELLIPTICAL")
+        return css::awt::GradientStyle_ELLIPTICAL;
+    else if (rStyle == u"SQUARE")
+        return css::awt::GradientStyle_SQUARE;
+    else if (rStyle == u"RECT")
+        return css::awt::GradientStyle_RECT;
+
+    return css::awt::GradientStyle_LINEAR;
+}
+
+StringMap lcl_jsonToStringMap(std::u16string_view rJSON)
+{
+    StringMap aArgs;
+    if (rJSON.size() && rJSON[0] != '\0')
+    {
+        std::stringstream aStream(std::string(OUStringToOString(rJSON, 
RTL_TEXTENCODING_ASCII_US)));
+        boost::property_tree::ptree aTree;
+        boost::property_tree::read_json(aStream, aTree);
+
+        for (const auto& rPair : aTree)
+        {
+            aArgs[OUString::fromUtf8(rPair.first)]
+                = OUString::fromUtf8(rPair.second.get_value<std::string>("."));
+        }
+    }
+    return aArgs;
+}
+
+basegfx::BGradient lcl_buildGradientFromStringMap(StringMap& rMap)
+{
+    basegfx::BGradient aGradient(
+        
basegfx::BColorStops(ColorToBColorConverter(rMap["startcolor"].toInt32(16)).getBColor(),
+                             
ColorToBColorConverter(rMap["endcolor"].toInt32(16)).getBColor()));
+
+    aGradient.SetGradientStyle(lcl_getStyleFromString(rMap["style"]));
+    aGradient.SetAngle(Degree10(rMap["angle"].toInt32()));
+
+    return aGradient;
+}
+}
+
+namespace basegfx
+{
+void BColorStops::setColorStopSequence(const css::awt::ColorStopSequence& 
rColorStops)
+{
+    const sal_Int32 nLen(rColorStops.getLength());
+
+    if (0 != nLen)
+    {
+        // we have ColorStops
+        reserve(nLen);
+        const css::awt::ColorStop* 
pSourceColorStop(rColorStops.getConstArray());
+
+        for (sal_Int32 a(0); a < nLen; a++, pSourceColorStop++)
+        {
+            emplace_back(pSourceColorStop->StopOffset,
+                         BColor(pSourceColorStop->StopColor.Red, 
pSourceColorStop->StopColor.Green,
+                                pSourceColorStop->StopColor.Blue));
+        }
+    }
+}
+
+BColorStops::BColorStops(const css::awt::ColorStopSequence& rColorStops)
+{
+    setColorStopSequence(rColorStops);
+}
+
+BColorStops::BColorStops(const css::uno::Any& rVal)
+{
+    css::awt::Gradient2 aGradient2;
+    if (rVal >>= aGradient2)
+    {
+        setColorStopSequence(aGradient2.ColorStops);
+    }
+}
+
+// constuctor with two colors to explicitly create a
+// BColorStops for a single StartColor @0.0 & EndColor @1.0
+BColorStops::BColorStops(const BColor& rStart, const BColor& rEnd)
+{
+    emplace_back(0.0, rStart);
+    emplace_back(1.0, rEnd);
+}
+
+/* Helper to grep the correct ColorStop out of
+        ColorStops and interpolate as needed for given
+        relative value in fPosition in the range of [0.0 .. 1.0].
+        It also takes care of evtl. given RequestedSteps.
+    */
+BColor BColorStops::getInterpolatedBColor(double fPosition, sal_uInt32 
nRequestedSteps,
+                                          BColorStopRange& 
rLastColorStopRange) const
+{
+    // no color at all, done
+    if (empty())
+        return BColor();
+
+    // outside range -> at start
+    const double fMin(front().getStopOffset());
+    if (fPosition < fMin)
+        return front().getStopColor();
+
+    // outside range -> at end
+    const double fMax(back().getStopOffset());
+    if (fPosition > fMax)
+        return back().getStopColor();
+
+    // special case for the 'classic' case with just two colors:
+    // we can optimize that and keep the speed/resources low
+    // by avoiding some calculations and an O(log(N)) array access
+    if (2 == size())
+    {
+        // if same StopOffset use front color
+        if (fTools::equal(fMin, fMax))
+            return front().getStopColor();
+
+        const basegfx::BColor aCStart(front().getStopColor());
+        const basegfx::BColor aCEnd(back().getStopColor());
+
+        // if colors are equal just return one
+        if (aCStart == aCEnd)
+            return aCStart;
+
+        // calculate Steps
+        const sal_uInt32 nSteps(
+            basegfx::utils::calculateNumberOfSteps(nRequestedSteps, aCStart, 
aCEnd));
+
+        // we need to extend the interpolation to the local
+        // range of ColorStops. Despite having two ColorStops
+        // these are not necessarily at 0.0 and 1.0, so may be
+        // not the classical Start/EndColor (what is allowed)
+        fPosition = (fPosition - fMin) / (fMax - fMin);
+        return basegfx::interpolate(aCStart, aCEnd,
+                                    nSteps > 1 ? floor(fPosition * nSteps) / 
double(nSteps - 1)
+                                               : fPosition);
+    }
+
+    // check if we need to newly populate the needed interpolation data
+    // or if we can re-use from last time.
+    // If this scope is not entered, we do not need the binary search. It's
+    // only a single buffered entry, and only used when more than three
+    // ColorStops exist, but makes a huge difference compared with accessing
+    // the sorted ColorStop vector each time.
+    // NOTE: with this simple change I get very high hit rates, e.g. rotating
+    //       a donut with gradient test '1' hit rate is at 0.99909440357755486
+    if (rLastColorStopRange.mfOffsetStart == rLastColorStopRange.mfOffsetEnd
+        || fPosition < rLastColorStopRange.mfOffsetStart
+        || fPosition > rLastColorStopRange.mfOffsetEnd)
+    {
+        // access needed spot in sorted array using binary search
+        // NOTE: This *seems* slow(er) when developing compared to just
+        //       looping/accessing, but that's just due to the extensive
+        //       debug test code created by the stl. In a pro version,
+        //       all is good/fast as expected
+        const auto upperBound(std::upper_bound(begin(), end(), 
BColorStop(fPosition),
+                                               [](const BColorStop& x, const 
BColorStop& y) {
+                                                   return x.getStopOffset() < 
y.getStopOffset();
+                                               }));
+
+        // no upper bound, done
+        if (end() == upperBound)
+            return back().getStopColor();
+
+        // lower bound is one entry back, access that
+        const auto lowerBound(upperBound - 1);
+
+        // no lower bound, done
+        if (end() == lowerBound)
+            return back().getStopColor();
+
+        // we have lower and upper bound, get colors and offsets
+        rLastColorStopRange.maColorStart = lowerBound->getStopColor();
+        rLastColorStopRange.maColorEnd = upperBound->getStopColor();
+        rLastColorStopRange.mfOffsetStart = lowerBound->getStopOffset();
+        rLastColorStopRange.mfOffsetEnd = upperBound->getStopOffset();
+    }
+
+    // when there are just two color steps this cannot happen, but when using
+    // a range of colors this *may* be used inside the range to represent
+    // single-colored regions inside a ColorRange. Use that color & done
+    if (rLastColorStopRange.maColorStart == rLastColorStopRange.maColorEnd)
+        return rLastColorStopRange.maColorStart;
+
+    // calculate number of steps and adapted proportional
+    // range for scaler in [0.0 .. 1.0]
+    const double fAdaptedScaler(
+        (fPosition - rLastColorStopRange.mfOffsetStart)
+        / (rLastColorStopRange.mfOffsetEnd - 
rLastColorStopRange.mfOffsetStart));
+    const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+        nRequestedSteps, rLastColorStopRange.maColorStart, 
rLastColorStopRange.maColorEnd));
+
+    // interpolate & evtl. apply steps
+    return interpolate(rLastColorStopRange.maColorStart, 
rLastColorStopRange.maColorEnd,
+                       nSteps > 1 ? floor(fAdaptedScaler * nSteps) / 
double(nSteps - 1)
+                                  : fAdaptedScaler);
+}
+
+/* Tooling method that allows to replace the StartColor in a
+        vector of ColorStops. A vector in 'ordered state' is expected,
+        so you may use/have used sortAndCorrect.
+        This method is for convenience & backwards compatibility, please
+        think about handling multi-colored gradients directly.
+    */
+void BColorStops::replaceStartColor(const BColor& rStart)
+{
+    BColorStops::iterator a1stNonStartColor(begin());
+
+    // search for highest existing non-StartColor - CAUTION,
+    // there might be none, one or multiple with StopOffset 0.0
+    while (a1stNonStartColor != end()
+           && basegfx::fTools::lessOrEqual(a1stNonStartColor->getStopOffset(), 
0.0))
+        a1stNonStartColor++;
+
+    // create new ColorStops by 1st adding new one and then all
+    // non-StartColor entries
+    BColorStops aNewColorStops;
+
+    aNewColorStops.reserve(size() + 1);
+    aNewColorStops.emplace_back(0.0, rStart);
+    aNewColorStops.insert(aNewColorStops.end(), a1stNonStartColor, end());
+
+    // assign & done
+    *this = aNewColorStops;
+}
+
+/* Tooling method that allows to replace the EndColor in a
+        vector of ColorStops. A vector in 'ordered state' is expected,
+        so you may use/have used sortAndCorrectColorStops.
+        This method is for convenience & backwards compatibility, please
+        think about handling multi-colored gradients directly.
+    */
+void BColorStops::replaceEndColor(const BColor& rEnd)
+{
+    // erase all evtl. existing EndColor(s)
+    while (!empty() && basegfx::fTools::moreOrEqual(back().getStopOffset(), 
1.0))
+        pop_back();
+
+    // add at the end of existing ColorStops
+    emplace_back(1.0, rEnd);
+}
+
+/* Tooling method to linearly blend the Colors contained in
+        a given ColorStop vector against a given Color using the
+        given intensity values.
+        The intensity values fStartIntensity, fEndIntensity are
+        in the range of [0.0 .. 1.0] and describe how much the
+        blend is supposed to be done at the start color position
+        and the end color position respectively, where 0.0 means
+        to fully use the given BlendColor, 1.0 means to not change
+        the existing color in the ColorStop.
+        Every color entry in the given ColorStop is blended
+        relative to it's StopPosition, interpolating the
+        given intensities with the range [0.0 .. 1.0] to do so.
+    */
+void BColorStops::blendToIntensity(double fStartIntensity, double 
fEndIntensity,
+                                   const BColor& rBlendColor)
+{
+    // no entries, done
+    if (empty())
+        return;
+
+    // correct intensities (maybe assert when input was wrong)
+    fStartIntensity = std::max(std::min(1.0, fStartIntensity), 0.0);
+    fEndIntensity = std::max(std::min(1.0, fEndIntensity), 0.0);
+
+    // all 100%, no real blend, done
+    if (basegfx::fTools::equal(fStartIntensity, 1.0) && 
basegfx::fTools::equal(fEndIntensity, 1.0))
+        return;
+
+    // blend relative to StopOffset position
+    for (auto& candidate : *this)
+    {
+        const double fOffset(candidate.getStopOffset());
+        const double fIntensity((fStartIntensity * (1.0 - fOffset)) + 
(fEndIntensity * fOffset));
+        candidate = basegfx::BColorStop(
+            fOffset, basegfx::interpolate(rBlendColor, 
candidate.getStopColor(), fIntensity));
+    }
+}
+
+/* Tooling method to guarantee sort and correctness for
+        the given ColorStops vector.
+        A vector fulfilling these conditions is called to be
+        in 'ordered state'.
+
+        At return, the following conditions are guaranteed:
+        - contains no ColorStops with offset < 0.0 (will
+            be removed)
+        - contains no ColorStops with offset > 1.0 (will
+            be removed)
+        - ColorStops with identical offsets are now allowed
+        - will be sorted from lowest offset to highest
+
+        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 technically required
+        - A entry with 1.0 is not required or forced, so
+            no 'EndColor' is technically required
+
+        All this is done in one run (sort + O(N)) without
+        creating a copy of the data in any form
+    */
+void BColorStops::sortAndCorrect()
+{
+    // no content, we are done
+    if (empty())
+        return;
+
+    if (1 == size())
+    {
+        // no gradient at all, but preserve given color
+        // evtl. correct offset to be in valid range [0.0 .. 1.0]
+        // NOTE: This does not move it to 0.0 or 1.0, it *can* still
+        //       be somewhere in-between what is allowed
+        const BColorStop aEntry(front());
+        clear();
+        emplace_back(std::max(0.0, std::min(1.0, aEntry.getStopOffset())), 
aEntry.getStopColor());
+
+        // done
+        return;
+    }
+
+    // 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(begin(), end());
+
+    // prepare status values
+    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 < size(); read++)
+    {
+        // get offset of entry at read position
+        double fOff((*this)[read].getStopOffset());
+
+        if (basegfx::fTools::less(fOff, 0.0) && read + 1 < size())
+        {
+            // value < 0.0 and we have a next entry. check for gradient snippet
+            // containing 0.0 resp. StartColor
+            const double fOff2((*this)[read + 1].getStopOffset());
+
+            if (basegfx::fTools::more(fOff2, 0.0))
+            {
+                // read is the start of a gradient snippet containing 0.0. 
Correct
+                // entry to StartColor, interpolate to correct StartColor
+                (*this)[read]
+                    = BColorStop(0.0, 
basegfx::interpolate((*this)[read].getStopColor(),
+                                                           (*this)[read + 
1].getStopColor(),
+                                                           (0.0 - fOff) / 
(fOff2 - fOff)));
+
+                // adapt fOff
+                fOff = 0.0;
+            }
+        }
+
+        // step over < 0 values, these are outside and will be removed
+        if (basegfx::fTools::less(fOff, 0.0))
+        {
+            continue;
+        }
+
+        if (basegfx::fTools::less(fOff, 1.0) && read + 1 < size())
+        {
+            // value < 1.0 and we have a next entry. check for gradient snippet
+            // containing 1.0 resp. EndColor
+            const double fOff2((*this)[read + 1].getStopOffset());
+
+            if (basegfx::fTools::more(fOff2, 1.0))
+            {
+                // read is the start of a gradient snippet containing 1.0. 
Correct
+                // next entry to EndColor, interpolate to correct EndColor
+                (*this)[read + 1]
+                    = BColorStop(1.0, 
basegfx::interpolate((*this)[read].getStopColor(),
+                                                           (*this)[read + 
1].getStopColor(),
+                                                           (1.0 - fOff) / 
(fOff2 - fOff)));
+
+                // adapt fOff
+                fOff = 1.0;
+            }
+        }
+
+        // step over > 1 values; even break, since all following
+        // entries will also be bigger due to being sorted, so done
+        if (basegfx::fTools::more(fOff, 1.0))
+        {
+            break;
+        }
+
+        // entry is valid value at read position
+        // copy if write target is empty (write at start) or when
+        // write target is different to read in color or offset
+        if (0 == write || !((*this)[read] == (*this)[write - 1]))
+        {
+            if (write != read)
+            {
+                // copy read to write backwards to close gaps
+                (*this)[write] = (*this)[read];
+            }
+
+            // always forward write position
+            write++;
+        }
+    }
+
+    // correct size when length is reduced. write is always at
+    // last used position + 1
+    if (size() > write)
+    {
+        if (0 == write)
+        {
+            // no valid entries at all, but not empty. This can only happen
+            // when all entries are below 0.0 or above 1.0 (else a gradient
+            // snippet spawning over both would have been detected)
+            if (basegfx::fTools::less(back().getStopOffset(), 0.0))
+            {
+                // all outside too low, rescue last due to being closest to 
content
+                const BColor aBackColor(back().getStopColor());
+                clear();
+                emplace_back(0.0, aBackColor);
+            }
+            else // if (basegfx::fTools::more(front().getStopOffset(), 1.0))
+            {
+                // all outside too high, rescue first due to being closest to 
content
+                const BColor aFrontColor(front().getStopColor());
+                clear();
+                emplace_back(1.0, aFrontColor);
+            }
+        }
+        else
+        {
+            resize(write);
+        }
+    }
+}
+
+bool BColorStops::checkPenultimate() const
+{
+    // not needed when no ColorStops
+    if (empty())
+        return false;
+
+    // not needed when last ColorStop at the end or outside
+    if (basegfx::fTools::moreOrEqual(back().getStopOffset(), 1.0))
+        return false;
+
+    // get penultimate entry
+    const auto penultimate(rbegin() + 1);
+
+    // if there is none, we need no correction and are done
+    if (penultimate == rend())
+        return false;
+
+    // not needed when the last two ColorStops have different offset, then
+    // a visible range will be processed already
+    if (!basegfx::fTools::equal(back().getStopOffset(), 
penultimate->getStopOffset()))
+        return false;
+
+    // not needed when the last two ColorStops have the same Color, then the
+    // range before solves the problem
+    if (back().getStopColor() == penultimate->getStopColor())
+        return false;
+
+    return true;
+}
+
+/* Tooling method to fill a awt::ColorStopSequence with
+        the data from the given ColorStops. This is used in
+        UNO API implementations.
+    */
+css::awt::ColorStopSequence BColorStops::getAsColorStopSequence() const
+{
+    css::awt::ColorStopSequence aRetval(size());
+    // rColorStopSequence.realloc(rColorStops.size());
+    css::awt::ColorStop* pTargetColorStop(aRetval.getArray());
+
+    for (const auto& candidate : *this)
+    {
+        pTargetColorStop->StopOffset = candidate.getStopOffset();
+        pTargetColorStop->StopColor = 
css::rendering::RGBColor(candidate.getStopColor().getRed(),
+                                                               
candidate.getStopColor().getGreen(),
+                                                               
candidate.getStopColor().getBlue());
+        pTargetColorStop++;
+    }
+
+    return aRetval;
+}
+
+/* Tooling method to check if a ColorStop vector is defined
+        by a single color. It returns true if this is the case.
+        If true is returned, rSingleColor contains that single
+        color for convenience.
+        NOTE: If no ColorStop is defined, a fallback to BColor-default
+                (which is black) and true will be returned
+    */
+bool BColorStops::isSingleColor(BColor& rSingleColor) const
+{
+    if (empty())
+    {
+        rSingleColor = BColor();
+        return true;
+    }
+
+    if (1 == size())
+    {
+        rSingleColor = front().getStopColor();
+        return true;
+    }
+
+    rSingleColor = front().getStopColor();
+
+    for (auto const& rCandidate : *this)
+    {
+        if (rCandidate.getStopColor() != rSingleColor)
+            return false;
+    }
+
+    return true;
+}
+
+/* Tooling method to reverse ColorStops, including offsets.
+        When also mirroring offsets a valid sort keeps valid.
+    */
+void BColorStops::reverseColorStops()
+{
+    // can use std::reverse, but also need to adapt offset(s)
+    std::reverse(begin(), end());
+    for (auto& candidate : *this)
+        candidate = BColorStop(1.0 - candidate.getStopOffset(), 
candidate.getStopColor());
+}
+
+std::string BGradient::GradientStyleToString(css::awt::GradientStyle eStyle)
+{
+    switch (eStyle)
+    {
+        case css::awt::GradientStyle::GradientStyle_LINEAR:
+            return "LINEAR";
+
+        case css::awt::GradientStyle::GradientStyle_AXIAL:
+            return "AXIAL";
+
+        case css::awt::GradientStyle::GradientStyle_RADIAL:
+            return "RADIAL";
+
+        case css::awt::GradientStyle::GradientStyle_ELLIPTICAL:
+            return "ELLIPTICAL";
+
+        case css::awt::GradientStyle::GradientStyle_SQUARE:
+            return "SQUARE";
+
+        case css::awt::GradientStyle::GradientStyle_RECT:
+            return "RECT";
+
+        case css::awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE:
+            return "MAKE_FIXED_SIZE";
+    }
+
+    return "";
+}
+
+BGradient BGradient::fromJSON(std::u16string_view rJSON)
+{
+    StringMap aMap(lcl_jsonToStringMap(rJSON));
+    return lcl_buildGradientFromStringMap(aMap);
+}
+
+BGradient::BGradient()
+    : eStyle(css::awt::GradientStyle_LINEAR)
+    , aColorStops()
+    , nAngle(0)
+    , nBorder(0)
+    , nOfsX(50)
+    , nOfsY(50)
+    , nIntensStart(100)
+    , nIntensEnd(100)
+    , nStepCount(0)
+{
+    aColorStops.emplace_back(0.0, BColor(0.0, 0.0, 0.0)); // COL_BLACK
+    aColorStops.emplace_back(1.0, BColor(1.0, 1.0, 1.0)); // COL_WHITE
+}
+
+BGradient::BGradient(const basegfx::BColorStops& rColorStops, 
css::awt::GradientStyle eTheStyle,
+                     Degree10 nTheAngle, sal_uInt16 nXOfs, sal_uInt16 nYOfs, 
sal_uInt16 nTheBorder,
+                     sal_uInt16 nStartIntens, sal_uInt16 nEndIntens, 
sal_uInt16 nSteps)
+    : eStyle(eTheStyle)
+    , aColorStops(rColorStops)
+    , nAngle(nTheAngle)
+    , nBorder(nTheBorder)
+    , nOfsX(nXOfs)
+    , nOfsY(nYOfs)
+    , nIntensStart(nStartIntens)
+    , nIntensEnd(nEndIntens)
+    , nStepCount(nSteps)
+{
+    SetColorStops(aColorStops);
+}
+
+BGradient::BGradient(const css::awt::Gradient2& rGradient2)
+{
+    // set values
+    SetGradientStyle(rGradient2.Style);
+    SetAngle(Degree10(rGradient2.Angle));
+    SetBorder(rGradient2.Border);
+    SetXOffset(rGradient2.XOffset);
+    SetYOffset(rGradient2.YOffset);
+    SetStartIntens(rGradient2.StartIntensity);
+    SetEndIntens(rGradient2.EndIntensity);
+    SetSteps(rGradient2.StepCount);
+
+    // set ColorStops
+    aColorStops = BColorStops(rGradient2.ColorStops);
+    aColorStops.sortAndCorrect();
+}
+
+BGradient::BGradient(const css::uno::Any& rVal)
+    : BGradient()
+{
+    if (rVal.has<css::awt::Gradient2>())
+    {
+        // we can use awt::Gradient2 directly
+        css::awt::Gradient2 aGradient2;
+        rVal >>= aGradient2;
+
+        // set values
+        SetGradientStyle(aGradient2.Style);
+        SetAngle(Degree10(aGradient2.Angle));
+        SetBorder(aGradient2.Border);
+        SetXOffset(aGradient2.XOffset);
+        SetYOffset(aGradient2.YOffset);
+        SetStartIntens(aGradient2.StartIntensity);
+        SetEndIntens(aGradient2.EndIntensity);
+        SetSteps(aGradient2.StepCount);
+
+        // set ColorStops
+        aColorStops = BColorStops(aGradient2.ColorStops);
+        aColorStops.sortAndCorrect();
+    }
+    else if (rVal.has<css::awt::Gradient>())
+    {
+        // use awt::Gradient
+        css::awt::Gradient aGradient;
+        rVal >>= aGradient;
+
+        // set values
+        SetGradientStyle(aGradient.Style);
+        SetAngle(Degree10(aGradient.Angle));
+        SetBorder(aGradient.Border);
+        SetXOffset(aGradient.XOffset);
+        SetYOffset(aGradient.YOffset);
+        SetStartIntens(aGradient.StartIntensity);
+        SetEndIntens(aGradient.EndIntensity);
+        SetSteps(aGradient.StepCount);
+
+        // complete data by creating ColorStops from fixe Start/EndColor
+        aColorStops = BColorStops{
+            BColorStop(0.0, 
ColorToBColorConverter(aGradient.StartColor).getBColor()),
+            BColorStop(1.0, 
ColorToBColorConverter(aGradient.EndColor).getBColor())
+        };
+    }
+}
+
+bool BGradient::operator==(const BGradient& rGradient) const
+{
+    return (eStyle == rGradient.eStyle && aColorStops == rGradient.aColorStops
+            && nAngle == rGradient.nAngle && nBorder == rGradient.nBorder
+            && nOfsX == rGradient.nOfsX && nOfsY == rGradient.nOfsY
+            && nIntensStart == rGradient.nIntensStart && nIntensEnd == 
rGradient.nIntensEnd
+            && nStepCount == rGradient.nStepCount);
+}
+
+void BGradient::SetColorStops(const basegfx::BColorStops& rSteps)
+{
+    aColorStops = rSteps;
+    aColorStops.sortAndCorrect();
+    if (aColorStops.empty())
+        aColorStops.emplace_back(0.0, basegfx::BColor());
+}
+
+namespace
+{
+OUString AsRGBHexString(const ColorToBColorConverter& rVal)
+{
+    std::stringstream ss;
+    ss << std::hex << std::setfill('0') << std::setw(6) << sal_uInt32(rVal);
+    return OUString::createFromAscii(ss.str());
+}
+}
+
+boost::property_tree::ptree BGradient::dumpAsJSON() const
+{
+    boost::property_tree::ptree aTree;
+
+    aTree.put("style", BGradient::GradientStyleToString(eStyle));
+    const ColorToBColorConverter 
aStart(GetColorStops().front().getStopColor());
+    aTree.put("startcolor", AsRGBHexString(aStart.GetRGBColor()));
+    const ColorToBColorConverter aEnd(GetColorStops().back().getStopColor());
+    aTree.put("endcolor", AsRGBHexString(aEnd.GetRGBColor()));
+    aTree.put("angle", std::to_string(nAngle.get()));
+    aTree.put("border", std::to_string(nBorder));
+    aTree.put("x", std::to_string(nOfsX));
+    aTree.put("y", std::to_string(nOfsY));
+    aTree.put("intensstart", std::to_string(nIntensStart));
+    aTree.put("intensend", std::to_string(nIntensEnd));
+    aTree.put("stepcount", std::to_string(nStepCount));
+
+    return aTree;
+}
+
+css::awt::Gradient2 BGradient::getAsGradient2() const
+{
+    css::awt::Gradient2 aRetval;
+
+    // standard values
+    aRetval.Style = GetGradientStyle();
+    aRetval.Angle = static_cast<short>(GetAngle());
+    aRetval.Border = GetBorder();
+    aRetval.XOffset = GetXOffset();
+    aRetval.YOffset = GetYOffset();
+    aRetval.StartIntensity = GetStartIntens();
+    aRetval.EndIntensity = GetEndIntens();
+    aRetval.StepCount = GetSteps();
+
+    // for compatibility, still set StartColor/EndColor
+    // const basegfx::BColorStops& rColorStops(GetColorStops());
+    aRetval.StartColor
+        = 
static_cast<sal_Int32>(ColorToBColorConverter(aColorStops.front().getStopColor()));
+    aRetval.EndColor
+        = 
static_cast<sal_Int32>(ColorToBColorConverter(aColorStops.back().getStopColor()));
+
+    // fill ColorStops to extended Gradient2
+    aRetval.ColorStops = aColorStops.getAsColorStopSequence();
+    // fillColorStopSequenceFromColorStops(rGradient2.ColorStops, rColorStops);
+
+    return aRetval;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basegfx/source/tools/gradienttools.cxx 
b/basegfx/source/tools/gradienttools.cxx
index 372abe08254b..c778a5237676 100644
--- a/basegfx/source/tools/gradienttools.cxx
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -265,38 +265,6 @@ namespace basegfx
 
     namespace utils
     {
-        /* Internal helper to convert ::Color from tools::color.hxx to BColor
-           without the need to link against tools library. Be on the
-           safe side by using the same union
-        */
-        namespace {
-        struct ColorToBColorConverter
-        {
-            union {
-                sal_uInt32 mValue;
-                struct {
-#ifdef OSL_BIGENDIAN
-                    sal_uInt8 T;
-                    sal_uInt8 R;
-                    sal_uInt8 G;
-                    sal_uInt8 B;
-#else
-                    sal_uInt8 B;
-                    sal_uInt8 G;
-                    sal_uInt8 R;
-                    sal_uInt8 T;
-#endif
-                };
-            };
-
-            ColorToBColorConverter(sal_uInt32 nColor) : mValue(nColor) { T=0; }
-            BColor getBColor() const
-            {
-                return BColor(R / 255.0, G / 255.0, B / 255.0);
-            }
-        };
-        }
-
         /// Tooling method to fill awt::Gradient2 from data contained in the 
given Any
         bool fillGradient2FromAny(css::awt::Gradient2& rGradient, const 
css::uno::Any& rVal)
         {
@@ -327,11 +295,10 @@ namespace basegfx
                     rGradient.StepCount = aTmp.StepCount;
 
                     // complete data by creating ColorStops for awt::Gradient2
-                    fillColorStopSequenceFromColorStops(
-                        rGradient.ColorStops,
-                        ColorStops {
-                            ColorStop(0.0, 
ColorToBColorConverter(aTmp.StartColor).getBColor()),
-                            ColorStop(1.0, 
ColorToBColorConverter(aTmp.EndColor).getBColor()) });
+                    const BColorStops aTempColorStops {
+                        BColorStop(0.0, 
ColorToBColorConverter(aTmp.StartColor).getBColor()),
+                        BColorStop(1.0, 
ColorToBColorConverter(aTmp.EndColor).getBColor()) };
+                    rGradient.ColorStops = 
aTempColorStops.getAsColorStopSequence();
                     bRetval = true;
                 }
             }
@@ -355,12 +322,12 @@ namespace basegfx
         */
         void prepareColorStops(
             const com::sun::star::awt::Gradient2& rGradient,
-            ColorStops& rColorStops,
+            BColorStops& rColorStops,
             BColor& rSingleColor)
         {
-            fillColorStopsFromGradient2(rColorStops, rGradient);
+            rColorStops = BColorStops(rGradient.ColorStops);
 
-            if (isSingleColor(rColorStops, rSingleColor))
+            if (rColorStops.isSingleColor(rSingleColor))
             {
                 // when single color, preserve value in rSingleColor
                 // and clear the ColorStops, done.
@@ -371,15 +338,14 @@ namespace basegfx
             if (rGradient.StartIntensity != 100 || rGradient.EndIntensity != 
100)
             {
                 // apply 'old' blend stuff, blend against black
-                blendColorStopsToIntensity(
-                    rColorStops,
+                rColorStops.blendToIntensity(
                     rGradient.StartIntensity * 0.01,
                     rGradient.EndIntensity * 0.01,
                     basegfx::BColor()); // COL_BLACK
 
                 // can lead to single color (e.g. both zero, so all black),
                 // so check again
-                if (isSingleColor(rColorStops, rSingleColor))
+                if (rColorStops.isSingleColor(rSingleColor))
                 {
                     rColorStops.clear();
                     return;
@@ -393,7 +359,7 @@ namespace basegfx
                 //       mechanism does not need entries at 0.0 and 1.0.
                 //       In case this is needed, do that in the caller
                 const double fFactor(rGradient.Border * 0.01);
-                ColorStops aNewStops;
+                BColorStops aNewStops;
 
                 for (const auto& candidate : rColorStops)
                 {
@@ -437,8 +403,8 @@ namespace basegfx
            'FillTransparenceGradient' method (at import time).
         */
         void synchronizeColorStops(
-            ColorStops& rColorStops,
-            ColorStops& rAlphaStops,
+            BColorStops& rColorStops,
+            BColorStops& rAlphaStops,
             const BColor& rSingleColor,
             const BColor& rSingleAlpha)
         {
@@ -448,12 +414,12 @@ namespace basegfx
                 {
                     // no AlphaStops and no ColorStops
                     // create two-stop fallbacks for both
-                    rColorStops = ColorStops {
-                        ColorStop(0.0, rSingleColor),
-                        ColorStop(1.0, rSingleColor) };
-                    rAlphaStops = ColorStops {
-                        ColorStop(0.0, rSingleAlpha),
-                        ColorStop(1.0, rSingleAlpha) };
+                    rColorStops = BColorStops {
+                        BColorStop(0.0, rSingleColor),
+                        BColorStop(1.0, rSingleColor) };
+                    rAlphaStops = BColorStops {
+                        BColorStop(0.0, rSingleAlpha),
+                        BColorStop(1.0, rSingleAlpha) };
                 }
                 else
                 {
@@ -489,8 +455,8 @@ namespace basegfx
             if (!bNeedToSyncronize)
             {
                 // check for same StopOffsets
-                ColorStops::const_iterator aCurrColor(rColorStops.begin());
-                ColorStops::const_iterator aCurrAlpha(rAlphaStops.begin());
+                BColorStops::const_iterator aCurrColor(rColorStops.begin());
+                BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin());
 
                 while (!bNeedToSyncronize &&
                     aCurrColor != rColorStops.end() &&
@@ -511,12 +477,12 @@ namespace basegfx
             if (bNeedToSyncronize)
             {
                 // synchronize sizes & StopOffsets
-                ColorStops::const_iterator aCurrColor(rColorStops.begin());
-                ColorStops::const_iterator aCurrAlpha(rAlphaStops.begin());
-                ColorStops aNewColor;
-                ColorStops aNewAlpha;
-                ColorStopRange aColorStopRange;
-                ColorStopRange aAlphaStopRange;
+                BColorStops::const_iterator aCurrColor(rColorStops.begin());
+                BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin());
+                BColorStops aNewColor;
+                BColorStops aNewAlpha;
+                BColorStops::BColorStopRange aColorStopRange;
+                BColorStops::BColorStopRange aAlphaStopRange;
                 bool bRealChange(false);
 
                 do {
@@ -532,14 +498,14 @@ namespace basegfx
                         {
                             // copy color, create alpha
                             aNewColor.emplace_back(fColorOff, 
aCurrColor->getStopColor());
-                            aNewAlpha.emplace_back(fColorOff, 
utils::modifyBColor(rAlphaStops, fColorOff, 0, aAlphaStopRange));
+                            aNewAlpha.emplace_back(fColorOff, 
rAlphaStops.getInterpolatedBColor(fColorOff, 0, aAlphaStopRange));
                             bRealChange = true;
                             aCurrColor++;
                         }
                         else if (fTools::more(fColorOff, fAlphaOff))
                         {
                             // copy alpha, create color
-                            aNewColor.emplace_back(fAlphaOff, 
utils::modifyBColor(rColorStops, fAlphaOff, 0, aColorStopRange));
+                            aNewColor.emplace_back(fAlphaOff, 
rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange));
                             aNewAlpha.emplace_back(fAlphaOff, 
aCurrAlpha->getStopColor());
                             bRealChange = true;
                             aCurrAlpha++;
@@ -556,14 +522,14 @@ namespace basegfx
                     else if (bColor)
                     {
                         const double fColorOff(aCurrColor->getStopOffset());
-                        aNewAlpha.emplace_back(fColorOff, 
utils::modifyBColor(rAlphaStops, fColorOff, 0, aAlphaStopRange));
+                        aNewAlpha.emplace_back(fColorOff, 
rAlphaStops.getInterpolatedBColor(fColorOff, 0, aAlphaStopRange));
                         bRealChange = true;
                         aCurrColor++;
                     }
                     else if (bAlpha)
                     {
                         const double fAlphaOff(aCurrAlpha->getStopOffset());
-                        aNewColor.emplace_back(fAlphaOff, 
utils::modifyBColor(rColorStops, fAlphaOff, 0, aColorStopRange));
+                        aNewColor.emplace_back(fAlphaOff, 
rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange));
                         bRealChange = true;
                         aCurrAlpha++;
                     }
@@ -586,467 +552,6 @@ namespace basegfx
             }
         }
 
-        /* Tooling method to linearly blend the Colors contained in
-           a given ColorStop vector against a given Color using the
-           given intensity values.
-           The intensity values fStartIntensity, fEndIntensity are
-           in the range of [0.0 .. 1.0] and describe how much the
-           blend is supposed to be done at the start color position
-           and the end color position respectively, where 0.0 means
-           to fully use the given BlendColor, 1.0 means to not change
-           the existing color in the ColorStop.
-           Every color entry in the given ColorStop is blended
-           relative to it's StopPosition, interpolating the
-           given intensities with the range [0.0 .. 1.0] to do so.
-        */
-        void blendColorStopsToIntensity(ColorStops& rColorStops, double 
fStartIntensity, double fEndIntensity, const basegfx::BColor& rBlendColor)
-        {
-            // no entries, done
-            if (rColorStops.empty())
-                return;
-
-            // correct intensities (maybe assert when input was wrong)
-            fStartIntensity = std::max(std::min(1.0, fStartIntensity), 0.0);
-            fEndIntensity = std::max(std::min(1.0, fEndIntensity), 0.0);
-
-            // all 100%, no real blend, done
-            if (basegfx::fTools::equal(fStartIntensity, 1.0) && 
basegfx::fTools::equal(fEndIntensity, 1.0))
-                return;
-
-            // blend relative to StopOffset position
-            for (auto& candidate : rColorStops)
-            {
-                const double fOffset(candidate.getStopOffset());
-                const double fIntensity((fStartIntensity * (1.0 - fOffset)) + 
(fEndIntensity * fOffset));
-                candidate = basegfx::ColorStop(
-                    fOffset,
-                    basegfx::interpolate(rBlendColor, 
candidate.getStopColor(), fIntensity));
-            }
-        }
-
-        /* Tooling method to check if a ColorStop vector is defined
-           by a single color. It returns true if this is the case.
-           If true is returned, rSingleColor contains that single
-           color for convenience.
-           NOTE: If no ColorStop is defined, a fallback to BColor-default
-                 (which is black) and true will be returned
-        */
-        bool isSingleColor(const ColorStops& rColorStops, BColor& rSingleColor)
-        {
-            if (rColorStops.empty())
-            {
-                rSingleColor = BColor();
-                return true;
-            }
-
-            if (1 == rColorStops.size())
-            {
-                rSingleColor = rColorStops.front().getStopColor();
-                return true;
-            }
-
-            rSingleColor = rColorStops.front().getStopColor();
-
-            for (auto const& rCandidate : rColorStops)
-            {
-                if (rCandidate.getStopColor() != rSingleColor)
-                    return false;
-            }
-
-            return true;
-        }
-
-        /* Tooling method to reverse ColorStops, including offsets.
-           When also mirroring offsets a valid sort keeps valid.
-        */
-        void reverseColorStops(ColorStops& rColorStops)
-        {
-            // can use std::reverse, but also need to adapt offset(s)
-            std::reverse(rColorStops.begin(), rColorStops.end());
-            for (auto& candidate : rColorStops)
-                candidate = ColorStop(1.0 - candidate.getStopOffset(), 
candidate.getStopColor());
-        }
-
-        /* Tooling method to convert UNO API data to ColorStops.
-           This will try to extract ColorStop data from the given
-           awt::Gradient2.
-        */
-        void fillColorStopsFromGradient2(ColorStops& rColorStops, const 
com::sun::star::awt::Gradient2& rGradient)
-        {
-            const sal_Int32 nLen(rGradient.ColorStops.getLength());
-
-            if (0 == nLen)
-                return;
-
-            // we have ColorStops
-            rColorStops.clear();
-            rColorStops.reserve(nLen);
-            const css::awt::ColorStop* 
pSourceColorStop(rGradient.ColorStops.getConstArray());
-
-            for (sal_Int32 a(0); a < nLen; a++, pSourceColorStop++)
-            {
-                rColorStops.emplace_back(
-                    pSourceColorStop->StopOffset,
-                    BColor(pSourceColorStop->StopColor.Red, 
pSourceColorStop->StopColor.Green, pSourceColorStop->StopColor.Blue));
-            }
-        }
-
-        /* Tooling method to convert UNO API data to ColorStops.
-           This will try to extract ColorStop data from the given
-           Any, so if it's of type awt::Gradient2 that data will be
-           extracted, converted and copied into the given ColorStops.
-        */
-        void fillColorStopsFromAny(ColorStops& rColorStops, const 
css::uno::Any& rVal)
-        {
-            css::awt::Gradient2 aGradient2;
-            if (!(rVal >>= aGradient2))
-                return;
-
-            fillColorStopsFromGradient2(rColorStops, aGradient2);
-        }
-
-        /* Tooling method to fill a awt::ColorStopSequence with
-           the data from the given ColorStops. This is used in
-           UNO API implementations.
-        */
-        void fillColorStopSequenceFromColorStops(css::awt::ColorStopSequence& 
rColorStopSequence, const ColorStops& rColorStops)
-        {
-            // fill ColorStops to extended Gradient2
-            rColorStopSequence.realloc(rColorStops.size());
-            css::awt::ColorStop* 
pTargetColorStop(rColorStopSequence.getArray());
-
-            for (const auto& candidate : rColorStops)
-            {
-                pTargetColorStop->StopOffset = candidate.getStopOffset();
-                pTargetColorStop->StopColor = css::rendering::RGBColor(
-                    candidate.getStopColor().getRed(),
-                    candidate.getStopColor().getGreen(),
-                    candidate.getStopColor().getBlue());
-                pTargetColorStop++;
-            }
-        }
-
-        /* Tooling method that allows to replace the StartColor in a
-           vector of ColorStops. A vector in 'ordered state' is expected,
-           so you may use/have used sortAndCorrectColorStops, see below.
-           This method is for convenience & backwards compatibility, please
-           think about handling multi-colored gradients directly.
-        */
-        void replaceStartColor(ColorStops& rColorStops, const BColor& rStart)
-        {
-            ColorStops::iterator a1stNonStartColor(rColorStops.begin());
-
-            // search for highest existing non-StartColor
-            while (a1stNonStartColor != rColorStops.end() && 
basegfx::fTools::lessOrEqual(a1stNonStartColor->getStopOffset(), 0.0))
-                a1stNonStartColor++;
-
-            // create new ColorStops by 1st adding new one and then all
-            // non-StartColor entries
-            ColorStops aNewColorStops;
-
-            aNewColorStops.reserve(rColorStops.size() + 1);
-            aNewColorStops.emplace_back(0.0, rStart);
-            aNewColorStops.insert(aNewColorStops.end(), a1stNonStartColor, 
rColorStops.end());
-
-            // assign & done
-            rColorStops = aNewColorStops;
-        }
-
-        /* Tooling method that allows to replace the EndColor in a
-           vector of ColorStops. A vector in 'ordered state' is expected,
-           so you may use/have used sortAndCorrectColorStops, see below.
-           This method is for convenience & backwards compatibility, please
-           think about handling multi-colored gradients directly.
-        */
-        void replaceEndColor(ColorStops& rColorStops, const BColor& rEnd)
-        {
-            // erase all evtl. existing EndColor(s)
-            while (!rColorStops.empty() && 
basegfx::fTools::moreOrEqual(rColorStops.back().getStopOffset(), 1.0))
-                rColorStops.pop_back();
-
-            // add at the end of existing ColorStops
-            rColorStops.emplace_back(1.0, rEnd);
-        }
-
-        // Tooling method to quickly create a ColorStop vector for a given set 
of Start/EndColor
-        ColorStops createColorStopsFromStartEndColor(const BColor& rStart, 
const BColor& rEnd)
-        {
-            return ColorStops {
-                ColorStop(0.0, rStart),
-                ColorStop(1.0, rEnd) };
-        }
-
-        /* Tooling method to guarantee sort and correctness for
-           the given ColorStops vector.
-           A vector fulfilling these conditions is called to be
-           in 'ordered state'.
-
-           At return, the following conditions are guaranteed:
-           - contains no ColorStops with offset < 0.0 (will
-             be removed)
-           - contains no ColorStops with offset > 1.0 (will
-             be removed)
-           - ColorStops with identical offsets are now allowed
-           - will be sorted from lowest offset to highest
-
-           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 technically required
-           - A entry with 1.0 is not required or forced, so
-             no 'EndColor' is technically required
-
-           All this is done in one run (sort + O(N)) without
-           creating a copy of the data in any form
-        */
-        void sortAndCorrectColorStops(ColorStops& rColorStops)
-        {
-            // no content, we are done
-            if (rColorStops.empty())
-                return;
-
-            if (1 == rColorStops.size())
-            {
-                // no gradient at all, but preserve given color
-                // evtl. correct offset to be in valid range [0.0 .. 1.0]
-                // NOTE: This does not move it to 0.0 or 1.0, it *can* still
-                //       be somewhere in-between what is allowed
-                rColorStops[0] = ColorStop(
-                    std::max(0.0, std::min(1.0, 
rColorStops[0].getStopOffset())),
-                    rColorStops[0].getStopColor());
-
-                // done
-                return;
-            }
-
-            // 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(rColorStops.begin(), rColorStops.end());
-
-            // prepare status values
-            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 < rColorStops.size(); read++)
-            {
-                // get offset of entry at read position
-                double fOff(rColorStops[read].getStopOffset());
-
-                if (basegfx::fTools::less(fOff, 0.0) && read + 1 < 
rColorStops.size())
-                {
-                    // value < 0.0 and we have a next entry. check for 
gradient snippet
-                    // containing 0.0 resp. StartColor
-                    const double fOff2(rColorStops[read + 1].getStopOffset());
-
-                    if (basegfx::fTools::more(fOff2, 0.0))
-                    {
-                        // read is the start of a gradient snippet containing 
0.0. Correct
-                        // entry to StartColor, interpolate to correct 
StartColor
-                        rColorStops[read] = ColorStop(0.0, 
basegfx::interpolate(
-                            rColorStops[read].getStopColor(),
-                            rColorStops[read + 1].getStopColor(),
-                            (0.0 - fOff) / (fOff2 - fOff)));
-
-                        // adapt fOff
-                        fOff = 0.0;
-                    }
-                }
-
-                // step over < 0 values, these are outside and will be removed
-                if (basegfx::fTools::less(fOff, 0.0))
-                {
-                    continue;
-                }
-
-                if (basegfx::fTools::less(fOff, 1.0) && read + 1 < 
rColorStops.size())
-                {
-                    // value < 1.0 and we have a next entry. check for 
gradient snippet
-                    // containing 1.0 resp. EndColor
-                    const double fOff2(rColorStops[read + 1].getStopOffset());
-
-                    if (basegfx::fTools::more(fOff2, 1.0))
-                    {
-                        // read is the start of a gradient snippet containing 
1.0. Correct
-                        // next entry to EndColor, interpolate to correct 
EndColor
-                        rColorStops[read + 1] = ColorStop(1.0, 
basegfx::interpolate(
-                            rColorStops[read].getStopColor(),
-                            rColorStops[read + 1].getStopColor(),
-                            (1.0 - fOff) / (fOff2 - fOff)));
-
-                        // adapt fOff
-                        fOff = 1.0;
-                    }
-                }
-
-                // step over > 1 values; even break, since all following
-                // entries will also be bigger due to being sorted, so done
-                if (basegfx::fTools::more(fOff, 1.0))
-                {
-                    break;
-                }
-
-                // entry is valid value at read position
-                // copy if write target is empty (write at start) or when
-                // write target is different to read in color or offset
-                if (0 == write || !(rColorStops[read] == rColorStops[write-1]))
-                {
-                    if (write != read)
-                    {
-                        // copy read to write backwards to close gaps
-                        rColorStops[write] = rColorStops[read];
-                    }
-
-                    // always forward write position
-                    write++;
-                }
-            }
-
-            // correct size when length is reduced. write is always at
-            // last used position + 1
-            if (rColorStops.size() > write)
-            {
-                if (0 == write)
-                {
-                    // no valid entries at all, but not empty. This can only 
happen
-                    // when all entries are below 0.0 or above 1.0 (else a 
gradient
-                    // snippet spawning over both would have been detected)
-                    if 
(basegfx::fTools::less(rColorStops.back().getStopOffset(), 0.0))
-                    {
-                        // all outside too low, rescue last due to being 
closest to content
-                        rColorStops = ColorStops { ColorStop(0.0, 
rColorStops.back().getStopColor()) };
-                    }
-                    else // if 
(basegfx::fTools::more(rColorStops.front().getStopOffset(), 1.0))
-                    {
-                        // all outside too high, rescue first due to being 
closest to content
-                        rColorStops = ColorStops { ColorStop(1.0, 
rColorStops.front().getStopColor()) };
-                    }
-                }
-                else
-                {
-                    rColorStops.resize(write);
-                }
-            }
-        }
-
-        BColor modifyBColor(
-            const ColorStops& rColorStops,
-            double fScaler,
-            sal_uInt32 nRequestedSteps,
-            ColorStopRange& rLastColorStopRange)
-        {
-            // no color at all, done
-            if (rColorStops.empty())
-                return BColor();
-
-            // outside range -> at start
-            const double fMin(rColorStops.front().getStopOffset());
-            if (fScaler < fMin)
-                return rColorStops.front().getStopColor();
-
-            // outside range -> at end
-            const double fMax(rColorStops.back().getStopOffset());
-            if (fScaler > fMax)
-                return rColorStops.back().getStopColor();
-
-            // special case for the 'classic' case with just two colors:
-            // we can optimize that and keep the speed/resources low
-            // by avoiding some calculations and an O(log(N)) array access
-            if (2 == rColorStops.size())
-            {
-                if (fTools::equal(fMin, fMax))
-                    return rColorStops.front().getStopColor();
-
-                const basegfx::BColor 
aCStart(rColorStops.front().getStopColor());
-                const basegfx::BColor aCEnd(rColorStops.back().getStopColor());
-                const sal_uInt32 nSteps(
-                    calculateNumberOfSteps(
-                        nRequestedSteps,
-                        aCStart,
-                        aCEnd));
-
-                // we need to extend the interpolation to the local
-                // range of ColorStops. Despite having two ColorStops
-                // these are not necessarily at 0.0 and 1.0, so may be
-                // not the classical Start/EndColor (what is allowed)
-                fScaler = (fScaler - fMin) / (fMax - fMin);
-                return basegfx::interpolate(
-                    aCStart,
-                    aCEnd,
-                    nSteps > 1 ? floor(fScaler * nSteps) / double(nSteps - 1) 
: fScaler);
-            }
-
-            // check if we need to newly populate the needed interpolation data
-            // or if we can re-use from last time.
-            // If this scope is not entered, we do not need the binary search. 
It's
-            // only a single buffered entry, and only used when more than three
-            // ColorStops exist, but makes a huge difference compared with 
accessing
-            // the sorted ColorStop vector each time.
-            // NOTE: with this simple change I get very high hit rates, e.g. 
rotating
-            //       a donut with gradient test '1' hit rate is at 
0.99909440357755486
-            if (rLastColorStopRange.mfOffsetStart == 
rLastColorStopRange.mfOffsetEnd
-                || fScaler < rLastColorStopRange.mfOffsetStart
-                || fScaler > rLastColorStopRange.mfOffsetEnd)
-            {
-                // access needed spot in sorted array using binary search
-                // NOTE: This *seems* slow(er) when developing compared to just
-                //       looping/accessing, but that's just due to the 
extensive
-                //       debug test code created by the stl. In a pro version,
-                //       all is good/fast as expected
-                const auto upperBound(
-                    std::upper_bound(
-                        rColorStops.begin(),
-                        rColorStops.end(),
-                        ColorStop(fScaler),
-                        [](const ColorStop& x, const ColorStop& y) { return 
x.getStopOffset() < y.getStopOffset(); }));
-
-                // no upper bound, done
-                if (rColorStops.end() == upperBound)
-                    return rColorStops.back().getStopColor();
-
-                // lower bound is one entry back, access that
-                const auto lowerBound(upperBound - 1);
-
-                // no lower bound, done
-                if (rColorStops.end() == lowerBound)
-                    return rColorStops.back().getStopColor();
-
-                // we have lower and upper bound, get colors and offsets
-                rLastColorStopRange.maColorStart = lowerBound->getStopColor();
-                rLastColorStopRange.maColorEnd = upperBound->getStopColor();
-                rLastColorStopRange.mfOffsetStart = 
lowerBound->getStopOffset();
-                rLastColorStopRange.mfOffsetEnd = upperBound->getStopOffset();
-            }
-
-            // when there are just two color steps this cannot happen, but 
when using
-            // a range of colors this *may* be used inside the range to 
represent
-            // single-colored regions inside a ColorRange. Use that color & 
done
-            if (rLastColorStopRange.maColorStart == 
rLastColorStopRange.maColorEnd)
-                return rLastColorStopRange.maColorStart;
-
-            // calculate number of steps and adapted proportional
-            // range for scaler in [0.0 .. 1.0]
-            const double fAdaptedScaler((fScaler - 
rLastColorStopRange.mfOffsetStart) /
-                (rLastColorStopRange.mfOffsetEnd - 
rLastColorStopRange.mfOffsetStart));
-            const sal_uInt32 nSteps(
-                calculateNumberOfSteps(
-                    nRequestedSteps,
-                    rLastColorStopRange.maColorStart,
-                    rLastColorStopRange.maColorEnd));
-
-            // interpolate & evtl. apply steps
-            return interpolate(
-                rLastColorStopRange.maColorStart,
-                rLastColorStopRange.maColorEnd,
-                nSteps > 1 ? floor(fAdaptedScaler * nSteps) / double(nSteps - 
1) : fAdaptedScaler);
-        }
-
         sal_uInt32 calculateNumberOfSteps(
             sal_uInt32 nRequestedSteps,
             const BColor& rStart,
diff --git a/chart2/inc/pch/precompiled_chartcontroller.hxx 
b/chart2/inc/pch/precompiled_chartcontroller.hxx
index 6839b807696d..ba4ccbddf15e 100644
--- a/chart2/inc/pch/precompiled_chartcontroller.hxx
+++ b/chart2/inc/pch/precompiled_chartcontroller.hxx
@@ -409,7 +409,6 @@
 #include <svx/svxdllapi.h>
 #include <svx/xdash.hxx>
 #include <svx/xdef.hxx>
-#include <svx/xgrad.hxx>
 #include <svx/xhatch.hxx>
 #include <svx/xit.hxx>
 #include <svx/xpoly.hxx>
diff --git a/chart2/qa/extras/chart2import.cxx 
b/chart2/qa/extras/chart2import.cxx
index fc5caf42c3a7..890841a79849 100644
--- a/chart2/qa/extras/chart2import.cxx
+++ b/chart2/qa/extras/chart2import.cxx
@@ -628,9 +628,7 @@ CPPUNIT_TEST_FIXTURE(Chart2ImportTest, testBnc889755)
     uno::Reference<beans::XPropertySet> xShapeProps(xPage->getByIndex(4), 
uno::UNO_QUERY_THROW);
     awt::Gradient2 aTransparence;
     xShapeProps->getPropertyValue("FillTransparenceGradient") >>= 
aTransparence;
-
-    basegfx::ColorStops aColorStops;
-    basegfx::utils::fillColorStopsFromGradient2(aColorStops, aTransparence);
+    const basegfx::BColorStops aColorStops(aTransparence.ColorStops);
 
     CPPUNIT_ASSERT_EQUAL(size_t(3), aColorStops.size());
     CPPUNIT_ASSERT(basegfx::fTools::equal(aColorStops[0].getStopOffset(), 
0.0));
@@ -673,9 +671,7 @@ CPPUNIT_TEST_FIXTURE(Chart2ImportTest, 
testTransparencyGradientValue)
     uno::Reference< container::XNameAccess > 
xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"),
 uno::UNO_QUERY);
     uno::Any rTransparenceValue = 
xTransparenceGradient->getByName(sTranspGradientName);
     CPPUNIT_ASSERT(rTransparenceValue >>= aTransparenceGradient);
-
-    basegfx::ColorStops aColorStops;
-    basegfx::utils::fillColorStopsFromGradient2(aColorStops, 
aTransparenceGradient);
+    const basegfx::BColorStops aColorStops(aTransparenceGradient.ColorStops);
 
     // MCGR: Use the whole completely imported transparency gradient to check 
for correctness
     CPPUNIT_ASSERT_EQUAL(size_t(2), aColorStops.size());
diff --git a/chart2/source/controller/main/ChartController_Tools.cxx 
b/chart2/source/controller/main/ChartController_Tools.cxx
index acc5dd950391..1a8f5071982f 100644
--- a/chart2/source/controller/main/ChartController_Tools.cxx
+++ b/chart2/source/controller/main/ChartController_Tools.cxx
@@ -74,7 +74,6 @@
 #include <svx/unoapi.hxx>
 #include <svx/unopage.hxx>
 #include <svx/unoshape.hxx>
-#include <svx/xgrad.hxx>
 #include <PropertyHelper.hxx>
 
 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
@@ -957,8 +956,8 @@ void ChartController::executeDispatch_FillColor(sal_uInt32 
nColor)
 
 void ChartController::executeDispatch_FillGradient(std::u16string_view 
sJSONGradient)
 {
-    XGradient aXGradient = XGradient::fromJSON(sJSONGradient);
-    css::awt::Gradient aGradient = aXGradient.toGradientUNO();
+    basegfx::BGradient aBGradient = 
basegfx::BGradient::fromJSON(sJSONGradient);
+    css::awt::Gradient aGradient = aBGradient.getAsGradient2();
 
     try
     {
@@ -973,9 +972,9 @@ void 
ChartController::executeDispatch_FillGradient(std::u16string_view sJSONGrad
             if( xPropSet.is() )
             {
                 OUString aPrefferedName =
-                    
OUString::number(static_cast<sal_Int32>(Color(aXGradient.GetColorStops().front().getStopColor())))
-                    + 
OUString::number(static_cast<sal_Int32>(Color(aXGradient.GetColorStops().back().getStopColor())))
-                    + 
OUString::number(static_cast<sal_Int32>(aXGradient.GetAngle().get()));
+                    
OUString::number(static_cast<sal_Int32>(Color(aBGradient.GetColorStops().front().getStopColor())))
+                    + 
OUString::number(static_cast<sal_Int32>(Color(aBGradient.GetColorStops().back().getStopColor())))
+                    + 
OUString::number(static_cast<sal_Int32>(aBGradient.GetAngle().get()));
 
                 OUString aNewName = 
PropertyHelper::addGradientUniqueNameToTable(css::uno::Any(aGradient),
                                         xChartModel,
diff --git a/cui/source/inc/cuitabarea.hxx b/cui/source/inc/cuitabarea.hxx
index 2db104f53c8c..64ec01ef5abf 100644
--- a/cui/source/inc/cuitabarea.hxx
+++ b/cui/source/inc/cuitabarea.hxx
@@ -170,7 +170,7 @@ class SvxTransparenceTabPage : public SfxTabPage
     std::unique_ptr<weld::CustomWeld> m_xCtlXRectPreview;
 
     // MCGR: Preserve in-between ColorStops until we have an UI to edit these
-    basegfx::ColorStops maColorStops;
+    basegfx::BColorStops maColorStops;
 
     DECL_LINK(ClickTransOffHdl_Impl, weld::Toggleable&, void);
     DECL_LINK(ClickTransLinearHdl_Impl, weld::Toggleable&, void);
@@ -188,7 +188,7 @@ class SvxTransparenceTabPage : public SfxTabPage
     void InvalidatePreview (bool bEnable = true );
 
     // MCGR: Preserve in-between ColorStops until we have an UI to edit these
-    basegfx::ColorStops createColorStops();
+    basegfx::BColorStops createColorStops();
 
 public:
     SvxTransparenceTabPage(weld::Container* pPage, weld::DialogController* 
pController, const SfxItemSet& rInAttrs);
@@ -368,7 +368,7 @@ private:
     SfxItemSet&         m_rXFSet;
 
     // MCGR: Preserve in-between ColorStops until we have an UI to edit these
-    basegfx::ColorStops m_aColorStops;
+    basegfx::BColorStops m_aColorStops;
 
     SvxXRectPreview m_aCtlPreview;
     std::unique_ptr<weld::ComboBox> m_xLbGradientType;
@@ -410,7 +410,7 @@ private:
     sal_Int32 SearchGradientList(std::u16string_view rGradientName);
 
     // MCGR: Preserve in-between ColorStops until we have an UI to edit these
-    basegfx::ColorStops createColorStops();
+    basegfx::BColorStops createColorStops();
 
 public:
     SvxGradientTabPage(weld::Container* pPage, weld::DialogController* 
pController, const SfxItemSet& rInAttrs);
diff --git a/cui/source/tabpages/tpgradnt.cxx b/cui/source/tabpages/tpgradnt.cxx
index 57eed4dd4ce5..7f6630747a42 100644
--- a/cui/source/tabpages/tpgradnt.cxx
+++ b/cui/source/tabpages/tpgradnt.cxx
@@ -84,8 +84,7 @@ SvxGradientTabPage::SvxGradientTabPage(weld::Container* 
pPage, weld::DialogContr
 
     // setting the output device
     m_rXFSet.Put( XFillStyleItem(drawing::FillStyle_GRADIENT) );
-    // XGradient() default already creates [COL_BLACK, COL_WHITE] as defaults
-    m_rXFSet.Put( XFillGradientItem(OUString(), XGradient()));
+    m_rXFSet.Put( XFillGradientItem(OUString(), basegfx::BGradient()));
     m_aCtlPreview.SetAttributes(m_aXFillAttr.GetItemSet());
 
     // set handler
@@ -185,18 +184,18 @@ DeactivateRC SvxGradientTabPage::DeactivatePage( 
SfxItemSet* _pSet )
 
 bool SvxGradientTabPage::FillItemSet( SfxItemSet* rSet )
 {
-    std::unique_ptr<XGradient> pXGradient;
+    std::unique_ptr<basegfx::BGradient> pBGradient;
     size_t nPos = m_xGradientLB->IsNoSelection() ? VALUESET_ITEM_NOTFOUND : 
m_xGradientLB->GetSelectItemPos();
     if( nPos != VALUESET_ITEM_NOTFOUND )
     {
-        pXGradient.reset(new XGradient( m_pGradientList->GetGradient( 
static_cast<sal_uInt16>(nPos) )->GetGradient() ));
+        pBGradient.reset(new basegfx::BGradient( m_pGradientList->GetGradient( 
static_cast<sal_uInt16>(nPos) )->GetGradient() ));
         OUString aString = m_xGradientLB->GetItemText( 
m_xGradientLB->GetSelectedItemId() );
-        rSet->Put( XFillGradientItem( aString, *pXGradient ) );
+        rSet->Put( XFillGradientItem( aString, *pBGradient ) );
     }
     else
     // gradient was passed (unidentified)
     {
-        pXGradient.reset(new XGradient(
+        pBGradient.reset(new basegfx::BGradient(
                     createColorStops(),
                     
static_cast<css::awt::GradientStyle>(m_xLbGradientType->get_active()),
                     
Degree10(static_cast<sal_Int16>(m_xMtrAngle->get_value(FieldUnit::NONE) * 10)), 
// should be changed in resource
@@ -206,14 +205,14 @@ bool SvxGradientTabPage::FillItemSet( SfxItemSet* rSet )
                     
static_cast<sal_uInt16>(m_xMtrColorFrom->get_value(FieldUnit::NONE)),
                     
static_cast<sal_uInt16>(m_xMtrColorTo->get_value(FieldUnit::NONE)),
                     static_cast<sal_uInt16>(m_xMtrIncrement->get_value()) ));
-        rSet->Put( XFillGradientItem( OUString(), *pXGradient ) );
+        rSet->Put( XFillGradientItem( OUString(), *pBGradient ) );
     }
 
     sal_uInt16 nValue = 0;
     if (!m_xCbIncrement->get_active())
         nValue = m_xMtrIncrement->get_value();
 
-    assert( pXGradient && "XGradient could not be created" );
+    assert( pBGradient && "basegfx::BGradient could not be created" );
     rSet->Put( XFillStyleItem( drawing::FillStyle_GRADIENT ) );
     rSet->Put( XGradientStepCountItem( nValue ) );
     return true;
@@ -294,7 +293,7 @@ void SvxGradientTabPage::ModifiedHdl_Impl( void const * 
pControl )
 
     css::awt::GradientStyle eXGS = 
static_cast<css::awt::GradientStyle>(m_xLbGradientType->get_active());
 
-    XGradient aXGradient(
+    basegfx::BGradient aBGradient(
                           createColorStops(),
                           eXGS,
                           
Degree10(static_cast<sal_Int16>(m_xMtrAngle->get_value(FieldUnit::NONE) * 10)), 
// should be changed in resource
@@ -315,7 +314,7 @@ void SvxGradientTabPage::ModifiedHdl_Impl( void const * 
pControl )
     m_rXFSet.Put( XGradientStepCountItem( nValue ) );
 
     // displaying in XOutDev
-    m_rXFSet.Put( XFillGradientItem( OUString(), aXGradient ) );
+    m_rXFSet.Put( XFillGradientItem( OUString(), aBGradient ) );
     m_aCtlPreview.SetAttributes(m_aXFillAttr.GetItemSet());
     m_aCtlPreview.Invalidate();
 }
@@ -361,7 +360,7 @@ IMPL_LINK_NOARG(SvxGradientTabPage, ClickAddHdl_Impl, 
weld::Button&, void)
 
     if( !nError )
     {
-        XGradient aXGradient(
+        basegfx::BGradient aBGradient(
                               createColorStops(),
                               
static_cast<css::awt::GradientStyle>(m_xLbGradientType->get_active()),
                               
Degree10(static_cast<sal_Int16>(m_xMtrAngle->get_value(FieldUnit::NONE) * 10)), 
// should be changed in resource
@@ -372,7 +371,7 @@ IMPL_LINK_NOARG(SvxGradientTabPage, ClickAddHdl_Impl, 
weld::Button&, void)
                               
static_cast<sal_uInt16>(m_xMtrColorTo->get_value(FieldUnit::NONE)),
                               
static_cast<sal_uInt16>(m_xMtrIncrement->get_value()) );
 
-        m_pGradientList->Insert(std::make_unique<XGradientEntry>(aXGradient, 
aName), nCount);
+        m_pGradientList->Insert(std::make_unique<XGradientEntry>(aBGradient, 
aName), nCount);
 
         sal_Int32 nId = m_xGradientLB->GetItemId(nCount - 1); //calculate the 
last ID
         BitmapEx aBitmap = m_pGradientList->GetBitmapForPreview( nCount, 
m_xGradientLB->GetIconSize() );
@@ -401,7 +400,7 @@ IMPL_LINK_NOARG(SvxGradientTabPage, ClickModifyHdl_Impl, 
weld::Button&, void)
 
     OUString aName( m_pGradientList->GetGradient( 
static_cast<sal_uInt16>(nPos) )->GetName() );
 
-    XGradient aXGradient(
+    basegfx::BGradient aBGradient(
                           createColorStops(),
                           
static_cast<css::awt::GradientStyle>(m_xLbGradientType->get_active()),
                           
Degree10(static_cast<sal_Int16>(m_xMtrAngle->get_value(FieldUnit::NONE) * 10)), 
// should be changed in resource
@@ -412,7 +411,7 @@ IMPL_LINK_NOARG(SvxGradientTabPage, ClickModifyHdl_Impl, 
weld::Button&, void)
                           
static_cast<sal_uInt16>(m_xMtrColorTo->get_value(FieldUnit::NONE)),
                           
static_cast<sal_uInt16>(m_xMtrIncrement->get_value()) );
 
-    m_pGradientList->Replace(std::make_unique<XGradientEntry>(aXGradient, 
aName), nPos);
+    m_pGradientList->Replace(std::make_unique<XGradientEntry>(aBGradient, 
aName), nPos);
 
     BitmapEx aBitmap = m_pGradientList->GetBitmapForPreview( 
static_cast<sal_uInt16>(nPos), m_xGradientLB->GetIconSize() );
     m_xGradientLB->RemoveItem( nId );
@@ -498,11 +497,11 @@ IMPL_LINK_NOARG(SvxGradientTabPage, ChangeGradientHdl, 
ValueSet*, void)
 
 void SvxGradientTabPage::ChangeGradientHdl_Impl()
 {
-    std::unique_ptr<XGradient> pGradient;
+    std::unique_ptr<basegfx::BGradient> pGradient;
     size_t nPos = m_xGradientLB->GetSelectItemPos();
 
     if( nPos != VALUESET_ITEM_NOTFOUND )
-        pGradient.reset(new XGradient( m_pGradientList->GetGradient( 
static_cast<sal_uInt16>( nPos ) )->GetGradient() ));
+        pGradient.reset(new basegfx::BGradient( m_pGradientList->GetGradient( 
static_cast<sal_uInt16>( nPos ) )->GetGradient() ));
     else
     {
         if( const XFillStyleItem* pFillStyleItem = m_rOutAttrs.GetItemIfSet( 
GetWhich( XATTR_FILLSTYLE ) ) )
@@ -511,7 +510,7 @@ void SvxGradientTabPage::ChangeGradientHdl_Impl()
             if( ( drawing::FillStyle_GRADIENT == pFillStyleItem->GetValue() ) 
&&
                 ( pGradientItem = m_rOutAttrs.GetItemIfSet( GetWhich( 
XATTR_FILLGRADIENT ) ) ) )
             {
-                pGradient.reset(new XGradient( 
pGradientItem->GetGradientValue() ));
+                pGradient.reset(new basegfx::BGradient( 
pGradientItem->GetGradientValue() ));
             }
         }
         if( !pGradient )
@@ -519,7 +518,7 @@ void SvxGradientTabPage::ChangeGradientHdl_Impl()
             sal_uInt16 nPosition = m_xGradientLB->GetItemId(0);
             m_xGradientLB->SelectItem( nPosition );
             if( nPosition != 0 )
-                pGradient.reset(new XGradient( m_pGradientList->GetGradient( 0 
)->GetGradient() ));
+                pGradient.reset(new basegfx::BGradient( 
m_pGradientList->GetGradient( 0 )->GetGradient() ));
         }
     }
 
@@ -551,7 +550,7 @@ void SvxGradientTabPage::ChangeGradientHdl_Impl()
 
     // MCGR: preserve in-between ColorStops if given
     if (pGradient->GetColorStops().size() > 2)
-        m_aColorStops = basegfx::ColorStops(pGradient->GetColorStops().begin() 
+ 1, pGradient->GetColorStops().end() - 1);
+        m_aColorStops = 
basegfx::BColorStops(pGradient->GetColorStops().begin() + 1, 
pGradient->GetColorStops().end() - 1);
     else
         m_aColorStops.clear();
 
@@ -638,9 +637,9 @@ sal_Int32 
SvxGradientTabPage::SearchGradientList(std::u16string_view rGradientNa
     return nPos;
 }
 
-basegfx::ColorStops SvxGradientTabPage::createColorStops()
+basegfx::BColorStops SvxGradientTabPage::createColorStops()
 {
-    basegfx::ColorStops aColorStops;
+    basegfx::BColorStops aColorStops;
 
     aColorStops.emplace_back(0.0, 
m_xLbColorFrom->GetSelectEntryColor().getBColor());
 
diff --git a/cui/source/tabpages/tptrans.cxx b/cui/source/tabpages/tptrans.cxx
index ef604e6470d5..ce77d61d2fc7 100644
--- a/cui/source/tabpages/tptrans.cxx
+++ b/cui/source/tabpages/tptrans.cxx
@@ -119,7 +119,7 @@ void SvxTransparenceTabPage::ModifiedTrgrHdl_Impl(const 
weld::ComboBox* pControl
     }
 
     // preview
-    XGradient aTmpGradient(
+    basegfx::BGradient aTmpGradient(
                 createColorStops(),
                 
static_cast<css::awt::GradientStyle>(m_xLbTrgrGradientType->get_active()),
                 
Degree10(static_cast<sal_Int16>(m_xMtrTrgrAngle->get_value(FieldUnit::DEGREE)) 
* 10),
@@ -287,7 +287,7 @@ bool SvxTransparenceTabPage::FillItemSet(SfxItemSet* rAttrs)
             || m_xMtrTrgrStartValue->get_value_changed_from_saved()
             || m_xMtrTrgrEndValue->get_value_changed_from_saved())
         {
-            XGradient aTmpGradient(
+            basegfx::BGradient aTmpGradient(
                         createColorStops(),
                         
static_cast<css::awt::GradientStyle>(m_xLbTrgrGradientType->get_active()),
                         
Degree10(static_cast<sal_Int16>(m_xMtrTrgrAngle->get_value(FieldUnit::DEGREE)) 
* 10),
@@ -317,9 +317,9 @@ bool SvxTransparenceTabPage::FillItemSet(SfxItemSet* rAttrs)
     // disable unused XFillFloatTransparenceItem
     if(bSwitchOffGradient && (bGradActive || bGradUsed))
     {
-        // XGradient() default already creates [COL_BLACK, COL_WHITE] with 
same defaults
-        // XGradient() default also sets the Start/EndIntensity to 100 already
-        XGradient aGrad;
+        // basegfx::BGradient() default already creates [COL_BLACK, COL_WHITE] 
with same defaults
+        // basegfx::BGradient() default also sets the Start/EndIntensity to 
100 already
+        basegfx::BGradient aGrad;
 
         XFillFloatTransparenceItem aItem(aGrad);
         aItem.SetEnabled(false);
@@ -355,7 +355,7 @@ void SvxTransparenceTabPage::Reset(const SfxItemSet* rAttrs)
         pLinearItem = &rAttrs->Get(XATTR_FILLTRANSPARENCE);
 
     // transparence gradient
-    const XGradient& rGradient = pGradientItem->GetGradientValue();
+    const basegfx::BGradient& rGradient = pGradientItem->GetGradientValue();
     css::awt::GradientStyle eXGS(rGradient.GetGradientStyle());
     m_xLbTrgrGradientType->set_active(sal::static_int_cast< sal_Int32 >(eXGS));
     m_xMtrTrgrAngle->set_value(rGradient.GetAngle().get() / 10, 
FieldUnit::DEGREE);
@@ -369,7 +369,7 @@ void SvxTransparenceTabPage::Reset(const SfxItemSet* rAttrs)
 
     // MCGR: preserve in-between ColorStops if given
     if (rGradient.GetColorStops().size() > 2)
-        maColorStops = basegfx::ColorStops(rGradient.GetColorStops().begin() + 
1, rGradient.GetColorStops().end() - 1);
+        maColorStops = basegfx::BColorStops(rGradient.GetColorStops().begin() 
+ 1, rGradient.GetColorStops().end() - 1);
     else
         maColorStops.clear();
 
@@ -508,9 +508,9 @@ void SvxTransparenceTabPage::InvalidatePreview (bool 
bEnable)
     }
 }
 
-basegfx::ColorStops SvxTransparenceTabPage::createColorStops()
+basegfx::BColorStops SvxTransparenceTabPage::createColorStops()
 {
-    basegfx::ColorStops aColorStops;
+    basegfx::BColorStops aColorStops;
     const sal_uInt8 
nStartCol(static_cast<sal_uInt8>((static_cast<sal_uInt16>(m_xMtrTrgrStartValue->get_value(FieldUnit::PERCENT))
 * 255) / 100));
     const sal_uInt8 
nEndCol(static_cast<sal_uInt8>((static_cast<sal_uInt16>(m_xMtrTrgrEndValue->get_value(FieldUnit::PERCENT))
 * 255) / 100));
 
diff --git a/drawinglayer/inc/texture/texture.hxx 
b/drawinglayer/inc/texture/texture.hxx
index 01d3ec5c64c5..5128a30cf2a8 100644
--- a/drawinglayer/inc/texture/texture.hxx
+++ b/drawinglayer/inc/texture/texture.hxx
@@ -49,21 +49,18 @@ namespace drawinglayer::texture
             basegfx::ODFGradientInfo            maGradientInfo;
             basegfx::B2DRange                   maDefinitionRange;
             sal_uInt32                          mnRequestedSteps;
-            basegfx::ColorStops                 mnColorStops;
+            basegfx::BColorStops                mnColorStops;
             double                              mfBorder;
 
             // provide a single buffer entry used for gradient texture
             // mapping, see ::modifyBColor implementations
-            mutable basegfx::ColorStopRange     maLastColorStopRange;
-
-            // check if we need last-ColorStop-correction
-            bool checkPenultimate();
+            mutable basegfx::BColorStops::BColorStopRange     
maLastColorStopRange;
 
         public:
             GeoTexSvxGradient(
                 const basegfx::B2DRange& rDefinitionRange,
                 sal_uInt32 nRequestedSteps,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 double fBorder);
             virtual ~GeoTexSvxGradient() override;
 
@@ -86,7 +83,7 @@ namespace drawinglayer::texture
                 const basegfx::B2DRange& rDefinitionRange,
                 const basegfx::B2DRange& rOutputRange,
                 sal_uInt32 nRequestedSteps,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 double fBorder,
                 double fAngle);
             virtual ~GeoTexSvxGradientLinear() override;
@@ -106,7 +103,7 @@ namespace drawinglayer::texture
                 const basegfx::B2DRange& rDefinitionRange,
                 const basegfx::B2DRange& rOutputRange,
                 sal_uInt32 nRequestedSteps,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 double fBorder,
                 double fAngle);
             virtual ~GeoTexSvxGradientAxial() override;
@@ -122,7 +119,7 @@ namespace drawinglayer::texture
             GeoTexSvxGradientRadial(
                 const basegfx::B2DRange& rDefinitionRange,
                 sal_uInt32 nRequestedSteps,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 double fBorder,
                 double fOffsetX,
                 double fOffsetY);
@@ -139,7 +136,7 @@ namespace drawinglayer::texture
             GeoTexSvxGradientElliptical(
                 const basegfx::B2DRange& rDefinitionRange,
                 sal_uInt32 nRequestedSteps,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 double fBorder,
                 double fOffsetX,
                 double fOffsetY,
@@ -157,7 +154,7 @@ namespace drawinglayer::texture
             GeoTexSvxGradientSquare(
                 const basegfx::B2DRange& rDefinitionRange,
                 sal_uInt32 nRequestedSteps,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 double fBorder,
                 double fOffsetX,
                 double fOffsetY,
@@ -175,7 +172,7 @@ namespace drawinglayer::texture
             GeoTexSvxGradientRect(
                 const basegfx::B2DRange& rDefinitionRange,
                 sal_uInt32 nRequestedSteps,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 double fBorder,
                 double fOffsetX,
                 double fOffsetY,
diff --git a/drawinglayer/qa/unit/vclpixelprocessor2d.cxx 
b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx
index 6ccf1e7c55ab..7f59e7e3edd0 100644
--- a/drawinglayer/qa/unit/vclpixelprocessor2d.cxx
+++ b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx
@@ -16,8 +16,10 @@
 #include <tools/stream.hxx>
 #include <drawinglayer/geometry/viewinformation2d.hxx>
 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
 #include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
 #include <basegfx/utils/gradienttools.hxx>
 
 using namespace drawinglayer;
@@ -58,16 +60,59 @@ public:
             processor2d::createProcessor2DFromOutputDevice(*device, view));
         CPPUNIT_ASSERT(processor);
 
+        // I stumbled over this when hunting another problem, but I have to 
correct
+        // this: This test does test something that is not supported. It seems 
to be
+        // based on the *misunderstanding* that in the version of the 
constructor of
+        // FillGradientPrimitive2D (and similar others) with two ranges the 2nd
+        // B2DRange parameter 'OutputRange' is a 'clipping' parameter. This is 
*not*
+        // the case --- it is in fact the *contrary*, it is there to *extend* 
the
+        // usual definition/paintRange of a gradient:
+        // It was originally needed to correctly display TextFrames (TF) in 
Writer: If you
+        // have a TF in SW filled with a gradient and that TF has sub-frames, 
it inhertits
+        // the gradient fill. Since you can freely move those sub-TFs even 
outside the
+        // parent TF there has to be a way to not only paint gradients in ther 
definition
+        // range (classical, all DrawObjects do that), but extended from that. 
This is
+        // needed e.g. for linerar gradients, but - dependent of e.g. the 
center settings -
+        // also for all other ones, all can have geometry 'outside' the 
DefinitionRange.
+        // This is now also used in various other locations which is proof 
that this is
+        // useful and needed. It is possible to see that basic history/reason 
for this
+        // parameter by following the git history and why and under which 
circumstances
+        // that parameter was originally added. Other hints are: It is *not* 
named
+        // 'ClipRange'. Using a B2DRange to define a ClipRange topology would 
be bad due
+        // to not being transformable, a PolyPolygon would be used in that 
case. Using as
+        // clipping mechanism would offer a 2nd pinciple to add clipping for 
primitives
+        // besides MaskPrimitive2D - always bad style in a sub-system. A quick 
look
+        // on it's usages gives hints, too.
+        // This means that when defining a outputRange tat resides completely 
*inside*
+        // the definitionRange *no change* at all is done by definition since 
this does
+        // not *extend* the target area of the gradient paint region at all. 
If an
+        // implementation does clip and limit output to 'outputRange' that 
should do no
+        // harm, but is not the expected/reliable way to paint primitives 
clipped.
+        // That's why all DrawObjects with gradient fill (and other fills do 
the same)
+        // embed the fill that is defined for a range (usually the BoundRange 
of a
+        // PolyPolygon) in a MaskPrimitive2D defined by the outline 
PolyPolygon of the
+        // shape. Nothing speaks against renderers detecting that combination 
and do
+        // something optimized if they want to, especially SDPRs, but this is 
not
+        // required. The standard embedded clipping of the mplementations of 
the
+        // MaskPrimitive2D do the right thing.
+        // This test intends to paint the lower part of a gradient, so define 
the
+        // gradient for the full target range and embed it to a MaskPrimitive2D
+        // defining the lower part of that area to do that.
+
         basegfx::B2DRange definitionRange(0, 0, 100, 200);
         basegfx::B2DRange outputRange(0, 100, 100, 200); // Paint only lower 
half of the gradient.
-        attribute::FillGradientAttribute attributes(
-            css::awt::GradientStyle_LINEAR, 0, 0, 0, 0,
-            
basegfx::utils::createColorStopsFromStartEndColor(COL_WHITE.getBColor(),
-                                                              
COL_BLACK.getBColor()));
-        rtl::Reference<primitive2d::FillGradientPrimitive2D> gradientPrimitive(
-            new primitive2d::FillGradientPrimitive2D(outputRange, 
definitionRange, attributes));
-        primitive2d::Primitive2DContainer primitives;
-        
primitives.push_back(primitive2d::Primitive2DReference(gradientPrimitive));
+
+        const primitive2d::Primitive2DContainer primitives{
+            rtl::Reference<primitive2d::MaskPrimitive2D>(new 
primitive2d::MaskPrimitive2D(
+                
basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(outputRange)),
+                primitive2d::Primitive2DContainer{
+                    rtl::Reference<primitive2d::FillGradientPrimitive2D>(
+                        new primitive2d::FillGradientPrimitive2D(
+                            definitionRange, attribute::FillGradientAttribute(
+                                                 
css::awt::GradientStyle_LINEAR, 0, 0, 0, 0,
+                                                 
basegfx::BColorStops(COL_WHITE.getBColor(),
+                                                                      
COL_BLACK.getBColor())))) }))
+        };
         processor->process(primitives);
 
         exportDevice("test-tdf139000.png", device);
diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx 
b/drawinglayer/source/attribute/fillgradientattribute.cxx
index 0233195113af..e02fdd4a5dad 100644
--- a/drawinglayer/source/attribute/fillgradientattribute.cxx
+++ b/drawinglayer/source/attribute/fillgradientattribute.cxx
@@ -30,7 +30,7 @@ namespace drawinglayer::attribute
             double                                  mfOffsetX;
             double                                  mfOffsetY;
             double                                  mfAngle;
-            basegfx::ColorStops                     maColorStops;
+            basegfx::BColorStops                    maColorStops;
             css::awt::GradientStyle                 meStyle;
             sal_uInt16                              mnSteps;
 
@@ -40,7 +40,7 @@ namespace drawinglayer::attribute
                 double fOffsetX,
                 double fOffsetY,
                 double fAngle,
-                const basegfx::ColorStops& rColorStops,
+                const basegfx::BColorStops& rColorStops,
                 sal_uInt16 nSteps)
             :   mfBorder(fBorder),
                 mfOffsetX(fOffsetX),
@@ -57,7 +57,7 @@ namespace drawinglayer::attribute
                 // 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::sortAndCorrectColorStops(maColorStops);
+                maColorStops.sortAndCorrect();
 
                 // sortAndCorrectColorStops is rigid and can return
                 // an empty result. To keep things simple, add a single
@@ -87,18 +87,9 @@ namespace drawinglayer::attribute
             double getOffsetX() const { return mfOffsetX; }
             double getOffsetY() const { return mfOffsetY; }
             double getAngle() const { return mfAngle; }
-            const basegfx::ColorStops& getColorStops() const { return 
maColorStops; }
+            const basegfx::BColorStops& getColorStops() const { return 
maColorStops; }
             sal_uInt16 getSteps() const { return mnSteps; }
 
-            bool hasSingleColor() const
-            {
-                // No entry (should not happen, see comments for startColor 
above)
-                // or single entry -> no gradient.
-                // No need to check for all-the-same color since this is 
checked/done
-                // in the constructor already, see there
-                return maColorStops.size() < 2;
-            }
-
             bool operator==(const ImpFillGradientAttribute& rCandidate) const
             {
                 return (getStyle() == rCandidate.getStyle()
@@ -126,7 +117,7 @@ namespace drawinglayer::attribute
             double fOffsetX,
             double fOffsetY,
             double fAngle,
-            const basegfx::ColorStops& rColorStops,
+            const basegfx::BColorStops& rColorStops,
             sal_uInt16 nSteps)
         :   mpFillGradientAttribute(ImpFillGradientAttribute(
                 eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rColorStops, 
nSteps))
@@ -149,11 +140,6 @@ namespace drawinglayer::attribute
             return mpFillGradientAttribute.same_object(theGlobalDefault());
         }
 
-        bool FillGradientAttribute::hasSingleColor() const
-        {
-            return mpFillGradientAttribute->hasSingleColor();
-        }
-
         // MCGR: Check if rendering cannot be handled by old vcl stuff
         bool FillGradientAttribute::cannotBeHandledByVCL() const
         {
@@ -202,7 +188,7 @@ namespace drawinglayer::attribute
             return rCandidate.mpFillGradientAttribute == 
mpFillGradientAttribute;
         }
 
-        const basegfx::ColorStops& FillGradientAttribute::getColorStops() const
+        const basegfx::BColorStops& FillGradientAttribute::getColorStops() 
const
         {
             return mpFillGradientAttribute->getColorStops();
         }
diff --git a/drawinglayer/source/primitive3d/textureprimitive3d.cxx 
b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
index ebae584e9dbf..ceeca0489487 100644
--- a/drawinglayer/source/primitive3d/textureprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
@@ -95,9 +95,9 @@ namespace drawinglayer::primitive3d
                 const basegfx::BColor aGray(getTransparence(), 
getTransparence(), getTransparence());
 
                 // create ColorStops with StartColor == EndColor == aGray
-                const basegfx::ColorStops aColorStops {
-                    basegfx::ColorStop(0.0, aGray),
-                    basegfx::ColorStop(1.0, aGray) };
+                const basegfx::BColorStops aColorStops {
+                    basegfx::BColorStop(0.0, aGray),
+                    basegfx::BColorStop(1.0, aGray) };
 
                 const attribute::FillGradientAttribute 
aFillGradient(css::awt::GradientStyle_LINEAR, 0.0, 0.0, 0.0, 0.0, aColorStops);
                 const Primitive3DReference xRef(new 
TransparenceTexturePrimitive3D(aFillGradient, getChildren(), getTextureSize()));
diff --git a/drawinglayer/source/processor3d/defaultprocessor3d.cxx 
b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
index 20d81871dbd7..0d07b0a5c343 100644
--- a/drawinglayer/source/processor3d/defaultprocessor3d.cxx
+++ b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
@@ -61,8 +61,9 @@ namespace drawinglayer::processor3d
             const basegfx::B2DRange aOutlineRange(0.0, 0.0, 
rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY());
             const css::awt::GradientStyle 
aGradientStyle(rFillGradient.getStyle());
             std::shared_ptr< texture::GeoTexSvx > pNewTex;
+            basegfx::BColor aSingleColor;
 
-            if(!rFillGradient.hasSingleColor())
+            if (!rFillGradient.getColorStops().isSingleColor(aSingleColor))
             {
                 switch(aGradientStyle)
                 {
@@ -147,8 +148,7 @@ namespace drawinglayer::processor3d
             else
             {
                 // only one color, so no real gradient -> use simple texture
-                const basegfx::BColor 
aStart(rFillGradient.getColorStops().front().getStopColor());
-                pNewTex = std::make_shared<texture::GeoTexSvxMono>(aStart, 1.0 
- aStart.luminance());
+                pNewTex = 
std::make_shared<texture::GeoTexSvxMono>(aSingleColor, 1.0 - 
aSingleColor.luminance());
                 mbSimpleTextureActive = true;
             }
 
diff --git a/drawinglayer/source/texture/texture.cxx 
b/drawinglayer/source/texture/texture.cxx
index eb9df6469225..5176838d610e 100644
--- a/drawinglayer/source/texture/texture.cxx
+++ b/drawinglayer/source/texture/texture.cxx
@@ -73,7 +73,7 @@ namespace drawinglayer::texture
         GeoTexSvxGradient::GeoTexSvxGradient(
             const basegfx::B2DRange& rDefinitionRange,
             sal_uInt32 nRequestedSteps,
-            const basegfx::ColorStops& rColorStops,
+            const basegfx::BColorStops& rColorStops,
             double fBorder)
         : maDefinitionRange(rDefinitionRange)
         , mnRequestedSteps(nRequestedSteps)
@@ -99,47 +99,11 @@ namespace drawinglayer::texture
                 && mfBorder == pCompare->mfBorder);
         }
 
-        bool GeoTexSvxGradient::checkPenultimate()
-        {
-            // not needed when no ColorStops
-            if (mnColorStops.empty())
-                return false;
-
-            // not needed when last ColorStop at the end or outside
-            if 
(basegfx::fTools::moreOrEqual(mnColorStops.back().getStopOffset(), 1.0))
-                return false;
-
-            // get penultimate entry
-            const auto penultimate(mnColorStops.rbegin() + 1);
-
-            // if there is none, we need no correction and are done
-            if (penultimate == mnColorStops.rend())
-                return false;
-
-            // not needed when the last two ColorStops have different offset, 
then
-            // a visible range will be processed already
-            if (!basegfx::fTools::equal(mnColorStops.back().getStopOffset(), 
penultimate->getStopOffset()))
-                return false;
-
-            // not needed when the last two ColorStops have the same Color, 
then the
-            // range before solves the problem
-            if (mnColorStops.back().getStopColor() == 
penultimate->getStopColor())
-                return false;
-
-            // Here we need to temporarily add a ColorStop entry with the
-            // same color as the last entry to correctly 'close' the
-            // created gradient geometry.
-            // The simplest way is to temporarily add an entry to the local
-            // ColorStops for this at 1.0 (using same color)
-            mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
-            return true;
-        }
-
         GeoTexSvxGradientLinear::GeoTexSvxGradientLinear(
             const basegfx::B2DRange& rDefinitionRange,
             const basegfx::B2DRange& rOutputRange,
             sal_uInt32 nRequestedSteps,
-            const basegfx::ColorStops& rColorStops,
+            const basegfx::BColorStops& rColorStops,
             double fBorder,
             double fAngle)
         : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, 
fBorder)
@@ -180,7 +144,17 @@ namespace drawinglayer::texture
                 return;
 
             // check if we need last-ColorStop-correction
-            const bool bPenultimateUsed(checkPenultimate());
+            const bool bPenultimateUsed(mnColorStops.checkPenultimate());
+
+            if (bPenultimateUsed)
+            {
+                // Here we need to temporarily add a ColorStop entry with the
+                // same color as the last entry to correctly 'close' the
+                // created gradient geometry.
+                // The simplest way is to temporarily add an entry to the local
+                // ColorStops for this at 1.0 (using same color)
+                mnColorStops.emplace_back(1.0, 
mnColorStops.back().getStopColor());
+            }
 
             // prepare unit range transform
             basegfx::B2DHomMatrix aPattern;
@@ -200,7 +174,7 @@ namespace drawinglayer::texture
                 const double fOffsetStart(cs_l->getStopOffset());
                 const double fOffsetEnd(cs_r->getStopOffset());
 
-                // same offset, empty ColorStopRange, continue with next step
+                // same offset, empty BColorStopRange, continue with next step
                 if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
                     continue;
 
@@ -249,7 +223,10 @@ namespace drawinglayer::texture
             }
 
             if (bPenultimateUsed)
+            {
+                // correct temporary change
                 mnColorStops.pop_back();
+            }
         }
 
         void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint& 
rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
@@ -267,14 +244,14 @@ namespace drawinglayer::texture
 
             // texture-back-transform X/Y -> t [0.0..1.0] and determine color
             const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, 
maGradientInfo));
-            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps, maLastColorStopRange);
+            rBColor = mnColorStops.getInterpolatedBColor(fScaler, 
mnRequestedSteps, maLastColorStopRange);
         }
 
         GeoTexSvxGradientAxial::GeoTexSvxGradientAxial(
             const basegfx::B2DRange& rDefinitionRange,
             const basegfx::B2DRange& rOutputRange,
             sal_uInt32 nRequestedSteps,
-            const basegfx::ColorStops& rColorStops,

... etc. - the rest is truncated

Reply via email to