xref: /aosp_15_r20/external/skia/src/shaders/gradients/SkGradientBaseShader.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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