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