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: