1 /*
2  * Copyright 2006 The Android Open Source Project
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 <algorithm>
9 #include "include/core/SkMallocPixelRef.h"
10 #include "include/private/SkFloatBits.h"
11 #include "include/private/SkHalf.h"
12 #include "include/private/SkTPin.h"
13 #include "include/private/SkVx.h"
14 #include "src/core/SkColorSpacePriv.h"
15 #include "src/core/SkConvertPixels.h"
16 #include "src/core/SkMatrixProvider.h"
17 #include "src/core/SkReadBuffer.h"
18 #include "src/core/SkVM.h"
19 #include "src/core/SkWriteBuffer.h"
20 #include "src/shaders/gradients/Sk4fLinearGradient.h"
21 #include "src/shaders/gradients/SkGradientShaderPriv.h"
22 #include "src/shaders/gradients/SkLinearGradient.h"
23 #include "src/shaders/gradients/SkRadialGradient.h"
24 #include "src/shaders/gradients/SkSweepGradient.h"
25 #include "src/shaders/gradients/SkTwoPointConicalGradient.h"
26 
27 enum GradientSerializationFlags {
28     // Bits 29:31 used for various boolean flags
29     kHasPosition_GSF    = 0x80000000,
30     kHasLocalMatrix_GSF = 0x40000000,
31     kHasColorSpace_GSF  = 0x20000000,
32 
33     // Bits 12:28 unused
34 
35     // Bits 8:11 for fTileMode
36     kTileModeShift_GSF  = 8,
37     kTileModeMask_GSF   = 0xF,
38 
39     // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
40     kGradFlagsShift_GSF = 0,
41     kGradFlagsMask_GSF  = 0xFF,
42 };
43 
flatten(SkWriteBuffer & buffer) const44 void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
45     uint32_t flags = 0;
46     if (fPos) {
47         flags |= kHasPosition_GSF;
48     }
49     if (fLocalMatrix) {
50         flags |= kHasLocalMatrix_GSF;
51     }
52     sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
53     if (colorSpaceData) {
54         flags |= kHasColorSpace_GSF;
55     }
56     SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
57     flags |= ((unsigned)fTileMode << kTileModeShift_GSF);
58     SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
59     flags |= (fGradFlags << kGradFlagsShift_GSF);
60 
61     buffer.writeUInt(flags);
62 
63     buffer.writeColor4fArray(fColors, fCount);
64     if (colorSpaceData) {
65         buffer.writeDataAsByteArray(colorSpaceData.get());
66     }
67     if (fPos) {
68         buffer.writeScalarArray(fPos, fCount);
69     }
70     if (fLocalMatrix) {
71         buffer.writeMatrix(*fLocalMatrix);
72     }
73 }
74 
75 template <int N, typename T, bool MEM_MOVE>
validate_array(SkReadBuffer & buffer,size_t count,SkSTArray<N,T,MEM_MOVE> * array)76 static bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) {
77     if (!buffer.validateCanReadN<T>(count)) {
78         return false;
79     }
80 
81     array->resize_back(count);
82     return true;
83 }
84 
unflatten(SkReadBuffer & buffer)85 bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
86     // New gradient format. Includes floating point color, color space, densely packed flags
87     uint32_t flags = buffer.readUInt();
88 
89     fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
90     fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
91 
92     fCount = buffer.getArrayCount();
93 
94     if (!(validate_array(buffer, fCount, &fColorStorage) &&
95           buffer.readColor4fArray(fColorStorage.begin(), fCount))) {
96         return false;
97     }
98     fColors = fColorStorage.begin();
99 
100     if (SkToBool(flags & kHasColorSpace_GSF)) {
101         sk_sp<SkData> data = buffer.readByteArrayAsData();
102         fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
103     } else {
104         fColorSpace = nullptr;
105     }
106     if (SkToBool(flags & kHasPosition_GSF)) {
107         if (!(validate_array(buffer, fCount, &fPosStorage) &&
108               buffer.readScalarArray(fPosStorage.begin(), fCount))) {
109             return false;
110         }
111         fPos = fPosStorage.begin();
112     } else {
113         fPos = nullptr;
114     }
115     if (SkToBool(flags & kHasLocalMatrix_GSF)) {
116         fLocalMatrix = &fLocalMatrixStorage;
117         buffer.readMatrix(&fLocalMatrixStorage);
118     } else {
119         fLocalMatrix = nullptr;
120     }
121     return buffer.isValid();
122 }
123 
124 ////////////////////////////////////////////////////////////////////////////////////////////
125 
SkGradientShaderBase(const Descriptor & desc,const SkMatrix & ptsToUnit)126 SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
127     : INHERITED(desc.fLocalMatrix)
128     , fPtsToUnit(ptsToUnit)
129     , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
130     , fColorsAreOpaque(true)
131 {
132     fPtsToUnit.getType();  // Precache so reads are threadsafe.
133     SkASSERT(desc.fCount > 1);
134 
135     fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
136 
137     SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
138     fTileMode = desc.fTileMode;
139 
140     /*  Note: we let the caller skip the first and/or last position.
141         i.e. pos[0] = 0.3, pos[1] = 0.7
142         In these cases, we insert dummy entries to ensure that the final data
143         will be bracketed by [0, 1].
144         i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
145 
146         Thus colorCount (the caller's value, and fColorCount (our value) may
147         differ by up to 2. In the above example:
148             colorCount = 2
149             fColorCount = 4
150      */
151     fColorCount = desc.fCount;
152     // check if we need to add in dummy start and/or end position/colors
153     bool dummyFirst = false;
154     bool dummyLast = false;
155     if (desc.fPos) {
156         dummyFirst = desc.fPos[0] != 0;
157         dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
158         fColorCount += dummyFirst + dummyLast;
159     }
160 
161     size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0));
162     fOrigColors4f      = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
163     fOrigPos           = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount)
164                                    : nullptr;
165 
166     // Now copy over the colors, adding the dummies as needed
167     SkColor4f* origColors = fOrigColors4f;
168     if (dummyFirst) {
169         *origColors++ = desc.fColors[0];
170     }
171     for (int i = 0; i < desc.fCount; ++i) {
172         origColors[i] = desc.fColors[i];
173         fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
174     }
175     if (dummyLast) {
176         origColors += desc.fCount;
177         *origColors = desc.fColors[desc.fCount - 1];
178     }
179 
180     if (desc.fPos) {
181         SkScalar prev = 0;
182         SkScalar* origPosPtr = fOrigPos;
183         *origPosPtr++ = prev; // force the first pos to 0
184 
185         int startIndex = dummyFirst ? 0 : 1;
186         int count = desc.fCount + dummyLast;
187 
188         bool uniformStops = true;
189         const SkScalar uniformStep = desc.fPos[startIndex] - prev;
190         for (int i = startIndex; i < count; i++) {
191             // Pin the last value to 1.0, and make sure pos is monotonic.
192             auto curr = (i == desc.fCount) ? 1 : SkTPin(desc.fPos[i], prev, 1.0f);
193             uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
194 
195             *origPosPtr++ = prev = curr;
196         }
197 
198         // If the stops are uniform, treat them as implicit.
199         if (uniformStops) {
200             fOrigPos = nullptr;
201         }
202     }
203 }
204 
~SkGradientShaderBase()205 SkGradientShaderBase::~SkGradientShaderBase() {}
206 
flatten(SkWriteBuffer & buffer) const207 void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
208     Descriptor desc;
209     desc.fColors = fOrigColors4f;
210     desc.fColorSpace = fColorSpace;
211     desc.fPos = fOrigPos;
212     desc.fCount = fColorCount;
213     desc.fTileMode = fTileMode;
214     desc.fGradFlags = fGradFlags;
215 
216     const SkMatrix& m = this->getLocalMatrix();
217     desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
218     desc.flatten(buffer);
219 }
220 
add_stop_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f Fs,SkPMColor4f Bs)221 static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f Fs, SkPMColor4f Bs) {
222     (ctx->fs[0])[stop] = Fs.fR;
223     (ctx->fs[1])[stop] = Fs.fG;
224     (ctx->fs[2])[stop] = Fs.fB;
225     (ctx->fs[3])[stop] = Fs.fA;
226 
227     (ctx->bs[0])[stop] = Bs.fR;
228     (ctx->bs[1])[stop] = Bs.fG;
229     (ctx->bs[2])[stop] = Bs.fB;
230     (ctx->bs[3])[stop] = Bs.fA;
231 }
232 
add_const_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f color)233 static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) {
234     add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color);
235 }
236 
237 // Calculate a factor F and a bias B so that color = F*t + B when t is in range of
238 // 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)239 static void init_stop_evenly(
240     SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop, SkPMColor4f c_l, SkPMColor4f c_r) {
241     // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
242     SkPMColor4f Fs = {
243         (c_r.fR - c_l.fR) * gapCount,
244         (c_r.fG - c_l.fG) * gapCount,
245         (c_r.fB - c_l.fB) * gapCount,
246         (c_r.fA - c_l.fA) * gapCount,
247     };
248     SkPMColor4f Bs = {
249         c_l.fR - Fs.fR*(stop/gapCount),
250         c_l.fG - Fs.fG*(stop/gapCount),
251         c_l.fB - Fs.fB*(stop/gapCount),
252         c_l.fA - Fs.fA*(stop/gapCount),
253     };
254     add_stop_color(ctx, stop, Fs, Bs);
255 }
256 
257 // For each stop we calculate a bias B and a scale factor F, such that
258 // 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)259 static void init_stop_pos(
260     SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPMColor4f c_l, SkPMColor4f c_r) {
261     // See note about Clankium's old compiler in init_stop_evenly().
262     SkPMColor4f Fs = {
263         (c_r.fR - c_l.fR) / (t_r - t_l),
264         (c_r.fG - c_l.fG) / (t_r - t_l),
265         (c_r.fB - c_l.fB) / (t_r - t_l),
266         (c_r.fA - c_l.fA) / (t_r - t_l),
267     };
268     SkPMColor4f Bs = {
269         c_l.fR - Fs.fR*t_l,
270         c_l.fG - Fs.fG*t_l,
271         c_l.fB - Fs.fB*t_l,
272         c_l.fA - Fs.fA*t_l,
273     };
274     ctx->ts[stop] = t_l;
275     add_stop_color(ctx, stop, Fs, Bs);
276 }
277 
onAppendStages(const SkStageRec & rec) const278 bool SkGradientShaderBase::onAppendStages(const SkStageRec& rec) const {
279     SkRasterPipeline* p = rec.fPipeline;
280     SkArenaAlloc* alloc = rec.fAlloc;
281     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
282 
283     SkMatrix matrix;
284     if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) {
285         return false;
286     }
287     matrix.postConcat(fPtsToUnit);
288 
289     SkRasterPipeline_<256> postPipeline;
290 
291     p->append(SkRasterPipeline::seed_shader);
292     p->append_matrix(alloc, matrix);
293     this->appendGradientStages(alloc, p, &postPipeline);
294 
295     switch(fTileMode) {
296         case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x_1); break;
297         case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x_1); break;
298         case SkTileMode::kDecal:
299             decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
300             decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
301             // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
302             p->append(SkRasterPipeline::decal_x, decal_ctx);
303             [[fallthrough]];
304 
305         case SkTileMode::kClamp:
306             if (!fOrigPos) {
307                 // We clamp only when the stops are evenly spaced.
308                 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
309                 // In that case, we must make sure we're using the general "gradient" stage,
310                 // which is the only stage that will correctly handle unclamped t.
311                 p->append(SkRasterPipeline::clamp_x_1);
312             }
313             break;
314     }
315 
316     const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
317 
318     // Transform all of the colors to destination color space
319     SkColor4fXformer xformedColors(fOrigColors4f, fColorCount, fColorSpace.get(), rec.fDstCS);
320 
321     auto prepareColor = [premulGrad, &xformedColors](int i) {
322         SkColor4f c = xformedColors.fColors[i];
323         return premulGrad ? c.premul()
324                           : SkPMColor4f{ c.fR, c.fG, c.fB, c.fA };
325     };
326 
327     // The two-stop case with stops at 0 and 1.
328     if (fColorCount == 2 && fOrigPos == nullptr) {
329         const SkPMColor4f c_l = prepareColor(0),
330                           c_r = prepareColor(1);
331 
332         // See F and B below.
333         auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
334         (Sk4f::Load(c_r.vec()) - Sk4f::Load(c_l.vec())).store(ctx->f);
335         (                        Sk4f::Load(c_l.vec())).store(ctx->b);
336         ctx->interpolatedInPremul = premulGrad;
337 
338         p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, ctx);
339     } else {
340         auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
341         ctx->interpolatedInPremul = premulGrad;
342 
343         // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
344         // at -inf. Therefore, the max number of stops is fColorCount+1.
345         for (int i = 0; i < 4; i++) {
346             // Allocate at least at for the AVX2 gather from a YMM register.
347             ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
348             ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
349         }
350 
351         if (fOrigPos == nullptr) {
352             // Handle evenly distributed stops.
353 
354             size_t stopCount = fColorCount;
355             float gapCount = stopCount - 1;
356 
357             SkPMColor4f c_l = prepareColor(0);
358             for (size_t i = 0; i < stopCount - 1; i++) {
359                 SkPMColor4f c_r = prepareColor(i + 1);
360                 init_stop_evenly(ctx, gapCount, i, c_l, c_r);
361                 c_l = c_r;
362             }
363             add_const_color(ctx, stopCount - 1, c_l);
364 
365             ctx->stopCount = stopCount;
366             p->append(SkRasterPipeline::evenly_spaced_gradient, ctx);
367         } else {
368             // Handle arbitrary stops.
369 
370             ctx->ts = alloc->makeArray<float>(fColorCount+1);
371 
372             // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
373             // because they are naturally handled by the search method.
374             int firstStop;
375             int lastStop;
376             if (fColorCount > 2) {
377                 firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
378                 lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
379                            ? fColorCount - 1 : fColorCount - 2;
380             } else {
381                 firstStop = 0;
382                 lastStop = 1;
383             }
384 
385             size_t stopCount = 0;
386             float  t_l = fOrigPos[firstStop];
387             SkPMColor4f c_l = prepareColor(firstStop);
388             add_const_color(ctx, stopCount++, c_l);
389             // N.B. lastStop is the index of the last stop, not one after.
390             for (int i = firstStop; i < lastStop; i++) {
391                 float  t_r = fOrigPos[i + 1];
392                 SkPMColor4f c_r = prepareColor(i + 1);
393                 SkASSERT(t_l <= t_r);
394                 if (t_l < t_r) {
395                     init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
396                     stopCount += 1;
397                 }
398                 t_l = t_r;
399                 c_l = c_r;
400             }
401 
402             ctx->ts[stopCount] = t_l;
403             add_const_color(ctx, stopCount++, c_l);
404 
405             ctx->stopCount = stopCount;
406             p->append(SkRasterPipeline::gradient, ctx);
407         }
408     }
409 
410     if (decal_ctx) {
411         p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
412     }
413 
414     if (!premulGrad && !this->colorsAreOpaque()) {
415         p->append(SkRasterPipeline::premul);
416     }
417 
418     p->extend(postPipeline);
419 
420     return true;
421 }
422 
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color,const SkMatrixProvider & mats,const SkMatrix * localM,const SkColorInfo & dstInfo,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const423 skvm::Color SkGradientShaderBase::onProgram(skvm::Builder* p,
424                                             skvm::Coord device, skvm::Coord local,
425                                             skvm::Color /*paint*/,
426                                             const SkMatrixProvider& mats, const SkMatrix* localM,
427                                             const SkColorInfo& dstInfo,
428                                             skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
429     SkMatrix inv;
430     if (!this->computeTotalInverse(mats.localToDevice(), localM, &inv)) {
431         return {};
432     }
433     inv.postConcat(fPtsToUnit);
434     inv.normalizePerspective();
435 
436     local = SkShaderBase::ApplyMatrix(p, inv, local, uniforms);
437 
438     skvm::I32 mask = p->splat(~0);
439     skvm::F32 t = this->transformT(p,uniforms, local, &mask);
440 
441     // Perhaps unexpectedly, clamping is handled naturally by our search, so we
442     // don't explicitly clamp t to [0,1].  That clamp would break hard stops
443     // right at 0 or 1 boundaries in kClamp mode.  (kRepeat and kMirror always
444     // produce values in [0,1].)
445     switch(fTileMode) {
446         case SkTileMode::kClamp:
447             break;
448 
449         case SkTileMode::kDecal:
450             mask &= (t == clamp01(t));
451             break;
452 
453         case SkTileMode::kRepeat:
454             t = fract(t);
455             break;
456 
457         case SkTileMode::kMirror: {
458             // t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 |
459             //       {-A-}      {--------B-------}
460             skvm::F32 A = t - 1.0f,
461                       B = floor(A * 0.5f);
462             t = abs(A - (B + B) - 1.0f);
463         } break;
464     }
465 
466     // Transform our colors as we want them interpolated, in dst color space, possibly premul.
467     SkImageInfo common = SkImageInfo::Make(fColorCount,1, kRGBA_F32_SkColorType
468                                                         , kUnpremul_SkAlphaType),
469                 src    = common.makeColorSpace(fColorSpace),
470                 dst    = common.makeColorSpace(dstInfo.refColorSpace());
471     if (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) {
472         dst = dst.makeAlphaType(kPremul_SkAlphaType);
473     }
474 
475     std::vector<float> rgba(4*fColorCount);  // TODO: SkSTArray?
476     SkAssertResult(SkConvertPixels(dst,   rgba.data(), dst.minRowBytes(),
477                                    src, fOrigColors4f, src.minRowBytes()));
478 
479     // Transform our colors into a scale factor f and bias b such that for
480     // any t between stops i and i+1, the color we want is mad(t, f[i], b[i]).
481     using F4 = skvx::Vec<4,float>;
482     struct FB { F4 f,b; };
483     skvm::Color color;
484 
485     auto uniformF = [&](float x) { return p->uniformF(uniforms->pushF(x)); };
486 
487     if (fColorCount == 2) {
488         // 2-stop gradients have colors at 0 and 1, and so must be evenly spaced.
489         SkASSERT(fOrigPos == nullptr);
490 
491         // With 2 stops, we upload the single FB as uniforms and interpolate directly with t.
492         F4 lo = F4::Load(rgba.data() + 0),
493            hi = F4::Load(rgba.data() + 4);
494         F4 F = hi - lo,
495            B = lo;
496 
497         auto T = clamp01(t);
498         color = {
499             T * uniformF(F[0]) + uniformF(B[0]),
500             T * uniformF(F[1]) + uniformF(B[1]),
501             T * uniformF(F[2]) + uniformF(B[2]),
502             T * uniformF(F[3]) + uniformF(B[3]),
503         };
504     } else {
505         // To handle clamps in search we add a conceptual stop at t=-inf, so we
506         // may need up to fColorCount+1 FBs and fColorCount t stops between them:
507         //
508         //   FBs:         [color 0]  [color 0->1]  [color 1->2]  [color 2->3]  ...
509         //   stops:  (-inf)        t0            t1            t2  ...
510         //
511         // Both these arrays could end up shorter if any hard stops share the same t.
512         FB* fb = alloc->makeArrayDefault<FB>(fColorCount+1);
513         std::vector<float> stops;  // TODO: SkSTArray?
514         stops.reserve(fColorCount);
515 
516         // Here's our conceptual stop at t=-inf covering all t<=0, clamping to our first color.
517         float  t_lo = this->getPos(0);
518         F4 color_lo = F4::Load(rgba.data());
519         fb[0] = { 0.0f, color_lo };
520         // N.B. No stops[] entry for this implicit -inf.
521 
522         // Now the non-edge cases, calculating scale and bias between adjacent normal stops.
523         for (int i = 1; i < fColorCount; i++) {
524             float  t_hi = this->getPos(i);
525             F4 color_hi = F4::Load(rgba.data() + 4*i);
526 
527             // If t_lo == t_hi, we're on a hard stop, and transition immediately to the next color.
528             SkASSERT(t_lo <= t_hi);
529             if (t_lo < t_hi) {
530                 F4 f = (color_hi - color_lo) / (t_hi - t_lo),
531                    b = color_lo - f*t_lo;
532                 stops.push_back(t_lo);
533                 fb[stops.size()] = {f,b};
534             }
535 
536             t_lo = t_hi;
537             color_lo = color_hi;
538         }
539         // Anything >= our final t clamps to our final color.
540         stops.push_back(t_lo);
541         fb[stops.size()] = { 0.0f, color_lo };
542 
543         // We'll gather FBs from that array we just created.
544         skvm::Uniform fbs = uniforms->pushPtr(fb);
545 
546         // Find the two stops we need to interpolate.
547         skvm::I32 ix;
548         if (fOrigPos == nullptr) {
549             // Evenly spaced stops... we can calculate ix directly.
550             // Of note: we need to clamp t and skip over that conceptual -inf stop we made up.
551             ix = trunc(clamp01(t) * uniformF(stops.size() - 1) + 1.0f);
552         } else {
553             // Starting ix at 0 bakes in our conceptual first stop at -inf.
554             // TODO: good place to experiment with a loop in skvm.... stops.size() can be huge.
555             ix = p->splat(0);
556             for (float stop : stops) {
557                 // ix += (t >= stop) ? +1 : 0 ~~>
558                 // ix -= (t >= stop) ? -1 : 0
559                 ix -= (t >= uniformF(stop));
560             }
561             // TODO: we could skip any of the dummy stops GradientShaderBase's ctor added
562             // to ensure the full [0,1] span is covered.  This linear search doesn't need
563             // them for correctness, and it'd be up to two fewer stops to check.
564             // N.B. we do still need those stops for the fOrigPos == nullptr direct math path.
565         }
566 
567         // A scale factor and bias for each lane, 8 total.
568         // TODO: simpler, faster, tidier to push 8 uniform pointers, one for each struct lane?
569         ix = shl(ix, 3);
570         skvm::F32 Fr = gatherF(fbs, ix + 0);
571         skvm::F32 Fg = gatherF(fbs, ix + 1);
572         skvm::F32 Fb = gatherF(fbs, ix + 2);
573         skvm::F32 Fa = gatherF(fbs, ix + 3);
574 
575         skvm::F32 Br = gatherF(fbs, ix + 4);
576         skvm::F32 Bg = gatherF(fbs, ix + 5);
577         skvm::F32 Bb = gatherF(fbs, ix + 6);
578         skvm::F32 Ba = gatherF(fbs, ix + 7);
579 
580         // This is what we've been building towards!
581         color = {
582             t * Fr + Br,
583             t * Fg + Bg,
584             t * Fb + Bb,
585             t * Fa + Ba,
586         };
587     }
588 
589     // If we interpolated unpremul, premul now to match our output convention.
590     if (0 == (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
591             && !fColorsAreOpaque) {
592         color = premul(color);
593     }
594 
595     return {
596         pun_to_F32(mask & pun_to_I32(color.r)),
597         pun_to_F32(mask & pun_to_I32(color.g)),
598         pun_to_F32(mask & pun_to_I32(color.b)),
599         pun_to_F32(mask & pun_to_I32(color.a)),
600     };
601 }
602 
603 
isOpaque() const604 bool SkGradientShaderBase::isOpaque() const {
605     return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
606 }
607 
rounded_divide(unsigned numer,unsigned denom)608 static unsigned rounded_divide(unsigned numer, unsigned denom) {
609     return (numer + (denom >> 1)) / denom;
610 }
611 
onAsLuminanceColor(SkColor * lum) const612 bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
613     // we just compute an average color.
614     // possibly we could weight this based on the proportional width for each color
615     //   assuming they are not evenly distributed in the fPos array.
616     int r = 0;
617     int g = 0;
618     int b = 0;
619     const int n = fColorCount;
620     // TODO: use linear colors?
621     for (int i = 0; i < n; ++i) {
622         SkColor c = this->getLegacyColor(i);
623         r += SkColorGetR(c);
624         g += SkColorGetG(c);
625         b += SkColorGetB(c);
626     }
627     *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
628     return true;
629 }
630 
SkColor4fXformer(const SkColor4f * colors,int colorCount,SkColorSpace * src,SkColorSpace * dst)631 SkColor4fXformer::SkColor4fXformer(const SkColor4f* colors, int colorCount,
632                                    SkColorSpace* src, SkColorSpace* dst) {
633     fColors = colors;
634 
635     if (dst && !SkColorSpace::Equals(src, dst)) {
636         fStorage.reset(colorCount);
637 
638         auto info = SkImageInfo::Make(colorCount,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType);
639 
640         auto dstInfo = info.makeColorSpace(sk_ref_sp(dst));
641         auto srcInfo = info.makeColorSpace(sk_ref_sp(src));
642         SkAssertResult(SkConvertPixels(dstInfo, fStorage.begin(), info.minRowBytes(),
643                                        srcInfo, fColors         , info.minRowBytes()));
644 
645         fColors = fStorage.begin();
646     }
647 }
648 
commonAsAGradient(GradientInfo * info) const649 void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
650     if (info) {
651         if (info->fColorCount >= fColorCount) {
652             if (info->fColors) {
653                 for (int i = 0; i < fColorCount; ++i) {
654                     info->fColors[i] = this->getLegacyColor(i);
655                 }
656             }
657             if (info->fColorOffsets) {
658                 for (int i = 0; i < fColorCount; ++i) {
659                     info->fColorOffsets[i] = this->getPos(i);
660                 }
661             }
662         }
663         info->fColorCount = fColorCount;
664         info->fTileMode = fTileMode;
665         info->fGradientFlags = fGradFlags;
666     }
667 }
668 
669 ///////////////////////////////////////////////////////////////////////////////
670 ///////////////////////////////////////////////////////////////////////////////
671 
672 // Return true if these parameters are valid/legal/safe to construct a gradient
673 //
valid_grad(const SkColor4f colors[],const SkScalar pos[],int count,SkTileMode tileMode)674 static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
675                        SkTileMode tileMode) {
676     return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount;
677 }
678 
desc_init(SkGradientShaderBase::Descriptor * desc,const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)679 static void desc_init(SkGradientShaderBase::Descriptor* desc,
680                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
681                       const SkScalar pos[], int colorCount,
682                       SkTileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
683     SkASSERT(colorCount > 1);
684 
685     desc->fColors       = colors;
686     desc->fColorSpace   = std::move(colorSpace);
687     desc->fPos          = pos;
688     desc->fCount        = colorCount;
689     desc->fTileMode     = mode;
690     desc->fGradFlags    = flags;
691     desc->fLocalMatrix  = localMatrix;
692 }
693 
average_gradient_color(const SkColor4f colors[],const SkScalar pos[],int colorCount)694 static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[],
695                                         int colorCount) {
696     // The gradient is a piecewise linear interpolation between colors. For a given interval,
697     // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
698     // intervals average color. The overall average color is thus the sum of each piece. The thing
699     // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
700     Sk4f blend(0.0f);
701     for (int i = 0; i < colorCount - 1; ++i) {
702         // Calculate the average color for the interval between pos(i) and pos(i+1)
703         Sk4f c0 = Sk4f::Load(&colors[i]);
704         Sk4f c1 = Sk4f::Load(&colors[i + 1]);
705 
706         // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
707         // so pos[i + 1] - pos[i] = 1/(colorCount-1)
708         SkScalar w;
709         if (pos) {
710             // Match position fixing in SkGradientShader's constructor, clamping positions outside
711             // [0, 1] and forcing the sequence to be monotonic
712             SkScalar p0 = SkTPin(pos[i], 0.f, 1.f);
713             SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f);
714             w = p1 - p0;
715 
716             // And account for any implicit intervals at the start or end of the positions
717             if (i == 0) {
718                 if (p0 > 0.0f) {
719                     // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi)
720                     // becomes 0.5*(c + c)*(pj - 0) = c * pj
721                     Sk4f c = Sk4f::Load(&colors[0]);
722                     blend += p0 * c;
723                 }
724             }
725             if (i == colorCount - 2) {
726                 if (p1 < 1.f) {
727                     // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi)
728                     // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi)
729                     Sk4f c = Sk4f::Load(&colors[colorCount - 1]);
730                     blend += (1.f - p1) * c;
731                 }
732             }
733         } else {
734             w = 1.f / (colorCount - 1);
735         }
736 
737         blend += 0.5f * w * (c1 + c0);
738     }
739 
740     SkColor4f avg;
741     blend.store(&avg);
742     return avg;
743 }
744 
745 // The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg
746 // gradients defined in the wild.
747 static constexpr SkScalar kDegenerateThreshold = SK_Scalar1 / (1 << 15);
748 
749 // Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
750 // can be mapped to the same fallbacks. The specific shape factories must account for special
751 // clamped conditions separately, this will always return the last color for clamped gradients.
make_degenerate_gradient(const SkColor4f colors[],const SkScalar pos[],int colorCount,sk_sp<SkColorSpace> colorSpace,SkTileMode mode)752 static sk_sp<SkShader> make_degenerate_gradient(const SkColor4f colors[], const SkScalar pos[],
753                                                 int colorCount, sk_sp<SkColorSpace> colorSpace,
754                                                 SkTileMode mode) {
755     switch(mode) {
756         case SkTileMode::kDecal:
757             // normally this would reject the area outside of the interpolation region, so since
758             // inside region is empty when the radii are equal, the entire draw region is empty
759             return SkShaders::Empty();
760         case SkTileMode::kRepeat:
761         case SkTileMode::kMirror:
762             // repeat and mirror are treated the same: the border colors are never visible,
763             // but approximate the final color as infinite repetitions of the colors, so
764             // it can be represented as the average color of the gradient.
765             return SkShaders::Color(
766                     average_gradient_color(colors, pos, colorCount), std::move(colorSpace));
767         case SkTileMode::kClamp:
768             // Depending on how the gradient shape degenerates, there may be a more specialized
769             // fallback representation for the factories to use, but this is a reasonable default.
770             return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
771     }
772     SkDEBUGFAIL("Should not be reached");
773     return nullptr;
774 }
775 
776 // assumes colors is SkColor4f* and pos is SkScalar*
777 #define EXPAND_1_COLOR(count)                \
778      SkColor4f tmp[2];                       \
779      do {                                    \
780          if (1 == count) {                   \
781              tmp[0] = tmp[1] = colors[0];    \
782              colors = tmp;                   \
783              pos = nullptr;                  \
784              count = 2;                      \
785          }                                   \
786      } while (0)
787 
788 struct ColorStopOptimizer {
ColorStopOptimizerColorStopOptimizer789     ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, int count, SkTileMode mode)
790         : fColors(colors)
791         , fPos(pos)
792         , fCount(count) {
793 
794             if (!pos || count != 3) {
795                 return;
796             }
797 
798             if (SkScalarNearlyEqual(pos[0], 0.0f) &&
799                 SkScalarNearlyEqual(pos[1], 0.0f) &&
800                 SkScalarNearlyEqual(pos[2], 1.0f)) {
801 
802                 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
803                     colors[0] == colors[1]) {
804 
805                     // Ignore the leftmost color/pos.
806                     fColors += 1;
807                     fPos    += 1;
808                     fCount   = 2;
809                 }
810             } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
811                        SkScalarNearlyEqual(pos[1], 1.0f) &&
812                        SkScalarNearlyEqual(pos[2], 1.0f)) {
813 
814                 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
815                     colors[1] == colors[2]) {
816 
817                     // Ignore the rightmost color/pos.
818                     fCount  = 2;
819                 }
820             }
821     }
822 
823     const SkColor4f* fColors;
824     const SkScalar*  fPos;
825     int              fCount;
826 };
827 
828 struct ColorConverter {
ColorConverterColorConverter829     ColorConverter(const SkColor* colors, int count) {
830         const float ONE_OVER_255 = 1.f / 255;
831         for (int i = 0; i < count; ++i) {
832             fColors4f.push_back({
833                 SkColorGetR(colors[i]) * ONE_OVER_255,
834                 SkColorGetG(colors[i]) * ONE_OVER_255,
835                 SkColorGetB(colors[i]) * ONE_OVER_255,
836                 SkColorGetA(colors[i]) * ONE_OVER_255 });
837         }
838     }
839 
840     SkSTArray<2, SkColor4f, true> fColors4f;
841 };
842 
MakeLinear(const SkPoint pts[2],const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)843 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
844                                              const SkColor colors[],
845                                              const SkScalar pos[], int colorCount,
846                                              SkTileMode mode,
847                                              uint32_t flags,
848                                              const SkMatrix* localMatrix) {
849     ColorConverter converter(colors, colorCount);
850     return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
851                       localMatrix);
852 }
853 
MakeLinear(const SkPoint pts[2],const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)854 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
855                                              const SkColor4f colors[],
856                                              sk_sp<SkColorSpace> colorSpace,
857                                              const SkScalar pos[], int colorCount,
858                                              SkTileMode mode,
859                                              uint32_t flags,
860                                              const SkMatrix* localMatrix) {
861     if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
862         return nullptr;
863     }
864     if (!valid_grad(colors, pos, colorCount, mode)) {
865         return nullptr;
866     }
867     if (1 == colorCount) {
868         return SkShaders::Color(colors[0], std::move(colorSpace));
869     }
870     if (localMatrix && !localMatrix->invert(nullptr)) {
871         return nullptr;
872     }
873 
874     if (SkScalarNearlyZero((pts[1] - pts[0]).length(), kDegenerateThreshold)) {
875         // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
876         // the gradient approaches two half planes of solid color (first and last). However, they
877         // are divided by the line perpendicular to the start and end point, which becomes undefined
878         // once start and end are exactly the same, so just use the end color for a stable solution.
879         return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
880     }
881 
882     ColorStopOptimizer opt(colors, pos, colorCount, mode);
883 
884     SkGradientShaderBase::Descriptor desc;
885     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
886               localMatrix);
887     return sk_make_sp<SkLinearGradient>(pts, desc);
888 }
889 
MakeRadial(const SkPoint & center,SkScalar radius,const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)890 sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
891                                              const SkColor colors[],
892                                              const SkScalar pos[], int colorCount,
893                                              SkTileMode mode,
894                                              uint32_t flags,
895                                              const SkMatrix* localMatrix) {
896     ColorConverter converter(colors, colorCount);
897     return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
898                       flags, localMatrix);
899 }
900 
MakeRadial(const SkPoint & center,SkScalar radius,const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)901 sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
902                                              const SkColor4f colors[],
903                                              sk_sp<SkColorSpace> colorSpace,
904                                              const SkScalar pos[], int colorCount,
905                                              SkTileMode mode,
906                                              uint32_t flags,
907                                              const SkMatrix* localMatrix) {
908     if (radius < 0) {
909         return nullptr;
910     }
911     if (!valid_grad(colors, pos, colorCount, mode)) {
912         return nullptr;
913     }
914     if (1 == colorCount) {
915         return SkShaders::Color(colors[0], std::move(colorSpace));
916     }
917     if (localMatrix && !localMatrix->invert(nullptr)) {
918         return nullptr;
919     }
920 
921     if (SkScalarNearlyZero(radius, kDegenerateThreshold)) {
922         // Degenerate gradient optimization, and no special logic needed for clamped radial gradient
923         return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
924     }
925 
926     ColorStopOptimizer opt(colors, pos, colorCount, mode);
927 
928     SkGradientShaderBase::Descriptor desc;
929     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
930               localMatrix);
931     return sk_make_sp<SkRadialGradient>(center, radius, desc);
932 }
933 
MakeTwoPointConical(const SkPoint & start,SkScalar startRadius,const SkPoint & end,SkScalar endRadius,const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)934 sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
935                                                       SkScalar startRadius,
936                                                       const SkPoint& end,
937                                                       SkScalar endRadius,
938                                                       const SkColor colors[],
939                                                       const SkScalar pos[],
940                                                       int colorCount,
941                                                       SkTileMode mode,
942                                                       uint32_t flags,
943                                                       const SkMatrix* localMatrix) {
944     ColorConverter converter(colors, colorCount);
945     return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
946                                nullptr, pos, colorCount, mode, flags, localMatrix);
947 }
948 
MakeTwoPointConical(const SkPoint & start,SkScalar startRadius,const SkPoint & end,SkScalar endRadius,const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)949 sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
950                                                       SkScalar startRadius,
951                                                       const SkPoint& end,
952                                                       SkScalar endRadius,
953                                                       const SkColor4f colors[],
954                                                       sk_sp<SkColorSpace> colorSpace,
955                                                       const SkScalar pos[],
956                                                       int colorCount,
957                                                       SkTileMode mode,
958                                                       uint32_t flags,
959                                                       const SkMatrix* localMatrix) {
960     if (startRadius < 0 || endRadius < 0) {
961         return nullptr;
962     }
963     if (!valid_grad(colors, pos, colorCount, mode)) {
964         return nullptr;
965     }
966     if (SkScalarNearlyZero((start - end).length(), kDegenerateThreshold)) {
967         // If the center positions are the same, then the gradient is the radial variant of a 2 pt
968         // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
969         // (startRadius == endRadius).
970         if (SkScalarNearlyEqual(startRadius, endRadius, kDegenerateThreshold)) {
971             // Degenerate case, where the interpolation region area approaches zero. The proper
972             // behavior depends on the tile mode, which is consistent with the default degenerate
973             // gradient behavior, except when mode = clamp and the radii > 0.
974             if (mode == SkTileMode::kClamp && endRadius > kDegenerateThreshold) {
975                 // The interpolation region becomes an infinitely thin ring at the radius, so the
976                 // final gradient will be the first color repeated from p=0 to 1, and then a hard
977                 // stop switching to the last color at p=1.
978                 static constexpr SkScalar circlePos[3] = {0, 1, 1};
979                 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
980                 return MakeRadial(start, endRadius, reColors, std::move(colorSpace),
981                                   circlePos, 3, mode, flags, localMatrix);
982             } else {
983                 // Otherwise use the default degenerate case
984                 return make_degenerate_gradient(
985                         colors, pos, colorCount, std::move(colorSpace), mode);
986             }
987         } else if (SkScalarNearlyZero(startRadius, kDegenerateThreshold)) {
988             // We can treat this gradient as radial, which is faster. If we got here, we know
989             // that endRadius is not equal to 0, so this produces a meaningful gradient
990             return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
991                               mode, flags, localMatrix);
992         }
993         // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
994         // regular 2pt constructor.
995     }
996 
997     if (localMatrix && !localMatrix->invert(nullptr)) {
998         return nullptr;
999     }
1000     EXPAND_1_COLOR(colorCount);
1001 
1002     ColorStopOptimizer opt(colors, pos, colorCount, mode);
1003 
1004     SkGradientShaderBase::Descriptor desc;
1005     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
1006               localMatrix);
1007     return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc);
1008 }
1009 
MakeSweep(SkScalar cx,SkScalar cy,const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,SkScalar startAngle,SkScalar endAngle,uint32_t flags,const SkMatrix * localMatrix)1010 sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
1011                                             const SkColor colors[],
1012                                             const SkScalar pos[],
1013                                             int colorCount,
1014                                             SkTileMode mode,
1015                                             SkScalar startAngle,
1016                                             SkScalar endAngle,
1017                                             uint32_t flags,
1018                                             const SkMatrix* localMatrix) {
1019     ColorConverter converter(colors, colorCount);
1020     return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount,
1021                      mode, startAngle, endAngle, flags, localMatrix);
1022 }
1023 
MakeSweep(SkScalar cx,SkScalar cy,const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int colorCount,SkTileMode mode,SkScalar startAngle,SkScalar endAngle,uint32_t flags,const SkMatrix * localMatrix)1024 sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
1025                                             const SkColor4f colors[],
1026                                             sk_sp<SkColorSpace> colorSpace,
1027                                             const SkScalar pos[],
1028                                             int colorCount,
1029                                             SkTileMode mode,
1030                                             SkScalar startAngle,
1031                                             SkScalar endAngle,
1032                                             uint32_t flags,
1033                                             const SkMatrix* localMatrix) {
1034     if (!valid_grad(colors, pos, colorCount, mode)) {
1035         return nullptr;
1036     }
1037     if (1 == colorCount) {
1038         return SkShaders::Color(colors[0], std::move(colorSpace));
1039     }
1040     if (!SkScalarIsFinite(startAngle) || !SkScalarIsFinite(endAngle) || startAngle > endAngle) {
1041         return nullptr;
1042     }
1043     if (localMatrix && !localMatrix->invert(nullptr)) {
1044         return nullptr;
1045     }
1046 
1047     if (SkScalarNearlyEqual(startAngle, endAngle, kDegenerateThreshold)) {
1048         // Degenerate gradient, which should follow default degenerate behavior unless it is
1049         // clamped and the angle is greater than 0.
1050         if (mode == SkTileMode::kClamp && endAngle > kDegenerateThreshold) {
1051             // In this case, the first color is repeated from 0 to the angle, then a hardstop
1052             // switches to the last color (all other colors are compressed to the infinitely thin
1053             // interpolation region).
1054             static constexpr SkScalar clampPos[3] = {0, 1, 1};
1055             SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
1056             return MakeSweep(cx, cy, reColors, std::move(colorSpace), clampPos, 3, mode, 0,
1057                              endAngle, flags, localMatrix);
1058         } else {
1059             return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
1060         }
1061     }
1062 
1063     if (startAngle <= 0 && endAngle >= 360) {
1064         // If the t-range includes [0,1], then we can always use clamping (presumably faster).
1065         mode = SkTileMode::kClamp;
1066     }
1067 
1068     ColorStopOptimizer opt(colors, pos, colorCount, mode);
1069 
1070     SkGradientShaderBase::Descriptor desc;
1071     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
1072               localMatrix);
1073 
1074     const SkScalar t0 = startAngle / 360,
1075                    t1 =   endAngle / 360;
1076 
1077     return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc);
1078 }
1079 
RegisterFlattenables()1080 void SkGradientShader::RegisterFlattenables() {
1081     SK_REGISTER_FLATTENABLE(SkLinearGradient);
1082     SK_REGISTER_FLATTENABLE(SkRadialGradient);
1083     SK_REGISTER_FLATTENABLE(SkSweepGradient);
1084     SK_REGISTER_FLATTENABLE(SkTwoPointConicalGradient);
1085 }
1086