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