1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
12 #include <basegfx/color/bcolor.hxx>
13 #include <basegfx/basegfxdllapi.h>
15 #include <com/sun/star/awt/GradientStyle.hpp>
16 #include <tools/degree.hxx>
17 #include <boost/property_tree/ptree_fwd.hpp>
21 class BColorModifierStack
;
26 /* MCGR: Provide ColorStop definition
28 This is the needed combination of offset and color:
31 - being in the range of [0.0 .. 1.0] (unit range)
32 - offsets outside are an error
33 - lowest/1st value equivalent to StartColor
34 - highest/last value equivalent to EndColor
35 - missing 0.0/1.0 entries are allowed
36 - at least one value (usually 0.0, StartColor) is required
37 - this allows to avoid massive testing in all places where
38 this data has to be accessed
41 - RGB with unit values [0.0 .. 1.0]
43 These definitions are packed in a std::vector<ColorStop> ColorStops,
46 class BASEGFX_DLLPUBLIC BColorStop
49 // offset in the range of [0.0 .. 1.0]
52 // RGB color of ColorStop entry
56 // constructor - defaults are needed to have a default constructor
57 // e.g. for usage in std::vector::insert (even when only reducing)
58 // ensure [0.0 .. 1.0] range for mfStopOffset
59 BColorStop(double fStopOffset
= 0.0, const BColor
& rStopColor
= BColor())
60 : mfStopOffset(fStopOffset
)
61 , maStopColor(rStopColor
)
63 // NOTE: I originally *corrected* mfStopOffset here by using
64 // mfStopOffset(std::max(0.0, std::min(fOffset, 1.0)))
65 // While that is formally correct, it moves an invalid
66 // entry to 0.0 or 1.0, thus creating additional wrong
67 // Start/EndColor entries. That may then 'overlay' the
68 // correct entry when corrections are applied to the
69 // vector of entries (see sortAndCorrectColorStops)
70 // which leads to getting the wanted Start/EndColor
71 // to be factically deleted, what is an error.
74 double getStopOffset() const { return mfStopOffset
; }
75 const BColor
& getStopColor() const { return maStopColor
; }
77 // needed for std::sort
78 bool operator<(const BColorStop
& rCandidate
) const
80 return getStopOffset() < rCandidate
.getStopOffset();
83 bool operator==(const BColorStop
& rCandidate
) const
85 return getStopOffset() == rCandidate
.getStopOffset()
86 && getStopColor() == rCandidate
.getStopColor();
89 bool operator!=(const BColorStop
& rCandidate
) const { return !(*this == rCandidate
); }
92 /* MCGR: Provide ColorStops definition to the FillGradientAttribute
94 This array should be sorted ascending by offsets, from lowest to
95 highest. Since all the primitive data definition where it is used
96 is read-only, this can/will be guaranteed by forcing/checking this
97 in the constructor, see ::FillGradientAttribute
99 class BASEGFX_DLLPUBLIC BColorStops final
: public std::vector
<BColorStop
>
102 explicit BColorStops()
106 BColorStops(const BColorStops
& other
)
110 BColorStops(BColorStops
&& other
) noexcept
111 : vector(std::move(other
))
114 BColorStops(std::initializer_list
<BColorStop
> init
)
118 BColorStops(const_iterator first
, const_iterator last
)
119 : vector(first
, last
)
123 // constructor with two colors to explicitly create a
124 // BColorStops for StartColor @0.0 & EndColor @1.0
125 BColorStops(const BColor
& rStart
, const BColor
& rEnd
);
127 BColorStops
& operator=(const BColorStops
& r
)
129 vector::operator=(r
);
132 BColorStops
& operator=(BColorStops
&& r
) noexcept
134 vector::operator=(std::move(r
));
138 // helper data struct to support buffering entries in
139 // gradient texture mapping, see usages for more info
140 struct BColorStopRange
142 basegfx::BColor maColorStart
;
143 basegfx::BColor maColorEnd
;
144 double mfOffsetStart
;
156 /* Helper to grep the correct ColorStop out of
157 ColorStops and interpolate as needed for given
158 relative value in fPosition in the range of [0.0 .. 1.0].
159 It also takes care of evtl. given RequestedSteps.
161 BColor
getInterpolatedBColor(double fPosition
, sal_uInt32 nRequestedSteps
,
162 BColorStopRange
& rLastColorStopRange
) const;
164 /* Tooling method that allows to replace the StartColor in a
165 vector of ColorStops. A vector in 'ordered state' is expected,
166 so you may use/have used sortAndCorrect.
167 This method is for convenience & backwards compatibility, please
168 think about handling multi-colored gradients directly.
170 void replaceStartColor(const BColor
& rStart
);
172 /* Tooling method that allows to replace the EndColor in a
173 vector of ColorStops. A vector in 'ordered state' is expected,
174 so you may use/have used sortAndCorrect.
175 This method is for convenience & backwards compatibility, please
176 think about handling multi-colored gradients directly.
178 void replaceEndColor(const BColor
& rEnd
);
180 /* Tooling method to linearly blend the Colors contained in
181 a given ColorStop vector against a given Color using the
182 given intensity values.
183 The intensity values fStartIntensity, fEndIntensity are
184 in the range of [0.0 .. 1.0] and describe how much the
185 blend is supposed to be done at the start color position
186 and the end color position respectively, where 0.0 means
187 to fully use the given BlendColor, 1.0 means to not change
188 the existing color in the ColorStop.
189 Every color entry in the given ColorStop is blended
190 relative to it's StopPosition, interpolating the
191 given intensities with the range [0.0 .. 1.0] to do so.
193 void blendToIntensity(double fStartIntensity
, double fEndIntensity
, const BColor
& rBlendColor
);
195 /* Tooling method to guarantee sort and correctness for
196 the given ColorStops vector.
197 A vector fulfilling these conditions is called to be
200 At return, the following conditions are guaranteed:
201 - contains no ColorStops with offset < 0.0 (will
203 - contains no ColorStops with offset > 1.0 (will
205 - ColorStops with identical offsets are now allowed
206 - will be sorted from lowest offset to highest
209 - It can happen that the result is empty
210 - It is allowed to have consecutive entries with
211 the same color, this represents single-color
212 regions inside the gradient
213 - A entry with 0.0 is not required or forced, so
214 no 'StartColor' is technically required
215 - A entry with 1.0 is not required or forced, so
216 no 'EndColor' is technically required
218 All this is done in one run (sort + O(N)) without
219 creating a copy of the data in any form
221 void sortAndCorrect();
223 // check if we need last-ColorStop-correction. This returns true if the last
224 // two ColorStops have the same offset but different Colors. In that case the
225 // tessellation for gradients does have to create an extra ending/closing entry
226 bool checkPenultimate() const;
228 /* Tooling method to check if a ColorStop vector is defined
229 by a single color. It returns true if this is the case.
230 If true is returned, rSingleColor contains that single
231 color for convenience.
232 NOTE: If no ColorStop is defined, a fallback to BColor-default
233 (which is black) and true will be returned
235 bool isSingleColor(BColor
& rSingleColor
) const;
237 /* Tooling method to reverse ColorStops, including offsets.
238 When also mirroring offsets a valid sort keeps valid.
240 void reverseColorStops();
242 // createSpaceAtStart creates fOffset space at start by
243 // translating/scaling all entries to the right
244 void createSpaceAtStart(double fOffset
);
246 // removeSpaceAtStart removes fOffset space from start by
247 // translating/scaling entries more or equal to fOffset
248 // to the left. Entries less than fOffset will be removed
249 void removeSpaceAtStart(double fOffset
);
251 // try to detect if an empty/no-color-change area exists
252 // at the start and return offset to it. Returns 0.0 if not.
253 double detectPossibleOffsetAtStart() const;
255 // returns true if the color stops are symmetrical in color and offset, otherwise false.
256 bool isSymmetrical() const;
257 // assume that the color stops represent an Axial gradient
258 // and replace with gradient stops to represent the same
259 // gradient as linear gradient
262 // apply Steps as 'hard' color stops
263 void doApplySteps(sal_uInt16 nStepCount
);
265 // Apply BColorModifierStack changes
266 void tryToApplyBColorModifierStack(const BColorModifierStack
& rBColorModifierStack
);
268 // check if local and given BColorStops have same count and distances,
270 bool sameSizeAndDistances(const BColorStops
& rComp
) const;
273 class BASEGFX_DLLPUBLIC BGradient final
276 css::awt::GradientStyle eStyle
;
278 // MCGS: ColorStops in the range [0.0 .. 1.0], including StartColor/EndColor
279 basegfx::BColorStops aColorStops
;
285 sal_uInt16 nIntensStart
;
286 sal_uInt16 nIntensEnd
;
287 sal_uInt16 nStepCount
;
289 static std::string
GradientStyleToString(css::awt::GradientStyle eStyle
);
293 BGradient(const basegfx::BColorStops
& rColorStops
,
294 css::awt::GradientStyle eStyle
= css::awt::GradientStyle_LINEAR
,
295 Degree10 nAngle
= 0_deg10
, sal_uInt16 nXOfs
= 50, sal_uInt16 nYOfs
= 50,
296 sal_uInt16 nBorder
= 0, sal_uInt16 nStartIntens
= 100, sal_uInt16 nEndIntens
= 100,
297 sal_uInt16 nSteps
= 0);
299 bool operator==(const BGradient
& rGradient
) const;
301 void SetGradientStyle(css::awt::GradientStyle eNewStyle
) { eStyle
= eNewStyle
; }
302 void SetColorStops(const basegfx::BColorStops
& rSteps
);
303 void SetAngle(Degree10 nNewAngle
) { nAngle
= nNewAngle
; }
304 void SetBorder(sal_uInt16 nNewBorder
) { nBorder
= nNewBorder
; }
305 void SetXOffset(sal_uInt16 nNewOffset
) { nOfsX
= nNewOffset
; }
306 void SetYOffset(sal_uInt16 nNewOffset
) { nOfsY
= nNewOffset
; }
307 void SetStartIntens(sal_uInt16 nNewIntens
) { nIntensStart
= nNewIntens
; }
308 void SetEndIntens(sal_uInt16 nNewIntens
) { nIntensEnd
= nNewIntens
; }
309 void SetSteps(sal_uInt16 nSteps
) { nStepCount
= nSteps
; }
311 css::awt::GradientStyle
GetGradientStyle() const { return eStyle
; }
312 const basegfx::BColorStops
& GetColorStops() const { return aColorStops
; }
313 Degree10
GetAngle() const { return nAngle
; }
314 sal_uInt16
GetBorder() const { return nBorder
; }
315 sal_uInt16
GetXOffset() const { return nOfsX
; }
316 sal_uInt16
GetYOffset() const { return nOfsY
; }
317 sal_uInt16
GetStartIntens() const { return nIntensStart
; }
318 sal_uInt16
GetEndIntens() const { return nIntensEnd
; }
319 sal_uInt16
GetSteps() const { return nStepCount
; }
321 boost::property_tree::ptree
dumpAsJSON() const;
322 static BGradient
fromJSON(std::u16string_view rJSON
);
325 // - border correction/integration
326 // - apply StartStopIntensity to color stops
327 // - convert type from 'axial' to linear
328 // - apply Steps as 'hard' color stops
329 void tryToRecreateBorder(basegfx::BColorStops
* pAssociatedTransparencyStops
= nullptr);
330 void tryToApplyBorder();
331 void tryToApplyStartEndIntensity();
333 // If a linear gradient is symmetrical it is converted to an axial gradient.
334 // Does nothing in other cases and for other gradient types.
335 void tryToConvertToAxial();
336 void tryToApplyAxial();
337 void tryToApplySteps();
341 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */