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