1 /*
2 * Copyright 2022 Google LLC
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 #include "src/shaders/gradients/SkGradientBaseShader.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkTileMode.h"
18 #include "include/private/base/SkFloatingPoint.h"
19 #include "include/private/base/SkMalloc.h"
20 #include "include/private/base/SkTArray.h"
21 #include "include/private/base/SkTPin.h"
22 #include "include/private/base/SkTo.h"
23 #include "modules/skcms/skcms.h"
24 #include "src/base/SkArenaAlloc.h"
25 #include "src/base/SkFloatBits.h"
26 #include "src/base/SkVx.h"
27 #include "src/core/SkColorData.h"
28 #include "src/core/SkColorSpacePriv.h"
29 #include "src/core/SkColorSpaceXformSteps.h"
30 #include "src/core/SkConvertPixels.h"
31 #include "src/core/SkEffectPriv.h"
32 #include "src/core/SkPicturePriv.h"
33 #include "src/core/SkRasterPipeline.h"
34 #include "src/core/SkRasterPipelineOpContexts.h"
35 #include "src/core/SkRasterPipelineOpList.h"
36 #include "src/core/SkReadBuffer.h"
37 #include "src/core/SkWriteBuffer.h"
38
39 #include <algorithm>
40 #include <cmath>
41 #include <optional>
42 #include <utility>
43
44 using namespace skia_private;
45
46 enum GradientSerializationFlags {
47 // Bits 29:31 used for various boolean flags
48 kHasPosition_GSF = 0x80000000,
49 kHasLegacyLocalMatrix_GSF = 0x40000000,
50 kHasColorSpace_GSF = 0x20000000,
51
52 // Bits 12:28 unused
53
54 // Bits 8:11 for fTileMode
55 kTileModeShift_GSF = 8,
56 kTileModeMask_GSF = 0xF,
57
58 // Bits 4:7 for fInterpolation.fColorSpace
59 kInterpolationColorSpaceShift_GSF = 4,
60 kInterpolationColorSpaceMask_GSF = 0xF,
61
62 // Bits 1:3 for fInterpolation.fHueMethod
63 kInterpolationHueMethodShift_GSF = 1,
64 kInterpolationHueMethodMask_GSF = 0x7,
65
66 // Bit 0 for fInterpolation.fInPremul
67 kInterpolationInPremul_GSF = 0x1,
68 };
69
Descriptor()70 SkGradientBaseShader::Descriptor::Descriptor() {
71 sk_bzero(this, sizeof(*this));
72 fTileMode = SkTileMode::kClamp;
73 }
74 SkGradientBaseShader::Descriptor::~Descriptor() = default;
75
flatten(SkWriteBuffer & buffer) const76 void SkGradientBaseShader::flatten(SkWriteBuffer& buffer) const {
77 uint32_t flags = 0;
78 if (fPositions) {
79 flags |= kHasPosition_GSF;
80 }
81 sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
82 if (colorSpaceData) {
83 flags |= kHasColorSpace_GSF;
84 }
85 if (fInterpolation.fInPremul == Interpolation::InPremul::kYes) {
86 flags |= kInterpolationInPremul_GSF;
87 }
88 SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
89 flags |= ((uint32_t)fTileMode << kTileModeShift_GSF);
90 SkASSERT(static_cast<uint32_t>(fInterpolation.fColorSpace) <= kInterpolationColorSpaceMask_GSF);
91 flags |= ((uint32_t)fInterpolation.fColorSpace << kInterpolationColorSpaceShift_GSF);
92 SkASSERT(static_cast<uint32_t>(fInterpolation.fHueMethod) <= kInterpolationHueMethodMask_GSF);
93 flags |= ((uint32_t)fInterpolation.fHueMethod << kInterpolationHueMethodShift_GSF);
94
95 buffer.writeUInt(flags);
96
97 // If we injected implicit first/last stops at construction time, omit those when serializing:
98 int colorCount = fColorCount;
99 const SkColor4f* colors = fColors;
100 const SkScalar* positions = fPositions;
101 if (fFirstStopIsImplicit) {
102 colorCount--;
103 colors++;
104 if (positions) {
105 positions++;
106 }
107 }
108 if (fLastStopIsImplicit) {
109 colorCount--;
110 }
111
112 buffer.writeColor4fArray(colors, colorCount);
113 if (colorSpaceData) {
114 buffer.writeDataAsByteArray(colorSpaceData.get());
115 }
116 if (positions) {
117 buffer.writeScalarArray(positions, colorCount);
118 }
119 }
120
121 template <int N, typename T, bool MEM_MOVE>
validate_array(SkReadBuffer & buffer,size_t count,STArray<N,T,MEM_MOVE> * array)122 static bool validate_array(SkReadBuffer& buffer, size_t count, STArray<N, T, MEM_MOVE>* array) {
123 if (!buffer.validateCanReadN<T>(count)) {
124 return false;
125 }
126
127 array->resize_back(count);
128 return true;
129 }
130
unflatten(SkReadBuffer & buffer,SkMatrix * legacyLocalMatrix)131 bool SkGradientBaseShader::DescriptorScope::unflatten(SkReadBuffer& buffer,
132 SkMatrix* legacyLocalMatrix) {
133 // New gradient format. Includes floating point color, color space, densely packed flags
134 uint32_t flags = buffer.readUInt();
135
136 fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
137
138 fInterpolation.fColorSpace = (Interpolation::ColorSpace)(
139 (flags >> kInterpolationColorSpaceShift_GSF) & kInterpolationColorSpaceMask_GSF);
140 fInterpolation.fHueMethod = (Interpolation::HueMethod)(
141 (flags >> kInterpolationHueMethodShift_GSF) & kInterpolationHueMethodMask_GSF);
142 fInterpolation.fInPremul = (flags & kInterpolationInPremul_GSF) ? Interpolation::InPremul::kYes
143 : Interpolation::InPremul::kNo;
144
145 fColorCount = buffer.getArrayCount();
146
147 if (!(validate_array(buffer, fColorCount, &fColorStorage) &&
148 buffer.readColor4fArray(fColorStorage.begin(), fColorCount))) {
149 return false;
150 }
151 fColors = fColorStorage.begin();
152
153 if (SkToBool(flags & kHasColorSpace_GSF)) {
154 sk_sp<SkData> data = buffer.readByteArrayAsData();
155 fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
156 } else {
157 fColorSpace = nullptr;
158 }
159 if (SkToBool(flags & kHasPosition_GSF)) {
160 if (!(validate_array(buffer, fColorCount, &fPositionStorage) &&
161 buffer.readScalarArray(fPositionStorage.begin(), fColorCount))) {
162 return false;
163 }
164 fPositions = fPositionStorage.begin();
165 } else {
166 fPositions = nullptr;
167 }
168 if (SkToBool(flags & kHasLegacyLocalMatrix_GSF)) {
169 SkASSERT(buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix));
170 buffer.readMatrix(legacyLocalMatrix);
171 } else {
172 *legacyLocalMatrix = SkMatrix::I();
173 }
174 return buffer.isValid();
175 }
176
177 ////////////////////////////////////////////////////////////////////////////////////////////
178
SkGradientBaseShader(const Descriptor & desc,const SkMatrix & ptsToUnit)179 SkGradientBaseShader::SkGradientBaseShader(const Descriptor& desc, const SkMatrix& ptsToUnit)
180 : fPtsToUnit(ptsToUnit)
181 , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
182 , fFirstStopIsImplicit(false)
183 , fLastStopIsImplicit(false)
184 , fColorsAreOpaque(true) {
185 fPtsToUnit.getType(); // Precache so reads are threadsafe.
186 SkASSERT(desc.fColorCount > 1);
187
188 fInterpolation = desc.fInterpolation;
189
190 SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
191 fTileMode = desc.fTileMode;
192
193 /* Note: we let the caller skip the first and/or last position.
194 i.e. pos[0] = 0.3, pos[1] = 0.7
195 In these cases, we insert entries to ensure that the final data
196 will be bracketed by [0, 1].
197 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
198
199 Thus colorCount (the caller's value, and fColorCount (our value) may
200 differ by up to 2. In the above example:
201 colorCount = 2
202 fColorCount = 4
203 */
204 fColorCount = desc.fColorCount;
205
206 // Check if we need to add in start and/or end position/colors
207 if (desc.fPositions) {
208 fFirstStopIsImplicit = desc.fPositions[0] > 0;
209 fLastStopIsImplicit = desc.fPositions[desc.fColorCount - 1] != SK_Scalar1;
210 fColorCount += fFirstStopIsImplicit + fLastStopIsImplicit;
211 }
212
213 size_t storageSize =
214 fColorCount * (sizeof(SkColor4f) + (desc.fPositions ? sizeof(SkScalar) : 0));
215 fColors = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
216 fPositions = desc.fPositions ? reinterpret_cast<SkScalar*>(fColors + fColorCount) : nullptr;
217
218 // Now copy over the colors, adding the duplicates at t=0 and t=1 as needed
219 SkColor4f* colors = fColors;
220 if (fFirstStopIsImplicit) {
221 *colors++ = desc.fColors[0];
222 }
223 for (int i = 0; i < desc.fColorCount; ++i) {
224 colors[i] = desc.fColors[i];
225 fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
226 }
227 if (fLastStopIsImplicit) {
228 colors += desc.fColorCount;
229 *colors = desc.fColors[desc.fColorCount - 1];
230 }
231
232 if (desc.fPositions) {
233 SkScalar prev = 0;
234 SkScalar* positions = fPositions;
235 *positions++ = prev; // force the first pos to 0
236
237 int startIndex = fFirstStopIsImplicit ? 0 : 1;
238 int count = desc.fColorCount + fLastStopIsImplicit;
239
240 bool uniformStops = true;
241 const SkScalar uniformStep = desc.fPositions[startIndex] - prev;
242 for (int i = startIndex; i < count; i++) {
243 // Pin the last value to 1.0, and make sure pos is monotonic.
244 float curr = 1.0f;
245 if (i != desc.fColorCount) {
246 curr = SkTPin(desc.fPositions[i], prev, 1.0f);
247
248 // If a value is clamped to 1.0 before the last stop, the last stop
249 // actually isn't implicit if we thought it was.
250 if (curr == 1.0f && fLastStopIsImplicit) {
251 fLastStopIsImplicit = false;
252 }
253 }
254
255 uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
256
257 *positions++ = prev = curr;
258 }
259
260 if (uniformStops) {
261 // If the stops are uniform, treat them as implicit.
262 fPositions = nullptr;
263 } else {
264 // Remove duplicate stops with more than two of the same stop,
265 // keeping the leftmost and rightmost stop colors.
266 // i.e. 0, 0, 0, 0.2, 0.2, 0.3, 0.3, 0.3, 1, 1
267 // w/ clamp 0, 0, 0.2, 0.2, 0.3, 0.3, 1, 1
268 // w/o clamp 0, 0.2, 0.2, 0.3, 0.3, 1
269 int i = 0;
270 int dedupedColorCount = 0;
271 for (int j = 1; j <= fColorCount; j++) {
272 // We can compare the current positions at i and j since once these fPosition
273 // values are overwritten, our i and j pointers will be past the overwritten values.
274 if (j == fColorCount || fPositions[i] != fPositions[j]) {
275 bool dupStop = j - i > 1;
276
277 // Ignore the leftmost stop (i) if it is a non-clamp tilemode with
278 // a duplicate stop on t = 0.
279 bool ignoreLeftmost = dupStop && fTileMode != SkTileMode::kClamp
280 && fPositions[i] == 0;
281 if (!ignoreLeftmost) {
282 fPositions[dedupedColorCount] = fPositions[i];
283 fColors[dedupedColorCount] = fColors[i];
284 dedupedColorCount++;
285 }
286
287 // Include the rightmost stop (j-1) only if the stop has a duplicate,
288 // ignoring the rightmost stop if it is a non-clamp tilemode with t = 1.
289 bool ignoreRightmost = fTileMode != SkTileMode::kClamp
290 && fPositions[j - 1] == 1;
291 if (dupStop && !ignoreRightmost) {
292 fPositions[dedupedColorCount] = fPositions[j - 1];
293 fColors[dedupedColorCount] = fColors[j - 1];
294 dedupedColorCount++;
295 }
296 i = j;
297 }
298 }
299 fColorCount = dedupedColorCount;
300 }
301 }
302 }
303
~SkGradientBaseShader()304 SkGradientBaseShader::~SkGradientBaseShader() {}
305
add_stop_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,const SkPMColor4f & Fs,const SkPMColor4f & Bs)306 static void add_stop_color(SkRasterPipeline_GradientCtx* ctx,
307 size_t stop,
308 const SkPMColor4f& Fs,
309 const SkPMColor4f& Bs) {
310 (ctx->factors[0])[stop] = Fs.fR;
311 (ctx->factors[1])[stop] = Fs.fG;
312 (ctx->factors[2])[stop] = Fs.fB;
313 (ctx->factors[3])[stop] = Fs.fA;
314
315 (ctx->biases[0])[stop] = Bs.fR;
316 (ctx->biases[1])[stop] = Bs.fG;
317 (ctx->biases[2])[stop] = Bs.fB;
318 (ctx->biases[3])[stop] = Bs.fA;
319 }
320
add_const_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,const SkPMColor4f & color)321 static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, const SkPMColor4f& color) {
322 add_stop_color(ctx, stop, {0, 0, 0, 0}, color);
323 }
324
325 // Calculate a factor F and a bias B so that color = F*t + B when t is in range of
326 // the stop. Assume that all stops have width 1/gapCount and the stop parameter
327 // refers to the nth stop.
init_stop_evenly(SkRasterPipeline_GradientCtx * ctx,float gapCount,size_t stop,const SkPMColor4f & leftC,const SkPMColor4f & rightC)328 static void init_stop_evenly(SkRasterPipeline_GradientCtx* ctx,
329 float gapCount,
330 size_t stop,
331 const SkPMColor4f& leftC,
332 const SkPMColor4f& rightC) {
333 auto left = skvx::float4::Load(leftC.vec());
334 auto right = skvx::float4::Load(rightC.vec());
335
336 SkPMColor4f factor, bias;
337
338 // We start with the following 2 linear equations and 2 unknowns (factor, bias)
339 // left = factor * t + bias
340 // right = factor * (t + gap) + bias
341 // gap = 1/gapCount
342 // t = gap * stop
343
344 // right - left = factor * (t + gap) - factor * t
345 // right - left = factor * gap
346 // factor = (right - left) / gap (and gap = 1/gapCount)
347 auto factor4 = ((right - left) * gapCount);
348 (left - (factor4 * (stop / gapCount))).store(bias.vec());
349 factor4.store(factor.vec());
350
351 add_stop_color(ctx, stop, factor, bias);
352 }
353
354 // Calculate a factor F and a bias B so that color = F*t + B when t is in range of
355 // the stop. Unlike init_stop_evenly, this handles stops
init_stop_pos(SkRasterPipeline_GradientCtx * ctx,size_t stop,float t_l,float gapReciprocal,const SkPMColor4f & leftC,const SkPMColor4f & rightC)356 static void init_stop_pos(SkRasterPipeline_GradientCtx* ctx,
357 size_t stop,
358 float t_l,
359 float gapReciprocal,
360 const SkPMColor4f& leftC,
361 const SkPMColor4f& rightC) {
362 // gapReciprocal is 1/gapWidth. If two colors were on top of each other, we should
363 // have skipped that as a "stop".
364 SkASSERT(SkIsFinite(gapReciprocal));
365
366 auto left = skvx::float4::Load(leftC.vec());
367 auto right = skvx::float4::Load(rightC.vec());
368
369 SkPMColor4f factor, bias;
370
371 // See init_stop_evenly for this derivation, noting that gap = 1/gapReciprocal
372 // and t = t_l
373 auto factor4 = ((right - left) * gapReciprocal);
374 (left - (factor4 * t_l)).store(bias.vec());
375 factor4.store(factor.vec());
376
377 ctx->ts[stop] = t_l;
378 add_stop_color(ctx, stop, factor, bias);
379 }
380
AppendGradientFillStages(SkRasterPipeline * p,SkArenaAlloc * alloc,const SkPMColor4f * pmColors,const SkScalar * positions,int count)381 void SkGradientBaseShader::AppendGradientFillStages(SkRasterPipeline* p,
382 SkArenaAlloc* alloc,
383 const SkPMColor4f* pmColors,
384 const SkScalar* positions,
385 int count) {
386 // The two-stop case with stops at 0 and 1.
387 if (count == 2 && positions == nullptr) {
388 const SkPMColor4f c_l = pmColors[0], c_r = pmColors[1];
389
390 auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
391 (skvx::float4::Load(c_r.vec()) - skvx::float4::Load(c_l.vec())).store(ctx->factor);
392 (skvx::float4::Load(c_l.vec())).store(ctx->bias);
393
394 p->append(SkRasterPipelineOp::evenly_spaced_2_stop_gradient, ctx);
395 return;
396 }
397 // Linear gradients with evenly spaced stops involve doing calculations to interpolate
398 // between color n and color n+1 based on t (in range [0.0,1.0]).
399 // color_n * (t - t_n) / gap_n + color_{n+1} * (t_{n+1} - t) / gap_n
400 // We could just stick the colors and the gaps calculation in RP and do this calculation,
401 // but instead we can precompute things to make the RP calculation simpler and faster.
402 // For each gap, we calculate four linear equations in the form y = m*x + b, or rather
403 // color_channel = factor * t + bias
404 // We do this pre-computation in init_stop_evenly and init_stop_pos.
405
406 auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
407
408 // Allocate at least enough for the AVX2 gather from a YMM register.
409 constexpr int kMaxRegisterSize = 8;
410
411 // There are n - 1 gaps between n colors plus 2 regions to the left and right
412 // of the gradient to account for colors. For evenly spaced gradients, we cheat
413 // and skip the left gap, using one block of floats unused.
414 const size_t factorBiasFloats = std::max(count + 1, kMaxRegisterSize);
415 const size_t tsForArbitraryStops = count + 1;
416 // We need space for all factors and biases, and while we are at it, some space
417 // if we need to include the arbitrary stops.
418 const size_t toAlloc = 2 * kRGBAChannels * factorBiasFloats + tsForArbitraryStops;
419 float* gradientCtxBuffer = alloc->makeArray<float>(toAlloc);
420 for (size_t i = 0; i < kRGBAChannels; i++) {
421 ctx->factors[i] = gradientCtxBuffer;
422 gradientCtxBuffer += factorBiasFloats;
423 ctx->biases[i] = gradientCtxBuffer;
424 gradientCtxBuffer += factorBiasFloats;
425 }
426
427 if (positions == nullptr) {
428 // Handle evenly distributed stops.
429
430 size_t stopCount = count;
431 float gapCount = stopCount - 1;
432
433 SkPMColor4f c_l = pmColors[0];
434 for (size_t i = 0; i < gapCount; i++) {
435 SkPMColor4f c_r = pmColors[i + 1];
436 init_stop_evenly(ctx, gapCount, i, c_l, c_r);
437 c_l = c_r;
438 }
439 add_const_color(ctx, stopCount - 1, c_l);
440
441 ctx->stopCount = stopCount;
442 p->append(SkRasterPipelineOp::evenly_spaced_gradient, ctx);
443 return;
444 }
445
446 // Handle arbitrary stops.
447 ctx->ts = gradientCtxBuffer;
448
449 // Remove the default stops inserted by SkGradientBaseShader::SkGradientBaseShader
450 // because they are naturally handled by the search method.
451 int firstStop;
452 int lastStop;
453 if (count > 2) {
454 firstStop = pmColors[0] != pmColors[1] ? 0 : 1;
455 lastStop = pmColors[count - 2] != pmColors[count - 1] ? count - 1 : count - 2;
456 } else {
457 firstStop = 0;
458 lastStop = 1;
459 }
460
461 size_t stopCount = 0;
462 float t_l = positions[firstStop];
463 SkPMColor4f c_l = pmColors[firstStop];
464 add_const_color(ctx, stopCount++, c_l);
465
466 for (int i = firstStop; i < lastStop; i++) {
467 float t_r = positions[i + 1];
468 SkPMColor4f c_r = pmColors[i + 1];
469 SkASSERT(t_l <= t_r);
470 if (t_l < t_r) {
471 float c_scale = sk_ieee_float_divide(1, t_r - t_l);
472 if (SkIsFinite(c_scale)) {
473 init_stop_pos(ctx, stopCount, t_l, c_scale, c_l, c_r);
474 stopCount += 1;
475 }
476 }
477 t_l = t_r;
478 c_l = c_r;
479 }
480
481 ctx->ts[stopCount] = t_l;
482 add_const_color(ctx, stopCount++, c_l);
483
484 ctx->stopCount = stopCount;
485 p->append(SkRasterPipelineOp::gradient, ctx);
486 }
487
AppendInterpolatedToDstStages(SkRasterPipeline * p,SkArenaAlloc * alloc,bool colorsAreOpaque,const Interpolation & interpolation,const SkColorSpace * intermediateColorSpace,const SkColorSpace * dstColorSpace)488 void SkGradientBaseShader::AppendInterpolatedToDstStages(SkRasterPipeline* p,
489 SkArenaAlloc* alloc,
490 bool colorsAreOpaque,
491 const Interpolation& interpolation,
492 const SkColorSpace* intermediateColorSpace,
493 const SkColorSpace* dstColorSpace) {
494 using ColorSpace = Interpolation::ColorSpace;
495 bool colorIsPremul = static_cast<bool>(interpolation.fInPremul);
496
497 // If we interpolated premul colors in any of the special color spaces, we need to unpremul
498 if (colorIsPremul && !colorsAreOpaque) {
499 switch (interpolation.fColorSpace) {
500 case ColorSpace::kLab:
501 case ColorSpace::kOKLab:
502 case ColorSpace::kOKLabGamutMap:
503 p->append(SkRasterPipelineOp::unpremul);
504 colorIsPremul = false;
505 break;
506 case ColorSpace::kLCH:
507 case ColorSpace::kOKLCH:
508 case ColorSpace::kOKLCHGamutMap:
509 case ColorSpace::kHSL:
510 case ColorSpace::kHWB:
511 p->append(SkRasterPipelineOp::unpremul_polar);
512 colorIsPremul = false;
513 break;
514 default:
515 break;
516 }
517 }
518
519 // Convert colors in exotic spaces back to their intermediate SkColorSpace
520 switch (interpolation.fColorSpace) {
521 case ColorSpace::kLab: p->append(SkRasterPipelineOp::css_lab_to_xyz); break;
522 case ColorSpace::kOKLab: p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
523 case ColorSpace::kOKLabGamutMap:
524 p->append(SkRasterPipelineOp::css_oklab_gamut_map_to_linear_srgb);
525 break;
526 case ColorSpace::kLCH: p->append(SkRasterPipelineOp::css_hcl_to_lab);
527 p->append(SkRasterPipelineOp::css_lab_to_xyz); break;
528 case ColorSpace::kOKLCH: p->append(SkRasterPipelineOp::css_hcl_to_lab);
529 p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
530 case ColorSpace::kOKLCHGamutMap:
531 p->append(SkRasterPipelineOp::css_hcl_to_lab);
532 p->append(SkRasterPipelineOp::css_oklab_gamut_map_to_linear_srgb);
533 break;
534 case ColorSpace::kHSL: p->append(SkRasterPipelineOp::css_hsl_to_srgb); break;
535 case ColorSpace::kHWB: p->append(SkRasterPipelineOp::css_hwb_to_srgb); break;
536 default: break;
537 }
538
539 // Now transform from intermediate to destination color space.
540 // See comments in GrGradientShader.cpp about the decisions here.
541 if (!dstColorSpace) {
542 dstColorSpace = sk_srgb_singleton();
543 }
544 SkAlphaType intermediateAlphaType = colorIsPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
545 // TODO(skia:13108): Get dst alpha type correctly
546 SkAlphaType dstAlphaType = kPremul_SkAlphaType;
547
548 if (colorsAreOpaque) {
549 intermediateAlphaType = dstAlphaType = kUnpremul_SkAlphaType;
550 }
551
552 alloc->make<SkColorSpaceXformSteps>(
553 intermediateColorSpace, intermediateAlphaType, dstColorSpace, dstAlphaType)
554 ->apply(p);
555 }
556
appendStages(const SkStageRec & rec,const SkShaders::MatrixRec & mRec) const557 bool SkGradientBaseShader::appendStages(const SkStageRec& rec,
558 const SkShaders::MatrixRec& mRec) const {
559 SkRasterPipeline* p = rec.fPipeline;
560 SkArenaAlloc* alloc = rec.fAlloc;
561 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
562
563 std::optional<SkShaders::MatrixRec> newMRec = mRec.apply(rec, fPtsToUnit);
564 if (!newMRec.has_value()) {
565 return false;
566 }
567
568 SkRasterPipeline_<256> postPipeline;
569
570 this->appendGradientStages(alloc, p, &postPipeline);
571
572 switch (fTileMode) {
573 case SkTileMode::kMirror:
574 p->append(SkRasterPipelineOp::mirror_x_1);
575 break;
576 case SkTileMode::kRepeat:
577 p->append(SkRasterPipelineOp::repeat_x_1);
578 break;
579 case SkTileMode::kDecal:
580 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
581 decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
582 // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
583 p->append(SkRasterPipelineOp::decal_x, decal_ctx);
584 [[fallthrough]];
585
586 case SkTileMode::kClamp:
587 if (!fPositions) {
588 // We clamp only when the stops are evenly spaced.
589 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
590 // In that case, we must make sure we're using the general "gradient" stage,
591 // which is the only stage that will correctly handle unclamped t.
592 p->append(SkRasterPipelineOp::clamp_x_1);
593 }
594 break;
595 }
596
597 // Transform all of the colors to destination color space, possibly premultiplied
598 SkColor4fXformer xformedColors(this, rec.fDstCS);
599 AppendGradientFillStages(p, alloc,
600 xformedColors.fColors.begin(),
601 xformedColors.fPositions,
602 xformedColors.fColors.size());
603 AppendInterpolatedToDstStages(p, alloc, fColorsAreOpaque, fInterpolation,
604 xformedColors.fIntermediateColorSpace.get(), rec.fDstCS);
605
606 if (decal_ctx) {
607 p->append(SkRasterPipelineOp::check_decal_mask, decal_ctx);
608 }
609
610 p->extend(postPipeline);
611
612 return true;
613 }
614
isOpaque() const615 bool SkGradientBaseShader::isOpaque() const {
616 return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
617 }
618
onAsLuminanceColor(SkColor4f * lum) const619 bool SkGradientBaseShader::onAsLuminanceColor(SkColor4f* lum) const {
620 // We just compute an average color. There are several things we could do better:
621 // 1) We already have a different average_gradient_color helper later in this file, that weights
622 // contribution by the relative size of each band.
623 // 2) Colors should be converted to some standard color space! These could be in any space.
624 // 3) Do we want to average in the source space, sRGB, or some linear space?
625 SkColor4f color{0, 0, 0, 1};
626 for (int i = 0; i < fColorCount; ++i) {
627 color.fR += fColors[i].fR;
628 color.fG += fColors[i].fG;
629 color.fB += fColors[i].fB;
630 }
631 const float scale = 1.0f / fColorCount;
632 color.fR *= scale;
633 color.fG *= scale;
634 color.fB *= scale;
635 *lum = color;
636 return true;
637 }
638
intermediate_color_space(SkGradientShader::Interpolation::ColorSpace cs,SkColorSpace * dst)639 static sk_sp<SkColorSpace> intermediate_color_space(SkGradientShader::Interpolation::ColorSpace cs,
640 SkColorSpace* dst) {
641 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
642 switch (cs) {
643 case ColorSpace::kDestination:
644 return sk_ref_sp(dst);
645
646 // css-color-4 allows XYZD50 and XYZD65. For gradients, those are redundant. Interpolating
647 // in any linear RGB space, (regardless of white point), gives the same answer.
648 case ColorSpace::kSRGBLinear:
649 return SkColorSpace::MakeSRGBLinear();
650
651 case ColorSpace::kSRGB:
652 case ColorSpace::kHSL:
653 case ColorSpace::kHWB:
654 return SkColorSpace::MakeSRGB();
655
656 case ColorSpace::kLab:
657 case ColorSpace::kLCH:
658 // Conversion to Lab (and LCH) starts with XYZD50
659 return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, SkNamedGamut::kXYZ);
660
661 case ColorSpace::kOKLab:
662 case ColorSpace::kOKLabGamutMap:
663 case ColorSpace::kOKLCH:
664 case ColorSpace::kOKLCHGamutMap:
665 // The "standard" conversion to these spaces starts with XYZD65. That requires extra
666 // effort to conjure. The author also has reference code for going directly from linear
667 // sRGB, so we use that.
668 // TODO(skia:13108): Even better would be to have an LMS color space, because the first
669 // part of the conversion is a matrix multiply, which could be absorbed into the
670 // color space xform.
671 return SkColorSpace::MakeSRGBLinear();
672
673 // These rectangular color spaces have their own transfer curves.
674 case ColorSpace::kDisplayP3:
675 return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
676
677 case ColorSpace::kRec2020:
678 return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020);
679
680 case ColorSpace::kProphotoRGB:
681 static skcms_Matrix3x3 lin_proPhoto_to_XYZ_D50;
682 SkNamedPrimaries::kProPhotoRGB.toXYZD50(&lin_proPhoto_to_XYZ_D50);
683 return SkColorSpace::MakeRGB(SkNamedTransferFn::kProPhotoRGB, lin_proPhoto_to_XYZ_D50);
684
685 case ColorSpace::kA98RGB:
686 return SkColorSpace::MakeRGB(SkNamedTransferFn::kA98RGB, SkNamedGamut::kAdobeRGB);
687 }
688 SkUNREACHABLE;
689 }
690
691 using ConvertColorProc = SkPMColor4f(*)(SkPMColor4f, bool*);
692 using PremulColorProc = SkPMColor4f(*)(SkPMColor4f);
693
srgb_to_hsl(SkPMColor4f rgb,bool * hueIsPowerless)694 static SkPMColor4f srgb_to_hsl(SkPMColor4f rgb, bool* hueIsPowerless) {
695 float mx = std::max({rgb.fR, rgb.fG, rgb.fB});
696 float mn = std::min({rgb.fR, rgb.fG, rgb.fB});
697 float hue = 0, sat = 0, light = (mn + mx) / 2;
698 float d = mx - mn;
699
700 if (d != 0) {
701 sat = (light == 0 || light == 1) ? 0 : (mx - light) / std::min(light, 1 - light);
702 if (mx == rgb.fR) {
703 hue = (rgb.fG - rgb.fB) / d + (rgb.fG < rgb.fB ? 6 : 0);
704 } else if (mx == rgb.fG) {
705 hue = (rgb.fB - rgb.fR) / d + 2;
706 } else {
707 hue = (rgb.fR - rgb.fG) / d + 4;
708 }
709
710 hue *= 60;
711 }
712 if (sat == 0) {
713 *hueIsPowerless = true;
714 }
715 return {hue, sat * 100, light * 100, rgb.fA};
716 }
717
srgb_to_hwb(SkPMColor4f rgb,bool * hueIsPowerless)718 static SkPMColor4f srgb_to_hwb(SkPMColor4f rgb, bool* hueIsPowerless) {
719 SkPMColor4f hsl = srgb_to_hsl(rgb, hueIsPowerless);
720 float white = std::min({rgb.fR, rgb.fG, rgb.fB});
721 float black = 1 - std::max({rgb.fR, rgb.fG, rgb.fB});
722 return {hsl.fR, white * 100, black * 100, rgb.fA};
723 }
724
xyzd50_to_lab(SkPMColor4f xyz,bool *)725 static SkPMColor4f xyzd50_to_lab(SkPMColor4f xyz, bool* /*hueIsPowerless*/) {
726 constexpr float D50[3] = {0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f};
727
728 constexpr float e = 216.0f / 24389;
729 constexpr float k = 24389.0f / 27;
730
731 SkPMColor4f f;
732 for (int i = 0; i < 3; ++i) {
733 float v = xyz[i] / D50[i];
734 f[i] = (v > e) ? std::cbrtf(v) : (k * v + 16) / 116;
735 }
736
737 return {(116 * f[1]) - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2]), xyz.fA};
738 }
739
740 // The color space is technically LCH, but we produce HCL, so that all polar spaces have hue in the
741 // first component. This simplifies the hue handling for HueMethod and premul/unpremul.
xyzd50_to_hcl(SkPMColor4f xyz,bool * hueIsPowerless)742 static SkPMColor4f xyzd50_to_hcl(SkPMColor4f xyz, bool* hueIsPowerless) {
743 SkPMColor4f Lab = xyzd50_to_lab(xyz, hueIsPowerless);
744 float hue = sk_float_radians_to_degrees(atan2f(Lab[2], Lab[1]));
745 float chroma = sqrtf(Lab[1] * Lab[1] + Lab[2] * Lab[2]);
746 // The LCH math produces small-ish (but not tiny) chroma values for achromatic colors:
747 constexpr float kMaxChromaForPowerlessHue = 1e-2f;
748 if (chroma <= kMaxChromaForPowerlessHue) {
749 *hueIsPowerless = true;
750 }
751 return {hue >= 0 ? hue : hue + 360, chroma, Lab[0], xyz.fA};
752 }
753
754 // https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
lin_srgb_to_oklab(SkPMColor4f rgb,bool *)755 static SkPMColor4f lin_srgb_to_oklab(SkPMColor4f rgb, bool* /*hueIsPowerless*/) {
756 float l = 0.4122214708f * rgb.fR + 0.5363325363f * rgb.fG + 0.0514459929f * rgb.fB;
757 float m = 0.2119034982f * rgb.fR + 0.6806995451f * rgb.fG + 0.1073969566f * rgb.fB;
758 float s = 0.0883024619f * rgb.fR + 0.2817188376f * rgb.fG + 0.6299787005f * rgb.fB;
759 l = std::cbrtf(l);
760 m = std::cbrtf(m);
761 s = std::cbrtf(s);
762 return {0.2104542553f * l + 0.7936177850f * m - 0.0040720468f * s,
763 1.9779984951f * l - 2.4285922050f * m + 0.4505937099f * s,
764 0.0259040371f * l + 0.7827717662f * m - 0.8086757660f * s,
765 rgb.fA};
766 }
767
768 // The color space is technically OkLCH, but we produce HCL, so that all polar spaces have hue in
769 // the first component. This simplifies the hue handling for HueMethod and premul/unpremul.
lin_srgb_to_okhcl(SkPMColor4f rgb,bool * hueIsPowerless)770 static SkPMColor4f lin_srgb_to_okhcl(SkPMColor4f rgb, bool* hueIsPowerless) {
771 SkPMColor4f OKLab = lin_srgb_to_oklab(rgb, hueIsPowerless);
772 float hue = sk_float_radians_to_degrees(atan2f(OKLab[2], OKLab[1]));
773 float chroma = sqrtf(OKLab[1] * OKLab[1] + OKLab[2] * OKLab[2]);
774 // The OKLCH math produces very small chroma values for achromatic colors:
775 constexpr float kMaxChromaForPowerlessHue = 1e-6f;
776 if (chroma <= kMaxChromaForPowerlessHue) {
777 *hueIsPowerless = true;
778 }
779 return {hue >= 0 ? hue : hue + 360, chroma, OKLab[0], rgb.fA};
780 }
781
premul_polar(SkPMColor4f hsl)782 static SkPMColor4f premul_polar(SkPMColor4f hsl) {
783 return {hsl.fR, hsl.fG * hsl.fA, hsl.fB * hsl.fA, hsl.fA};
784 }
785
premul_rgb(SkPMColor4f rgb)786 static SkPMColor4f premul_rgb(SkPMColor4f rgb) {
787 return {rgb.fR * rgb.fA, rgb.fG * rgb.fA, rgb.fB * rgb.fA, rgb.fA};
788 }
789
color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs)790 static bool color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs) {
791 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
792 switch (cs) {
793 case ColorSpace::kLCH:
794 case ColorSpace::kOKLCH:
795 case ColorSpace::kHSL:
796 case ColorSpace::kHWB:
797 return true;
798 default:
799 return false;
800 }
801 }
802
803 // Given `colors` in `src` color space, an interpolation space, and a `dst` color space,
804 // we are doing several things. First, some definitions:
805 //
806 // The interpolation color space is "special" if it can't be represented as an SkColorSpace. This
807 // applies to any color space that isn't an RGB space, like Lab or HSL. These need special handling
808 // because we have to run bespoke code to do the conversion (before interpolation here, and after
809 // interpolation in the backend shader/pipeline).
810 //
811 // The interpolation color space is "polar" if it involves hue (HSL, HWB, LCH, Oklch). These need
812 // special handling, becuase hue is never premultiplied, and because HueMethod comes into play.
813 //
814 // 1) Pick an `intermediate` SkColorSpace. If the interpolation color space is not "special",
815 // (kDestination, kSRGB, etc... ), then `intermediate` is exact. Otherwise, `intermediate` is the
816 // RGB space that prepares us to do the final conversion. For example, conversion to Lab starts
817 // with XYZD50, so `intermediate` will be XYZD50 if we're actually interpolating in Lab.
818 // 2) Transform all colors to the `intermediate` color space, leaving them unpremultiplied.
819 // 3) If the interpolation color space is "special", transform the colors to that space.
820 // 4) If the interpolation color space is "polar", adjust the angles to respect HueMethod.
821 // 5) If premul interpolation is requested, apply that. For "polar" interpolated colors, don't
822 // premultiply hue, only the other two channels. Note that there are four polar spaces.
823 // Two have hue as the first component, and two have it as the third component. To reduce
824 // complexity, we always store hue in the first component, swapping it with luminance for
825 // LCH and Oklch. The backend code (eg, shaders) needs to know about this.
SkColor4fXformer(const SkGradientBaseShader * shader,SkColorSpace * dst,bool forceExplicitPositions)826 SkColor4fXformer::SkColor4fXformer(const SkGradientBaseShader* shader,
827 SkColorSpace* dst,
828 bool forceExplicitPositions) {
829 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
830 using HueMethod = SkGradientShader::Interpolation::HueMethod;
831
832 int colorCount = shader->fColorCount;
833 const SkGradientShader::Interpolation interpolation = shader->fInterpolation;
834
835 // 0) Copy the shader's position pointer. Certain interpolation modes might force us to add
836 // new stops, in which case we'll allocate & edit the positions.
837 fPositions = shader->fPositions;
838
839 // 1) Determine the color space of our intermediate colors.
840 fIntermediateColorSpace = intermediate_color_space(interpolation.fColorSpace, dst);
841
842 // 2) Convert all colors to the intermediate color space
843 auto info = SkImageInfo::Make(colorCount, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType);
844
845 auto dstInfo = info.makeColorSpace(fIntermediateColorSpace);
846 auto srcInfo = info.makeColorSpace(shader->fColorSpace);
847
848 fColors.reset(colorCount);
849 SkAssertResult(SkConvertPixels(dstInfo,
850 fColors.begin(),
851 info.minRowBytes(),
852 srcInfo,
853 shader->fColors,
854 info.minRowBytes()));
855
856 // 3) Transform to the interpolation color space (if it's special)
857 ConvertColorProc convertFn = nullptr;
858 switch (interpolation.fColorSpace) {
859 case ColorSpace::kHSL: convertFn = srgb_to_hsl; break;
860 case ColorSpace::kHWB: convertFn = srgb_to_hwb; break;
861 case ColorSpace::kLab: convertFn = xyzd50_to_lab; break;
862 case ColorSpace::kLCH: convertFn = xyzd50_to_hcl; break;
863 case ColorSpace::kOKLab: convertFn = lin_srgb_to_oklab; break;
864 case ColorSpace::kOKLabGamutMap: convertFn = lin_srgb_to_oklab; break;
865 case ColorSpace::kOKLCH: convertFn = lin_srgb_to_okhcl; break;
866 case ColorSpace::kOKLCHGamutMap: convertFn = lin_srgb_to_okhcl; break;
867 default: break;
868 }
869
870 skia_private::STArray<4, bool> hueIsPowerless;
871 bool anyPowerlessHue = false;
872 hueIsPowerless.push_back_n(colorCount, false);
873 if (convertFn) {
874 for (int i = 0; i < colorCount; ++i) {
875 fColors[i] = convertFn(fColors[i], hueIsPowerless.data() + i);
876 anyPowerlessHue = anyPowerlessHue || hueIsPowerless[i];
877 }
878 }
879
880 if (anyPowerlessHue) {
881 // In theory, if we knew we were just going to adjust the existing colors (without adding
882 // new ones), we could do it all in-place. To keep things simple, we always generate the
883 // new colors in separate storage.
884 ColorStorage newColors;
885 PositionStorage newPositions;
886
887 for (int i = 0; i < colorCount; ++i) {
888 const SkPMColor4f& curColor = fColors[i];
889 float curPos = shader->getPos(i);
890
891 if (!hueIsPowerless[i]) {
892 newColors.push_back(curColor);
893 newPositions.push_back(curPos);
894 continue;
895 }
896
897 auto colorWithHueFrom = [](const SkPMColor4f& color, const SkPMColor4f& hueColor) {
898 // If we have any powerless hue, then all colors are already in (some) polar space,
899 // and they all store their hue in the red channel.
900 return SkPMColor4f{hueColor.fR, color.fG, color.fB, color.fA};
901 };
902
903 // In each case, we might be copying a powerless (invalid) hue from the neighbor, but
904 // that should be fine, as it will match that neighbor perfectly, and any hue is ok.
905 if (i != 0) {
906 newPositions.push_back(curPos);
907 newColors.push_back(colorWithHueFrom(curColor, fColors[i - 1]));
908 }
909 if (i != colorCount - 1) {
910 newPositions.push_back(curPos);
911 newColors.push_back(colorWithHueFrom(curColor, fColors[i + 1]));
912 }
913 }
914
915 fColors.swap(newColors);
916 fPositionStorage.swap(newPositions);
917 fPositions = fPositionStorage.data();
918 colorCount = fColors.size();
919 }
920
921 // 4) For polar colors, adjust hue values to respect the hue method. We're using a trick here...
922 // The specification looks at adjacent colors, and adjusts one or the other. Because we store
923 // the stops in uniforms (and our backend conversions normalize the hue angle), we can
924 // instead always apply the adjustment to the *second* color. That lets us keep a running
925 // total, and do a single pass across all the colors to respect the requested hue method,
926 // without needing to do any extra work per-pixel.
927 if (color_space_is_polar(interpolation.fColorSpace)) {
928 float delta = 0;
929 for (int i = 0; i < colorCount - 1; ++i) {
930 float h1 = fColors[i].fR;
931 float& h2 = fColors[i + 1].fR;
932 h2 += delta;
933 switch (interpolation.fHueMethod) {
934 case HueMethod::kShorter:
935 if (h2 - h1 > 180) {
936 h2 -= 360; // i.e. h1 += 360
937 delta -= 360;
938 } else if (h2 - h1 < -180) {
939 h2 += 360;
940 delta += 360;
941 }
942 break;
943 case HueMethod::kLonger:
944 if ((i == 0 && shader->fFirstStopIsImplicit) ||
945 (i == colorCount - 2 && shader->fLastStopIsImplicit)) {
946 // Do nothing. We don't want to introduce a full revolution for these stops
947 // Full rationale at skbug.com/13941
948 } else if (0 < h2 - h1 && h2 - h1 < 180) {
949 h2 -= 360; // i.e. h1 += 360
950 delta -= 360;
951 } else if (-180 < h2 - h1 && h2 - h1 <= 0) {
952 h2 += 360;
953 delta += 360;
954 }
955 break;
956 case HueMethod::kIncreasing:
957 if (h2 < h1) {
958 h2 += 360;
959 delta += 360;
960 }
961 break;
962 case HueMethod::kDecreasing:
963 if (h1 < h2) {
964 h2 -= 360; // i.e. h1 += 360;
965 delta -= 360;
966 }
967 break;
968 }
969 }
970 }
971
972 // 5) Apply premultiplication
973 PremulColorProc premulFn = nullptr;
974 if (static_cast<bool>(interpolation.fInPremul)) {
975 switch (interpolation.fColorSpace) {
976 case ColorSpace::kHSL:
977 case ColorSpace::kHWB:
978 case ColorSpace::kLCH:
979 case ColorSpace::kOKLCH:
980 premulFn = premul_polar;
981 break;
982 default:
983 premulFn = premul_rgb;
984 break;
985 }
986 }
987
988 if (premulFn) {
989 for (int i = 0; i < colorCount; ++i) {
990 fColors[i] = premulFn(fColors[i]);
991 }
992 }
993
994 // Ganesh requires that the positions be explicit (rather than implicitly evenly spaced)
995 if (forceExplicitPositions && !fPositions) {
996 fPositionStorage.reserve_exact(colorCount);
997 float posScale = 1.0f / (colorCount - 1);
998 for (int i = 0; i < colorCount; i++) {
999 fPositionStorage.push_back(i * posScale);
1000 }
1001 fPositions = fPositionStorage.data();
1002 }
1003 }
1004
SkColorConverter(const SkColor * colors,int count)1005 SkColorConverter::SkColorConverter(const SkColor* colors, int count) {
1006 constexpr float ONE_OVER_255 = 1.f / 255;
1007 for (int i = 0; i < count; ++i) {
1008 fColors4f.push_back({SkColorGetR(colors[i]) * ONE_OVER_255,
1009 SkColorGetG(colors[i]) * ONE_OVER_255,
1010 SkColorGetB(colors[i]) * ONE_OVER_255,
1011 SkColorGetA(colors[i]) * ONE_OVER_255});
1012 }
1013 }
1014
commonAsAGradient(GradientInfo * info) const1015 void SkGradientBaseShader::commonAsAGradient(GradientInfo* info) const {
1016 if (info) {
1017 if (info->fColorCount >= fColorCount) {
1018 if (info->fColors) {
1019 for (int i = 0; i < fColorCount; ++i) {
1020 info->fColors[i] = this->getLegacyColor(i);
1021 }
1022 }
1023 if (info->fColorOffsets) {
1024 for (int i = 0; i < fColorCount; ++i) {
1025 info->fColorOffsets[i] = this->getPos(i);
1026 }
1027 }
1028 }
1029 info->fColorCount = fColorCount;
1030 info->fTileMode = fTileMode;
1031
1032 info->fGradientFlags =
1033 this->interpolateInPremul() ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0;
1034 }
1035 }
1036
1037 // Return true if these parameters are valid/legal/safe to construct a gradient
1038 //
ValidGradient(const SkColor4f colors[],int count,SkTileMode tileMode,const Interpolation & interpolation)1039 bool SkGradientBaseShader::ValidGradient(const SkColor4f colors[],
1040 int count,
1041 SkTileMode tileMode,
1042 const Interpolation& interpolation) {
1043 return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount &&
1044 (unsigned)interpolation.fColorSpace < Interpolation::kColorSpaceCount &&
1045 (unsigned)interpolation.fHueMethod < Interpolation::kHueMethodCount;
1046 }
1047
Descriptor(const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar positions[],int colorCount,SkTileMode mode,const Interpolation & interpolation)1048 SkGradientBaseShader::Descriptor::Descriptor(const SkColor4f colors[],
1049 sk_sp<SkColorSpace> colorSpace,
1050 const SkScalar positions[],
1051 int colorCount,
1052 SkTileMode mode,
1053 const Interpolation& interpolation)
1054 : fColors(colors)
1055 , fColorSpace(std::move(colorSpace))
1056 , fPositions(positions)
1057 , fColorCount(colorCount)
1058 , fTileMode(mode)
1059 , fInterpolation(interpolation) {
1060 SkASSERT(fColorCount > 1);
1061 }
1062
average_gradient_color(const SkColor4f colors[],const SkScalar pos[],int colorCount)1063 static SkColor4f average_gradient_color(const SkColor4f colors[],
1064 const SkScalar pos[],
1065 int colorCount) {
1066 // The gradient is a piecewise linear interpolation between colors. For a given interval,
1067 // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
1068 // intervals average color. The overall average color is thus the sum of each piece. The thing
1069 // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
1070 skvx::float4 blend(0.0f);
1071 for (int i = 0; i < colorCount - 1; ++i) {
1072 // Calculate the average color for the interval between pos(i) and pos(i+1)
1073 auto c0 = skvx::float4::Load(&colors[i]);
1074 auto c1 = skvx::float4::Load(&colors[i + 1]);
1075
1076 // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
1077 // so pos[i + 1] - pos[i] = 1/(colorCount-1)
1078 SkScalar w;
1079 if (pos) {
1080 // Match position fixing in SkGradientShader's constructor, clamping positions outside
1081 // [0, 1] and forcing the sequence to be monotonic
1082 SkScalar p0 = SkTPin(pos[i], 0.f, 1.f);
1083 SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f);
1084 w = p1 - p0;
1085
1086 // And account for any implicit intervals at the start or end of the positions
1087 if (i == 0) {
1088 if (p0 > 0.0f) {
1089 // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi)
1090 // becomes 0.5*(c + c)*(pj - 0) = c * pj
1091 auto c = skvx::float4::Load(&colors[0]);
1092 blend += p0 * c;
1093 }
1094 }
1095 if (i == colorCount - 2) {
1096 if (p1 < 1.f) {
1097 // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi)
1098 // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi)
1099 auto c = skvx::float4::Load(&colors[colorCount - 1]);
1100 blend += (1.f - p1) * c;
1101 }
1102 }
1103 } else {
1104 w = 1.f / (colorCount - 1);
1105 }
1106
1107 blend += 0.5f * w * (c1 + c0);
1108 }
1109
1110 SkColor4f avg;
1111 blend.store(&avg);
1112 return avg;
1113 }
1114
1115 // Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
1116 // can be mapped to the same fallbacks. The specific shape factories must account for special
1117 // clamped conditions separately, this will always return the last color for clamped gradients.
MakeDegenerateGradient(const SkColor4f colors[],const SkScalar pos[],int colorCount,sk_sp<SkColorSpace> colorSpace,SkTileMode mode)1118 sk_sp<SkShader> SkGradientBaseShader::MakeDegenerateGradient(const SkColor4f colors[],
1119 const SkScalar pos[],
1120 int colorCount,
1121 sk_sp<SkColorSpace> colorSpace,
1122 SkTileMode mode) {
1123 switch (mode) {
1124 case SkTileMode::kDecal:
1125 // normally this would reject the area outside of the interpolation region, so since
1126 // inside region is empty when the radii are equal, the entire draw region is empty
1127 return SkShaders::Empty();
1128 case SkTileMode::kRepeat:
1129 case SkTileMode::kMirror:
1130 // repeat and mirror are treated the same: the border colors are never visible,
1131 // but approximate the final color as infinite repetitions of the colors, so
1132 // it can be represented as the average color of the gradient.
1133 return SkShaders::Color(average_gradient_color(colors, pos, colorCount),
1134 std::move(colorSpace));
1135 case SkTileMode::kClamp:
1136 // Depending on how the gradient shape degenerates, there may be a more specialized
1137 // fallback representation for the factories to use, but this is a reasonable default.
1138 return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
1139 }
1140 SkDEBUGFAIL("Should not be reached");
1141 return nullptr;
1142 }
1143