• 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/SkGradientShaderBase.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "src/base/SkVx.h"
12 #include "src/core/SkColorSpacePriv.h"
13 #include "src/core/SkColorSpaceXformSteps.h"
14 #include "src/core/SkConvertPixels.h"
15 #include "src/core/SkMatrixProvider.h"
16 #include "src/core/SkRasterPipeline.h"
17 #include "src/core/SkReadBuffer.h"
18 #include "src/core/SkVM.h"
19 #include "src/core/SkWriteBuffer.h"
20 
21 #if defined(SK_GRAPHITE)
22 #include "src/core/SkColorSpacePriv.h"
23 #include "src/gpu/graphite/KeyContext.h"
24 #include "src/gpu/graphite/KeyHelpers.h"
25 #include "src/gpu/graphite/PaintParamsKey.h"
26 #endif
27 
28 #include <cmath>
29 
30 enum GradientSerializationFlags {
31     // Bits 29:31 used for various boolean flags
32     kHasPosition_GSF          = 0x80000000,
33     kHasLegacyLocalMatrix_GSF = 0x40000000,
34     kHasColorSpace_GSF        = 0x20000000,
35 
36     // Bits 12:28 unused
37 
38     // Bits 8:11 for fTileMode
39     kTileModeShift_GSF  = 8,
40     kTileModeMask_GSF   = 0xF,
41 
42     // Bits 4:7 for fInterpolation.fColorSpace
43     kInterpolationColorSpaceShift_GSF = 4,
44     kInterpolationColorSpaceMask_GSF  = 0xF,
45 
46     // Bits 1:3 for fInterpolation.fHueMethod
47     kInterpolationHueMethodShift_GSF = 1,
48     kInterpolationHueMethodMask_GSF  = 0x7,
49 
50     // Bit 0 for fInterpolation.fInPremul
51     kInterpolationInPremul_GSF = 0x1,
52 };
53 
Descriptor()54 SkGradientShaderBase::Descriptor::Descriptor() {
55     sk_bzero(this, sizeof(*this));
56     fTileMode = SkTileMode::kClamp;
57 }
58 SkGradientShaderBase::Descriptor::~Descriptor() = default;
59 
flatten(SkWriteBuffer & buffer) const60 void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
61     uint32_t flags = 0;
62     if (fPositions) {
63         flags |= kHasPosition_GSF;
64     }
65     sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
66     if (colorSpaceData) {
67         flags |= kHasColorSpace_GSF;
68     }
69     if (fInterpolation.fInPremul == Interpolation::InPremul::kYes) {
70         flags |= kInterpolationInPremul_GSF;
71     }
72     SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
73     flags |= ((uint32_t)fTileMode << kTileModeShift_GSF);
74     SkASSERT(static_cast<uint32_t>(fInterpolation.fColorSpace) <= kInterpolationColorSpaceMask_GSF);
75     flags |= ((uint32_t)fInterpolation.fColorSpace << kInterpolationColorSpaceShift_GSF);
76     SkASSERT(static_cast<uint32_t>(fInterpolation.fHueMethod) <= kInterpolationHueMethodMask_GSF);
77     flags |= ((uint32_t)fInterpolation.fHueMethod << kInterpolationHueMethodShift_GSF);
78 
79     buffer.writeUInt(flags);
80 
81     // If we injected implicit first/last stops at construction time, omit those when serializing:
82     int colorCount = fColorCount;
83     const SkColor4f* colors = fColors;
84     const SkScalar* positions = fPositions;
85     if (fFirstStopIsImplicit) {
86         colorCount--;
87         colors++;
88         if (positions) {
89             positions++;
90         }
91     }
92     if (fLastStopIsImplicit) {
93         colorCount--;
94     }
95 
96     buffer.writeColor4fArray(colors, colorCount);
97     if (colorSpaceData) {
98         buffer.writeDataAsByteArray(colorSpaceData.get());
99     }
100     if (positions) {
101         buffer.writeScalarArray(positions, colorCount);
102     }
103 }
104 
105 template <int N, typename T, bool MEM_MOVE>
validate_array(SkReadBuffer & buffer,size_t count,SkSTArray<N,T,MEM_MOVE> * array)106 static bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) {
107     if (!buffer.validateCanReadN<T>(count)) {
108         return false;
109     }
110 
111     array->resize_back(count);
112     return true;
113 }
114 
unflatten(SkReadBuffer & buffer,SkMatrix * legacyLocalMatrix)115 bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer,
116                                                       SkMatrix* legacyLocalMatrix) {
117     // New gradient format. Includes floating point color, color space, densely packed flags
118     uint32_t flags = buffer.readUInt();
119 
120     fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
121 
122     fInterpolation.fColorSpace = (Interpolation::ColorSpace)(
123             (flags >> kInterpolationColorSpaceShift_GSF) & kInterpolationColorSpaceMask_GSF);
124     fInterpolation.fHueMethod = (Interpolation::HueMethod)(
125             (flags >> kInterpolationHueMethodShift_GSF) & kInterpolationHueMethodMask_GSF);
126     fInterpolation.fInPremul = (flags & kInterpolationInPremul_GSF) ? Interpolation::InPremul::kYes
127                                                                     : Interpolation::InPremul::kNo;
128 
129     fColorCount = buffer.getArrayCount();
130 
131     if (!(validate_array(buffer, fColorCount, &fColorStorage) &&
132           buffer.readColor4fArray(fColorStorage.begin(), fColorCount))) {
133         return false;
134     }
135     fColors = fColorStorage.begin();
136 
137     if (SkToBool(flags & kHasColorSpace_GSF)) {
138         sk_sp<SkData> data = buffer.readByteArrayAsData();
139         fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
140     } else {
141         fColorSpace = nullptr;
142     }
143     if (SkToBool(flags & kHasPosition_GSF)) {
144         if (!(validate_array(buffer, fColorCount, &fPositionStorage) &&
145               buffer.readScalarArray(fPositionStorage.begin(), fColorCount))) {
146             return false;
147         }
148         fPositions = fPositionStorage.begin();
149     } else {
150         fPositions = nullptr;
151     }
152     if (SkToBool(flags & kHasLegacyLocalMatrix_GSF)) {
153         SkASSERT(buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix));
154         buffer.readMatrix(legacyLocalMatrix);
155     } else {
156         *legacyLocalMatrix = SkMatrix::I();
157     }
158     return buffer.isValid();
159 }
160 
161 ////////////////////////////////////////////////////////////////////////////////////////////
162 
SkGradientShaderBase(const Descriptor & desc,const SkMatrix & ptsToUnit)163 SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
164         : fPtsToUnit(ptsToUnit)
165         , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
166         , fFirstStopIsImplicit(false)
167         , fLastStopIsImplicit(false)
168         , fColorsAreOpaque(true) {
169     fPtsToUnit.getType();  // Precache so reads are threadsafe.
170     SkASSERT(desc.fColorCount > 1);
171 
172     fInterpolation = desc.fInterpolation;
173 
174     SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
175     fTileMode = desc.fTileMode;
176 
177     /*  Note: we let the caller skip the first and/or last position.
178         i.e. pos[0] = 0.3, pos[1] = 0.7
179         In these cases, we insert entries to ensure that the final data
180         will be bracketed by [0, 1].
181         i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
182 
183         Thus colorCount (the caller's value, and fColorCount (our value) may
184         differ by up to 2. In the above example:
185             colorCount = 2
186             fColorCount = 4
187      */
188     fColorCount = desc.fColorCount;
189     // check if we need to add in start and/or end position/colors
190     if (desc.fPositions) {
191         fFirstStopIsImplicit = desc.fPositions[0] != 0;
192         fLastStopIsImplicit = desc.fPositions[desc.fColorCount - 1] != SK_Scalar1;
193         fColorCount += fFirstStopIsImplicit + fLastStopIsImplicit;
194     }
195 
196     size_t storageSize =
197             fColorCount * (sizeof(SkColor4f) + (desc.fPositions ? sizeof(SkScalar) : 0));
198     fColors    = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
199     fPositions = desc.fPositions ? reinterpret_cast<SkScalar*>(fColors + fColorCount) : nullptr;
200 
201     // Now copy over the colors, adding the duplicates at t=0 and t=1 as needed
202     SkColor4f* colors = fColors;
203     if (fFirstStopIsImplicit) {
204         *colors++ = desc.fColors[0];
205     }
206     for (int i = 0; i < desc.fColorCount; ++i) {
207         colors[i] = desc.fColors[i];
208         fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
209     }
210     if (fLastStopIsImplicit) {
211         colors += desc.fColorCount;
212         *colors = desc.fColors[desc.fColorCount - 1];
213     }
214 
215     if (desc.fPositions) {
216         SkScalar prev = 0;
217         SkScalar* positions = fPositions;
218         *positions++ = prev; // force the first pos to 0
219 
220         int startIndex = fFirstStopIsImplicit ? 0 : 1;
221         int count = desc.fColorCount + fLastStopIsImplicit;
222 
223         bool uniformStops = true;
224         const SkScalar uniformStep = desc.fPositions[startIndex] - prev;
225         for (int i = startIndex; i < count; i++) {
226             // Pin the last value to 1.0, and make sure pos is monotonic.
227             auto curr = (i == desc.fColorCount) ? 1 : SkTPin(desc.fPositions[i], prev, 1.0f);
228             uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
229 
230             *positions++ = prev = curr;
231         }
232 
233         // If the stops are uniform, treat them as implicit.
234         if (uniformStops) {
235             fPositions = nullptr;
236         }
237     }
238 }
239 
~SkGradientShaderBase()240 SkGradientShaderBase::~SkGradientShaderBase() {}
241 
add_stop_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f Fs,SkPMColor4f Bs)242 static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop,
243                            SkPMColor4f Fs, SkPMColor4f Bs) {
244     (ctx->fs[0])[stop] = Fs.fR;
245     (ctx->fs[1])[stop] = Fs.fG;
246     (ctx->fs[2])[stop] = Fs.fB;
247     (ctx->fs[3])[stop] = Fs.fA;
248 
249     (ctx->bs[0])[stop] = Bs.fR;
250     (ctx->bs[1])[stop] = Bs.fG;
251     (ctx->bs[2])[stop] = Bs.fB;
252     (ctx->bs[3])[stop] = Bs.fA;
253 }
254 
add_const_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f color)255 static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) {
256     add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color);
257 }
258 
259 // Calculate a factor F and a bias B so that color = F*t + B when t is in range of
260 // 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)261 static void init_stop_evenly(SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop,
262                              SkPMColor4f c_l, SkPMColor4f c_r) {
263     // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
264     SkPMColor4f Fs = {
265         (c_r.fR - c_l.fR) * gapCount,
266         (c_r.fG - c_l.fG) * gapCount,
267         (c_r.fB - c_l.fB) * gapCount,
268         (c_r.fA - c_l.fA) * gapCount,
269     };
270     SkPMColor4f Bs = {
271         c_l.fR - Fs.fR*(stop/gapCount),
272         c_l.fG - Fs.fG*(stop/gapCount),
273         c_l.fB - Fs.fB*(stop/gapCount),
274         c_l.fA - Fs.fA*(stop/gapCount),
275     };
276     add_stop_color(ctx, stop, Fs, Bs);
277 }
278 
279 // For each stop we calculate a bias B and a scale factor F, such that
280 // 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 t_r,SkPMColor4f c_l,SkPMColor4f c_r)281 static void init_stop_pos(SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r,
282                           SkPMColor4f c_l, SkPMColor4f c_r) {
283     // See note about Clankium's old compiler in init_stop_evenly().
284     SkPMColor4f Fs = {
285         (c_r.fR - c_l.fR) / (t_r - t_l),
286         (c_r.fG - c_l.fG) / (t_r - t_l),
287         (c_r.fB - c_l.fB) / (t_r - t_l),
288         (c_r.fA - c_l.fA) / (t_r - t_l),
289     };
290     SkPMColor4f Bs = {
291         c_l.fR - Fs.fR*t_l,
292         c_l.fG - Fs.fG*t_l,
293         c_l.fB - Fs.fB*t_l,
294         c_l.fA - Fs.fA*t_l,
295     };
296     ctx->ts[stop] = t_l;
297     add_stop_color(ctx, stop, Fs, Bs);
298 }
299 
AppendGradientFillStages(SkRasterPipeline * p,SkArenaAlloc * alloc,const SkPMColor4f * pmColors,const SkScalar * positions,int count)300 void SkGradientShaderBase::AppendGradientFillStages(SkRasterPipeline* p,
301                                                     SkArenaAlloc* alloc,
302                                                     const SkPMColor4f* pmColors,
303                                                     const SkScalar* positions,
304                                                     int count) {
305     // The two-stop case with stops at 0 and 1.
306     if (count == 2 && positions == nullptr) {
307         const SkPMColor4f c_l = pmColors[0],
308                           c_r = pmColors[1];
309 
310         // See F and B below.
311         auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
312         (skvx::float4::Load(c_r.vec()) - skvx::float4::Load(c_l.vec())).store(ctx->f);
313         (                                skvx::float4::Load(c_l.vec())).store(ctx->b);
314 
315         p->append(SkRasterPipelineOp::evenly_spaced_2_stop_gradient, ctx);
316     } else {
317         auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
318 
319         // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
320         // at -inf. Therefore, the max number of stops is fColorCount+1.
321         for (int i = 0; i < 4; i++) {
322             // Allocate at least at for the AVX2 gather from a YMM register.
323             ctx->fs[i] = alloc->makeArray<float>(std::max(count + 1, 8));
324             ctx->bs[i] = alloc->makeArray<float>(std::max(count + 1, 8));
325         }
326 
327         if (positions == nullptr) {
328             // Handle evenly distributed stops.
329 
330             size_t stopCount = count;
331             float gapCount = stopCount - 1;
332 
333             SkPMColor4f c_l = pmColors[0];
334             for (size_t i = 0; i < stopCount - 1; i++) {
335                 SkPMColor4f c_r = pmColors[i + 1];
336                 init_stop_evenly(ctx, gapCount, i, c_l, c_r);
337                 c_l = c_r;
338             }
339             add_const_color(ctx, stopCount - 1, c_l);
340 
341             ctx->stopCount = stopCount;
342             p->append(SkRasterPipelineOp::evenly_spaced_gradient, ctx);
343         } else {
344             // Handle arbitrary stops.
345 
346             ctx->ts = alloc->makeArray<float>(count + 1);
347 
348             // Remove the default stops inserted by SkGradientShaderBase::SkGradientShaderBase
349             // because they are naturally handled by the search method.
350             int firstStop;
351             int lastStop;
352             if (count > 2) {
353                 firstStop = pmColors[0] != pmColors[1] ? 0 : 1;
354                 lastStop = pmColors[count - 2] != pmColors[count - 1] ? count - 1 : count - 2;
355             } else {
356                 firstStop = 0;
357                 lastStop = 1;
358             }
359 
360             size_t stopCount = 0;
361             float  t_l = positions[firstStop];
362             SkPMColor4f c_l = pmColors[firstStop];
363             add_const_color(ctx, stopCount++, c_l);
364             // N.B. lastStop is the index of the last stop, not one after.
365             for (int i = firstStop; i < lastStop; i++) {
366                 float  t_r = positions[i + 1];
367                 SkPMColor4f c_r = pmColors[i + 1];
368                 SkASSERT(t_l <= t_r);
369                 if (t_l < t_r) {
370                     init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
371                     stopCount += 1;
372                 }
373                 t_l = t_r;
374                 c_l = c_r;
375             }
376 
377             ctx->ts[stopCount] = t_l;
378             add_const_color(ctx, stopCount++, c_l);
379 
380             ctx->stopCount = stopCount;
381             p->append(SkRasterPipelineOp::gradient, ctx);
382         }
383     }
384 }
385 
appendStages(const SkStageRec & rec,const MatrixRec & mRec) const386 bool SkGradientShaderBase::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
387     SkRasterPipeline* p = rec.fPipeline;
388     SkArenaAlloc* alloc = rec.fAlloc;
389     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
390 
391     std::optional<MatrixRec> newMRec = mRec.apply(rec, fPtsToUnit);
392     if (!newMRec.has_value()) {
393         return false;
394     }
395 
396     SkRasterPipeline_<256> postPipeline;
397 
398     this->appendGradientStages(alloc, p, &postPipeline);
399 
400     switch(fTileMode) {
401         case SkTileMode::kMirror: p->append(SkRasterPipelineOp::mirror_x_1); break;
402         case SkTileMode::kRepeat: p->append(SkRasterPipelineOp::repeat_x_1); break;
403         case SkTileMode::kDecal:
404             decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
405             decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
406             // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
407             p->append(SkRasterPipelineOp::decal_x, decal_ctx);
408             [[fallthrough]];
409 
410         case SkTileMode::kClamp:
411             if (!fPositions) {
412                 // We clamp only when the stops are evenly spaced.
413                 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
414                 // In that case, we must make sure we're using the general "gradient" stage,
415                 // which is the only stage that will correctly handle unclamped t.
416                 p->append(SkRasterPipelineOp::clamp_x_1);
417             }
418             break;
419     }
420 
421     // Transform all of the colors to destination color space, possibly premultiplied
422     SkColor4fXformer xformedColors(this, rec.fDstCS);
423     AppendGradientFillStages(p, alloc, xformedColors.fColors.begin(), fPositions, fColorCount);
424 
425     using ColorSpace = Interpolation::ColorSpace;
426     bool colorIsPremul = this->interpolateInPremul();
427 
428     // If we interpolated premul colors in any of the special color spaces, we need to unpremul
429     if (colorIsPremul && !fColorsAreOpaque) {
430         switch (fInterpolation.fColorSpace) {
431             case ColorSpace::kLab:
432             case ColorSpace::kOKLab:
433                 p->append(SkRasterPipelineOp::unpremul);
434                 colorIsPremul = false;
435                 break;
436             case ColorSpace::kLCH:
437             case ColorSpace::kOKLCH:
438             case ColorSpace::kHSL:
439             case ColorSpace::kHWB:
440                 p->append(SkRasterPipelineOp::unpremul_polar);
441                 colorIsPremul = false;
442                 break;
443             default: break;
444         }
445     }
446 
447     // Convert colors in exotic spaces back to their intermediate SkColorSpace
448     switch (fInterpolation.fColorSpace) {
449         case ColorSpace::kLab:   p->append(SkRasterPipelineOp::css_lab_to_xyz);           break;
450         case ColorSpace::kOKLab: p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
451         case ColorSpace::kLCH:   p->append(SkRasterPipelineOp::css_hcl_to_lab);
452                                  p->append(SkRasterPipelineOp::css_lab_to_xyz);           break;
453         case ColorSpace::kOKLCH: p->append(SkRasterPipelineOp::css_hcl_to_lab);
454                                  p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
455         case ColorSpace::kHSL:   p->append(SkRasterPipelineOp::css_hsl_to_srgb);          break;
456         case ColorSpace::kHWB:   p->append(SkRasterPipelineOp::css_hwb_to_srgb);          break;
457         default: break;
458     }
459 
460     // Now transform from intermediate to destination color space.
461     // See comments in GrGradientShader.cpp about the decisions here.
462     SkColorSpace* dstColorSpace = rec.fDstCS ? rec.fDstCS : sk_srgb_singleton();
463     SkAlphaType intermediateAlphaType = colorIsPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
464     // TODO(skia:13108): Get dst alpha type correctly
465     SkAlphaType dstAlphaType = kPremul_SkAlphaType;
466 
467     if (fColorsAreOpaque) {
468         intermediateAlphaType = dstAlphaType = kUnpremul_SkAlphaType;
469     }
470 
471     alloc->make<SkColorSpaceXformSteps>(xformedColors.fIntermediateColorSpace.get(),
472                                         intermediateAlphaType,
473                                         dstColorSpace,
474                                         dstAlphaType)
475             ->apply(p);
476 
477     if (decal_ctx) {
478         p->append(SkRasterPipelineOp::check_decal_mask, decal_ctx);
479     }
480 
481     p->extend(postPipeline);
482 
483     return true;
484 }
485 
486 // Color conversion functions used in gradient interpolation, based on
487 // https://www.w3.org/TR/css-color-4/#color-conversion-code
css_lab_to_xyz(skvm::Color lab)488 static skvm::Color css_lab_to_xyz(skvm::Color lab) {
489     constexpr float k = 24389 / 27.0f;
490     constexpr float e = 216 / 24389.0f;
491 
492     skvm::F32 f[3];
493     f[1] = (lab.r + 16) * (1 / 116.0f);
494     f[0] = (lab.g * (1 / 500.0f)) + f[1];
495     f[2] = f[1] - (lab.b * (1 / 200.0f));
496 
497     skvm::F32 f_cubed[3] = { f[0]*f[0]*f[0], f[1]*f[1]*f[1], f[2]*f[2]*f[2] };
498 
499     skvm::F32 xyz[3] = {
500         skvm::select(f_cubed[0] > e, f_cubed[0], (116 * f[0] - 16) * (1 / k)),
501         skvm::select(lab.r > k * e , f_cubed[1], lab.r * (1 / k)),
502         skvm::select(f_cubed[2] > e, f_cubed[2], (116 * f[2] - 16) * (1 / k))
503     };
504 
505     constexpr float D50[3] = { 0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f };
506     return skvm::Color { xyz[0]*D50[0], xyz[1]*D50[1], xyz[2]*D50[2], lab.a };
507 }
508 
509 // Skia stores all polar colors with hue in the first component, so this "LCH -> Lab" transform
510 // actually takes "HCL". This is also used to do the same polar transform for OkHCL to OkLAB.
css_hcl_to_lab(skvm::Color hcl)511 static skvm::Color css_hcl_to_lab(skvm::Color hcl) {
512     skvm::F32 hueRadians = hcl.r * (SK_FloatPI / 180);
513     return skvm::Color {
514         hcl.b,
515         hcl.g * approx_cos(hueRadians),
516         hcl.g * approx_sin(hueRadians),
517         hcl.a
518     };
519 }
520 
css_hcl_to_xyz(skvm::Color hcl)521 static skvm::Color css_hcl_to_xyz(skvm::Color hcl) {
522     return css_lab_to_xyz(css_hcl_to_lab(hcl));
523 }
524 
css_oklab_to_linear_srgb(skvm::Color oklab)525 static skvm::Color css_oklab_to_linear_srgb(skvm::Color oklab) {
526     skvm::F32 l_ = oklab.r + 0.3963377774f * oklab.g + 0.2158037573f * oklab.b,
527               m_ = oklab.r - 0.1055613458f * oklab.g - 0.0638541728f * oklab.b,
528               s_ = oklab.r - 0.0894841775f * oklab.g - 1.2914855480f * oklab.b;
529 
530     skvm::F32 l = l_*l_*l_,
531               m = m_*m_*m_,
532               s = s_*s_*s_;
533 
534     return skvm::Color {
535         +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
536         -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
537         -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
538         oklab.a
539     };
540 
541 }
542 
css_okhcl_to_linear_srgb(skvm::Color okhcl)543 static skvm::Color css_okhcl_to_linear_srgb(skvm::Color okhcl) {
544     return css_oklab_to_linear_srgb(css_hcl_to_lab(okhcl));
545 }
546 
mod_f(skvm::F32 x,float y)547 static skvm::F32 mod_f(skvm::F32 x, float y) {
548     return x - y * skvm::floor(x * (1 / y));
549 }
550 
css_hsl_to_srgb(skvm::Color hsl)551 static skvm::Color css_hsl_to_srgb(skvm::Color hsl) {
552     hsl.r = mod_f(hsl.r, 360);
553     hsl.r = skvm::select(hsl.r < 0, hsl.r + 360, hsl.r);
554 
555     hsl.g *= 0.01f;
556     hsl.b *= 0.01f;
557 
558     skvm::F32 k[3] = {
559         mod_f(0 + hsl.r * (1 / 30.0f), 12),
560         mod_f(8 + hsl.r * (1 / 30.0f), 12),
561         mod_f(4 + hsl.r * (1 / 30.0f), 12),
562     };
563     skvm::F32 a = hsl.g * min(hsl.b, 1 - hsl.b);
564     return skvm::Color {
565         hsl.b - a * clamp(min(k[0] - 3, 9 - k[0]), -1, 1),
566         hsl.b - a * clamp(min(k[1] - 3, 9 - k[1]), -1, 1),
567         hsl.b - a * clamp(min(k[2] - 3, 9 - k[2]), -1, 1),
568         hsl.a
569     };
570 }
571 
css_hwb_to_srgb(skvm::Color hwb,skvm::Builder * p)572 static skvm::Color css_hwb_to_srgb(skvm::Color hwb, skvm::Builder* p) {
573     hwb.g *= 0.01f;
574     hwb.b *= 0.01f;
575 
576     skvm::F32 gray = hwb.g / (hwb.g + hwb.b);
577 
578     skvm::Color rgb = css_hsl_to_srgb(skvm::Color{hwb.r, p->splat(100.0f), p->splat(50.0f), hwb.a});
579     rgb.r = rgb.r * (1 - hwb.g - hwb.b) + hwb.g;
580     rgb.g = rgb.g * (1 - hwb.g - hwb.b) + hwb.g;
581     rgb.b = rgb.b * (1 - hwb.g - hwb.b) + hwb.g;
582 
583     skvm::I32 isGray = (hwb.g + hwb.b) >= 1;
584 
585     return skvm::Color {
586         select(isGray, gray, rgb.r),
587         select(isGray, gray, rgb.g),
588         select(isGray, gray, rgb.b),
589         hwb.a
590     };
591 }
592 
program(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color,const MatrixRec & mRec,const SkColorInfo & dstInfo,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const593 skvm::Color SkGradientShaderBase::program(skvm::Builder* p,
594                                           skvm::Coord device,
595                                           skvm::Coord local,
596                                           skvm::Color /*paint*/,
597                                           const MatrixRec& mRec,
598                                           const SkColorInfo& dstInfo,
599                                           skvm::Uniforms* uniforms,
600                                           SkArenaAlloc* alloc) const {
601     if (!mRec.apply(p, &local, uniforms, fPtsToUnit).has_value()) {
602         return {};
603     }
604 
605     skvm::I32 mask = p->splat(~0);
606     skvm::F32 t = this->transformT(p,uniforms, local, &mask);
607 
608     // Perhaps unexpectedly, clamping is handled naturally by our search, so we
609     // don't explicitly clamp t to [0,1].  That clamp would break hard stops
610     // right at 0 or 1 boundaries in kClamp mode.  (kRepeat and kMirror always
611     // produce values in [0,1].)
612     switch(fTileMode) {
613         case SkTileMode::kClamp:
614             break;
615 
616         case SkTileMode::kDecal:
617             mask &= (t == clamp01(t));
618             break;
619 
620         case SkTileMode::kRepeat:
621             t = fract(t);
622             break;
623 
624         case SkTileMode::kMirror: {
625             // t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 |
626             //       {-A-}      {--------B-------}
627             skvm::F32 A = t - 1.0f,
628                       B = floor(A * 0.5f);
629             t = abs(A - (B + B) - 1.0f);
630         } break;
631     }
632 
633     // Transform our colors as we want them interpolated, in dst color space, possibly premul.
634     SkColor4fXformer xformedColors(this, dstInfo.colorSpace());
635     const SkPMColor4f* rgba = xformedColors.fColors.begin();
636 
637     // Transform our colors into a scale factor f and bias b such that for
638     // any t between stops i and i+1, the color we want is mad(t, f[i], b[i]).
639     using F4 = skvx::Vec<4,float>;
640     struct FB { F4 f,b; };
641     skvm::Color color;
642 
643     auto uniformF = [&](float x) { return p->uniformF(uniforms->pushF(x)); };
644 
645     if (fColorCount == 2) {
646         // 2-stop gradients have colors at 0 and 1, and so must be evenly spaced.
647         SkASSERT(fPositions == nullptr);
648 
649         // With 2 stops, we upload the single FB as uniforms and interpolate directly with t.
650         F4 lo = F4::Load(rgba + 0),
651            hi = F4::Load(rgba + 1);
652         F4 F = hi - lo,
653            B = lo;
654 
655         auto T = clamp01(t);
656         color = {
657             T * uniformF(F[0]) + uniformF(B[0]),
658             T * uniformF(F[1]) + uniformF(B[1]),
659             T * uniformF(F[2]) + uniformF(B[2]),
660             T * uniformF(F[3]) + uniformF(B[3]),
661         };
662     } else {
663         // To handle clamps in search we add a conceptual stop at t=-inf, so we
664         // may need up to fColorCount+1 FBs and fColorCount t stops between them:
665         //
666         //   FBs:         [color 0]  [color 0->1]  [color 1->2]  [color 2->3]  ...
667         //   stops:  (-inf)        t0            t1            t2  ...
668         //
669         // Both these arrays could end up shorter if any hard stops share the same t.
670         FB* fb = alloc->makeArrayDefault<FB>(fColorCount+1);
671         std::vector<float> stops;  // TODO: SkSTArray?
672         stops.reserve(fColorCount);
673 
674         // Here's our conceptual stop at t=-inf covering all t<=0, clamping to our first color.
675         float  t_lo = this->getPos(0);
676         F4 color_lo = F4::Load(rgba);
677         fb[0] = { 0.0f, color_lo };
678         // N.B. No stops[] entry for this implicit -inf.
679 
680         // Now the non-edge cases, calculating scale and bias between adjacent normal stops.
681         for (int i = 1; i < fColorCount; i++) {
682             float  t_hi = this->getPos(i);
683             F4 color_hi = F4::Load(rgba + i);
684 
685             // If t_lo == t_hi, we're on a hard stop, and transition immediately to the next color.
686             SkASSERT(t_lo <= t_hi);
687             if (t_lo < t_hi) {
688                 F4 f = (color_hi - color_lo) / (t_hi - t_lo),
689                    b = color_lo - f*t_lo;
690                 stops.push_back(t_lo);
691                 fb[stops.size()] = {f,b};
692             }
693 
694             t_lo = t_hi;
695             color_lo = color_hi;
696         }
697         // Anything >= our final t clamps to our final color.
698         stops.push_back(t_lo);
699         fb[stops.size()] = { 0.0f, color_lo };
700 
701         // We'll gather FBs from that array we just created.
702         skvm::Uniform fbs = uniforms->pushPtr(fb);
703 
704         // Find the two stops we need to interpolate.
705         skvm::I32 ix;
706         if (fPositions == nullptr) {
707             // Evenly spaced stops... we can calculate ix directly.
708             ix = trunc(clamp(t * uniformF(stops.size() - 1) + 1.0f, 0.0f, uniformF(stops.size())));
709         } else {
710             // Starting ix at 0 bakes in our conceptual first stop at -inf.
711             // TODO: good place to experiment with a loop in skvm.... stops.size() can be huge.
712             ix = p->splat(0);
713             for (float stop : stops) {
714                 // ix += (t >= stop) ? +1 : 0 ~~>
715                 // ix -= (t >= stop) ? -1 : 0
716                 ix -= (t >= uniformF(stop));
717             }
718             // TODO: we could skip any of the default stops GradientShaderBase's ctor added
719             // to ensure the full [0,1] span is covered.  This linear search doesn't need
720             // them for correctness, and it'd be up to two fewer stops to check.
721             // N.B. we do still need those stops for the fPositions == nullptr direct math path.
722         }
723 
724         // A scale factor and bias for each lane, 8 total.
725         // TODO: simpler, faster, tidier to push 8 uniform pointers, one for each struct lane?
726         ix = shl(ix, 3);
727         skvm::F32 Fr = gatherF(fbs, ix + 0);
728         skvm::F32 Fg = gatherF(fbs, ix + 1);
729         skvm::F32 Fb = gatherF(fbs, ix + 2);
730         skvm::F32 Fa = gatherF(fbs, ix + 3);
731 
732         skvm::F32 Br = gatherF(fbs, ix + 4);
733         skvm::F32 Bg = gatherF(fbs, ix + 5);
734         skvm::F32 Bb = gatherF(fbs, ix + 6);
735         skvm::F32 Ba = gatherF(fbs, ix + 7);
736 
737         // This is what we've been building towards!
738         color = {
739             t * Fr + Br,
740             t * Fg + Bg,
741             t * Fb + Bb,
742             t * Fa + Ba,
743         };
744     }
745 
746     using ColorSpace = Interpolation::ColorSpace;
747     bool colorIsPremul = this->interpolateInPremul();
748 
749     // If we interpolated premul colors in any of the special color spaces, we need to unpremul
750     if (colorIsPremul) {
751         switch (fInterpolation.fColorSpace) {
752             case ColorSpace::kLab:
753             case ColorSpace::kOKLab:
754                 color = unpremul(color);
755                 colorIsPremul = false;
756                 break;
757             case ColorSpace::kLCH:
758             case ColorSpace::kOKLCH:
759             case ColorSpace::kHSL:
760             case ColorSpace::kHWB: {
761                 // Avoid unpremuling hue
762                 skvm::F32 hue = color.r;
763                 color = unpremul(color);
764                 color.r = hue;
765                 colorIsPremul = false;
766             } break;
767             default: break;
768         }
769     }
770 
771     // Convert colors in exotic spaces back to their intermediate SkColorSpace
772     switch (fInterpolation.fColorSpace) {
773             case ColorSpace::kLab:   color = css_lab_to_xyz(color);           break;
774             case ColorSpace::kOKLab: color = css_oklab_to_linear_srgb(color); break;
775             case ColorSpace::kLCH:   color = css_hcl_to_xyz(color);           break;
776             case ColorSpace::kOKLCH: color = css_okhcl_to_linear_srgb(color); break;
777             case ColorSpace::kHSL:   color = css_hsl_to_srgb(color);          break;
778             case ColorSpace::kHWB:   color = css_hwb_to_srgb(color, p);       break;
779             default: break;
780     }
781 
782     // Now transform from intermediate to destination color space.
783     // See comments in GrGradientShader.cpp about the decisions here.
784     SkColorSpace* dstColorSpace = dstInfo.colorSpace() ? dstInfo.colorSpace() : sk_srgb_singleton();
785     SkAlphaType intermediateAlphaType = colorIsPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
786     SkAlphaType dstAlphaType = dstInfo.alphaType();
787 
788     if (fColorsAreOpaque) {
789         intermediateAlphaType = dstAlphaType = kUnpremul_SkAlphaType;
790     }
791 
792     color = SkColorSpaceXformSteps{xformedColors.fIntermediateColorSpace.get(),
793                                    intermediateAlphaType,
794                                    dstColorSpace,
795                                    dstAlphaType}
796                     .program(p, uniforms, color);
797 
798     return {
799         pun_to_F32(mask & pun_to_I32(color.r)),
800         pun_to_F32(mask & pun_to_I32(color.g)),
801         pun_to_F32(mask & pun_to_I32(color.b)),
802         pun_to_F32(mask & pun_to_I32(color.a)),
803     };
804 }
805 
isOpaque() const806 bool SkGradientShaderBase::isOpaque() const {
807     return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
808 }
809 
rounded_divide(unsigned numer,unsigned denom)810 static unsigned rounded_divide(unsigned numer, unsigned denom) {
811     return (numer + (denom >> 1)) / denom;
812 }
813 
onAsLuminanceColor(SkColor * lum) const814 bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
815     // we just compute an average color.
816     // possibly we could weight this based on the proportional width for each color
817     //   assuming they are not evenly distributed in the fPos array.
818     int r = 0;
819     int g = 0;
820     int b = 0;
821     const int n = fColorCount;
822     // TODO: use linear colors?
823     for (int i = 0; i < n; ++i) {
824         SkColor c = this->getLegacyColor(i);
825         r += SkColorGetR(c);
826         g += SkColorGetG(c);
827         b += SkColorGetB(c);
828     }
829     *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
830     return true;
831 }
832 
intermediate_color_space(SkGradientShader::Interpolation::ColorSpace cs,SkColorSpace * dst)833 static sk_sp<SkColorSpace> intermediate_color_space(SkGradientShader::Interpolation::ColorSpace cs,
834                                                     SkColorSpace* dst) {
835     using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
836     switch (cs) {
837         case ColorSpace::kDestination: return sk_ref_sp(dst);
838 
839         // css-color-4 allows XYZD50 and XYZD65. For gradients, those are redundant. Interpolating
840         // in any linear RGB space, (regardless of white point), gives the same answer.
841         case ColorSpace::kSRGBLinear: return SkColorSpace::MakeSRGBLinear();
842 
843         case ColorSpace::kSRGB:
844         case ColorSpace::kHSL:
845         case ColorSpace::kHWB: return SkColorSpace::MakeSRGB();
846 
847         case ColorSpace::kLab:
848         case ColorSpace::kLCH:
849             // Conversion to Lab (and LCH) starts with XYZD50
850             return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, SkNamedGamut::kXYZ);
851 
852         case ColorSpace::kOKLab:
853         case ColorSpace::kOKLCH:
854             // The "standard" conversion to these spaces starts with XYZD65. That requires extra
855             // effort to conjure. The author also has reference code for going directly from linear
856             // sRGB, so we use that.
857             // TODO(skia:13108): Even better would be to have an LMS color space, because the first
858             // part of the conversion is a matrix multiply, which could be absorbed into the
859             // color space xform.
860             return SkColorSpace::MakeSRGBLinear();
861     }
862     SkUNREACHABLE;
863 }
864 
865 typedef SkPMColor4f (*ConvertColorProc)(SkPMColor4f);
866 
srgb_to_hsl(SkPMColor4f rgb)867 static SkPMColor4f srgb_to_hsl(SkPMColor4f rgb) {
868     float mx = std::max({rgb.fR, rgb.fG, rgb.fB});
869     float mn = std::min({rgb.fR, rgb.fG, rgb.fB});
870     float hue = 0, sat = 0, light = (mn + mx) / 2;
871     float d = mx - mn;
872 
873     if (d != 0) {
874         sat = (light == 0 || light == 1) ? 0 : (mx - light) / std::min(light, 1 - light);
875         if (mx == rgb.fR) {
876             hue = (rgb.fG - rgb.fB) / d + (rgb.fG < rgb.fB ? 6 : 0);
877         } else if (mx == rgb.fG) {
878             hue = (rgb.fB - rgb.fR) / d + 2;
879         } else {
880             hue = (rgb.fR - rgb.fG) / d + 4;
881         }
882 
883         hue *= 60;
884     }
885     return { hue, sat * 100, light * 100, rgb.fA };
886 }
887 
srgb_to_hwb(SkPMColor4f rgb)888 static SkPMColor4f srgb_to_hwb(SkPMColor4f rgb) {
889     SkPMColor4f hsl = srgb_to_hsl(rgb);
890     float white =     std::min({rgb.fR, rgb.fG, rgb.fB});
891     float black = 1 - std::max({rgb.fR, rgb.fG, rgb.fB});
892     return { hsl.fR, white * 100, black * 100, rgb.fA };
893 }
894 
xyzd50_to_lab(SkPMColor4f xyz)895 static SkPMColor4f xyzd50_to_lab(SkPMColor4f xyz) {
896     constexpr float D50[3] = { 0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f };
897 
898     constexpr float e = 216.0f / 24389;
899     constexpr float k = 24389.0f / 27;
900 
901     SkPMColor4f f;
902     for (int i = 0; i < 3; ++i) {
903         float v = xyz[i] / D50[i];
904         f[i] = (v > e) ? std::cbrtf(v) : (k * v + 16) / 116;
905     }
906 
907     return { (116 * f[1]) - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2]), xyz.fA };
908 }
909 
910 // The color space is technically LCH, but we produce HCL, so that all polar spaces have hue in the
911 // first component. This simplifies the hue handling for HueMethod and premul/unpremul.
xyzd50_to_hcl(SkPMColor4f xyz)912 static SkPMColor4f xyzd50_to_hcl(SkPMColor4f xyz) {
913     SkPMColor4f Lab = xyzd50_to_lab(xyz);
914     float hue = sk_float_radians_to_degrees(atan2f(Lab[2], Lab[1]));
915     return {hue >= 0 ? hue : hue + 360,
916             sqrtf(Lab[1] * Lab[1] + Lab[2] * Lab[2]),
917             Lab[0],
918             xyz.fA};
919 }
920 
921 // https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
lin_srgb_to_oklab(SkPMColor4f rgb)922 static SkPMColor4f lin_srgb_to_oklab(SkPMColor4f rgb) {
923     float l = 0.4122214708f * rgb.fR + 0.5363325363f * rgb.fG + 0.0514459929f * rgb.fB;
924     float m = 0.2119034982f * rgb.fR + 0.6806995451f * rgb.fG + 0.1073969566f * rgb.fB;
925     float s = 0.0883024619f * rgb.fR + 0.2817188376f * rgb.fG + 0.6299787005f * rgb.fB;
926     l = std::cbrtf(l);
927     m = std::cbrtf(m);
928     s = std::cbrtf(s);
929     return {
930         0.2104542553f*l + 0.7936177850f*m - 0.0040720468f*s,
931         1.9779984951f*l - 2.4285922050f*m + 0.4505937099f*s,
932         0.0259040371f*l + 0.7827717662f*m - 0.8086757660f*s,
933         rgb.fA
934     };
935 }
936 
937 // The color space is technically OkLCH, but we produce HCL, so that all polar spaces have hue in
938 // the first component. This simplifies the hue handling for HueMethod and premul/unpremul.
lin_srgb_to_okhcl(SkPMColor4f rgb)939 static SkPMColor4f lin_srgb_to_okhcl(SkPMColor4f rgb) {
940     SkPMColor4f OKLab = lin_srgb_to_oklab(rgb);
941     float hue = sk_float_radians_to_degrees(atan2f(OKLab[2], OKLab[1]));
942     return {hue >= 0 ? hue : hue + 360,
943             sqrtf(OKLab[1] * OKLab[1] + OKLab[2] * OKLab[2]),
944             OKLab[0],
945             rgb.fA};
946 }
947 
premul_polar(SkPMColor4f hsl)948 static SkPMColor4f premul_polar(SkPMColor4f hsl) {
949     return { hsl.fR, hsl.fG * hsl.fA, hsl.fB * hsl.fA, hsl.fA };
950 }
951 
premul_rgb(SkPMColor4f rgb)952 static SkPMColor4f premul_rgb(SkPMColor4f rgb) {
953     return { rgb.fR * rgb.fA, rgb.fG * rgb.fA, rgb.fB * rgb.fA, rgb.fA };
954 }
955 
color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs)956 static bool color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs) {
957     using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
958     switch (cs) {
959         case ColorSpace::kLCH:
960         case ColorSpace::kOKLCH:
961         case ColorSpace::kHSL:
962         case ColorSpace::kHWB:
963             return true;
964         default:
965             return false;
966     }
967 }
968 
969 // Given `colors` in `src` color space, an interpolation space, and a `dst` color space,
970 // we are doing several things. First, some definitions:
971 //
972 // The interpolation color space is "special" if it can't be represented as an SkColorSpace. This
973 // applies to any color space that isn't an RGB space, like Lab or HSL. These need special handling
974 // because we have to run bespoke code to do the conversion (before interpolation here, and after
975 // interpolation in the backend shader/pipeline).
976 //
977 // The interpolation color space is "polar" if it involves hue (HSL, HWB, LCH, Oklch). These need
978 // special handling, becuase hue is never premultiplied, and because HueMethod comes into play.
979 //
980 // 1) Pick an `intermediate` SkColorSpace. If the interpolation color space is not "special",
981 //    (kDestination, kSRGB, etc... ), then `intermediate` is exact. Otherwise, `intermediate` is the
982 //    RGB space that prepares us to do the final conversion. For example, conversion to Lab starts
983 //    with XYZD50, so `intermediate` will be XYZD50 if we're actually interpolating in Lab.
984 // 2) Transform all colors to the `intermediate` color space, leaving them unpremultiplied.
985 // 3) If the interpolation color space is "special", transform the colors to that space.
986 // 4) If the interpolation color space is "polar", adjust the angles to respect HueMethod.
987 // 5) If premul interpolation is requested, apply that. For "polar" interpolated colors, don't
988 //    premultiply hue, only the other two channels. Note that there are four polar spaces.
989 //    Two have hue as the first component, and two have it as the third component. To reduce
990 //    complexity, we always store hue in the first component, swapping it with luminance for
991 //    LCH and Oklch. The backend code (eg, shaders) needs to know about this.
SkColor4fXformer(const SkGradientShaderBase * shader,SkColorSpace * dst)992 SkColor4fXformer::SkColor4fXformer(const SkGradientShaderBase* shader, SkColorSpace* dst) {
993     using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
994     using HueMethod = SkGradientShader::Interpolation::HueMethod;
995 
996     const int colorCount = shader->fColorCount;
997     const SkGradientShader::Interpolation interpolation = shader->fInterpolation;
998 
999     // 1) Determine the color space of our intermediate colors
1000     fIntermediateColorSpace = intermediate_color_space(interpolation.fColorSpace, dst);
1001 
1002     // 2) Convert all colors to the intermediate color space
1003     auto info = SkImageInfo::Make(colorCount, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType);
1004 
1005     auto dstInfo = info.makeColorSpace(fIntermediateColorSpace);
1006     auto srcInfo = info.makeColorSpace(shader->fColorSpace);
1007 
1008     fColors.reset(colorCount);
1009     SkAssertResult(SkConvertPixels(dstInfo, fColors.begin(), info.minRowBytes(),
1010                                    srcInfo, shader->fColors, info.minRowBytes()));
1011 
1012     // 3) Transform to the interpolation color space (if it's special)
1013     ConvertColorProc convertFn = nullptr;
1014     switch (interpolation.fColorSpace) {
1015         case ColorSpace::kHSL:   convertFn = srgb_to_hsl;       break;
1016         case ColorSpace::kHWB:   convertFn = srgb_to_hwb;       break;
1017         case ColorSpace::kLab:   convertFn = xyzd50_to_lab;     break;
1018         case ColorSpace::kLCH:   convertFn = xyzd50_to_hcl;     break;
1019         case ColorSpace::kOKLab: convertFn = lin_srgb_to_oklab; break;
1020         case ColorSpace::kOKLCH: convertFn = lin_srgb_to_okhcl; break;
1021         default: break;
1022     }
1023 
1024     if (convertFn) {
1025         for (int i = 0; i < colorCount; ++i) {
1026             fColors[i] = convertFn(fColors[i]);
1027         }
1028     }
1029 
1030     // 4) For polar colors, adjust hue values to respect the hue method. We're using a trick here...
1031     //    The specification looks at adjacent colors, and adjusts one or the other. Because we store
1032     //    the stops in uniforms (and our backend conversions normalize the hue angle), we can
1033     //    instead always apply the adjustment to the *second* color. That lets us keep a running
1034     //    total, and do a single pass across all the colors to respect the requested hue method,
1035     //    without needing to do any extra work per-pixel.
1036     if (color_space_is_polar(interpolation.fColorSpace)) {
1037         float delta = 0;
1038         for (int i = 0; i < colorCount - 1; ++i) {
1039             float  h1 = fColors[i].fR;
1040             float& h2 = fColors[i+1].fR;
1041             h2 += delta;
1042             switch (interpolation.fHueMethod) {
1043                 case HueMethod::kShorter:
1044                     if (h2 - h1 > 180) {
1045                         h2 -= 360;  // i.e. h1 += 360
1046                         delta -= 360;
1047                     } else if (h2 - h1 < -180) {
1048                         h2 += 360;
1049                         delta += 360;
1050                     }
1051                     break;
1052                 case HueMethod::kLonger:
1053                     if ((i == 0 && shader->fFirstStopIsImplicit) ||
1054                         (i == colorCount - 2 && shader->fLastStopIsImplicit)) {
1055                         // Do nothing. We don't want to introduce a full revolution for these stops
1056                         // Full rationale at skbug.com/13941
1057                     } else if (0 < h2 - h1 && h2 - h1 < 180) {
1058                         h2 -= 360;  // i.e. h1 += 360
1059                         delta -= 360;
1060                     } else if (-180 < h2 - h1 && h2 - h1 <= 0) {
1061                         h2 += 360;
1062                         delta += 360;
1063                     }
1064                     break;
1065                 case HueMethod::kIncreasing:
1066                     if (h2 < h1) {
1067                         h2 += 360;
1068                         delta += 360;
1069                     }
1070                     break;
1071                 case HueMethod::kDecreasing:
1072                     if (h1 < h2) {
1073                         h2 -= 360;  // i.e. h1 += 360;
1074                         delta -= 360;
1075                     }
1076                     break;
1077             }
1078         }
1079     }
1080 
1081     // 5) Apply premultiplication
1082     ConvertColorProc premulFn = nullptr;
1083     if (static_cast<bool>(interpolation.fInPremul)) {
1084         switch (interpolation.fColorSpace) {
1085             case ColorSpace::kHSL:
1086             case ColorSpace::kHWB:
1087             case ColorSpace::kLCH:
1088             case ColorSpace::kOKLCH: premulFn = premul_polar; break;
1089             default:                 premulFn = premul_rgb;   break;
1090         }
1091     }
1092 
1093     if (premulFn) {
1094         for (int i = 0; i < colorCount; ++i) {
1095             fColors[i] = premulFn(fColors[i]);
1096         }
1097     }
1098 }
1099 
SkColorConverter(const SkColor * colors,int count)1100 SkColorConverter::SkColorConverter(const SkColor* colors, int count) {
1101     const float ONE_OVER_255 = 1.f / 255;
1102     for (int i = 0; i < count; ++i) {
1103         fColors4f.push_back({ SkColorGetR(colors[i]) * ONE_OVER_255,
1104                               SkColorGetG(colors[i]) * ONE_OVER_255,
1105                               SkColorGetB(colors[i]) * ONE_OVER_255,
1106                               SkColorGetA(colors[i]) * ONE_OVER_255 });
1107     }
1108 }
1109 
commonAsAGradient(GradientInfo * info) const1110 void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
1111     if (info) {
1112         if (info->fColorCount >= fColorCount) {
1113             if (info->fColors) {
1114                 for (int i = 0; i < fColorCount; ++i) {
1115                     info->fColors[i] = this->getLegacyColor(i);
1116                 }
1117             }
1118             if (info->fColorOffsets) {
1119                 for (int i = 0; i < fColorCount; ++i) {
1120                     info->fColorOffsets[i] = this->getPos(i);
1121                 }
1122             }
1123         }
1124         info->fColorCount = fColorCount;
1125         info->fTileMode = fTileMode;
1126 
1127         info->fGradientFlags =
1128                 this->interpolateInPremul() ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0;
1129     }
1130 }
1131 
1132 // Return true if these parameters are valid/legal/safe to construct a gradient
1133 //
ValidGradient(const SkColor4f colors[],int count,SkTileMode tileMode,const Interpolation & interpolation)1134 bool SkGradientShaderBase::ValidGradient(const SkColor4f colors[], int count, SkTileMode tileMode,
1135                                          const Interpolation& interpolation) {
1136     return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount &&
1137            (unsigned)interpolation.fColorSpace < Interpolation::kColorSpaceCount &&
1138            (unsigned)interpolation.fHueMethod < Interpolation::kHueMethodCount;
1139 }
1140 
Descriptor(const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar positions[],int colorCount,SkTileMode mode,const Interpolation & interpolation)1141 SkGradientShaderBase::Descriptor::Descriptor(const SkColor4f colors[],
1142                                              sk_sp<SkColorSpace> colorSpace,
1143                                              const SkScalar positions[],
1144                                              int colorCount,
1145                                              SkTileMode mode,
1146                                              const Interpolation& interpolation)
1147         : fColors(colors)
1148         , fColorSpace(std::move(colorSpace))
1149         , fPositions(positions)
1150         , fColorCount(colorCount)
1151         , fTileMode(mode)
1152         , fInterpolation(interpolation) {
1153     SkASSERT(fColorCount > 1);
1154 }
1155 
average_gradient_color(const SkColor4f colors[],const SkScalar pos[],int colorCount)1156 static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[],
1157                                         int colorCount) {
1158     // The gradient is a piecewise linear interpolation between colors. For a given interval,
1159     // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
1160     // intervals average color. The overall average color is thus the sum of each piece. The thing
1161     // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
1162     skvx::float4 blend(0.0f);
1163     for (int i = 0; i < colorCount - 1; ++i) {
1164         // Calculate the average color for the interval between pos(i) and pos(i+1)
1165         auto c0 = skvx::float4::Load(&colors[i]);
1166         auto c1 = skvx::float4::Load(&colors[i + 1]);
1167 
1168         // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
1169         // so pos[i + 1] - pos[i] = 1/(colorCount-1)
1170         SkScalar w;
1171         if (pos) {
1172             // Match position fixing in SkGradientShader's constructor, clamping positions outside
1173             // [0, 1] and forcing the sequence to be monotonic
1174             SkScalar p0 = SkTPin(pos[i], 0.f, 1.f);
1175             SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f);
1176             w = p1 - p0;
1177 
1178             // And account for any implicit intervals at the start or end of the positions
1179             if (i == 0) {
1180                 if (p0 > 0.0f) {
1181                     // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi)
1182                     // becomes 0.5*(c + c)*(pj - 0) = c * pj
1183                     auto c = skvx::float4::Load(&colors[0]);
1184                     blend += p0 * c;
1185                 }
1186             }
1187             if (i == colorCount - 2) {
1188                 if (p1 < 1.f) {
1189                     // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi)
1190                     // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi)
1191                     auto c = skvx::float4::Load(&colors[colorCount - 1]);
1192                     blend += (1.f - p1) * c;
1193                 }
1194             }
1195         } else {
1196             w = 1.f / (colorCount - 1);
1197         }
1198 
1199         blend += 0.5f * w * (c1 + c0);
1200     }
1201 
1202     SkColor4f avg;
1203     blend.store(&avg);
1204     return avg;
1205 }
1206 
1207 // Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
1208 // can be mapped to the same fallbacks. The specific shape factories must account for special
1209 // 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)1210 sk_sp<SkShader> SkGradientShaderBase::MakeDegenerateGradient(const SkColor4f colors[],
1211                                                              const SkScalar pos[],
1212                                                              int colorCount,
1213                                                              sk_sp<SkColorSpace> colorSpace,
1214                                                              SkTileMode mode) {
1215     switch(mode) {
1216         case SkTileMode::kDecal:
1217             // normally this would reject the area outside of the interpolation region, so since
1218             // inside region is empty when the radii are equal, the entire draw region is empty
1219             return SkShaders::Empty();
1220         case SkTileMode::kRepeat:
1221         case SkTileMode::kMirror:
1222             // repeat and mirror are treated the same: the border colors are never visible,
1223             // but approximate the final color as infinite repetitions of the colors, so
1224             // it can be represented as the average color of the gradient.
1225             return SkShaders::Color(
1226                     average_gradient_color(colors, pos, colorCount), std::move(colorSpace));
1227         case SkTileMode::kClamp:
1228             // Depending on how the gradient shape degenerates, there may be a more specialized
1229             // fallback representation for the factories to use, but this is a reasonable default.
1230             return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
1231     }
1232     SkDEBUGFAIL("Should not be reached");
1233     return nullptr;
1234 }
1235 
ColorStopOptimizer(const SkColor4f * colors,const SkScalar * pos,int count,SkTileMode mode)1236 SkGradientShaderBase::ColorStopOptimizer::ColorStopOptimizer(const SkColor4f* colors,
1237                                                              const SkScalar* pos,
1238                                                              int count,
1239                                                              SkTileMode mode)
1240         : fColors(colors)
1241         , fPos(pos)
1242         , fCount(count) {
1243 
1244     if (!pos || count != 3) {
1245         return;
1246     }
1247 
1248     if (SkScalarNearlyEqual(pos[0], 0.0f) &&
1249         SkScalarNearlyEqual(pos[1], 0.0f) &&
1250         SkScalarNearlyEqual(pos[2], 1.0f)) {
1251 
1252         if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
1253             colors[0] == colors[1]) {
1254 
1255             // Ignore the leftmost color/pos.
1256             fColors += 1;
1257             fPos    += 1;
1258             fCount   = 2;
1259         }
1260     } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
1261                SkScalarNearlyEqual(pos[1], 1.0f) &&
1262                SkScalarNearlyEqual(pos[2], 1.0f)) {
1263 
1264         if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
1265             colors[1] == colors[2]) {
1266 
1267             // Ignore the rightmost color/pos.
1268             fCount  = 2;
1269         }
1270     }
1271 }
1272 
1273 #if defined(SK_GRAPHITE)
1274 // Please see GrGradientShader.cpp::make_interpolated_to_dst for substantial comments
1275 // as to why this code is structured this way.
MakeInterpolatedToDst(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer,const skgpu::graphite::GradientShaderBlocks::GradientData & gradData,const SkGradientShaderBase::Interpolation & interp,SkColorSpace * intermediateCS)1276 void SkGradientShaderBase::MakeInterpolatedToDst(
1277         const skgpu::graphite::KeyContext& keyContext,
1278         skgpu::graphite::PaintParamsKeyBuilder* builder,
1279         skgpu::graphite::PipelineDataGatherer* gatherer,
1280         const skgpu::graphite::GradientShaderBlocks::GradientData& gradData,
1281         const SkGradientShaderBase::Interpolation& interp,
1282         SkColorSpace* intermediateCS) {
1283     using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
1284     using namespace skgpu::graphite;
1285 
1286     bool inputPremul = static_cast<bool>(interp.fInPremul);
1287 
1288     switch (interp.fColorSpace) {
1289         case ColorSpace::kLab:
1290         case ColorSpace::kOKLab:
1291         case ColorSpace::kLCH:
1292         case ColorSpace::kOKLCH:
1293         case ColorSpace::kHSL:
1294         case ColorSpace::kHWB:
1295             inputPremul = false;
1296             break;
1297         default:
1298             break;
1299     }
1300 
1301     const SkColorInfo& dstColorInfo = keyContext.dstColorInfo();
1302 
1303     SkColorSpace* dstColorSpace = dstColorInfo.colorSpace() ? dstColorInfo.colorSpace()
1304                                                             : sk_srgb_singleton();
1305 
1306     SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType
1307                                                     : kUnpremul_SkAlphaType;
1308 
1309     ColorSpaceTransformBlock::ColorSpaceTransformData data(intermediateCS, intermediateAlphaType,
1310                                                            dstColorSpace, dstColorInfo.alphaType());
1311 
1312     // The gradient block and colorSpace conversion block need to be combined together
1313     // (via the colorFilterShader block) so that the localMatrix block can treat them as
1314     // one child.
1315     ColorFilterShaderBlock::BeginBlock(keyContext, builder, gatherer);
1316 
1317         GradientShaderBlocks::BeginBlock(keyContext, builder, gatherer, gradData);
1318         builder->endBlock();
1319 
1320         ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data);
1321         builder->endBlock();
1322 
1323     builder->endBlock();
1324 }
1325 #endif
1326