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 <config_options.h>
13 #include <basegfx/color/bcolor.hxx>
14 #include <basegfx/basegfxdllapi.h>
16 #include <com/sun/star/awt/GradientStyle.hpp>
17 #include <tools/degree.hxx>
18 #include <boost/property_tree/ptree_fwd.hpp>
36 /* MCGR: Provide ColorStop definition
38 This is the needed combination of offset and color:
41 - being in the range of [0.0 .. 1.0] (unit range)
42 - offsets outside are an error
43 - lowest/1st value equivalent to StartColor
44 - highest/last value equivalent to EndColor
45 - missing 0.0/1.0 entries are allowed
46 - at least one value (usually 0.0, StartColor) is required
47 - this allows to avoid massive testing in all places where
48 this data has to be accessed
51 - RGB with unit values [0.0 .. 1.0]
53 These definitions are packed in a std::vector<ColorStop> ColorStops,
56 class BASEGFX_DLLPUBLIC BColorStop
59 // offset in the range of [0.0 .. 1.0]
62 // RGB color of ColorStop entry
66 // constructor - defaults are needed to have a default constructor
67 // e.g. for usage in std::vector::insert (even when only reducing)
68 // ensure [0.0 .. 1.0] range for mfStopOffset
69 BColorStop(double fStopOffset
= 0.0, const BColor
& rStopColor
= BColor())
70 : mfStopOffset(fStopOffset
)
71 , maStopColor(rStopColor
)
73 // NOTE: I originally *corrected* mfStopOffset here by using
74 // mfStopOffset(std::max(0.0, std::min(fOffset, 1.0)))
75 // While that is formally correct, it moves an invalid
76 // entry to 0.0 or 1.0, thus creating additional wrong
77 // Start/EndColor entries. That may then 'overlay' the
78 // correct entry when corrections are applied to the
79 // vector of entries (see sortAndCorrectColorStops)
80 // which leads to getting the wanted Start/EndColor
81 // to be factically deleted, what is an error.
84 double getStopOffset() const { return mfStopOffset
; }
85 const BColor
& getStopColor() const { return maStopColor
; }
87 // needed for std::sort
88 bool operator<(const BColorStop
& rCandidate
) const
90 return getStopOffset() < rCandidate
.getStopOffset();
93 bool operator==(const BColorStop
& rCandidate
) const
95 return getStopOffset() == rCandidate
.getStopOffset()
96 && getStopColor() == rCandidate
.getStopColor();
99 bool operator!=(const BColorStop
& rCandidate
) const { return !(*this == rCandidate
); }
102 /* MCGR: Provide ColorStops definition to the FillGradientAttribute
104 This array should be sorted ascending by offsets, from lowest to
105 highest. Since all the primitive data definition where it is used
106 is read-only, this can/will be guaranteed by forcing/checking this
107 in the constructor, see ::FillGradientAttribute
109 class BASEGFX_DLLPUBLIC BColorStops final
: public std::vector
<BColorStop
>
112 explicit BColorStops()
116 BColorStops(const BColorStops
& other
)
120 BColorStops(BColorStops
&& other
) noexcept
121 : vector(std::move(other
))
124 BColorStops(std::initializer_list
<BColorStop
> init
)
128 BColorStops(const_iterator first
, const_iterator last
)
129 : vector(first
, last
)
133 // constructor with two colors to explicitly create a
134 // BColorStops for StartColor @0.0 & EndColor @1.0
135 BColorStops(const BColor
& rStart
, const BColor
& rEnd
);
137 BColorStops
& operator=(const BColorStops
& r
)
139 vector::operator=(r
);
142 BColorStops
& operator=(BColorStops
&& r
) noexcept
144 vector::operator=(std::move(r
));
148 // helper data struct to support buffering entries in
149 // gradient texture mapping, see usages for more info
150 struct BColorStopRange
152 basegfx::BColor maColorStart
;
153 basegfx::BColor maColorEnd
;
154 double mfOffsetStart
;
166 /* Helper to grep the correct ColorStop out of
167 ColorStops and interpolate as needed for given
168 relative value in fPosition in the range of [0.0 .. 1.0].
169 It also takes care of evtl. given RequestedSteps.
171 BColor
getInterpolatedBColor(double fPosition
, sal_uInt32 nRequestedSteps
,
172 BColorStopRange
& rLastColorStopRange
) const;
174 /* Tooling method that allows to replace the StartColor in a
175 vector of ColorStops. A vector in 'ordered state' is expected,
176 so you may use/have used sortAndCorrect.
177 This method is for convenience & backwards compatibility, please
178 think about handling multi-colored gradients directly.
180 void replaceStartColor(const BColor
& rStart
);
182 /* Tooling method that allows to replace the EndColor in a
183 vector of ColorStops. A vector in 'ordered state' is expected,
184 so you may use/have used sortAndCorrect.
185 This method is for convenience & backwards compatibility, please
186 think about handling multi-colored gradients directly.
188 void replaceEndColor(const BColor
& rEnd
);
190 /* Tooling method to linearly blend the Colors contained in
191 a given ColorStop vector against a given Color using the
192 given intensity values.
193 The intensity values fStartIntensity, fEndIntensity are
194 in the range of [0.0 .. 1.0] and describe how much the
195 blend is supposed to be done at the start color position
196 and the end color position respectively, where 0.0 means
197 to fully use the given BlendColor, 1.0 means to not change
198 the existing color in the ColorStop.
199 Every color entry in the given ColorStop is blended
200 relative to it's StopPosition, interpolating the
201 given intensities with the range [0.0 .. 1.0] to do so.
203 void blendToIntensity(double fStartIntensity
, double fEndIntensity
, const BColor
& rBlendColor
);
205 /* Tooling method to guarantee sort and correctness for
206 the given ColorStops vector.
207 A vector fulfilling these conditions is called to be
210 At return, the following conditions are guaranteed:
211 - contains no ColorStops with offset < 0.0 (will
213 - contains no ColorStops with offset > 1.0 (will
215 - ColorStops with identical offsets are now allowed
216 - will be sorted from lowest offset to highest
219 - It can happen that the result is empty
220 - It is allowed to have consecutive entries with
221 the same color, this represents single-color
222 regions inside the gradient
223 - A entry with 0.0 is not required or forced, so
224 no 'StartColor' is technically required
225 - A entry with 1.0 is not required or forced, so
226 no 'EndColor' is technically required
228 All this is done in one run (sort + O(N)) without
229 creating a copy of the data in any form
231 void sortAndCorrect();
233 // check if we need last-ColorStop-correction. This returns true if the last
234 // two ColorStops have the same offset but different Colors. In that case the
235 // tessellation for gradients does have to create an extra ending/closing entry
236 bool checkPenultimate() const;
238 /* Tooling method to check if a ColorStop vector is defined
239 by a single color. It returns true if this is the case.
240 If true is returned, rSingleColor contains that single
241 color for convenience.
242 NOTE: If no ColorStop is defined, a fallback to BColor-default
243 (which is black) and true will be returned
245 bool isSingleColor(BColor
& rSingleColor
) const;
247 /* Tooling method to reverse ColorStops, including offsets.
248 When also mirroring offsets a valid sort keeps valid.
250 void reverseColorStops();
252 // createSpaceAtStart creates fOffset space at start by
253 // translating/scaling all entries to the right
254 void createSpaceAtStart(double fOffset
);
256 // removeSpaceAtStart removes fOffset space from start by
257 // translating/scaling entries more or equal to fOffset
258 // to the left. Entries less than fOffset will be removed
259 void removeSpaceAtStart(double fOffset
);
261 // try to detect if an empty/no-color-change area exists
262 // at the start and return offset to it. Returns 0.0 if not.
263 double detectPossibleOffsetAtStart() const;
265 // returns true if the color stops are symmetrical in color and offset, otherwise false.
266 bool isSymmetrical() const;
267 // assume that the color stops represent an Axial gradient
268 // and replace with gradient stops to represent the same
269 // gradient as linear gradient
272 // apply Steps as 'hard' color stops
273 void doApplySteps(sal_uInt16 nStepCount
);
276 class BASEGFX_DLLPUBLIC BGradient final
279 css::awt::GradientStyle eStyle
;
281 // MCGS: ColorStops in the range [0.0 .. 1.0], including StartColor/EndColor
282 basegfx::BColorStops aColorStops
;
288 sal_uInt16 nIntensStart
;
289 sal_uInt16 nIntensEnd
;
290 sal_uInt16 nStepCount
;
292 static std::string
GradientStyleToString(css::awt::GradientStyle eStyle
);
296 BGradient(const basegfx::BColorStops
& rColorStops
,
297 css::awt::GradientStyle eStyle
= css::awt::GradientStyle_LINEAR
,
298 Degree10 nAngle
= 0_deg10
, sal_uInt16 nXOfs
= 50, sal_uInt16 nYOfs
= 50,
299 sal_uInt16 nBorder
= 0, sal_uInt16 nStartIntens
= 100, sal_uInt16 nEndIntens
= 100,
300 sal_uInt16 nSteps
= 0);
302 bool operator==(const BGradient
& rGradient
) const;
304 void SetGradientStyle(css::awt::GradientStyle eNewStyle
) { eStyle
= eNewStyle
; }
305 void SetColorStops(const basegfx::BColorStops
& rSteps
);
306 void SetAngle(Degree10 nNewAngle
) { nAngle
= nNewAngle
; }
307 void SetBorder(sal_uInt16 nNewBorder
) { nBorder
= nNewBorder
; }
308 void SetXOffset(sal_uInt16 nNewOffset
) { nOfsX
= nNewOffset
; }
309 void SetYOffset(sal_uInt16 nNewOffset
) { nOfsY
= nNewOffset
; }
310 void SetStartIntens(sal_uInt16 nNewIntens
) { nIntensStart
= nNewIntens
; }
311 void SetEndIntens(sal_uInt16 nNewIntens
) { nIntensEnd
= nNewIntens
; }
312 void SetSteps(sal_uInt16 nSteps
) { nStepCount
= nSteps
; }
314 css::awt::GradientStyle
GetGradientStyle() const { return eStyle
; }
315 const basegfx::BColorStops
& GetColorStops() const { return aColorStops
; }
316 Degree10
GetAngle() const { return nAngle
; }
317 sal_uInt16
GetBorder() const { return nBorder
; }
318 sal_uInt16
GetXOffset() const { return nOfsX
; }
319 sal_uInt16
GetYOffset() const { return nOfsY
; }
320 sal_uInt16
GetStartIntens() const { return nIntensStart
; }
321 sal_uInt16
GetEndIntens() const { return nIntensEnd
; }
322 sal_uInt16
GetSteps() const { return nStepCount
; }
324 boost::property_tree::ptree
dumpAsJSON() const;
325 static BGradient
fromJSON(std::u16string_view rJSON
);
328 // - border correction/integration
329 // - apply StartStopIntensity to color stops
330 // - convert type from 'axial' to linear
331 // - apply Steps as 'hard' color stops
332 void tryToRecreateBorder(basegfx::BColorStops
* pAssociatedTransparencyStops
= nullptr);
333 void tryToApplyBorder();
334 void tryToApplyStartEndIntensity();
336 // If a linear gradient is symmetrical it is converted to an axial gradient.
337 // Does nothing in other cases and for other gradient types.
338 void tryToConvertToAxial();
339 void tryToApplyAxial();
340 void tryToApplySteps();
344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */