diff options
Diffstat (limited to 'drawinglayer/source/texture/texture.cxx')
-rw-r--r-- | drawinglayer/source/texture/texture.cxx | 664 |
1 files changed, 488 insertions, 176 deletions
diff --git a/drawinglayer/source/texture/texture.cxx b/drawinglayer/source/texture/texture.cxx index a7e9dca54c79..ccfaa13bd8bf 100644 --- a/drawinglayer/source/texture/texture.cxx +++ b/drawinglayer/source/texture/texture.cxx @@ -20,6 +20,7 @@ #include <sal/config.h> #include <algorithm> +#include <limits> #include <texture/texture.hxx> #include <basegfx/numeric/ftools.hxx> @@ -71,15 +72,14 @@ namespace drawinglayer::texture GeoTexSvxGradient::GeoTexSvxGradient( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder) - : GeoTexSvx(), - maGradientInfo(), - maDefinitionRange(rDefinitionRange), - maStart(rStart), - maEnd(rEnd), - mfBorder(fBorder) + : maDefinitionRange(rDefinitionRange) + , mnRequestedSteps(nRequestedSteps) + , mnColorStops(rColorStops) + , mfBorder(fBorder) + , maLastColorStopRange() { } @@ -94,26 +94,26 @@ namespace drawinglayer::texture return (pCompare && maGradientInfo == pCompare->maGradientInfo && maDefinitionRange == pCompare->maDefinitionRange + && mnRequestedSteps == pCompare->mnRequestedSteps + && mnColorStops == pCompare->mnColorStops && mfBorder == pCompare->mfBorder); } - GeoTexSvxGradientLinear::GeoTexSvxGradientLinear( const basegfx::B2DRange& rDefinitionRange, const basegfx::B2DRange& rOutputRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder), - mfUnitMinX(0.0), - mfUnitWidth(1.0), - mfUnitMaxY(1.0) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) + , mfUnitMinX(0.0) + , mfUnitWidth(1.0) + , mfUnitMaxY(1.0) { maGradientInfo = basegfx::utils::createLinearODFGradientInfo( rDefinitionRange, - nSteps, + nRequestedSteps, fBorder, fAngle); @@ -133,16 +133,30 @@ namespace drawinglayer::texture } void GeoTexSvxGradientLinear::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStripeWidth(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + 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; // bring from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1] @@ -153,54 +167,106 @@ namespace drawinglayer::texture aPattern.scale(mfUnitWidth, 1.0); aPattern.translate(mfUnitMinX, 0.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - const double fPos(fStripeWidth * a); - basegfx::B2DHomMatrix aNew(aPattern); - - // scale and translate in Y - double fHeight(1.0 - fPos); - - if(a + 1 == maGradientInfo.getSteps() && mfUnitMaxY > 1.0) + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + // nSteps is >= 1, see getRequestedSteps, so no check needed here + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // for the 1st color range we do not need to create the 1st step + // since it will be equal to StartColor and thus OuterColor, so + // will be painted by the 1st, always-created background polygon + // colored using OuterColor. + // We *need* to create this though for all 'inner' color ranges + // to get a correct start + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) { - fHeight += mfUnitMaxY - 1.0; + // calculate pos in Y + const double fPos(fOffsetStart + (fStripeWidth * innerLoop)); + + // scale and translate in Y. For GradientLinear we always have + // the full height + double fHeight(1.0 - fPos); + + if (mfUnitMaxY > 1.0) + { + // extend when difference between definition and OutputRange exists + fHeight += mfUnitMaxY - 1.0; + } + + basegfx::B2DHomMatrix aNew(aPattern); + aNew.scale(1.0, fHeight); + aNew.translate(0.0, fPos); + + // set and add at target + aCallback( + maGradientInfo.getTextureTransform() * aNew, + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); } + } - aNew.scale(1.0, fHeight); - aNew.translate(0.0, fPos); - - // set at target - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew; - - // interpolate and set color - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - - rEntries.push_back(aB2DHomMatrixAndBColor); + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientAxial::GeoTexSvxGradientAxial( const basegfx::B2DRange& rDefinitionRange, const basegfx::B2DRange& rOutputRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder), - mfUnitMinX(0.0), - mfUnitWidth(1.0) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) + , mfUnitMinX(0.0) + , mfUnitWidth(1.0) { + // ARGH! GradientAxial uses the ColorStops in reverse order compared + // with the other gradients. Either stay 'thinking reverse' for the + // rest of time or adapt it here and go in same order as the other five, + // so unifications/tooling will be possible + mnColorStops.reverseColorStops(); + maGradientInfo = basegfx::utils::createAxialODFGradientInfo( rDefinitionRange, - nSteps, + nRequestedSteps, fBorder, fAngle); @@ -219,65 +285,118 @@ namespace drawinglayer::texture } void GeoTexSvxGradientAxial::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) { - rOuterColor = maEnd; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStripeWidth(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); + + if (bPenultimateUsed) + { + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); + } + + // prepare unit range transform + basegfx::B2DHomMatrix aPattern; + + // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1] + aPattern.scale(0.5, 1.0); + aPattern.translate(0.5, 0.0); + + // scale/translate in X + aPattern.scale(mfUnitWidth, 1.0); + aPattern.translate(mfUnitMinX, 0.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - const double fPos(fStripeWidth * a); - basegfx::B2DHomMatrix aNew; + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); - // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1] - aNew.scale(0.5, 1.0); - aNew.translate(0.5, 0.0); + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; - // scale/translate in X - aNew.scale(mfUnitWidth, 1.0); - aNew.translate(mfUnitMinX, 0.0); + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); - // already centered in Y on X-Axis, just scale in Y - aNew.scale(1.0, 1.0 - fPos); + // calculate StripeWidth + // nSteps is >= 1, see getRequestedSteps, so no check needed here + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); - // set at target - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew; + // for the 1st color range we do not need to create the 1st step, see above + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate pos in Y + const double fPos(fOffsetStart + (fStripeWidth * innerLoop)); - // interpolate and set color - aB2DHomMatrixAndBColor.maBColor = interpolate(maEnd, maStart, double(a) / double(maGradientInfo.getSteps() - 1)); + // already centered in Y on X-Axis, just scale in Y + basegfx::B2DHomMatrix aNew(aPattern); + aNew.scale(1.0, 1.0 - fPos); - rEntries.push_back(aB2DHomMatrixAndBColor); + // set and add at target + aCallback( + maGradientInfo.getTextureTransform() * aNew, + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientAxial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { + // no color at all, done + if (mnColorStops.empty()) + return; + + // just single color, done + if (mnColorStops.size() < 2) + { + // we use the reverse ColorSteps here, so use front value + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV, maGradientInfo)); - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // we use the reverse ColorSteps here, so mirror scaler value + rBColor = mnColorStops.getInterpolatedBColor(1.0 - fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientRadial::GeoTexSvxGradientRadial( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createRadialODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder); } @@ -286,49 +405,100 @@ namespace drawinglayer::texture } void GeoTexSvxGradientRadial::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStepSize(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); + + if (bPenultimateUsed) + { + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); + } + + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) + { + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate size/radius + const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop))); + + // set and add at target + aCallback( + maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + if (bPenultimateUsed) { - const double fSize(1.0 - (fStepSize * a)); - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientRadial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; + + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientElliptical::GeoTexSvxGradientElliptical( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createEllipticalODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder, fAngle); } @@ -338,67 +508,107 @@ namespace drawinglayer::texture } void GeoTexSvxGradientElliptical::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - double fWidth(1.0); - double fHeight(1.0); - double fIncrementX(0.0); - double fIncrementY(0.0); + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); - if(maGradientInfo.getAspectRatio() > 1.0) + if (bPenultimateUsed) { - fIncrementY = fHeight / maGradientInfo.getSteps(); - fIncrementX = fIncrementY / maGradientInfo.getAspectRatio(); - } - else - { - fIncrementX = fWidth / maGradientInfo.getSteps(); - fIncrementY = fIncrementX * maGradientInfo.getAspectRatio(); + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); } - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // prepare vars dependent on aspect ratio + const double fAR(maGradientInfo.getAspectRatio()); + const bool bMTO(fAR > 1.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - // next step - fWidth -= fIncrementX; - fHeight -= fIncrementY; + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate offset position for entry + const double fSize(fOffsetStart + (fStripeWidth * innerLoop)); + + // set and add at target + aCallback( + maGradientInfo.getTextureTransform() + * basegfx::utils::createScaleB2DHomMatrix( + 1.0 - (bMTO ? fSize / fAR : fSize), + 1.0 - (bMTO ? fSize : fSize * fAR)), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientElliptical::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientSquare::GeoTexSvxGradientSquare( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createSquareODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder, fAngle); } @@ -408,49 +618,100 @@ namespace drawinglayer::texture } void GeoTexSvxGradientSquare::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStepSize(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + if (bPenultimateUsed) { - const double fSize(1.0 - (fStepSize * a)); - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); + } + + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) + { + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate size/radius + const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop))); + + // set and add at target + aCallback( + maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientSquare::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; + + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientRect::GeoTexSvxGradientRect( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createRectangularODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder, fAngle); } @@ -460,49 +721,90 @@ namespace drawinglayer::texture } void GeoTexSvxGradientRect::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - double fWidth(1.0); - double fHeight(1.0); - double fIncrementX(0.0); - double fIncrementY(0.0); + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); - if(maGradientInfo.getAspectRatio() > 1.0) - { - fIncrementY = fHeight / maGradientInfo.getSteps(); - fIncrementX = fIncrementY / maGradientInfo.getAspectRatio(); - } - else + if (bPenultimateUsed) { - fIncrementX = fWidth / maGradientInfo.getSteps(); - fIncrementY = fIncrementX * maGradientInfo.getAspectRatio(); + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); } - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // prepare vars dependent on aspect ratio + const double fAR(maGradientInfo.getAspectRatio()); + const bool bMTO(fAR > 1.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - // next step - fWidth -= fIncrementX; - fHeight -= fIncrementY; + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate offset position for entry + const double fSize(fOffsetStart + (fStripeWidth * innerLoop)); + + // set and add at target + aCallback( + maGradientInfo.getTextureTransform() + * basegfx::utils::createScaleB2DHomMatrix( + 1.0 - (bMTO ? fSize / fAR : fSize), + 1.0 - (bMTO ? fSize : fSize * fAR)), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientRect::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } @@ -512,8 +814,6 @@ namespace drawinglayer::texture double fDistance, double fAngle) : maOutputRange(rOutputRange), - maTextureTransform(), - maBackTextureTransform(), mfDistance(0.1), mfAngle(fAngle), mnSteps(10), @@ -631,8 +931,20 @@ namespace drawinglayer::texture double GeoTexSvxHatch::getDistanceToHatch(const basegfx::B2DPoint& rUV) const { - const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV); - return fmod(aCoor.getY(), mfDistance); + // the below is an inlined and optimised version of + // const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV); + // return fmod(aCoor.getY(), mfDistance); + + const basegfx::B2DHomMatrix& rMat = getBackTextureTransform(); + double fX = rUV.getX(); + double fY = rUV.getY(); + + double fTempY( + rMat.get(1, 0) * fX + + rMat.get(1, 1) * fY + + rMat.get(1, 2)); + + return fmod(fTempY, mfDistance); } const basegfx::B2DHomMatrix& GeoTexSvxHatch::getBackTextureTransform() const |