summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--basegfx/source/tools/gradienttools.cxx101
-rw-r--r--drawinglayer/source/attribute/fillgradientattribute.cxx40
-rw-r--r--drawinglayer/source/texture/texture.cxx95
-rw-r--r--include/basegfx/utils/gradienttools.hxx23
-rw-r--r--svx/source/sdr/primitive2d/sdrattributecreator.cxx94
5 files changed, 292 insertions, 61 deletions
diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx
index b3bb18f918da..a8d36b9f543d 100644
--- a/basegfx/source/tools/gradienttools.cxx
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -21,6 +21,8 @@
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+#include <algorithm>
#include <cmath>
namespace basegfx
@@ -261,6 +263,95 @@ namespace basegfx
namespace utils
{
+ BColor modifyBColor(
+ const ColorSteps& rColorSteps,
+ double fScaler,
+ sal_uInt32 nRequestedSteps)
+ {
+ // no color at all, done
+ if (rColorSteps.empty())
+ return BColor();
+
+ // outside range -> at start
+ if (fScaler <= 0.0)
+ return rColorSteps.front().getColor();
+
+ // outside range -> at end
+ if (fScaler >= 1.0)
+ return rColorSteps.back().getColor();
+
+ // special case for the 'classic' case with just two colors:
+ // we can optimize that and keep the speed/ressources low
+ // by avoiding some calculatins and an O(log(N)) array access
+ if (2 == rColorSteps.size())
+ {
+ const basegfx::BColor aCStart(rColorSteps.front().getColor());
+ const basegfx::BColor aCEnd(rColorSteps.back().getColor());
+ const sal_uInt32 nSteps(
+ calculateNumberOfSteps(
+ nRequestedSteps,
+ aCStart,
+ aCEnd));
+
+ return basegfx::interpolate(
+ aCStart,
+ aCEnd,
+ 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::lower_bound(
+ rColorSteps.begin(),
+ rColorSteps.end(),
+ ColorStep(fScaler),
+ [](const ColorStep& x, const ColorStep& y) { return x.getOffset() < y.getOffset(); }));
+
+ // no upper bound, done
+ if (rColorSteps.end() == upperBound)
+ return rColorSteps.back().getColor();
+
+ // lower bound is one entry back
+ const auto lowerBound(upperBound - 1);
+
+ // no lower bound, done
+ if (rColorSteps.end() == lowerBound)
+ return rColorSteps.back().getColor();
+
+ // we have lower and upper bound, get colors
+ const BColor aCStart(lowerBound->getColor());
+ const BColor aCEnd(upperBound->getColor());
+
+ // 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
+ 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->getOffset());
+ const double fOffsetEnd(upperBound->getOffset());
+ const double fAdaptedScaler((fScaler - fOffsetStart) / (fOffsetEnd - fOffsetStart));
+
+ // interpolate & evtl. apply steps
+ return interpolate(
+ aCStart,
+ aCEnd,
+ nSteps > 1 ? floor(fAdaptedScaler * nSteps) / double(nSteps - 1) : fAdaptedScaler);
+ }
+
sal_uInt32 calculateNumberOfSteps(
sal_uInt32 nRequestedSteps,
const BColor& rStart,
@@ -392,12 +483,12 @@ namespace basegfx
return 1.0; // end value for outside
}
- const sal_uInt32 nSteps(rGradInfo.getRequestedSteps());
+ // const sal_uInt32 nSteps(rGradInfo.getRequestedSteps());
- if(nSteps)
- {
- return floor(aCoor.getY() * nSteps) / double(nSteps - 1);
- }
+ // if(nSteps)
+ // {
+ // return floor(aCoor.getY() * nSteps) / double(nSteps - 1);
+ // }
return aCoor.getY();
}
diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx b/drawinglayer/source/attribute/fillgradientattribute.cxx
index 9f6e1f879d42..fa6a0f876cae 100644
--- a/drawinglayer/source/attribute/fillgradientattribute.cxx
+++ b/drawinglayer/source/attribute/fillgradientattribute.cxx
@@ -59,32 +59,45 @@ namespace drawinglayer::attribute
// if we have given ColorSteps, integrate these
if(nullptr != pColorSteps && !pColorSteps->empty())
{
- // append early & sort to local to prepare processing and correction(s)
+ // append early to local & sort to prepare the following correction(s)
+ // and later processing
maColorSteps.insert(maColorSteps.end(), pColorSteps->begin(), pColorSteps->end());
- std::sort(maColorSteps.begin(), maColorSteps.end());
+
+ // no need to sort knowingly lowest entry StartColor, also guarantees that
+ // entry to stay at begin
+ std::sort(maColorSteps.begin() + 1, maColorSteps.end());
// use two r/w heads on the data band maColorSteps
size_t curr(0), next(1);
- // check if all colors are the same. We know the StartColor, so
- // to all be the same all have to be equal to StartColor, including
- // EndColor
+ // during procesing, check if all colors are the same. We know the
+ // StartColor, so to all be the same, all also have to be equal to
+ // StartColor (including EndColor, use to initialize)
bool bAllTheSameColor(rStartColor == rEndColor);
- // remove entries < 0.0, > 1.0 and with equal offset. do
+ // remove entries <= 0.0, >= 1.0 and with equal offset. do
// this inside the already sorted local vector by evtl.
- // moving entries towards begin to keep and adapting size
+ // moving entries closer to begin to keep and adapting size
// at the end
for(; next < maColorSteps.size(); next++)
{
const double fNextOffset(maColorSteps[next].getOffset());
- if(basegfx::fTools::less(fNextOffset, 0.0))
+ // check for < 0.0 (should not really happen, see ::ColorStep)
+ // also check for == 0.0 which would mean than an implicit
+ // StartColor was given in ColorSteps - ignore that, we want
+ // the explicitely given StartColor to always win
+ if(basegfx::fTools::lessOrEqual(fNextOffset, 0.0))
continue;
- if(basegfx::fTools::more(fNextOffset, 1.0))
+ // check for > 1.0 (should not really happen, see ::ColorStep)
+ // also check for == 1.0 which would mean than an implicit
+ // EndColor was given in ColorSteps - ignore that, we want
+ // the explicitely given EndColor to always win
+ if(basegfx::fTools::moreOrEqual(fNextOffset, 1.0))
continue;
+ // check for equal current offset
const double fCurrOffset(maColorSteps[curr].getOffset());
if(basegfx::fTools::equal(fNextOffset, fCurrOffset))
continue;
@@ -97,18 +110,18 @@ namespace drawinglayer::attribute
maColorSteps[curr] = maColorSteps[next];
}
- // new entry added, check it for all the same color
+ // new valid entry detected, check it for all the same color
bAllTheSameColor = bAllTheSameColor && maColorSteps[curr].getColor() == rStartColor;
}
if(bAllTheSameColor)
{
- // if all the same, reset to StartColor only
+ // if all are the same color, reset to StartColor only
maColorSteps.resize(1);
}
else
{
- // adapt size to useful entries
+ // adapt size to detected useful entries
curr++;
if(curr != maColorSteps.size())
{
@@ -116,7 +129,8 @@ namespace drawinglayer::attribute
}
// add EndColor if in-between colors were added
- if(curr > 1)
+ // or StartColor != EndColor
+ if(curr > 1 || rStartColor != rEndColor)
{
maColorSteps.emplace_back(1.0, rEndColor);
}
diff --git a/drawinglayer/source/texture/texture.cxx b/drawinglayer/source/texture/texture.cxx
index 623336187495..326d3bbe9146 100644
--- a/drawinglayer/source/texture/texture.cxx
+++ b/drawinglayer/source/texture/texture.cxx
@@ -134,17 +134,23 @@ namespace drawinglayer::texture
std::vector< B2DHomMatrixAndBColor >& rEntries,
basegfx::BColor& rOuterColor)
{
- if(mnColorSteps.size() <= 1)
+ // no color at all, done
+ if (mnColorSteps.empty())
return;
- const basegfx::BColor maStart(mnColorSteps.front().getColor());
- const basegfx::BColor maEnd(mnColorSteps.back().getColor());
- const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
- maGradientInfo.getRequestedSteps(), maStart, maEnd));
+ // get start color and also cc to rOuterColor
+ basegfx::BColor aCStart(mnColorSteps[0].getColor());
+ rOuterColor = aCStart;
- rOuterColor = maStart;
- const double fStripeWidth(1.0 / nSteps);
- B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ // only one color, done
+ if (mnColorSteps.size() < 2)
+ return;
+
+ // here we could check that fOffsetStart == mnColorSteps[0].getOffset(),
+ // but just assume/correct that StartColor is at 0.0
+ double fOffsetStart(0.0 /*mnColorSteps[0].getOffset()*/);
+
+ // prepare unit range transform
basegfx::B2DHomMatrix aPattern;
// bring from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
@@ -155,47 +161,72 @@ namespace drawinglayer::texture
aPattern.scale(mfUnitWidth, 1.0);
aPattern.translate(mfUnitMinX, 0.0);
- for(sal_uInt32 a(1); a < nSteps; a++)
+ for (size_t outerLoop(1); outerLoop < mnColorSteps.size(); outerLoop++)
{
- const double fPos(fStripeWidth * a);
- basegfx::B2DHomMatrix aNew(aPattern);
+ const basegfx::BColor aCEnd(mnColorSteps[outerLoop].getColor());
+ const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+ maGradientInfo.getRequestedSteps(), aCStart, aCEnd));
+ const double fOffsetEnd(mnColorSteps[outerLoop].getOffset());
- // scale and translate in Y
- double fHeight(1.0 - fPos);
+ // nSteps is >= 1, see getRequestedSteps, so no check needed here
+ const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
- if(a + 1 == nSteps && mfUnitMaxY > 1.0)
+ // for the 1st color range we do not need to create the 1st step
+ // since it will be equal to StartColor and thus rOuterColor, so
+ // will be painted by the 1st, always-created background polygon
+ // colored using rOuterColor.
+ // We *need* to create this though for all 'inner' color ranges
+ // to get a correct start
+ const sal_uInt32 nStartInnerLoop(1 == outerLoop ? 1 : 0);
+
+ for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
{
- fHeight += mfUnitMaxY - 1.0;
- }
+ // calculate pos in Y
+ const double fPos(fOffsetStart + (fStripeWidth * innerLoop));
- aNew.scale(1.0, fHeight);
- aNew.translate(0.0, fPos);
+ // scale and translate in Y. For GradientLinear we always have
+ // the full height
+ double fHeight(1.0 - fPos);
- // set at target
- aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew;
+ if (mfUnitMaxY > 1.0)
+ {
+ // extend when difference between definition and OutputRange exists
+ fHeight += mfUnitMaxY - 1.0;
+ }
- // interpolate and set color
- aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(nSteps - 1));
+ basegfx::B2DHomMatrix aNew(aPattern);
+ aNew.scale(1.0, fHeight);
+ aNew.translate(0.0, fPos);
- rEntries.push_back(aB2DHomMatrixAndBColor);
+ // set and add at target
+ B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+
+ aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew;
+ aB2DHomMatrixAndBColor.maBColor = interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1));
+ rEntries.push_back(aB2DHomMatrixAndBColor);
+ }
+
+ aCStart = aCEnd;
+ fOffsetStart = fOffsetEnd;
}
}
void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
{
- if(mnColorSteps.size() <= 1)
+ // no color at all, done
+ if (mnColorSteps.empty())
+ return;
+
+ // just single color, done
+ if (mnColorSteps.size() < 2)
{
rBColor = mnColorSteps.front().getColor();
+ return;
}
- else
- {
- const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo));
-
- const basegfx::BColor maStart(mnColorSteps.front().getColor());
- const basegfx::BColor maEnd(mnColorSteps.back().getColor());
- rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
- }
+ // 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(mnColorSteps, fScaler, mnRequestedSteps);
}
GeoTexSvxGradientAxial::GeoTexSvxGradientAxial(
diff --git a/include/basegfx/utils/gradienttools.hxx b/include/basegfx/utils/gradienttools.hxx
index 289fc730b790..793d07a75577 100644
--- a/include/basegfx/utils/gradienttools.hxx
+++ b/include/basegfx/utils/gradienttools.hxx
@@ -58,14 +58,18 @@ namespace basegfx
class UNLESS_MERGELIBS(BASEGFX_DLLPUBLIC) ColorStep
{
private:
+ // offset in the range of [0.0 .. 1.0], checked & force by constructor
double mfOffset;
+
+ // color of ColorStep entry
BColor maColor;
public:
// constructor - defaults are needed to have a default constructor
// e.g. for usage in std::vector::insert
+ // ensure [0.0 .. 1.0] range for mfOffset
ColorStep(double fOffset = 0.0, const BColor& rColor = BColor())
- : mfOffset(fOffset)
+ : mfOffset(std::max(0.0, std::min(fOffset, 1.0)))
, maColor(rColor)
{
}
@@ -86,9 +90,10 @@ namespace basegfx
/* MCGR: Provide ColorSteps definition to the FillGradientAttribute
- This array is sorted ascending by offsets, from lowest to
- highest. Since all this primitive data definition is read-only,
- this can be guaranteed by forcing/checking this in the constructor.
+ This array should be sorted ascending by offsets, from lowest to
+ highest. Since all the primitive data definition where it is used
+ is read-only, this can/will be guaranteed by forcing/checking this
+ in the constructor, see ::FillGradientAttribute
*/
typedef std::vector<ColorStep> ColorSteps;
@@ -179,6 +184,16 @@ namespace basegfx
namespace utils
{
+ /* Helper to grep the correct ColorStep out of
+ ColorSteps and interpolate as needed for given
+ relative value in fScaler in the range of [0.0 .. 1.0].
+ It also takes care of evtl. given RequestedSteps.
+ */
+ BASEGFX_DLLPUBLIC BColor modifyBColor(
+ const ColorSteps& rColorSteps,
+ double fScaler,
+ sal_uInt32 nRequestedSteps);
+
/* Helper to calculate numberOfSteps needed to represent
gradient for the given two colors:
- to define only based on color distance, give 0 == nRequestedSteps
diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
index 4c2849366153..2c29c6380ff0 100644
--- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx
+++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
@@ -498,13 +498,93 @@ namespace drawinglayer::primitive2d
basegfx::ColorSteps aColorSteps;
- // test code here, will be removed later
- // if(false)
- // {
- // aStart = basegfx::BColor(1.0, 0.0, 0.0); // red
- // aEnd = basegfx::BColor(0.0, 0.0, 1.0); // blue
- // aColorSteps.emplace_back(0.25, basegfx::BColor(0.0, 1.0, 0.0)); // green@25%
- // }
+ // test code here, can/will be removed later
+ static sal_uInt32 nUseGradientSteps(0);
+ switch(nUseGradientSteps)
+ {
+ case 1:
+ {
+ // just test a nice valid gradient
+ aStart = basegfx::BColor(1.0, 0.0, 0.0); // red
+ aEnd = basegfx::BColor(0.0, 0.0, 1.0); // blue
+ aColorSteps.emplace_back(0.25, basegfx::BColor(0.0, 1.0, 0.0)); // green@25%
+ aColorSteps.emplace_back(0.50, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ aColorSteps.emplace_back(0.75, basegfx::BColor(1.0, 0.0, 1.0)); // pink@75%
+ break;
+ }
+
+ case 2:
+ {
+ // single added in-between, no change of start/end
+ aColorSteps.emplace_back(0.5, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ break;
+ }
+
+ case 3:
+ {
+ // check additional StartColor, the one given directly has to win
+ aColorSteps.emplace_back(0.0, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ break;
+ }
+
+ case 4:
+ {
+ // check additional EndColor, the one given directly has to win
+ aColorSteps.emplace_back(1.0, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ break;
+ }
+
+ case 5:
+ {
+ // check invalid color (too low index), has to be ignored
+ aColorSteps.emplace_back(-1.0, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ break;
+ }
+
+ case 6:
+ {
+ // check invalid color (too high index), has to be ignored
+ aColorSteps.emplace_back(2.0, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ break;
+ }
+
+ case 7:
+ {
+ // check in-between single-color part
+ aColorSteps.emplace_back(0.3, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ aColorSteps.emplace_back(0.7, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ break;
+ }
+
+ case 8:
+ {
+ // check in-between single-color parts
+ aColorSteps.emplace_back(0.2, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ aColorSteps.emplace_back(0.4, aEnd);
+ aColorSteps.emplace_back(0.6, aStart);
+ aColorSteps.emplace_back(0.8, basegfx::BColor(1.0, 1.0, 0.0)); // yellow@50%
+ break;
+ }
+
+ case 9:
+ {
+ // check single-color start area
+ aColorSteps.emplace_back(0.6, aStart);
+ break;
+ }
+
+ case 10:
+ {
+ // check single-color end area
+ aColorSteps.emplace_back(0.6, aEnd);
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
aGradient = attribute::FillGradientAttribute(
XGradientStyleToGradientStyle(aXGradient.GetGradientStyle()),