basegfx/source/tools/bgradient.cxx      |  206 +++++++++++++++++++++++++++++++-
 basegfx/source/tools/gradienttools.cxx  |   97 ++++-----------
 include/basegfx/utils/bgradient.hxx     |   29 +++-
 include/basegfx/utils/gradienttools.hxx |   11 +
 oox/source/drawingml/fillproperties.cxx |   98 ++++++---------
 5 files changed, 306 insertions(+), 135 deletions(-)

New commits:
commit c2bea1bedd2ee8bc4007fd23c6cb839a692297a7
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Wed May 24 12:31:48 2023 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Fri May 26 10:08:52 2023 +0200

    MCGR: Border restoration support
    
    Due to tdf#155362 I added code to be able in case we
    would need it to convert a BGradient using added
    tooling from having offsets in the GradientSteps
    and no border to adapted GradientSteps and border.
    
    This is preferrable due to our GradientStyle_RECT
    (and GradientStyle_ELLIPTICAL too) use that 'non-
    linear' paint aka move-two-pixels-inside, someone
    else called it 'frame-paint'. This does not bode
    well with the border being applied 'linear' at the
    same time (argh). Thus - while in principle all is
    correct when re-importing a GradientStyle_RECT
    with a border after export to pptx, it looks
    slightly better ('correcter') wen doing so. That is
    because when being able to and restoring a border
    at least that border part *is* applied linearly.
    
    I took the chance to further apply tooling, move
    it to classes involved and instead of awt::Gradient2
    use more basegfx::BGradient since it can and does
    host tooling. This is also a preparation to be
    able to adapt (restore) border in case of turn-
    around in ODF where the importing instance is before
    MCGR existance and has to handle Start/EndColor.
    
    Needed to take more care with using BGradient instead
    of awt::Gradient2 for initialization, also better
    dividing/organization of tooling, already preparation
    to use for other purposes.
    
    Change-Id: I2d3a4240a5ac6fff9211b46642ee80366dc09e3d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152194
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/basegfx/source/tools/bgradient.cxx 
b/basegfx/source/tools/bgradient.cxx
index 52aa721f36a6..b7ee0780a2cc 100644
--- a/basegfx/source/tools/bgradient.cxx
+++ b/basegfx/source/tools/bgradient.cxx
@@ -563,6 +563,100 @@ void BColorStops::reverseColorStops()
         candidate = BColorStop(1.0 - candidate.getStopOffset(), 
candidate.getStopColor());
 }
 
