/src/skia/include/effects/SkGradientShader.h
Line | Count | Source (jump to first uncovered line) |
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 | | |
116 | 10.5k | static Interpolation FromFlags(uint32_t flags) { |
117 | 10.5k | return {flags & kInterpolateColorsInPremul_Flag ? InPremul::kYes : InPremul::kNo, |
118 | 10.5k | ColorSpace::kDestination, |
119 | 10.5k | HueMethod::kShorter}; |
120 | 10.5k | } |
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 | 5.96k | uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { |
171 | 5.96k | return MakeLinear(pts, colors, std::move(colorSpace), pos, count, mode, |
172 | 5.96k | Interpolation::FromFlags(flags), localMatrix); |
173 | 5.96k | } |
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 | 715 | uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { |
221 | 715 | return MakeRadial(center, radius, colors, std::move(colorSpace), pos, count, mode, |
222 | 715 | Interpolation::FromFlags(flags), localMatrix); |
223 | 715 | } |
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 | 2.12k | const SkMatrix* localMatrix = nullptr) { |
258 | 2.12k | return MakeTwoPointConical(start, startRadius, end, endRadius, colors, |
259 | 2.12k | std::move(colorSpace), pos, count, mode, |
260 | 2.12k | Interpolation::FromFlags(flags), localMatrix); |
261 | 2.12k | } |
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 | 762 | uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { |
298 | 762 | return MakeSweep(cx, cy, colors, pos, count, SkTileMode::kClamp, 0, 360, flags, |
299 | 762 | localMatrix); |
300 | 762 | } |
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); |
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 | 1.77k | uint32_t flags, const SkMatrix* localMatrix) { |
342 | 1.77k | return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, mode, startAngle, |
343 | 1.77k | endAngle, Interpolation::FromFlags(flags), localMatrix); |
344 | 1.77k | } |
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 | 0 | uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { |
349 | 0 | return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, SkTileMode::kClamp, |
350 | 0 | 0, 360, flags, localMatrix); |
351 | 0 | } |
352 | | }; |
353 | | |
354 | | #endif |