• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             kDisplayP3,
98             kRec2020,
99             kProphotoRGB,
100             kA98RGB,
101 
102             kLastColorSpace = kA98RGB,
103         };
104         static constexpr int kColorSpaceCount = static_cast<int>(ColorSpace::kLastColorSpace) + 1;
105 
106         enum class HueMethod : uint8_t {
107             // https://www.w3.org/TR/css-color-4/#hue-interpolation
108             kShorter,
109             kLonger,
110             kIncreasing,
111             kDecreasing,
112 
113             kLastHueMethod = kDecreasing,
114         };
115         static constexpr int kHueMethodCount = static_cast<int>(HueMethod::kLastHueMethod) + 1;
116 
117         InPremul   fInPremul = InPremul::kNo;
118         ColorSpace fColorSpace = ColorSpace::kDestination;
119         HueMethod  fHueMethod  = HueMethod::kShorter;  // Only relevant for LCH, OKLCH, HSL, or HWB
120 
FromFlagsInterpolation121         static Interpolation FromFlags(uint32_t flags) {
122             return {flags & kInterpolateColorsInPremul_Flag ? InPremul::kYes : InPremul::kNo,
123                     ColorSpace::kDestination,
124                     HueMethod::kShorter};
125         }
126     };
127 
128     /** Returns a shader that generates a linear gradient between the two specified points.
129         <p />
130         @param  pts     The start and end points for the gradient.
131         @param  colors  The array[count] of colors, to be distributed between the two points
132         @param  pos     May be NULL. array[count] of SkScalars, or NULL, of the relative position of
133                         each corresponding color in the colors array. If this is NULL,
134                         the the colors are distributed evenly between the start and end point.
135                         If this is not null, the values must lie between 0.0 and 1.0, and be
136                         strictly increasing. If the first value is not 0.0, then an additional
137                         color stop is added at position 0.0, with the same color as colors[0].
138                         If the the last value is not 1.0, then an additional color stop is added
139                         at position 1.0, with the same color as colors[count - 1].
140         @param  count   Must be >=2. The number of colors (and pos if not NULL) entries.
141         @param  mode    The tiling mode
142 
143         example: https://fiddle.skia.org/c/@GradientShader_MakeLinear
144     */
145     static sk_sp<SkShader> MakeLinear(const SkPoint pts[2],
146                                       const SkColor colors[], const SkScalar pos[], int count,
147                                       SkTileMode mode,
148                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr);
149 
150     /** Returns a shader that generates a linear gradient between the two specified points.
151         <p />
152         @param  pts     The start and end points for the gradient.
153         @param  colors  The array[count] of colors, to be distributed between the two points
154         @param  pos     May be NULL. array[count] of SkScalars, or NULL, of the relative position of
155                         each corresponding color in the colors array. If this is NULL,
156                         the the colors are distributed evenly between the start and end point.
157                         If this is not null, the values must lie between 0.0 and 1.0, and be
158                         strictly increasing. If the first value is not 0.0, then an additional
159                         color stop is added at position 0.0, with the same color as colors[0].
160                         If the the last value is not 1.0, then an additional color stop is added
161                         at position 1.0, with the same color as colors[count - 1].
162         @param  count   Must be >=2. The number of colors (and pos if not NULL) entries.
163         @param  mode    The tiling mode
164 
165         example: https://fiddle.skia.org/c/@GradientShader_MakeLinear
166     */
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                                       const Interpolation& interpolation,
171                                       const SkMatrix* localMatrix);
172     static sk_sp<SkShader> MakeLinear(const SkPoint pts[2],
173                                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
174                                       const SkScalar pos[], int count, SkTileMode mode,
175                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
176         return MakeLinear(pts, colors, std::move(colorSpace), pos, count, mode,
177                           Interpolation::FromFlags(flags), localMatrix);
178     }
179 
180     /** Returns a shader that generates a radial gradient given the center and radius.
181         <p />
182         @param  center  The center of the circle for this gradient
183         @param  radius  Must be positive. The radius of the circle for this gradient
184         @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
185         @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
186                         each corresponding color in the colors array. If this is NULL,
187                         the the colors are distributed evenly between the center and edge of the circle.
188                         If this is not null, the values must lie between 0.0 and 1.0, and be
189                         strictly increasing. If the first value is not 0.0, then an additional
190                         color stop is added at position 0.0, with the same color as colors[0].
191                         If the the last value is not 1.0, then an additional color stop is added
192                         at position 1.0, with the same color as colors[count - 1].
193         @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
194         @param  mode    The tiling mode
195     */
196     static sk_sp<SkShader> MakeRadial(const SkPoint& center, SkScalar radius,
197                                       const SkColor colors[], const SkScalar pos[], int count,
198                                       SkTileMode mode,
199                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr);
200 
201     /** Returns a shader that generates a radial gradient given the center and radius.
202         <p />
203         @param  center  The center of the circle for this gradient
204         @param  radius  Must be positive. The radius of the circle for this gradient
205         @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
206         @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
207                         each corresponding color in the colors array. If this is NULL,
208                         the the colors are distributed evenly between the center and edge of the circle.
209                         If this is not null, the values must lie between 0.0 and 1.0, and be
210                         strictly increasing. If the first value is not 0.0, then an additional
211                         color stop is added at position 0.0, with the same color as colors[0].
212                         If the the last value is not 1.0, then an additional color stop is added
213                         at position 1.0, with the same color as colors[count - 1].
214         @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
215         @param  mode    The tiling mode
216     */
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                                       const Interpolation& interpolation,
221                                       const SkMatrix* localMatrix);
222     static sk_sp<SkShader> MakeRadial(const SkPoint& center, SkScalar radius,
223                                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
224                                       const SkScalar pos[], int count, SkTileMode mode,
225                                       uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
226         return MakeRadial(center, radius, colors, std::move(colorSpace), pos, count, mode,
227                           Interpolation::FromFlags(flags), localMatrix);
228     }
229 
230     /**
231      *  Returns a shader that generates a conical gradient given two circles, or
232      *  returns NULL if the inputs are invalid. The gradient interprets the
233      *  two circles according to the following HTML spec.
234      *  http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
235      */
236     static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
237                                                const SkPoint& end, SkScalar endRadius,
238                                                const SkColor colors[], const SkScalar pos[],
239                                                int count, SkTileMode mode,
240                                                uint32_t flags = 0,
241                                                const SkMatrix* localMatrix = nullptr);
242 
243     /**
244      *  Returns a shader that generates a conical gradient given two circles, or
245      *  returns NULL if the inputs are invalid. The gradient interprets the
246      *  two circles according to the following HTML spec.
247      *  http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
248      */
249     static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
250                                                const SkPoint& end, SkScalar endRadius,
251                                                const SkColor4f colors[],
252                                                sk_sp<SkColorSpace> colorSpace, const SkScalar pos[],
253                                                int count, SkTileMode mode,
254                                                const Interpolation& interpolation,
255                                                const SkMatrix* localMatrix);
256     static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
257                                                const SkPoint& end, SkScalar endRadius,
258                                                const SkColor4f colors[],
259                                                sk_sp<SkColorSpace> colorSpace, const SkScalar pos[],
260                                                int count, SkTileMode mode,
261                                                uint32_t flags = 0,
262                                                const SkMatrix* localMatrix = nullptr) {
263         return MakeTwoPointConical(start, startRadius, end, endRadius, colors,
264                                    std::move(colorSpace), pos, count, mode,
265                                    Interpolation::FromFlags(flags), localMatrix);
266     }
267 
268     /** Returns a shader that generates a sweep gradient given a center.
269 
270         The shader accepts negative angles and angles larger than 360, draws
271         between 0 and 360 degrees, similar to the CSS conic-gradient
272         semantics. 0 degrees means horizontal positive x axis. The start angle
273         must be less than the end angle, otherwise a null pointer is
274         returned. If color stops do not contain 0 and 1 but are within this
275         range, the respective outer color stop is repeated for 0 and 1. Color
276         stops less than 0 are clamped to 0, and greater than 1 are clamped to 1.
277         <p />
278         @param  cx         The X coordinate of the center of the sweep
279         @param  cx         The Y coordinate of the center of the sweep
280         @param  colors     The array[count] of colors, to be distributed around the center, within
281                            the gradient angle range.
282         @param  pos        May be NULL. The array[count] of SkScalars, or NULL, of the relative
283                            position of each corresponding color in the colors array. If this is
284                            NULL, then the colors are distributed evenly within the angular range.
285                            If this is not null, the values must lie between 0.0 and 1.0, and be
286                            strictly increasing. If the first value is not 0.0, then an additional
287                            color stop is added at position 0.0, with the same color as colors[0].
288                            If the the last value is not 1.0, then an additional color stop is added
289                            at position 1.0, with the same color as colors[count - 1].
290         @param  count      Must be >= 2. The number of colors (and pos if not NULL) entries
291         @param  mode       Tiling mode: controls drawing outside of the gradient angular range.
292         @param  startAngle Start of the angular range, corresponding to pos == 0.
293         @param  endAngle   End of the angular range, corresponding to pos == 1.
294     */
295     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
296                                      const SkColor colors[], const SkScalar pos[], int count,
297                                      SkTileMode mode,
298                                      SkScalar startAngle, SkScalar endAngle,
299                                      uint32_t flags, const SkMatrix* localMatrix);
300     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
301                                      const SkColor colors[], const SkScalar pos[], int count,
302                                      uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
303         return MakeSweep(cx, cy, colors, pos, count, SkTileMode::kClamp, 0, 360, flags,
304                          localMatrix);
305     }
306 
307     /** Returns a shader that generates a sweep gradient given a center.
308 
309         The shader accepts negative angles and angles larger than 360, draws
310         between 0 and 360 degrees, similar to the CSS conic-gradient
311         semantics. 0 degrees means horizontal positive x axis. The start angle
312         must be less than the end angle, otherwise a null pointer is
313         returned. If color stops do not contain 0 and 1 but are within this
314         range, the respective outer color stop is repeated for 0 and 1. Color
315         stops less than 0 are clamped to 0, and greater than 1 are clamped to 1.
316         <p />
317         @param  cx         The X coordinate of the center of the sweep
318         @param  cx         The Y coordinate of the center of the sweep
319         @param  colors     The array[count] of colors, to be distributed around the center, within
320                            the gradient angle range.
321         @param  pos        May be NULL. The array[count] of SkScalars, or NULL, of the relative
322                            position of each corresponding color in the colors array. If this is
323                            NULL, then the colors are distributed evenly within the angular range.
324                            If this is not null, the values must lie between 0.0 and 1.0, and be
325                            strictly increasing. If the first value is not 0.0, then an additional
326                            color stop is added at position 0.0, with the same color as colors[0].
327                            If the the last value is not 1.0, then an additional color stop is added
328                            at position 1.0, with the same color as colors[count - 1].
329         @param  count      Must be >= 2. The number of colors (and pos if not NULL) entries
330         @param  mode       Tiling mode: controls drawing outside of the gradient angular range.
331         @param  startAngle Start of the angular range, corresponding to pos == 0.
332         @param  endAngle   End of the angular range, corresponding to pos == 1.
333     */
334     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
335                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
336                                      const SkScalar pos[], int count,
337                                      SkTileMode mode,
338                                      SkScalar startAngle, SkScalar endAngle,
339                                      const Interpolation& interpolation,
340                                      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)341     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
342                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
343                                      const SkScalar pos[], int count,
344                                      SkTileMode mode,
345                                      SkScalar startAngle, SkScalar endAngle,
346                                      uint32_t flags, const SkMatrix* localMatrix) {
347         return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, mode, startAngle,
348                          endAngle, Interpolation::FromFlags(flags), localMatrix);
349     }
350     static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
351                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
352                                      const SkScalar pos[], int count,
353                                      uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) {
354         return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, SkTileMode::kClamp,
355                          0, 360, flags, localMatrix);
356     }
357 };
358 
359 #endif
360