+// createSpaceAtStart creates fOffset space at start by
+// translating/scaling all entries to the right
+void BColorStops::createSpaceAtStart(double fOffset)
+{
+    // nothing to do if empty
+    if (empty())
+        return;
+
+    // correct offset to [0.0 .. 1.0]
+    fOffset = std::max(std::min(1.0, fOffset), 0.0);
+
+    // nothing to do if 0.0 == offset
+    if (basegfx::fTools::equalZero(fOffset))
+        return;
+
+    BColorStops aNewStops;
+
+    for (const auto& candidate : *this)
+    {
+        aNewStops.emplace_back(fOffset + (candidate.getStopOffset() * (1.0 - 
fOffset)),
+                               candidate.getStopColor());
+    }
+
+    *this = aNewStops;
+}
+
+// removeSpaceAtStart removes fOffset space from start by
+// translating/scaling entries more or equal to fOffset
+// to the left. Entries less than fOffset will be removed
+void BColorStops::removeSpaceAtStart(double fOffset)
+{
+    // nothing to do if empty
+    if (empty())
+        return;
+
+    // correct factor to [0.0 .. 1.0]
+    fOffset = std::max(std::min(1.0, fOffset), 0.0);
+
+    // nothing to do if fOffset == 0.0
+    if (basegfx::fTools::equalZero(fOffset))
+        return;
+
+    BColorStops aNewStops;
+    const double fMul(basegfx::fTools::equal(fOffset, 1.0) ? 1.0 : 1.0 / (1.0 
- fOffset));
+
+    for (const auto& candidate : *this)
+    {
+        if (basegfx::fTools::moreOrEqual(candidate.getStopOffset(), fOffset))
+        {
+            aNewStops.emplace_back((candidate.getStopOffset() - fOffset) * 
fMul,
+                                   candidate.getStopColor());
+        }
+    }
+
+    *this = aNewStops;
+}
+
+// try to detect if an empty/no-color-change area exists
+// at the start and return offset to it. Returns 0.0 if not.
+double BColorStops::detectPossibleOffsetAtStart() const
+{
+    BColor aSingleColor;
+    const bool bSingleColor(isSingleColor(aSingleColor));
+
+    // no useful offset for single color
+    if (bSingleColor)
+        return 0.0;
+
+    // here we know that we have at least two colors, so we have a
+    // color change. Find colors left and right of that first color change
+    BColorStops::const_iterator aColorR(begin());
+    BColorStops::const_iterator aColorL(aColorR++);
+
+    // aColorR would 1st get equal to end(), so no need to also check aColorL
+    // for end(). Loop as long as same color. Since we *have* a color change
+    // not even aColorR can get equal to end() before color inequality, but
+    // keep for safety
+    while (aColorR != end() && aColorL->getStopColor() == 
aColorR->getStopColor())
+    {
+        aColorL++;
+        aColorR++;
+    }
+
+    // also for safety: access values at aColorL below *only*
+    // if not equal to end(), but can theoretically not happen
+    if (aColorL == end())
+    {
+        return 0.0;
+    }
+
+    // return offset (maybe 0.0 what is OK)
+    return aColorL->getStopOffset();
+}
+
 std::string BGradient::GradientStyleToString(css::awt::GradientStyle eStyle)
 {
     switch (eStyle)
@@ -757,7 +851,24 @@ css::awt::Gradient2 BGradient::getAsGradient2() const
     aRetval.StepCount = GetSteps();
 
     // for compatibility, still set StartColor/EndColor
-    // const basegfx::BColorStops& rColorStops(GetColorStops());
+    // NOTE: All code after adapting to multi color gradients works
+    //       using the ColorSteps, so in principle Start/EndColor might
+    //       be either
+    //        (a) ignored consequently everywhere or
+    //        (b) be set/added consequently everywhere
+    //       since this is - in principle - redundant data.
+    //       Be aware that e.g. cases like DrawingML::EqualGradients
+    //       and others would have to be identified and adapted (!)
+    //       Since awt::Gradient2 is UNO API data there might
+    //       be cases where just awt::Gradient is transferred, so (b)
+    //       is far better backwards compatible and thus more safe, so
+    //       all changes will make use of additionally using/setting
+    //       these additionally, but will only make use of the given
+    //       ColorSteps if these are not empty, assuming that these
+    //       already contain Start/EndColor.
+    //       In principle that redundancy and that it is conflict-free
+    //       could even be checked and asserted, but consequently using
+    //       (b) methodically should be safe.
     aRetval.StartColor
         = 
static_cast<sal_Int32>(ColorToBColorConverter(aColorStops.front().getStopColor()));
     aRetval.EndColor
@@ -765,10 +876,101 @@ css::awt::Gradient2 BGradient::getAsGradient2() const
 
     // fill ColorStops to extended Gradient2
     aRetval.ColorStops = aColorStops.getAsColorStopSequence();
-    // fillColorStopSequenceFromColorStops(rGradient2.ColorStops, rColorStops);
 
     return aRetval;
 }
+
+void BGradient::tryToRecreateBorder(basegfx::BColorStops* 
pAssociatedTransparencyStops)
+{
+    // border already set, do not try to recreate
+    if (0 != GetBorder())
+        return;
+
+    BColor aSingleColor;
+    const bool bSingleColor(GetColorStops().isSingleColor(aSingleColor));
+
+    // no need to recreate with single color
+    if (bSingleColor)
+        return;
+
+    const bool bIsAxial(css::awt::GradientStyle_AXIAL == GetGradientStyle());
+
+    if (bIsAxial)
+    {
+        // for axial due to reverse used gradient work reversed
+        aColorStops.reverseColorStops();
+        if (nullptr != pAssociatedTransparencyStops)
+            pAssociatedTransparencyStops->reverseColorStops();
+    }
+
+    // check if we have space at start of range [0.0 .. 1.0] that
+    // may be interpreted as 'border' -> same color. That may involve
+    // different scenarios, e.g. 1st index > 0.0, but also a non-zero
+    // number of same color entries, or a combination of both
+    const double fOffset(aColorStops.detectPossibleOffsetAtStart());
+
+    if (!basegfx::fTools::equalZero(fOffset))
+    {
+        // we have a border area, indeed re-create
+        aColorStops.removeSpaceAtStart(fOffset);
+        if (nullptr != pAssociatedTransparencyStops)
+            pAssociatedTransparencyStops->removeSpaceAtStart(fOffset);
+
+        // ...and create border value
+        SetBorder(static_cast<sal_uInt16>(fOffset * 100.0));
+    }
+
+    if (bIsAxial)
+    {
+        // take back reverse
+        aColorStops.reverseColorStops();
+        if (nullptr != pAssociatedTransparencyStops)
+            pAssociatedTransparencyStops->reverseColorStops();
+    }
+}
+
+void BGradient::tryToApplyBorder()
+{
+    // no border to apply, done
+    if (0 == GetBorder())
+        return;
+
+    // NOTE: no new start node is added. The new ColorStop
+    //       mechanism does not need entries at 0.0 and 1.0.
+    //       In case this is needed, do that in the caller
+    const double fOffset(GetBorder() * 0.01);
+
+    if (css::awt::GradientStyle_AXIAL == GetGradientStyle())
+    {
+        // for axial due to reverse used gradient work reversed
+        aColorStops.reverseColorStops();
+        aColorStops.createSpaceAtStart(fOffset);
+        aColorStops.reverseColorStops();
+    }
+    else
+    {
+        // apply border to GradientSteps
+        aColorStops.createSpaceAtStart(fOffset);
+    }
+
+    // set changed values
+    SetBorder(0);
+}
+
+void BGradient::tryToApplyStartEndIntensity()
+{
+    // already on default, nothing to apply
+    if (100 == GetStartIntens() && 100 == GetEndIntens())
+        return;
+
+    // apply 'old' blend stuff, blend against black
+    aColorStops.blendToIntensity(GetStartIntens() * 0.01, GetEndIntens() * 
0.01,
+                                 BColor()); // COL_BLACK
+
+    // set values to default
+    SetStartIntens(100);
+    SetEndIntens(100);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basegfx/source/tools/gradienttools.cxx 
b/basegfx/source/tools/gradienttools.cxx
index d7b1bb167613..366e0b0840b8 100644
--- a/basegfx/source/tools/gradienttools.cxx
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -265,28 +265,15 @@ namespace basegfx
 
     namespace utils
     {
-        /* Tooling method to extract data from given awt::Gradient2
+        /* Tooling method to extract data from given BGradient
            to ColorStops, doing some corrections, partially based
-           on given SingleColor.
-           This will do quite some preparations for the gradient
-           as follows:
-           - It will check for single color (resetting rSingleColor when
-             this is the case) and return with empty ColorStops
-           - It will blend ColorStops to Intensity if StartIntensity/
-             EndIntensity != 100 is set in awt::Gradient2, so applying
-             that value(s) to the gradient directly
-           - It will adapt to Border if Border != 0 is set at the
-             given awt::Gradient2, so applying that value to the gradient
-             directly
-        */
+           on given SingleColor */
         void prepareColorStops(
             const basegfx::BGradient& rGradient,
             BColorStops& rColorStops,
             BColor& rSingleColor)
         {
-            rColorStops = rGradient.GetColorStops();
-
-            if (rColorStops.isSingleColor(rSingleColor))
+            if (rGradient.GetColorStops().isSingleColor(rSingleColor))
             {
                 // when single color, preserve value in rSingleColor
                 // and clear the ColorStops, done.
@@ -294,73 +281,45 @@ namespace basegfx
                 return;
             }
 
-            if (100 != rGradient.GetStartIntens() || 100 != 
rGradient.GetEndIntens())
+            const bool bAdaptStartEndIntensity(100 != 
rGradient.GetStartIntens() || 100 != rGradient.GetEndIntens());
+            const bool bAdaptBorder(0 != rGradient.GetBorder());
+
+            if (!bAdaptStartEndIntensity && !bAdaptBorder)
             {
-                // apply 'old' blend stuff, blend against black
-                rColorStops.blendToIntensity(
-                    rGradient.GetStartIntens() * 0.01,
-                    rGradient.GetEndIntens() * 0.01,
-                    basegfx::BColor()); // COL_BLACK
-
-                // can lead to single color (e.g. both zero, so all black),
-                // so check again
-                if (rColorStops.isSingleColor(rSingleColor))
+                // copy unchanged ColorStops & done
+                rColorStops = rGradient.GetColorStops();
+                return;
+            }
+
+            // prepare a copy to work on
+            basegfx::BGradient aWorkCopy(rGradient);
+
+            if (bAdaptStartEndIntensity)
+            {
+                aWorkCopy.tryToApplyStartEndIntensity();
+
+                // this can again lead to single color (e.g. both zero, so
+                // all black), so check again for it
+                if (aWorkCopy.GetColorStops().isSingleColor(rSingleColor))
                 {
                     rColorStops.clear();
                     return;
                 }
             }
 
-            if (0 != rGradient.GetBorder())
+            if (bAdaptBorder)
             {
-                // apply Border if set
-                // NOTE: no new start node is added. The new ColorStop
-                //       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.GetBorder() * 0.01);
-                BColorStops aNewStops;
-
-                for (const auto& candidate : rColorStops)
-                {
-                    if (css::awt::GradientStyle_AXIAL == 
rGradient.GetGradientStyle())
-                    {
-                        // for axial add the 'gap' at the start due to reverse 
used gradient
-                        aNewStops.emplace_back((1.0 - fFactor) * 
candidate.getStopOffset(), candidate.getStopColor());
-                    }
-                    else
-                    {
-                        // css::awt::GradientStyle_LINEAR
-                        // case awt::GradientStyle_RADIAL
-                        // case awt::GradientStyle_ELLIPTICAL
-                        // case awt::GradientStyle_RECT
-                        // case awt::GradientStyle_SQUARE
-
-                        // for all others add the 'gap' at the end
-                        aNewStops.emplace_back(fFactor + 
(candidate.getStopOffset() * (1.0 - fFactor)), candidate.getStopColor());
-                    }
-                }
-
-                rColorStops = aNewStops;
+                aWorkCopy.tryToApplyBorder();
             }
+
+            // extract ColorStops, that's all we need here
+            rColorStops = aWorkCopy.GetColorStops();
         }
 
         /* Tooling method to synchronize the given ColorStops.
            The intention is that a color GradientStops and an
            alpha/transparence GradientStops gets synchronized
-           for export.
-           For the corrections the single values for color and
-           alpha may be used, e.g. when ColorStops is given
-           and not empty, but AlphaStops is empty, it will get
-           synchronized so that it will have the same number and
-           offsets in AlphaStops as in ColorStops, but with
-           the given SingleAlpha as value.
-           At return it guarantees that both have the same
-           number of entries with the same StopOffsets, so
-           that synchronized pair of ColorStops can e.g. be used
-           to export a Gradient with defined/adapted alpha
-           being 'coupled' indirectly using the
-           'FillTransparenceGradient' method (at import time).
-        */
+           for export. */
         void synchronizeColorStops(
             BColorStops& rColorStops,
             BColorStops& rAlphaStops,
diff --git a/include/basegfx/utils/bgradient.hxx 
b/include/basegfx/utils/bgradient.hxx
index a0e853ec7099..fa9591cdb266 100644
--- a/include/basegfx/utils/bgradient.hxx
+++ b/include/basegfx/utils/bgradient.hxx
@@ -40,13 +40,10 @@ namespace basegfx
 
         Offset is defined as:
         - being in the range of [0.0 .. 1.0] (unit range)
-          - 0.0 being reserved for StartColor
-          - 1.0 being reserved for EndColor
-        - in-between offsets thus being in the range of ]0.0 .. 1.0[
-        - no two equal offsets are allowed
-            - this is an error
-        - missing 1.0 entry (EndColor) is allowed
-          - it means that EndColor == StartColor
+          - offsets outside are an error
+        - lowest/1st value equivalent to StartColor
+        - highest/last value equivalent to EndColor
+        - missing 0.0/1.0 entries are allowed
         - at least one value (usually 0.0, StartColor) is required
             - this allows to avoid massive testing in all places where
               this data has to be accessed
@@ -263,6 +260,19 @@ public:
            When also mirroring offsets a valid sort keeps valid.
         */
     void reverseColorStops();
+
+    // createSpaceAtStart creates fOffset space at start by
+    // translating/scaling all entries to the right
+    void createSpaceAtStart(double fOffset);
+
+    // removeSpaceAtStart removes fOffset space from start by
+    // translating/scaling entries more or equal to fOffset
+    // to the left. Entries less than fOffset will be removed
+    void removeSpaceAtStart(double fOffset);
+
+    // try to detect if an empty/no-color-change area exists
+    // at the start and return offset to it. Returns 0.0 if not.
+    double detectPossibleOffsetAtStart() const;
 };
 
 class BASEGFX_DLLPUBLIC BGradient final
@@ -323,6 +333,11 @@ public:
 
     /// Tooling method to fill awt::Gradient2 from data contained in the given 
basegfx::BGradient
     css::awt::Gradient2 getAsGradient2() const;
+
+    /// Tooling to handle border correction/integration and StartStopIntensity
+    void tryToRecreateBorder(basegfx::BColorStops* 
pAssociatedTransparencyStops);
+    void tryToApplyBorder();
+    void tryToApplyStartEndIntensity();
 };
 }
 
diff --git a/include/basegfx/utils/gradienttools.hxx 
b/include/basegfx/utils/gradienttools.hxx
index 9672259be64b..6fc51adc0e37 100644
--- a/include/basegfx/utils/gradienttools.hxx
+++ b/include/basegfx/utils/gradienttools.hxx
@@ -186,18 +186,23 @@ namespace basegfx
 
     namespace utils
     {
-        /* Tooling method to extract data from given awt::Gradient2
+        /* Tooling method to extract data from given BGradient
            to ColorStops, doing some corrections, partially based
            on given SingleColor.
+           This is used for export preparations in case these exports
+           do neither support Start/EndIntensity nor Border settings,
+           both will be elliminated if possible (see below).
+           The BGradient rGradient and BColorStops& rColorStops
+           are both return parameters and may be changed.
            This will do quite some preparations for the gradient
            as follows:
            - It will check for single color (resetting rSingleColor when
              this is the case) and return with empty ColorStops
            - It will blend ColorStops to Intensity if StartIntensity/
-             EndIntensity != 100 is set in awt::Gradient2, so applying
+             EndIntensity != 100 is set in BGradient, so applying
              that value(s) to the gradient directly
            - It will adapt to Border if Border != 0 is set at the
-             given awt::Gradient2, so applying that value to the gradient
+             given BGradient, so applying that value to the gradient
              directly
         */
         BASEGFX_DLLPUBLIC void prepareColorStops(
diff --git a/oox/source/drawingml/fillproperties.cxx 
b/oox/source/drawingml/fillproperties.cxx
index a42418198c12..705f2dc3b55a 100644
--- a/oox/source/drawingml/fillproperties.cxx
+++ b/oox/source/drawingml/fillproperties.cxx
@@ -458,19 +458,11 @@ void FillProperties::pushToPropMap(ShapePropertyMap& 
rPropMap, const GraphicHelp
             // do not create gradient struct if property is not supported...
             if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) )
             {
-                // use awt::Gradient2, prepare ColorStops
-                awt::Gradient2 aGradient;
-                assert(aGradient.ColorStops.get() && "cid#1524676 
aGradient.ColorStops._pSequence won't be null here");
+                // prepare ColorStops
                 basegfx::BColorStops aColorStops;
                 basegfx::BColorStops aTransparencyStops;
                 bool bContainsTransparency(false);
 
-                // set defaults
-                aGradient.Angle = 900;
-                aGradient.StartIntensity = 100;
-                aGradient.EndIntensity = 100;
-                aGradient.Style = awt::GradientStyle_LINEAR;
-
                 // convert to BColorStops, check for contained transparency
                 for (const auto& rCandidate : maGradientProps.maGradientStops)
                 {
@@ -489,6 +481,20 @@ void FillProperties::pushToPropMap(ShapePropertyMap& 
rPropMap, const GraphicHelp
                     }
                 }
 
+                // prepare BGradient with some defaults
+                // CAUTION: This used awt::Gradient2 before who's empty 
constructor
+                //          (see 
workdir/UnoApiHeadersTarget/offapi/normal/com/sun/
+                //          star/awt/Gradient.hpp) initializes all to zeros, 
so reflect
+                //          this here. OTOH set all that were set, e.g. 
Start/EndIntens
+                //          were set to 100, so just use default of BGradient 
constructor
+                basegfx::BGradient aGradient(
+                    aColorStops,
+                    awt::GradientStyle_LINEAR,
+                    Degree10(900),
+                    0,  // border
+                    0,  // OfsX -> 0, not 50 (!)
+                    0); // OfsY -> 0, not 50 (!)
+
                 // "rotate with shape" set to false -> do not rotate
                 if (!maGradientProps.moRotateWithShape.value_or(true))
                 {
@@ -499,77 +505,61 @@ void FillProperties::pushToPropMap(ShapePropertyMap& 
rPropMap, const GraphicHelp
                 {
                     IntegerRectangle2D aFillToRect = 
maGradientProps.moFillToRect.value_or( IntegerRectangle2D( 0, 0, MAX_PERCENT, 
MAX_PERCENT ) );
                     sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - 
aFillToRect.X2) / 2;
-                    aGradient.XOffset = getLimitedValue<sal_Int16, sal_Int32>(
-                        nCenterX / PER_PERCENT, 0, 100);
+                    aGradient.SetXOffset(getLimitedValue<sal_Int16, sal_Int32>(
+                        nCenterX / PER_PERCENT, 0, 100));
                     sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - 
aFillToRect.Y2) / 2;
-                    aGradient.YOffset = getLimitedValue<sal_Int16, sal_Int32>(
-                        nCenterY / PER_PERCENT, 0, 100);
+                    aGradient.SetYOffset(getLimitedValue<sal_Int16, sal_Int32>(
+                        nCenterY / PER_PERCENT, 0, 100));
 
                     if( maGradientProps.moGradientPath.value() == XML_circle )
                     {
                         // Style should be radial at least when the horizontal 
center is at 50%.
                         // Otherwise import as a linear gradient, because it 
is the most similar to the MSO radial style.
-                        // aGradient.Style = awt::GradientStyle_LINEAR;
-                        if( aGradient.XOffset == 100 && aGradient.YOffset == 
100 )
-                            aGradient.Angle = 450;
-                        else if( aGradient.XOffset == 0 && aGradient.YOffset 
== 100 )
-                            aGradient.Angle = 3150;
-                        else if( aGradient.XOffset == 100 && aGradient.YOffset 
== 0 )
-                            aGradient.Angle = 1350;
-                        else if( aGradient.XOffset == 0 && aGradient.YOffset 
== 0 )
-                            aGradient.Angle = 2250;
+                        // 
aGradient.SetGradientStyle(awt::GradientStyle_LINEAR);
+                        if( 100 == aGradient.GetXOffset() && 100 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(450) );
+                        else if( 0 == aGradient.GetXOffset() && 100 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(3150) );
+                        else if( 100 == aGradient.GetXOffset() && 0 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(1350) );
+                        else if( 0 == aGradient.GetXOffset() && 0 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(2250) );
                         else
-                            aGradient.Style = awt::GradientStyle_RADIAL;
+                            
aGradient.SetGradientStyle(awt::GradientStyle_RADIAL);
                     }
                     else
                     {
-                        aGradient.Style = awt::GradientStyle_RECT;
+                        aGradient.SetGradientStyle(awt::GradientStyle_RECT);
                     }
 
                     aColorStops.reverseColorStops();
+                    aGradient.SetColorStops(aColorStops);
                     aTransparencyStops.reverseColorStops();
                 }
                 else if (!maGradientProps.maGradientStops.empty())
                 {
-                    // aGradient.Style = awt::GradientStyle_LINEAR;
-                    sal_Int32 nShadeAngle = 
maGradientProps.moShadeAngle.value_or( 0 );
+                    // aGradient.SetGradientStyle(awt::GradientStyle_LINEAR);
+                    sal_Int32 
nShadeAngle(maGradientProps.moShadeAngle.value_or( 0 ));
                     // Adjust for flips
                     if ( bFlipH )
                         nShadeAngle = 180*60000 - nShadeAngle;
                     if ( bFlipV )
                         nShadeAngle = -nShadeAngle;
                     const sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation;
+
                     // convert DrawingML angle (in 1/60000 degrees) to API 
angle (in 1/10 degrees)
-                    aGradient.Angle = static_cast< sal_Int16 >( (8100 - 
(nDmlAngle / (PER_DEGREE / 10))) % 3600 );
+                    aGradient.SetAngle(Degree10(static_cast< sal_Int16 >( 
(8100 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 )));
                 }
 
-                // set BColorStops using UNO API
-                aGradient.ColorStops = aColorStops.getAsColorStopSequence();
-
-                // for compatibility, still set StartColor/EndColor
-                // NOTE: All code after adapting to multi color gradients works
-                //       using the ColorSteps, so in principle Start/EndColor 
might
-                //       be either
-                //        (a) ignored consequently everywhere or
-                //        (b) be set/added consequently everywhere
-                //       since this is - in principle - redundant data.
-                //       Be aware that e.g. cases like 
DrawingML::EqualGradients
-                //       and others would have to be identified and adapted (!)
-                //       Since awt::Gradient2 is UNO API data there might
-                //       be cases where just awt::Gradient is transferred, so 
(b)
-                //       is far better backwards compatible and thus more 
safe, so
-                //       all changes will make use of additionally 
using/setting
-                //       these additionally, but will only make use of the 
given
-                //       ColorSteps if these are not empty, assuming that these
-                //       already contain Start/EndColor.
-                //       In principle that redundancy and that it is 
conflict-free
-                //       could even be checked and asserted, but consequently 
using
-                //       (b) methodically should be safe.
-                aGradient.StartColor = 
static_cast<sal_Int32>(::Color(aColorStops.front().getStopColor()));
-                aGradient.EndColor = 
static_cast<sal_Int32>(::Color(aColorStops.back().getStopColor()));
+                if (awt::GradientStyle_RECT == aGradient.GetGradientStyle())
+                {
+                    // MCGR: tdf#155362: better support border
+                    // CAUTION: Need to handle TransparencyStops if used
+                    aGradient.tryToRecreateBorder(aTransparencyStops.empty() ? 
nullptr : &aTransparencyStops);
+                }
 
                 // push gradient or named gradient to property map
-                if (rPropMap.setProperty(ShapeProperty::FillGradient, 
aGradient))
+                if (rPropMap.setProperty(ShapeProperty::FillGradient, 
aGradient.getAsGradient2()))
                 {
                     eFillStyle = FillStyle_GRADIENT;
                 }
@@ -577,8 +567,8 @@ void FillProperties::pushToPropMap(ShapePropertyMap& 
rPropMap, const GraphicHelp
                 // push gradient transparency to property map if it exists
                 if (!aTransparencyStops.empty())
                 {
-                    aGradient.ColorStops = 
aTransparencyStops.getAsColorStopSequence();
-                    rPropMap.setProperty(ShapeProperty::GradientTransparency, 
aGradient);
+                    aGradient.SetColorStops(aTransparencyStops);
+                    rPropMap.setProperty(ShapeProperty::GradientTransparency, 
aGradient.getAsGradient2());
                 }
             }
         break;

Reply via email to