• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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