MCGR: Use tryToRecreateBorder for better BW comp with LO
[LibreOffice.git] / include / basegfx / utils / bgradient.hxx
blob49598c8266fa36f5c959b0032482a398ba362920
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #pragma once
12 #include <config_options.h>
13 #include <basegfx/color/bcolor.hxx>
14 #include <basegfx/basegfxdllapi.h>
15 #include <vector>
16 #include <com/sun/star/awt/Gradient2.hpp>
17 #include <com/sun/star/awt/GradientStyle.hpp>
18 #include <tools/degree.hxx>
19 #include <boost/property_tree/ptree_fwd.hpp>
21 namespace com
23 namespace sun
25 namespace star
27 namespace uno
29 class Any;
35 namespace basegfx
37 /* MCGR: Provide ColorStop definition
39 This is the needed combination of offset and color:
41 Offset is defined as:
42 - being in the range of [0.0 .. 1.0] (unit range)
43 - offsets outside are an error
44 - lowest/1st value equivalent to StartColor
45 - highest/last value equivalent to EndColor
46 - missing 0.0/1.0 entries are allowed
47 - at least one value (usually 0.0, StartColor) is required
48 - this allows to avoid massive testing in all places where
49 this data has to be accessed
51 Color is defined as:
52 - RGB with unit values [0.0 .. 1.0]
54 These definitions are packed in a std::vector<ColorStop> ColorStops,
55 see typedef below.
57 class BASEGFX_DLLPUBLIC BColorStop
59 private:
60 // offset in the range of [0.0 .. 1.0]
61 double mfStopOffset;
63 // RGB color of ColorStop entry
64 BColor maStopColor;
66 public:
67 // constructor - defaults are needed to have a default constructor
68 // e.g. for usage in std::vector::insert (even when only reducing)
69 // ensure [0.0 .. 1.0] range for mfStopOffset
70 BColorStop(double fStopOffset = 0.0, const BColor& rStopColor = BColor())
71 : mfStopOffset(fStopOffset)
72 , maStopColor(rStopColor)
74 // NOTE: I originally *corrected* mfStopOffset here by using
75 // mfStopOffset(std::max(0.0, std::min(fOffset, 1.0)))
76 // While that is formally correct, it moves an invalid
77 // entry to 0.0 or 1.0, thus creating additional wrong
78 // Start/EndColor entries. That may then 'overlay' the
79 // correct entry when corrections are applied to the
80 // vector of entries (see sortAndCorrectColorStops)
81 // which leads to getting the wanted Start/EndColor
82 // to be factically deleted, what is an error.
85 double getStopOffset() const { return mfStopOffset; }
86 const BColor& getStopColor() const { return maStopColor; }
88 // needed for std::sort
89 bool operator<(const BColorStop& rCandidate) const
91 return getStopOffset() < rCandidate.getStopOffset();
94 bool operator==(const BColorStop& rCandidate) const
96 return getStopOffset() == rCandidate.getStopOffset()
97 && getStopColor() == rCandidate.getStopColor();
101 /* MCGR: Provide ColorStops definition to the FillGradientAttribute
103 This array should be sorted ascending by offsets, from lowest to
104 highest. Since all the primitive data definition where it is used
105 is read-only, this can/will be guaranteed by forcing/checking this
106 in the constructor, see ::FillGradientAttribute
108 class BASEGFX_DLLPUBLIC BColorStops final : public std::vector<BColorStop>
110 private:
111 void setColorStopSequence(const css::awt::ColorStopSequence& rColorStops);
113 public:
114 explicit BColorStops()
115 : vector()
118 BColorStops(const BColorStops& other)
119 : vector(other)
122 BColorStops(BColorStops&& other) noexcept
123 : vector(std::move(other))
126 BColorStops(std::initializer_list<BColorStop> init)
127 : vector(init)
130 BColorStops(const_iterator first, const_iterator last)
131 : vector(first, last)
134 BColorStops(const css::awt::ColorStopSequence& rColorStops);
136 // needs true == rVal.has<css::awt::ColorStopSequence>()
137 BColorStops(const css::uno::Any& rVal);
139 // constructor with two colors to explicitly create a
140 // BColorStops for StartColor @0.0 & EndColor @1.0
141 BColorStops(const BColor& rStart, const BColor& rEnd);
143 BColorStops& operator=(const BColorStops& r)
145 vector::operator=(r);
146 return *this;
148 BColorStops& operator=(BColorStops&& r) noexcept
150 vector::operator=(std::move(r));
151 return *this;
154 // helper data struct to support buffering entries in
155 // gradient texture mapping, see usages for more info
156 struct BColorStopRange
158 basegfx::BColor maColorStart;
159 basegfx::BColor maColorEnd;
160 double mfOffsetStart;
161 double mfOffsetEnd;
163 BColorStopRange()
164 : maColorStart()
165 , maColorEnd()
166 , mfOffsetStart(0.0)
167 , mfOffsetEnd(0.0)
172 /* Helper to grep the correct ColorStop out of
173 ColorStops and interpolate as needed for given
174 relative value in fPosition in the range of [0.0 .. 1.0].
175 It also takes care of evtl. given RequestedSteps.
177 BColor getInterpolatedBColor(double fPosition, sal_uInt32 nRequestedSteps,
178 BColorStopRange& rLastColorStopRange) const;
180 /* Tooling method that allows to replace the StartColor in a
181 vector of ColorStops. A vector in 'ordered state' is expected,
182 so you may use/have used sortAndCorrect.
183 This method is for convenience & backwards compatibility, please
184 think about handling multi-colored gradients directly.
186 void replaceStartColor(const BColor& rStart);
188 /* Tooling method that allows to replace the EndColor in a
189 vector of ColorStops. A vector in 'ordered state' is expected,
190 so you may use/have used sortAndCorrect.
191 This method is for convenience & backwards compatibility, please
192 think about handling multi-colored gradients directly.
194 void replaceEndColor(const BColor& rEnd);
196 /* Tooling method to linearly blend the Colors contained in
197 a given ColorStop vector against a given Color using the
198 given intensity values.
199 The intensity values fStartIntensity, fEndIntensity are
200 in the range of [0.0 .. 1.0] and describe how much the
201 blend is supposed to be done at the start color position
202 and the end color position respectively, where 0.0 means
203 to fully use the given BlendColor, 1.0 means to not change
204 the existing color in the ColorStop.
205 Every color entry in the given ColorStop is blended
206 relative to it's StopPosition, interpolating the
207 given intensities with the range [0.0 .. 1.0] to do so.
209 void blendToIntensity(double fStartIntensity, double fEndIntensity, const BColor& rBlendColor);
211 /* Tooling method to guarantee sort and correctness for
212 the given ColorStops vector.
213 A vector fulfilling these conditions is called to be
214 in 'ordered state'.
216 At return, the following conditions are guaranteed:
217 - contains no ColorStops with offset < 0.0 (will
218 be removed)
219 - contains no ColorStops with offset > 1.0 (will
220 be removed)
221 - ColorStops with identical offsets are now allowed
222 - will be sorted from lowest offset to highest
224 Some more notes:
225 - It can happen that the result is empty
226 - It is allowed to have consecutive entries with
227 the same color, this represents single-color
228 regions inside the gradient
229 - A entry with 0.0 is not required or forced, so
230 no 'StartColor' is technically required
231 - A entry with 1.0 is not required or forced, so
232 no 'EndColor' is technically required
234 All this is done in one run (sort + O(N)) without
235 creating a copy of the data in any form
237 void sortAndCorrect();
239 // check if we need last-ColorStop-correction. This returns true if the last
240 // two ColorStops have the same offset but different Colors. In that case the
241 // tessellation for gradients does have to create an extra ending/closing entry
242 bool checkPenultimate() const;
244 /* Tooling method to fill a awt::ColorStopSequence with
245 the data from the given ColorStops. This is used in
246 UNO API implementations.
248 css::awt::ColorStopSequence getAsColorStopSequence() const;
250 /* Tooling method to check if a ColorStop vector is defined
251 by a single color. It returns true if this is the case.
252 If true is returned, rSingleColor contains that single
253 color for convenience.
254 NOTE: If no ColorStop is defined, a fallback to BColor-default
255 (which is black) and true will be returned
257 bool isSingleColor(BColor& rSingleColor) const;
259 /* Tooling method to reverse ColorStops, including offsets.
260 When also mirroring offsets a valid sort keeps valid.
262 void reverseColorStops();
264 // createSpaceAtStart creates fOffset space at start by
265 // translating/scaling all entries to the right
266 void createSpaceAtStart(double fOffset);
268 // removeSpaceAtStart removes fOffset space from start by
269 // translating/scaling entries more or equal to fOffset
270 // to the left. Entries less than fOffset will be removed
271 void removeSpaceAtStart(double fOffset);
273 // try to detect if an empty/no-color-change area exists
274 // at the start and return offset to it. Returns 0.0 if not.
275 double detectPossibleOffsetAtStart() const;
278 class BASEGFX_DLLPUBLIC BGradient final
280 private:
281 css::awt::GradientStyle eStyle;
283 // MCGS: ColorStops in the range [0.0 .. 1.0], including StartColor/EndColor
284 basegfx::BColorStops aColorStops;
286 Degree10 nAngle;
287 sal_uInt16 nBorder;
288 sal_uInt16 nOfsX;
289 sal_uInt16 nOfsY;
290 sal_uInt16 nIntensStart;
291 sal_uInt16 nIntensEnd;
292 sal_uInt16 nStepCount;
294 static std::string GradientStyleToString(css::awt::GradientStyle eStyle);
295 void setGradient2(const css::awt::Gradient2& rGradient2);
297 public:
298 BGradient();
299 BGradient(const basegfx::BColorStops& rColorStops,
300 css::awt::GradientStyle eStyle = css::awt::GradientStyle_LINEAR,
301 Degree10 nAngle = 0_deg10, sal_uInt16 nXOfs = 50, sal_uInt16 nYOfs = 50,
302 sal_uInt16 nBorder = 0, sal_uInt16 nStartIntens = 100, sal_uInt16 nEndIntens = 100,
303 sal_uInt16 nSteps = 0);
304 BGradient(const css::awt::Gradient2& rGradient2);
306 // needs true == (rVal.has<css::awt::Gradient>() || rVal.has<css::awt::Gradient2>())
307 BGradient(const css::uno::Any& rVal);
309 bool operator==(const BGradient& rGradient) const;
311 void SetGradientStyle(css::awt::GradientStyle eNewStyle) { eStyle = eNewStyle; }
312 void SetColorStops(const basegfx::BColorStops& rSteps);
313 void SetAngle(Degree10 nNewAngle) { nAngle = nNewAngle; }
314 void SetBorder(sal_uInt16 nNewBorder) { nBorder = nNewBorder; }
315 void SetXOffset(sal_uInt16 nNewOffset) { nOfsX = nNewOffset; }
316 void SetYOffset(sal_uInt16 nNewOffset) { nOfsY = nNewOffset; }
317 void SetStartIntens(sal_uInt16 nNewIntens) { nIntensStart = nNewIntens; }
318 void SetEndIntens(sal_uInt16 nNewIntens) { nIntensEnd = nNewIntens; }
319 void SetSteps(sal_uInt16 nSteps) { nStepCount = nSteps; }
321 css::awt::GradientStyle GetGradientStyle() const { return eStyle; }
322 const basegfx::BColorStops& GetColorStops() const { return aColorStops; }
323 Degree10 GetAngle() const { return nAngle; }
324 sal_uInt16 GetBorder() const { return nBorder; }
325 sal_uInt16 GetXOffset() const { return nOfsX; }
326 sal_uInt16 GetYOffset() const { return nOfsY; }
327 sal_uInt16 GetStartIntens() const { return nIntensStart; }
328 sal_uInt16 GetEndIntens() const { return nIntensEnd; }
329 sal_uInt16 GetSteps() const { return nStepCount; }
331 boost::property_tree::ptree dumpAsJSON() const;
332 static BGradient fromJSON(std::u16string_view rJSON);
334 /// Tooling method to fill awt::Gradient2 from data contained in the given basegfx::BGradient
335 css::awt::Gradient2 getAsGradient2() const;
337 /// Tooling to handle border correction/integration and StartStopIntensity
338 void tryToRecreateBorder(basegfx::BColorStops* pAssociatedTransparencyStops = nullptr);
339 void tryToApplyBorder();
340 void tryToApplyStartEndIntensity();
344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */