Coverage Report

Created: 2024-05-20 07:14

/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