xref: /aosp_15_r20/external/skia/include/effects/SkGradientShader.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2006 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkGradientShader_DEFINED
9 #define SkGradientShader_DEFINED
10 
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkShader.h" // IWYU pragma: keep
17 #include "include/core/SkTileMode.h"
18 #include "include/private/base/SkAPI.h"
19 
20 #include <cstdint>
21 #include <utility>
22 
23 class SkMatrix;
24 
25 /** \class SkGradientShader
26 
27     SkGradientShader hosts factories for creating subclasses of SkShader that
28     render linear and radial gradients. In general, degenerate cases should not
29     produce surprising results, but there are several types of degeneracies:
30 
31      * A linear gradient made from the same two points.
32      * A radial gradient with a radius of zero.
33      * A sweep gradient where the start and end angle are the same.
34      * A two point conical gradient where the two centers and the two radii are
35        the same.
36 
37     For any degenerate gradient with a decal tile mode, it will draw empty since the interpolating
38     region is zero area and the outer region is discarded by the decal mode.
39 
40     For any degenerate gradient with a repeat or mirror tile mode, it will draw a solid color that
41     is the average gradient color, since infinitely many repetitions of the gradients will fill the
42     shape.
43 
44     For a clamped gradient, every type is well-defined at the limit except for linear gradients. The
45     radial gradient with zero radius becomes the last color. The sweep gradient draws the sector
46     from 0 to the provided angle with the first color, with a hardstop switching to the last color.
47     When the provided angle is 0, this is just the solid last color again. Similarly, the two point
48     conical gradient becomes a circle filled with the first color, sized to the provided radius,
49     with a hardstop switching to the last color. When the two radii are both zero, this is just the
50     solid last color.
51 
52     As a linear gradient approaches the degenerate case, its shader will approach the appearance of
53     two half planes, each filled by the first and last colors of the gradient. The planes will be
54     oriented perpendicular to the vector between the two defining points of the gradient. However,
55     once they become the same point, Skia cannot reconstruct what that expected orientation is. To
56     provide a stable and predictable color in this case, Skia just uses the last color as a solid
57     fill to be similar to many of the other degenerate gradients' behaviors in clamp mode.
58 */
59 class SK_API SkGradientShader {
60 public:
61     enum Flags {
62         /** By default gradients will interpolate their colors in unpremul space
63          *  and then premultiply each of the results. By setting this flag, the
64          *  gradients will premultiply their colors first, and then interpolate
65          *  between them.
66          *  example: https://fiddle.skia.org/c/@GradientShader_MakeLinear
67          */
68         kInterpolateColorsInPremul_Flag = 1 << 0,
69     };
70 
71     struct Interpolation {
72         enum class InPremul : bool { kNo = false, kYes = true };
73 
74         enum class ColorSpace : uint8_t {
75             // Default Skia behavior: interpolate in the color space of the destination surface
76             kDestination,
77 
78             // https://www.w3.org/TR/css-color-4/#interpolation-space
79             kSRGBLinear,
80             kLab,
81             kOKLab,
82             // This is the same as kOKLab, except it has a simplified version of the CSS gamut
83             // mapping algorithm (https://www.w3.org/TR/css-color-4/#css-gamut-mapping)
84             // into Rec2020 space applied to it.
85             // Warning: This space is experimental and should not be used in production.
86             kOKLabGamutMap,
87             kLCH,
88             kOKLCH,
89             // This is the same as kOKLCH, except it has the same gamut mapping applied to it
90             // as kOKLabGamutMap does.
91             // Warning: This space is experimental and should not be used in production.
92             kOKLCHGamutMap,
93             kSRGB,
94             kHSL,
95             kHWB,
96 
97             kLastColorSpace = kHWB,
98         };
99         static constexpr int kColorSpaceCount = static_cast<int>(ColorSpace::kLastColorSpace) + 1;
100 
101         enum class HueMethod : uint8_t {
102             // https://www.w3.org/TR/css-color-4/#hue-interpolation
103             kShorter,
104             kLonger,
105             kIncreasing,
106             kDecreasing,
107 
108             kLastHueMethod = kDecreasing,
109         };
110         static constexpr int kHueMethodCount = static_cast<int>(HueMethod::kLastHueMethod) + 1;
111 
112         InPremul   fInPremul = InPremul::kNo;
113         ColorSpace fColorSpace = ColorSpace::kDestination;
114         HueMethod  fHueMethod  = HueMethod::kShorter;  // Only relevant for LCH, OKLCH, HSL, or HWB
115 
FromFlagsInterpolation116         static Interpolation FromFlags(uint32_t flags) {
117             return {flags & kInterpolateColorsInPremul_Flag ? InPremul::kYes : InPremul::kNo,
118                     ColorSpace::kDestination,
119                     HueMethod::kShorter};
120         }
121     };
122 
123     /** Returns a shader that generates a linear gradient between the two specified points.
124         <p />
125         @param  pts     The start and end points for the gradient.
126         @param  colors  The array[count] of colors, to be distributed between the two points
127         @param  pos     May be NULL. array[count] of SkScalars, or NULL, of the relative position of
128                         each corresponding color in the colors array. If this is NULL,
129                         the the colors are distributed evenly between the start and end point.
130                         If this is not null, the values must lie between 0.0 and 1.0, and be
131                         strictly increasing. If the first value is not 0.0, then an additional
132                         color stop is added at position 0.0, with the same color as colors[0].
133                         If the the last value is not 1.0, then an additional color stop is added
134                         at position 1.0, with the same color as colors[count - 1].
135         @param  count   Must be >=2. The number of colors (and pos if not NULL) entries.
136         @param  mode    The tiling mode
137 
138         example: https://fiddle.skia.org/c/@GradientShader_MakeLinear
139     */
140     static sk_sp<SkShader> MakeLinear(const SkPoint pts[2],
141                                       const SkColor colors[], const SkScalar pos[], int count,
142                                       SkTileMode mode,
143                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr);
144 
145     /** Returns a shader that generates a linear gradient between the two specified points.
146         <p />
147         @param  pts     The start and end points for the gradient.
148         @param  colors  The array[count] of colors, to be distributed between the two points
149         @param  pos     May be NULL. array[count] of SkScalars, or NULL, of the relative position of
150                         each corresponding color in the colors array. If this is NULL,
151                         the the colors are distributed evenly between the start and end point.
152                         If this is not null, the values must lie between 0.0 and 1.0, and be
153                         strictly increasing. If the first value is not 0.0, then an additional
154                         color stop is added at position 0.0, with the same color as colors[0].
155                         If the the last value is not 1.0, then an additional color stop is added
156                         at position 1.0, with the same color as colors[count - 1].
157         @param  count   Must be >=2. The number of colors (and pos if not NULL) entries.
158         @param  mode    The tiling mode
159 
160         example: https://fiddle.skia.org/c/@GradientShader_MakeLinear
161     */
162     static sk_sp<SkShader> MakeLinear(const SkPoint pts[2],
163                                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
164                                       const SkScalar pos[], int count, SkTileMode mode,
165                                       const Interpolation& interpolation,
166                                       const SkMatrix* localMatrix);
167     static sk_sp<SkShader> MakeLinear(const SkPoint pts[2],
168                                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
169                                       const SkScalar pos[], int count, SkTileMode mode,
170                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
171         return MakeLinear(pts, colors, std::move(colorSpace), pos, count, mode,
172                           Interpolation::FromFlags(flags), localMatrix);
173     }
174 
175     /** Returns a shader that generates a radial gradient given the center and radius.
176         <p />
177         @param  center  The center of the circle for this gradient
178         @param  radius  Must be positive. The radius of the circle for this gradient
179         @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
180         @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
181                         each corresponding color in the colors array. If this is NULL,
182                         the the colors are distributed evenly between the center and edge of the circle.
183                         If this is not null, the values must lie between 0.0 and 1.0, and be
184                         strictly increasing. If the first value is not 0.0, then an additional
185                         color stop is added at position 0.0, with the same color as colors[0].
186                         If the the last value is not 1.0, then an additional color stop is added
187                         at position 1.0, with the same color as colors[count - 1].
188         @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
189         @param  mode    The tiling mode
190     */
191     static sk_sp<SkShader> MakeRadial(const SkPoint& center, SkScalar radius,
192                                       const SkColor colors[], const SkScalar pos[], int count,
193                                       SkTileMode mode,
194                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr);
195 
196     /** Returns a shader that generates a radial gradient given the center and radius.
197         <p />
198         @param  center  The center of the circle for this gradient
199         @param  radius  Must be positive. The radius of the circle for this gradient
200         @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
201         @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
202                         each corresponding color in the colors array. If this is NULL,
203                         the the colors are distributed evenly between the center and edge of the circle.
204                         If this is not null, the values must lie between 0.0 and 1.0, and be
205                         strictly increasing. If the first value is not 0.0, then an additional
206                         color stop is added at position 0.0, with the same color as colors[0].
207                         If the the last value is not 1.0, then an additional color stop is added
208                         at position 1.0, with the same color as colors[count - 1].
209         @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
210         @param  mode    The tiling mode
211     */
212     static sk_sp<SkShader> MakeRadial(const SkPoint& center, SkScalar radius,
213                                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
214                                       const SkScalar pos[], int count, SkTileMode mode,
215                                       const Interpolation& interpolation,
216                                       const SkMatrix* localMatrix);
217     static sk_sp<SkShader> MakeRadial(const SkPoint& center, SkScalar radius,
218                                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
219                                       const SkScalar pos[], int count, SkTileMode mode,
220                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
221         return MakeRadial(center, radius, colors, std::move(colorSpace), pos, count, mode,
222                           Interpolation::FromFlags(flags), localMatrix);
223     }
224 
225     /**
226      *  Returns a shader that generates a conical gradient given two circles, or
227      *  returns NULL if the inputs are invalid. The gradient interprets the
228      *  two circles according to the following HTML spec.
229      *  http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
230      */
231     static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
232                                                const SkPoint& end, SkScalar endRadius,
233                                                const SkColor colors[], const SkScalar pos[],
234                                                int count, SkTileMode mode,
235                                                uint32_t flags = 0,
236                                                const SkMatrix* localMatrix = nullptr);
237 
238     /**
239      *  Returns a shader that generates a conical gradient given two circles, or
240      *  returns NULL if the inputs are invalid. The gradient interprets the
241      *  two circles according to the following HTML spec.
242      *  http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
243      */
244     static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
245                                                const SkPoint& end, SkScalar endRadius,
246                                                const SkColor4f colors[],
247                                                sk_sp<SkColorSpace> colorSpace, const SkScalar pos[],
248                                                int count, SkTileMode mode,
249                                                const Interpolation& interpolation,
250                                                const SkMatrix* localMatrix);
251     static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
252                                                const SkPoint& end, SkScalar endRadius,
253                                                const SkColor4f colors[],
254                                                sk_sp<SkColorSpace> colorSpace, const SkScalar pos[],
255                                                int count, SkTileMode mode,
256                                                uint32_t flags = 0,
257                                                const SkMatrix* localMatrix = nullptr) {
258         return MakeTwoPointConical(start, startRadius, end, endRadius, colors,
259                                    std::move(colorSpace), pos, count, mode,
260                                    Interpolation::FromFlags(flags), localMatrix);
261     }
262 
263     /** Returns a shader that generates a sweep gradient given a center.
264 
265         The shader accepts negative angles and angles larger than 360, draws
266         between 0 and 360 degrees, similar to the CSS conic-gradient
267         semantics. 0 degrees means horizontal positive x axis. The start angle
268         must be less than the end angle, otherwise a null pointer is
269         returned. If color stops do not contain 0 and 1 but are within this
270         range, the respective outer color stop is repeated for 0 and 1. Color
271         stops less than 0 are clamped to 0, and greater than 1 are clamped to 1.
272         <p />
273         @param  cx         The X coordinate of the center of the sweep
274         @param  cx         The Y coordinate of the center of the sweep
275         @param  colors     The array[count] of colors, to be distributed around the center, within
276                            the gradient angle range.
277         @param  pos        May be NULL. The array[count] of SkScalars, or NULL, of the relative
278                            position of each corresponding color in the colors array. If this is
279                            NULL, then the colors are distributed evenly within the angular range.
280                            If this is not null, the values must lie between 0.0 and 1.0, and be
281                            strictly increasing. If the first value is not 0.0, then an additional
282                            color stop is added at position 0.0, with the same color as colors[0].
283                            If the the last value is not 1.0, then an additional color stop is added
284                            at position 1.0, with the same color as colors[count - 1].
285         @param  count      Must be >= 2. The number of colors (and pos if not NULL) entries
286         @param  mode       Tiling mode: controls drawing outside of the gradient angular range.
287         @param  startAngle Start of the angular range, corresponding to pos == 0.
288         @param  endAngle   End of the angular range, corresponding to pos == 1.
289     */
290     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
291                                      const SkColor colors[], const SkScalar pos[], int count,
292                                      SkTileMode mode,
293                                      SkScalar startAngle, SkScalar endAngle,
294                                      uint32_t flags, const SkMatrix* localMatrix);
295     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
296                                      const SkColor colors[], const SkScalar pos[], int count,
297                                      uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
298         return MakeSweep(cx, cy, colors, pos, count, SkTileMode::kClamp, 0, 360, flags,
299                          localMatrix);
300     }
301 
302     /** Returns a shader that generates a sweep gradient given a center.
303 
304         The shader accepts negative angles and angles larger than 360, draws
305         between 0 and 360 degrees, similar to the CSS conic-gradient
306         semantics. 0 degrees means horizontal positive x axis. The start angle
307         must be less than the end angle, otherwise a null pointer is
308         returned. If color stops do not contain 0 and 1 but are within this
309         range, the respective outer color stop is repeated for 0 and 1. Color
310         stops less than 0 are clamped to 0, and greater than 1 are clamped to 1.
311         <p />
312         @param  cx         The X coordinate of the center of the sweep
313         @param  cx         The Y coordinate of the center of the sweep
314         @param  colors     The array[count] of colors, to be distributed around the center, within
315                            the gradient angle range.
316         @param  pos        May be NULL. The array[count] of SkScalars, or NULL, of the relative
317                            position of each corresponding color in the colors array. If this is
318                            NULL, then the colors are distributed evenly within the angular range.
319                            If this is not null, the values must lie between 0.0 and 1.0, and be
320                            strictly increasing. If the first value is not 0.0, then an additional
321                            color stop is added at position 0.0, with the same color as colors[0].
322                            If the the last value is not 1.0, then an additional color stop is added
323                            at position 1.0, with the same color as colors[count - 1].
324         @param  count      Must be >= 2. The number of colors (and pos if not NULL) entries
325         @param  mode       Tiling mode: controls drawing outside of the gradient angular range.
326         @param  startAngle Start of the angular range, corresponding to pos == 0.
327         @param  endAngle   End of the angular range, corresponding to pos == 1.
328     */
329     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
330                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
331                                      const SkScalar pos[], int count,
332                                      SkTileMode mode,
333                                      SkScalar startAngle, SkScalar endAngle,
334                                      const Interpolation& interpolation,
335                                      const SkMatrix* localMatrix);
MakeSweep(SkScalar cx,SkScalar cy,const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int count,SkTileMode mode,SkScalar startAngle,SkScalar endAngle,uint32_t flags,const SkMatrix * localMatrix)336     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
337                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
338                                      const SkScalar pos[], int count,
339                                      SkTileMode mode,
340                                      SkScalar startAngle, SkScalar endAngle,
341                                      uint32_t flags, const SkMatrix* localMatrix) {
342         return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, mode, startAngle,
343                          endAngle, Interpolation::FromFlags(flags), localMatrix);
344     }
345     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
346                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
347                                      const SkScalar pos[], int count,
348                                      uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
349         return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, SkTileMode::kClamp,
350                          0, 360, flags, localMatrix);
351     }
352 };
353 
354 #endif
355