basegfx/source/tools/gradienttools.cxx  |  100 ++++++++++++++++++--------------
 drawinglayer/inc/texture/texture.hxx    |    4 +
 drawinglayer/source/texture/texture.cxx |   13 ++--
 include/basegfx/utils/gradienttools.hxx |   20 ++++++
 4 files changed, 87 insertions(+), 50 deletions(-)

New commits:
commit 4b5203ebf4ca5894f4d7dd37a141832df26e8b9a
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Tue Apr 4 14:57:06 2023 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Tue Apr 4 18:55:49 2023 +0200

    MCGR: Improve performance for texture-mapped MCGR processing
    
    Change-Id: I20b32e7c272112c6c3d9f7ee0ef59c6d4d006d94
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150020
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/basegfx/source/tools/gradienttools.cxx 
b/basegfx/source/tools/gradienttools.cxx
index dc6e4dc3f66a..fdc8dd189531 100644
--- a/basegfx/source/tools/gradienttools.cxx
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -607,7 +607,8 @@ namespace basegfx
         BColor modifyBColor(
             const ColorStops& rColorStops,
             double fScaler,
-            sal_uInt32 nRequestedSteps)
+            sal_uInt32 nRequestedSteps,
+            ColorStopRange& rLastColorStopRange)
         {
             // no color at all, done
             if (rColorStops.empty())
@@ -650,56 +651,69 @@ namespace basegfx
                     nSteps > 1 ? floor(fScaler * nSteps) / double(nSteps - 1) 
: fScaler);
             }
 
-            // 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
-            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
-            const BColor aCStart(lowerBound->getStopColor());
-            const BColor aCEnd(upperBound->getStopColor());
+            // 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 
acessing
+            // 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();
+
+                // 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 (lowerBound->getStopColor() == upperBound->getStopColor())
+                    return rLastColorStopRange.maColorStart;
+
+                // 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 (aCStart == aCEnd)
-                return aCStart;
 
-            // calculate number of steps
+            // calculate number of steps and adapted proportinal
+            // range for scaler in [0.0 .. 1.0]
+            const double fAdaptedScaler((fScaler - 
rLastColorStopRange.mfOffsetStart) /
+                (rLastColorStopRange.mfOffsetEnd - 
rLastColorStopRange.mfOffsetStart));
             const sal_uInt32 nSteps(
                 calculateNumberOfSteps(
                     nRequestedSteps,
-                    aCStart,
-                    aCEnd));
-
-            // get offsets and scale to new [0.0 .. 1.0] relative range for
-            // partial outer range
-            const double fOffsetStart(lowerBound->getStopOffset());
-            const double fOffsetEnd(upperBound->getStopOffset());
-            const double fAdaptedScaler((fScaler - fOffsetStart) / (fOffsetEnd 
- fOffsetStart));
+                    rLastColorStopRange.maColorStart,
+                    rLastColorStopRange.maColorEnd));
 
             // interpolate & evtl. apply steps
             return interpolate(
-                aCStart,
-                aCEnd,
+                rLastColorStopRange.maColorStart,
+                rLastColorStopRange.maColorEnd,
                 nSteps > 1 ? floor(fAdaptedScaler * nSteps) / double(nSteps - 
1) : fAdaptedScaler);
         }
 
diff --git a/drawinglayer/inc/texture/texture.hxx 
b/drawinglayer/inc/texture/texture.hxx
index 54ace6ba3b47..01d3ec5c64c5 100644
--- a/drawinglayer/inc/texture/texture.hxx
+++ b/drawinglayer/inc/texture/texture.hxx
@@ -52,6 +52,10 @@ namespace drawinglayer::texture
             basegfx::ColorStops                 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();
 
diff --git a/drawinglayer/source/texture/texture.cxx 
b/drawinglayer/source/texture/texture.cxx
index 1390ee3d82bf..0960ffbbbde3 100644
--- a/drawinglayer/source/texture/texture.cxx
+++ b/drawinglayer/source/texture/texture.cxx
@@ -78,6 +78,7 @@ namespace drawinglayer::texture
         , mnRequestedSteps(nRequestedSteps)
         , mnColorStops(rColorStops)
         , mfBorder(fBorder)
+        , maLastColorStopRange()
         {
         }
 
@@ -265,7 +266,7 @@ 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);
+            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps, maLastColorStopRange);
         }
 
         GeoTexSvxGradientAxial::GeoTexSvxGradientAxial(
@@ -392,7 +393,7 @@ namespace drawinglayer::texture
             const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV, 
maGradientInfo));
 
                 // we use the reverse ColorSteps here, so mirror scaler value
-            rBColor = basegfx::utils::modifyBColor(mnColorStops, 1.0 - 
fScaler, mnRequestedSteps);
+            rBColor = basegfx::utils::modifyBColor(mnColorStops, 1.0 - 
fScaler, mnRequestedSteps, maLastColorStopRange);
         }
 
 
@@ -484,7 +485,7 @@ namespace drawinglayer::texture
 
             // texture-back-transform X/Y -> t [0.0..1.0] and determine color
             const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, 
maGradientInfo));
-            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps);
+            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps, maLastColorStopRange);
         }
 
 
@@ -585,7 +586,7 @@ namespace drawinglayer::texture
 
             // texture-back-transform X/Y -> t [0.0..1.0] and determine color
             const double 
fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo));
-            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps);
+            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps, maLastColorStopRange);
         }
 
 
@@ -679,7 +680,7 @@ namespace drawinglayer::texture
 
             // texture-back-transform X/Y -> t [0.0..1.0] and determine color
             const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, 
maGradientInfo));
-            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps);
+            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps, maLastColorStopRange);
         }
 
 
@@ -780,7 +781,7 @@ namespace drawinglayer::texture
 
             // texture-back-transform X/Y -> t [0.0..1.0] and determine color
             const double 
fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo));
-            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps);
+            rBColor = basegfx::utils::modifyBColor(mnColorStops, fScaler, 
mnRequestedSteps, maLastColorStopRange);
         }
 
 
diff --git a/include/basegfx/utils/gradienttools.hxx 
b/include/basegfx/utils/gradienttools.hxx
index ea1802c291f8..f438fdf3bd4a 100644
--- a/include/basegfx/utils/gradienttools.hxx
+++ b/include/basegfx/utils/gradienttools.hxx
@@ -194,6 +194,23 @@ namespace basegfx
         }
     };
 
+    // helper data struct to support buffering entries in
+    // gradient texture mapping, see usages for more info
+    struct UNLESS_MERGELIBS(BASEGFX_DLLPUBLIC) ColorStopRange
+    {
+        basegfx::BColor     maColorStart;
+        basegfx::BColor     maColorEnd;
+        double              mfOffsetStart;
+        double              mfOffsetEnd;
+
+        ColorStopRange()
+        : maColorStart()
+        , maColorEnd()
+        , mfOffsetStart(0.0)
+        , mfOffsetEnd(0.0)
+        {}
+    };
+
     namespace utils
     {
         /* Tooling method to linearly blend the Colors contained in
@@ -293,7 +310,8 @@ namespace basegfx
         BASEGFX_DLLPUBLIC BColor modifyBColor(
             const ColorStops& rColorStops,
             double fScaler,
-            sal_uInt32 nRequestedSteps);
+            sal_uInt32 nRequestedSteps,
+            ColorStopRange& rLastColorStopRange);
 
         /* Helper to calculate numberOfSteps needed to represent
            gradient for the given two colors:

Reply via email to