summaryrefslogtreecommitdiff
path: root/drawinglayer/source/texture/texture.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/texture/texture.cxx')
-rw-r--r--drawinglayer/source/texture/texture.cxx664
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