summaryrefslogtreecommitdiff
path: root/basegfx
diff options
context:
space:
mode:
authorArmin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de>2023-03-21 13:13:15 +0100
committerArmin Le Grand <Armin.Le.Grand@me.com>2023-03-21 15:53:06 +0000
commit808c5bf66370f78f36e98887db0848ee7e55bb3a (patch)
tree11ddcb034efd1d32663a774a85797fff390762e8 /basegfx
parent5ccbdcf182851375486386c0df8bb3f04cf2f9cd (diff)
MCGR: Model data changes for ColorSteps (II)
The biggest change here is to allow multiple ColorStops with the same Offset. That allows to define gradients with 'hard' color changes at any place using two ColorStops with the same Offset and different Colors. This required quite some adaptions, but works well. Also removed in this context checking for all Colors being the same to not mix up things. Also works well. Also changed the need for having Start/EndColors AKA ColorStops for 0.0 and 1.0 in place, instead 'imply' the 1st ColorStop to also define the StartColor and the last one the EndColor. This allows e.g. Gradient definitions with two GradientStops at the same Offset e.g. 0.5 with different colors to already define a full Gradient. Also added a tooling method to reverse ColorSteps, which changes the order and mirrors the Offsets (what even keeps an existing sort valid). This is useful e.g. for GradientAxial which is the only one where for decomposition the Gradient had to be interpreted 'reverse' since it's defined from center to edge, but for creating correct filled polygons to represent this the inverse order had to be used, creating polygons from edge to center. This led to 'wild' code for this one of six cases and prevented unifications with the other cases (also made your brain flip). Thus I adapted this now to use the reversed ColorSteps consequently, and the same principle loops than the other implementations to make things easier for the future and to use common tooling. Change-Id: If2943348d17d5b9cd165f4d78f22638a1dff5237 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149208 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Diffstat (limited to 'basegfx')
-rw-r--r--basegfx/source/tools/gradienttools.cxx76
1 files changed, 37 insertions, 39 deletions
diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx
index d43f7899121a..6055143d5abd 100644
--- a/basegfx/source/tools/gradienttools.cxx
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -264,6 +264,17 @@ namespace basegfx
namespace utils
{
+ /* Tooling method to reverse ColorStops, including offsets.
+ When also mirroring offsets a valid sort keeps valid.
+ */
+ void reverseColorStops(ColorStops& rColorStops)
+ {
+ // can use std::reverse, but also need to adapt offset(s)
+ std::reverse(rColorStops.begin(), rColorStops.end());
+ for (auto& candidate : rColorStops)
+ candidate = ColorStop(1.0 - candidate.getStopOffset(), candidate.getStopColor());
+ }
+
/* Tooling method to convert UNO API data to ColorStops
This will try to extract ColorStop data from the given
Any, so if it's of type awt::Gradient2 that data will be
@@ -374,13 +385,8 @@ namespace basegfx
be removed)
- contains no ColorStops with offset > 1.0 (will
be removed)
- - contains no two ColorStops with identical offsets
- (will be removed, 1st one/smallest offset wins
- which is also given by sort tooling)
+ - ColorStops with identical offsets are now allowed
- will be sorted from lowest offset to highest
- - if all colors are the same, the content will
- be reduced to a single entry with offset 0.0
- (force to StartColor)
Some more notes:
- It can happen that the result is empty
@@ -404,8 +410,15 @@ namespace basegfx
if (1 == rColorStops.size())
{
// no gradient at all, but preserve given color
- // and force it to be the StartColor
- rColorStops[0] = ColorStop(0.0, rColorStops[0].getStopColor());
+ // evtl. correct offset to be in valid range [0.0 .. 1.0]
+ // NOTE: This does not move it to 0.0 or 1.0, it *can* still
+ // be somewhere in-between what is allowed
+ rColorStops[0] = ColorStop(
+ std::max(0.0, std::min(1.0, rColorStops[0].getStopOffset())),
+ rColorStops[0].getStopColor());
+
+ // done
+ return;
}
// start with sorting the input data. Remember that
@@ -414,9 +427,6 @@ namespace basegfx
std::sort(rColorStops.begin(), rColorStops.end());
// prepare status values
- bool bSameColorInit(false);
- bool bAllTheSameColor(true);
- basegfx::BColor aFirstColor;
size_t write(0);
// use the paradigm of a band machine with two heads, read
@@ -428,7 +438,7 @@ namespace basegfx
// get offset of entry at read position
const double rOff(rColorStops[read].getStopOffset());
- // step over < 0 values
+ // step over < 0 values, these are outside and will be removed
if (basegfx::fTools::less(rOff, 0.0))
continue;
@@ -438,23 +448,9 @@ namespace basegfx
break;
// entry is valid value at read position
-
- // check/init for all-the-same color
- if(bSameColorInit)
- {
- // already initialized, compare
- bAllTheSameColor = bAllTheSameColor && aFirstColor == rColorStops[read].getStopColor();
- }
- else
- {
- // do initialize, remember 1st valid color
- bSameColorInit = true;
- aFirstColor = rColorStops[read].getStopColor();
- }
-
// copy if write target is empty (write at start) or when
- // write target is different to read
- if (0 == write || rOff != rColorStops[write-1].getStopOffset())
+ // write target is different to read in color or offset
+ if (0 == write || !(rColorStops[read] == rColorStops[write-1]))
{
if (write != read)
{
@@ -473,14 +469,6 @@ namespace basegfx
{
rColorStops.resize(write);
}
-
- if (bSameColorInit && bAllTheSameColor && rColorStops.size() > 1)
- {
- // id all-the-same color is detected, reset to single
- // entry, but also force to StartColor and preserve the color
- rColorStops.resize(1);
- rColorStops[0] = ColorStop(0.0, aFirstColor);
- }
}
BColor modifyBColor(
@@ -493,11 +481,13 @@ namespace basegfx
return BColor();
// outside range -> at start
- if (fScaler <= 0.0)
+ const double fMin(rColorStops.front().getStopOffset());
+ if (fScaler < fMin)
return rColorStops.front().getStopColor();
// outside range -> at end
- if (fScaler >= 1.0)
+ const double fMax(rColorStops.back().getStopOffset());
+ if (fScaler > fMax)
return rColorStops.back().getStopColor();
// special case for the 'classic' case with just two colors:
@@ -505,6 +495,9 @@ namespace basegfx
// by avoiding some calculations and an O(log(N)) array access
if (2 == rColorStops.size())
{
+ if (fTools::equal(fMin, fMax))
+ return rColorStops.front().getStopColor();
+
const basegfx::BColor aCStart(rColorStops.front().getStopColor());
const basegfx::BColor aCEnd(rColorStops.back().getStopColor());
const sal_uInt32 nSteps(
@@ -513,6 +506,11 @@ namespace basegfx
aCStart,
aCEnd));
+ // we need to extend the interpolation to the local
+ // range of ColorStops. Despite having two ColorStops
+ // these are not necessarily at 0.0 and 1.0, so mabe
+ // not the classical Start/EndColor (what is allowed)
+ fScaler = (fScaler - fMin) / (fMax - fMin);
return basegfx::interpolate(
aCStart,
aCEnd,
@@ -525,7 +523,7 @@ namespace basegfx
// debug test code created by the stl. In a pro version,
// all is good/fast as expected
const auto upperBound(
- std::lower_bound(
+ std::upper_bound(
rColorStops.begin(),
rColorStops.end(),
ColorStop(fScaler),