• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/core/SkColorSpacePriv.h"
13 #include "src/core/SkConvertPixels.h"
14 #include "src/core/SkReadBuffer.h"
15 #include "src/core/SkWriteBuffer.h"
16 #include "src/shaders/gradients/Sk4fLinearGradient.h"
17 #include "src/shaders/gradients/SkGradientShaderPriv.h"
18 #include "src/shaders/gradients/SkLinearGradient.h"
19 #include "src/shaders/gradients/SkRadialGradient.h"
20 #include "src/shaders/gradients/SkSweepGradient.h"
21 #include "src/shaders/gradients/SkTwoPointConicalGradient.h"
22 
23 enum GradientSerializationFlags {
24     // Bits 29:31 used for various boolean flags
25     kHasPosition_GSF    = 0x80000000,
26     kHasLocalMatrix_GSF = 0x40000000,
27     kHasColorSpace_GSF  = 0x20000000,
28 
29     // Bits 12:28 unused
30 
31     // Bits 8:11 for fTileMode
32     kTileModeShift_GSF  = 8,
33     kTileModeMask_GSF   = 0xF,
34 
35     // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
36     kGradFlagsShift_GSF = 0,
37     kGradFlagsMask_GSF  = 0xFF,
38 };
39 
flatten(SkWriteBuffer & buffer) const40 void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
41     uint32_t flags = 0;
42     if (fPos) {
43         flags |= kHasPosition_GSF;
44     }
45     if (fLocalMatrix) {
46         flags |= kHasLocalMatrix_GSF;
47     }
48     sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
49     if (colorSpaceData) {
50         flags |= kHasColorSpace_GSF;
51     }
52     SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
53     flags |= ((unsigned)fTileMode << kTileModeShift_GSF);
54     SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
55     flags |= (fGradFlags << kGradFlagsShift_GSF);
56 
57     buffer.writeUInt(flags);
58 
59     buffer.writeColor4fArray(fColors, fCount);
60     if (colorSpaceData) {
61         buffer.writeDataAsByteArray(colorSpaceData.get());
62     }
63     if (fPos) {
64         buffer.writeScalarArray(fPos, fCount);
65     }
66     if (fLocalMatrix) {
67         buffer.writeMatrix(*fLocalMatrix);
68     }
69 }
70 
71 template <int N, typename T, bool MEM_MOVE>
validate_array(SkReadBuffer & buffer,size_t count,SkSTArray<N,T,MEM_MOVE> * array)72 static bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) {
73     if (!buffer.validateCanReadN<T>(count)) {
74         return false;
75     }
76 
77     array->resize_back(count);
78     return true;
79 }
80 
unflatten(SkReadBuffer & buffer)81 bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
82     // New gradient format. Includes floating point color, color space, densely packed flags
83     uint32_t flags = buffer.readUInt();
84 
85     fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
86     fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
87 
88     fCount = buffer.getArrayCount();
89 
90     if (!(validate_array(buffer, fCount, &fColorStorage) &&
91           buffer.readColor4fArray(fColorStorage.begin(), fCount))) {
92         return false;
93     }
94     fColors = fColorStorage.begin();
95 
96     if (SkToBool(flags & kHasColorSpace_GSF)) {
97         sk_sp<SkData> data = buffer.readByteArrayAsData();
98         fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
99     } else {
100         fColorSpace = nullptr;
101     }
102     if (SkToBool(flags & kHasPosition_GSF)) {
103         if (!(validate_array(buffer, fCount, &fPosStorage) &&
104               buffer.readScalarArray(fPosStorage.begin(), fCount))) {
105             return false;
106         }
107         fPos = fPosStorage.begin();
108     } else {
109         fPos = nullptr;
110     }
111     if (SkToBool(flags & kHasLocalMatrix_GSF)) {
112         fLocalMatrix = &fLocalMatrixStorage;
113         buffer.readMatrix(&fLocalMatrixStorage);
114     } else {
115         fLocalMatrix = nullptr;
116     }
117     return buffer.isValid();
118 }
119 
120 ////////////////////////////////////////////////////////////////////////////////////////////
121 
SkGradientShaderBase(const Descriptor & desc,const SkMatrix & ptsToUnit)122 SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
123     : INHERITED(desc.fLocalMatrix)
124     , fPtsToUnit(ptsToUnit)
125     , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
126     , fColorsAreOpaque(true)
127 {
128     fPtsToUnit.getType();  // Precache so reads are threadsafe.
129     SkASSERT(desc.fCount > 1);
130 
131     fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
132 
133     SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
134     fTileMode = desc.fTileMode;
135 
136     /*  Note: we let the caller skip the first and/or last position.
137         i.e. pos[0] = 0.3, pos[1] = 0.7
138         In these cases, we insert dummy entries to ensure that the final data
139         will be bracketed by [0, 1].
140         i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
141 
142         Thus colorCount (the caller's value, and fColorCount (our value) may
143         differ by up to 2. In the above example:
144             colorCount = 2
145             fColorCount = 4
146      */
147     fColorCount = desc.fCount;
148     // check if we need to add in dummy start and/or end position/colors
149     bool dummyFirst = false;
150     bool dummyLast = false;
151     if (desc.fPos) {
152         dummyFirst = desc.fPos[0] != 0;
153         dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
154         fColorCount += dummyFirst + dummyLast;
155     }
156 
157     size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0));
158     fOrigColors4f      = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
159     fOrigPos           = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount)
160                                    : nullptr;
161 
162     // Now copy over the colors, adding the dummies as needed
163     SkColor4f* origColors = fOrigColors4f;
164     if (dummyFirst) {
165         *origColors++ = desc.fColors[0];
166     }
167     for (int i = 0; i < desc.fCount; ++i) {
168         origColors[i] = desc.fColors[i];
169         fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
170     }
171     if (dummyLast) {
172         origColors += desc.fCount;
173         *origColors = desc.fColors[desc.fCount - 1];
174     }
175 
176     if (desc.fPos) {
177         SkScalar prev = 0;
178         SkScalar* origPosPtr = fOrigPos;
179         *origPosPtr++ = prev; // force the first pos to 0
180 
181         int startIndex = dummyFirst ? 0 : 1;
182         int count = desc.fCount + dummyLast;
183 
184         bool uniformStops = true;
185         const SkScalar uniformStep = desc.fPos[startIndex] - prev;
186         for (int i = startIndex; i < count; i++) {
187             // Pin the last value to 1.0, and make sure pos is monotonic.
188             auto curr = (i == desc.fCount) ? 1 : SkScalarPin(desc.fPos[i], prev, 1);
189             uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
190 
191             *origPosPtr++ = prev = curr;
192         }
193 
194         // If the stops are uniform, treat them as implicit.
195         if (uniformStops) {
196             fOrigPos = nullptr;
197         }
198     }
199 }
200 
~SkGradientShaderBase()201 SkGradientShaderBase::~SkGradientShaderBase() {}
202 
flatten(SkWriteBuffer & buffer) const203 void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
204     Descriptor desc;
205     desc.fColors = fOrigColors4f;
206     desc.fColorSpace = fColorSpace;
207     desc.fPos = fOrigPos;
208     desc.fCount = fColorCount;
209     desc.fTileMode = fTileMode;
210     desc.fGradFlags = fGradFlags;
211 
212     const SkMatrix& m = this->getLocalMatrix();
213     desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
214     desc.flatten(buffer);
215 }
216 
add_stop_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f Fs,SkPMColor4f Bs)217 static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f Fs, SkPMColor4f Bs) {
218     (ctx->fs[0])[stop] = Fs.fR;
219     (ctx->fs[1])[stop] = Fs.fG;
220     (ctx->fs[2])[stop] = Fs.fB;
221     (ctx->fs[3])[stop] = Fs.fA;
222     (ctx->bs[0])[stop] = Bs.fR;
223     (ctx->bs[1])[stop] = Bs.fG;
224     (ctx->bs[2])[stop] = Bs.fB;
225     (ctx->bs[3])[stop] = Bs.fA;
226 }
227 
add_const_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f color)228 static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) {
229     add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color);
230 }
231 
232 // Calculate a factor F and a bias B so that color = F*t + B when t is in range of
233 // 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)234 static void init_stop_evenly(
235     SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop, SkPMColor4f c_l, SkPMColor4f c_r) {
236     // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
237     SkPMColor4f Fs = {
238         (c_r.fR - c_l.fR) * gapCount,
239         (c_r.fG - c_l.fG) * gapCount,
240         (c_r.fB - c_l.fB) * gapCount,
241         (c_r.fA - c_l.fA) * gapCount,
242     };
243     SkPMColor4f Bs = {
244         c_l.fR - Fs.fR*(stop/gapCount),
245         c_l.fG - Fs.fG*(stop/gapCount),
246         c_l.fB - Fs.fB*(stop/gapCount),
247         c_l.fA - Fs.fA*(stop/gapCount),
248     };
249     add_stop_color(ctx, stop, Fs, Bs);
250 }
251 
252 // For each stop we calculate a bias B and a scale factor F, such that
253 // 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)254 static void init_stop_pos(
255     SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPMColor4f c_l, SkPMColor4f c_r) {
256     // See note about Clankium's old compiler in init_stop_evenly().
257     SkPMColor4f Fs = {
258         (c_r.fR - c_l.fR) / (t_r - t_l),
259         (c_r.fG - c_l.fG) / (t_r - t_l),
260         (c_r.fB - c_l.fB) / (t_r - t_l),
261         (c_r.fA - c_l.fA) / (t_r - t_l),
262     };
263     SkPMColor4f Bs = {
264         c_l.fR - Fs.fR*t_l,
265         c_l.fG - Fs.fG*t_l,
266         c_l.fB - Fs.fB*t_l,
267         c_l.fA - Fs.fA*t_l,
268     };
269     ctx->ts[stop] = t_l;
270     add_stop_color(ctx, stop, Fs, Bs);
271 }
272 
onAppendStages(const SkStageRec & rec) const273 bool SkGradientShaderBase::onAppendStages(const SkStageRec& rec) const {
274     SkRasterPipeline* p = rec.fPipeline;
275     SkArenaAlloc* alloc = rec.fAlloc;
276     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
277 
278     SkMatrix matrix;
279     if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
280         return false;
281     }
282     matrix.postConcat(fPtsToUnit);
283 
284     SkRasterPipeline_<256> postPipeline;
285 
286     p->append(SkRasterPipeline::seed_shader);
287     p->append_matrix(alloc, matrix);
288     this->appendGradientStages(alloc, p, &postPipeline);
289 
290     switch(fTileMode) {
291         case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x_1); break;
292         case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x_1); break;
293         case SkTileMode::kDecal:
294             decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
295             decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
296             // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
297             p->append(SkRasterPipeline::decal_x, decal_ctx);
298             // fall-through to clamp
299         case SkTileMode::kClamp:
300             if (!fOrigPos) {
301                 // We clamp only when the stops are evenly spaced.
302                 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
303                 // In that case, we must make sure we're using the general "gradient" stage,
304                 // which is the only stage that will correctly handle unclamped t.
305                 p->append(SkRasterPipeline::clamp_x_1);
306             }
307             break;
308     }
309 
310     const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
311 
312     // Transform all of the colors to destination color space
313     SkColor4fXformer xformedColors(fOrigColors4f, fColorCount, fColorSpace.get(), rec.fDstCS);
314 
315     auto prepareColor = [premulGrad, &xformedColors](int i) {
316         SkColor4f c = xformedColors.fColors[i];
317         return premulGrad ? c.premul()
318                           : SkPMColor4f{ c.fR, c.fG, c.fB, c.fA };
319     };
320 
321     // The two-stop case with stops at 0 and 1.
322     if (fColorCount == 2 && fOrigPos == nullptr) {
323         const SkPMColor4f c_l = prepareColor(0),
324                           c_r = prepareColor(1);
325 
326         // See F and B below.
327         auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
328         (Sk4f::Load(c_r.vec()) - Sk4f::Load(c_l.vec())).store(ctx->f);
329         (                        Sk4f::Load(c_l.vec())).store(ctx->b);
330         ctx->interpolatedInPremul = premulGrad;
331 
332         p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, ctx);
333     } else {
334         auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
335         ctx->interpolatedInPremul = premulGrad;
336 
337         // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
338         // at -inf. Therefore, the max number of stops is fColorCount+1.
339         for (int i = 0; i < 4; i++) {
340             // Allocate at least at for the AVX2 gather from a YMM register.
341             ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
342             ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
343         }
344 
345         if (fOrigPos == nullptr) {
346             // Handle evenly distributed stops.
347 
348             size_t stopCount = fColorCount;
349             float gapCount = stopCount - 1;
350 
351             SkPMColor4f c_l = prepareColor(0);
352             for (size_t i = 0; i < stopCount - 1; i++) {
353                 SkPMColor4f c_r = prepareColor(i + 1);
354                 init_stop_evenly(ctx, gapCount, i, c_l, c_r);
355                 c_l = c_r;
356             }
357             add_const_color(ctx, stopCount - 1, c_l);
358 
359             ctx->stopCount = stopCount;
360             p->append(SkRasterPipeline::evenly_spaced_gradient, ctx);
361         } else {
362             // Handle arbitrary stops.
363 
364             ctx->ts = alloc->makeArray<float>(fColorCount+1);
365 
366             // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
367             // because they are naturally handled by the search method.
368             int firstStop;
369             int lastStop;
370             if (fColorCount > 2) {
371                 firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
372                 lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
373                            ? fColorCount - 1 : fColorCount - 2;
374             } else {
375                 firstStop = 0;
376                 lastStop = 1;
377             }
378 
379             size_t stopCount = 0;
380             float  t_l = fOrigPos[firstStop];
381             SkPMColor4f c_l = prepareColor(firstStop);
382             add_const_color(ctx, stopCount++, c_l);
383             // N.B. lastStop is the index of the last stop, not one after.
384             for (int i = firstStop; i < lastStop; i++) {
385                 float  t_r = fOrigPos[i + 1];
386                 SkPMColor4f c_r = prepareColor(i + 1);
387                 SkASSERT(t_l <= t_r);
388                 if (t_l < t_r) {
389                     init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
390                     stopCount += 1;
391                 }
392                 t_l = t_r;
393                 c_l = c_r;
394             }
395 
396             ctx->ts[stopCount] = t_l;
397             add_const_color(ctx, stopCount++, c_l);
398 
399             ctx->stopCount = stopCount;
400             p->append(SkRasterPipeline::gradient, ctx);
401         }
402     }
403 
404     if (decal_ctx) {
405         p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
406     }
407 
408     if (!premulGrad && !this->colorsAreOpaque()) {
409         p->append(SkRasterPipeline::premul);
410     }
411 
412     p->extend(postPipeline);
413 
414     return true;
415 }
416 
417 
isOpaque() const418 bool SkGradientShaderBase::isOpaque() const {
419     return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
420 }
421 
rounded_divide(unsigned numer,unsigned denom)422 static unsigned rounded_divide(unsigned numer, unsigned denom) {
423     return (numer + (denom >> 1)) / denom;
424 }
425 
onAsLuminanceColor(SkColor * lum) const426 bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
427     // we just compute an average color.
428     // possibly we could weight this based on the proportional width for each color
429     //   assuming they are not evenly distributed in the fPos array.
430     int r = 0;
431     int g = 0;
432     int b = 0;
433     const int n = fColorCount;
434     // TODO: use linear colors?
435     for (int i = 0; i < n; ++i) {
436         SkColor c = this->getLegacyColor(i);
437         r += SkColorGetR(c);
438         g += SkColorGetG(c);
439         b += SkColorGetB(c);
440     }
441     *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
442     return true;
443 }
444 
SkColor4fXformer(const SkColor4f * colors,int colorCount,SkColorSpace * src,SkColorSpace * dst)445 SkColor4fXformer::SkColor4fXformer(const SkColor4f* colors, int colorCount,
446                                    SkColorSpace* src, SkColorSpace* dst) {
447     fColors = colors;
448 
449     if (dst && !SkColorSpace::Equals(src, dst)) {
450         fStorage.reset(colorCount);
451 
452         auto info = SkImageInfo::Make(colorCount,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType);
453 
454         SkConvertPixels(info.makeColorSpace(sk_ref_sp(dst)), fStorage.begin(), info.minRowBytes(),
455                         info.makeColorSpace(sk_ref_sp(src)), fColors         , info.minRowBytes());
456 
457         fColors = fStorage.begin();
458     }
459 }
460 
commonAsAGradient(GradientInfo * info) const461 void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
462     if (info) {
463         if (info->fColorCount >= fColorCount) {
464             if (info->fColors) {
465                 for (int i = 0; i < fColorCount; ++i) {
466                     info->fColors[i] = this->getLegacyColor(i);
467                 }
468             }
469             if (info->fColorOffsets) {
470                 for (int i = 0; i < fColorCount; ++i) {
471                     info->fColorOffsets[i] = this->getPos(i);
472                 }
473             }
474         }
475         info->fColorCount = fColorCount;
476         info->fTileMode = fTileMode;
477         info->fGradientFlags = fGradFlags;
478     }
479 }
480 
481 ///////////////////////////////////////////////////////////////////////////////
482 ///////////////////////////////////////////////////////////////////////////////
483 
484 // Return true if these parameters are valid/legal/safe to construct a gradient
485 //
valid_grad(const SkColor4f colors[],const SkScalar pos[],int count,SkTileMode tileMode)486 static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
487                        SkTileMode tileMode) {
488     return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount;
489 }
490 
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)491 static void desc_init(SkGradientShaderBase::Descriptor* desc,
492                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
493                       const SkScalar pos[], int colorCount,
494                       SkTileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
495     SkASSERT(colorCount > 1);
496 
497     desc->fColors       = colors;
498     desc->fColorSpace   = std::move(colorSpace);
499     desc->fPos          = pos;
500     desc->fCount        = colorCount;
501     desc->fTileMode     = mode;
502     desc->fGradFlags    = flags;
503     desc->fLocalMatrix  = localMatrix;
504 }
505 
average_gradient_color(const SkColor4f colors[],const SkScalar pos[],int colorCount)506 static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[],
507                                         int colorCount) {
508     // The gradient is a piecewise linear interpolation between colors. For a given interval,
509     // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
510     // intervals average color. The overall average color is thus the sum of each piece. The thing
511     // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
512     Sk4f blend(0.0);
513     // Bake 1/(colorCount - 1) uniform stop difference into this scale factor
514     SkScalar wScale = pos ? 0.5 : 0.5 / (colorCount - 1);
515     for (int i = 0; i < colorCount - 1; ++i) {
516         // Calculate the average color for the interval between pos(i) and pos(i+1)
517         Sk4f c0 = Sk4f::Load(&colors[i]);
518         Sk4f c1 = Sk4f::Load(&colors[i + 1]);
519         // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
520         // so pos[i + 1] - pos[i] = 1/(colorCount-1)
521         SkScalar w = pos ? (pos[i + 1] - pos[i]) : SK_Scalar1;
522         blend += wScale * w * (c1 + c0);
523     }
524 
525     // Now account for any implicit intervals at the start or end of the stop definitions
526     if (pos) {
527         if (pos[0] > 0.0) {
528             // The first color is fixed between p = 0 to pos[0], so 0.5 * (ci + cj) * (pj - pi)
529             // becomes 0.5 * (c + c) * (pj - 0) = c * pj
530             Sk4f c = Sk4f::Load(&colors[0]);
531             blend += pos[0] * c;
532         }
533         if (pos[colorCount - 1] < SK_Scalar1) {
534             // The last color is fixed between pos[n-1] to p = 1, so 0.5 * (ci + cj) * (pj - pi)
535             // becomes 0.5 * (c + c) * (1 - pi) = c * (1 - pi)
536             Sk4f c = Sk4f::Load(&colors[colorCount - 1]);
537             blend += (1 - pos[colorCount - 1]) * c;
538         }
539     }
540 
541     SkColor4f avg;
542     blend.store(&avg);
543     return avg;
544 }
545 
546 // The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg
547 // gradients defined in the wild.
548 static constexpr SkScalar kDegenerateThreshold = SK_Scalar1 / (1 << 15);
549 
550 // Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
551 // can be mapped to the same fallbacks. The specific shape factories must account for special
552 // 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)553 static sk_sp<SkShader> make_degenerate_gradient(const SkColor4f colors[], const SkScalar pos[],
554                                                 int colorCount, sk_sp<SkColorSpace> colorSpace,
555                                                 SkTileMode mode) {
556     switch(mode) {
557         case SkTileMode::kDecal:
558             // normally this would reject the area outside of the interpolation region, so since
559             // inside region is empty when the radii are equal, the entire draw region is empty
560             return SkShaders::Empty();
561         case SkTileMode::kRepeat:
562         case SkTileMode::kMirror:
563             // repeat and mirror are treated the same: the border colors are never visible,
564             // but approximate the final color as infinite repetitions of the colors, so
565             // it can be represented as the average color of the gradient.
566             return SkShaders::Color(
567                     average_gradient_color(colors, pos, colorCount), std::move(colorSpace));
568         case SkTileMode::kClamp:
569             // Depending on how the gradient shape degenerates, there may be a more specialized
570             // fallback representation for the factories to use, but this is a reasonable default.
571             return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
572     }
573     SkDEBUGFAIL("Should not be reached");
574     return nullptr;
575 }
576 
577 // assumes colors is SkColor4f* and pos is SkScalar*
578 #define EXPAND_1_COLOR(count)                \
579      SkColor4f tmp[2];                       \
580      do {                                    \
581          if (1 == count) {                   \
582              tmp[0] = tmp[1] = colors[0];    \
583              colors = tmp;                   \
584              pos = nullptr;                  \
585              count = 2;                      \
586          }                                   \
587      } while (0)
588 
589 struct ColorStopOptimizer {
ColorStopOptimizerColorStopOptimizer590     ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, int count, SkTileMode mode)
591         : fColors(colors)
592         , fPos(pos)
593         , fCount(count) {
594 
595             if (!pos || count != 3) {
596                 return;
597             }
598 
599             if (SkScalarNearlyEqual(pos[0], 0.0f) &&
600                 SkScalarNearlyEqual(pos[1], 0.0f) &&
601                 SkScalarNearlyEqual(pos[2], 1.0f)) {
602 
603                 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
604                     colors[0] == colors[1]) {
605 
606                     // Ignore the leftmost color/pos.
607                     fColors += 1;
608                     fPos    += 1;
609                     fCount   = 2;
610                 }
611             } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
612                        SkScalarNearlyEqual(pos[1], 1.0f) &&
613                        SkScalarNearlyEqual(pos[2], 1.0f)) {
614 
615                 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
616                     colors[1] == colors[2]) {
617 
618                     // Ignore the rightmost color/pos.
619                     fCount  = 2;
620                 }
621             }
622     }
623 
624     const SkColor4f* fColors;
625     const SkScalar*  fPos;
626     int              fCount;
627 };
628 
629 struct ColorConverter {
ColorConverterColorConverter630     ColorConverter(const SkColor* colors, int count) {
631         const float ONE_OVER_255 = 1.f / 255;
632         for (int i = 0; i < count; ++i) {
633             fColors4f.push_back({
634                 SkColorGetR(colors[i]) * ONE_OVER_255,
635                 SkColorGetG(colors[i]) * ONE_OVER_255,
636                 SkColorGetB(colors[i]) * ONE_OVER_255,
637                 SkColorGetA(colors[i]) * ONE_OVER_255 });
638         }
639     }
640 
641     SkSTArray<2, SkColor4f, true> fColors4f;
642 };
643 
MakeLinear(const SkPoint pts[2],const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)644 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
645                                              const SkColor colors[],
646                                              const SkScalar pos[], int colorCount,
647                                              SkTileMode mode,
648                                              uint32_t flags,
649                                              const SkMatrix* localMatrix) {
650     ColorConverter converter(colors, colorCount);
651     return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
652                       localMatrix);
653 }
654 
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)655 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
656                                              const SkColor4f colors[],
657                                              sk_sp<SkColorSpace> colorSpace,
658                                              const SkScalar pos[], int colorCount,
659                                              SkTileMode mode,
660                                              uint32_t flags,
661                                              const SkMatrix* localMatrix) {
662     if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
663         return nullptr;
664     }
665     if (!valid_grad(colors, pos, colorCount, mode)) {
666         return nullptr;
667     }
668     if (1 == colorCount) {
669         return SkShaders::Color(colors[0], std::move(colorSpace));
670     }
671     if (localMatrix && !localMatrix->invert(nullptr)) {
672         return nullptr;
673     }
674 
675     if (SkScalarNearlyZero((pts[1] - pts[0]).length(), kDegenerateThreshold)) {
676         // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
677         // the gradient approaches two half planes of solid color (first and last). However, they
678         // are divided by the line perpendicular to the start and end point, which becomes undefined
679         // once start and end are exactly the same, so just use the end color for a stable solution.
680         return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
681     }
682 
683     ColorStopOptimizer opt(colors, pos, colorCount, mode);
684 
685     SkGradientShaderBase::Descriptor desc;
686     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
687               localMatrix);
688     return sk_make_sp<SkLinearGradient>(pts, desc);
689 }
690 
MakeRadial(const SkPoint & center,SkScalar radius,const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)691 sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
692                                              const SkColor colors[],
693                                              const SkScalar pos[], int colorCount,
694                                              SkTileMode mode,
695                                              uint32_t flags,
696                                              const SkMatrix* localMatrix) {
697     ColorConverter converter(colors, colorCount);
698     return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
699                       flags, localMatrix);
700 }
701 
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)702 sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
703                                              const SkColor4f colors[],
704                                              sk_sp<SkColorSpace> colorSpace,
705                                              const SkScalar pos[], int colorCount,
706                                              SkTileMode mode,
707                                              uint32_t flags,
708                                              const SkMatrix* localMatrix) {
709     if (radius < 0) {
710         return nullptr;
711     }
712     if (!valid_grad(colors, pos, colorCount, mode)) {
713         return nullptr;
714     }
715     if (1 == colorCount) {
716         return SkShaders::Color(colors[0], std::move(colorSpace));
717     }
718     if (localMatrix && !localMatrix->invert(nullptr)) {
719         return nullptr;
720     }
721 
722     if (SkScalarNearlyZero(radius, kDegenerateThreshold)) {
723         // Degenerate gradient optimization, and no special logic needed for clamped radial gradient
724         return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
725     }
726 
727     ColorStopOptimizer opt(colors, pos, colorCount, mode);
728 
729     SkGradientShaderBase::Descriptor desc;
730     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
731               localMatrix);
732     return sk_make_sp<SkRadialGradient>(center, radius, desc);
733 }
734 
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)735 sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
736                                                       SkScalar startRadius,
737                                                       const SkPoint& end,
738                                                       SkScalar endRadius,
739                                                       const SkColor colors[],
740                                                       const SkScalar pos[],
741                                                       int colorCount,
742                                                       SkTileMode mode,
743                                                       uint32_t flags,
744                                                       const SkMatrix* localMatrix) {
745     ColorConverter converter(colors, colorCount);
746     return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
747                                nullptr, pos, colorCount, mode, flags, localMatrix);
748 }
749 
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)750 sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
751                                                       SkScalar startRadius,
752                                                       const SkPoint& end,
753                                                       SkScalar endRadius,
754                                                       const SkColor4f colors[],
755                                                       sk_sp<SkColorSpace> colorSpace,
756                                                       const SkScalar pos[],
757                                                       int colorCount,
758                                                       SkTileMode mode,
759                                                       uint32_t flags,
760                                                       const SkMatrix* localMatrix) {
761     if (startRadius < 0 || endRadius < 0) {
762         return nullptr;
763     }
764     if (!valid_grad(colors, pos, colorCount, mode)) {
765         return nullptr;
766     }
767     if (SkScalarNearlyZero((start - end).length(), kDegenerateThreshold)) {
768         // If the center positions are the same, then the gradient is the radial variant of a 2 pt
769         // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
770         // (startRadius == endRadius).
771         if (SkScalarNearlyEqual(startRadius, endRadius, kDegenerateThreshold)) {
772             // Degenerate case, where the interpolation region area approaches zero. The proper
773             // behavior depends on the tile mode, which is consistent with the default degenerate
774             // gradient behavior, except when mode = clamp and the radii > 0.
775             if (mode == SkTileMode::kClamp && endRadius > kDegenerateThreshold) {
776                 // The interpolation region becomes an infinitely thin ring at the radius, so the
777                 // final gradient will be the first color repeated from p=0 to 1, and then a hard
778                 // stop switching to the last color at p=1.
779                 static constexpr SkScalar circlePos[3] = {0, 1, 1};
780                 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
781                 return MakeRadial(start, endRadius, reColors, std::move(colorSpace),
782                                   circlePos, 3, mode, flags, localMatrix);
783             } else {
784                 // Otherwise use the default degenerate case
785                 return make_degenerate_gradient(
786                         colors, pos, colorCount, std::move(colorSpace), mode);
787             }
788         } else if (SkScalarNearlyZero(startRadius, kDegenerateThreshold)) {
789             // We can treat this gradient as radial, which is faster. If we got here, we know
790             // that endRadius is not equal to 0, so this produces a meaningful gradient
791             return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
792                               mode, flags, localMatrix);
793         }
794         // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
795         // regular 2pt constructor.
796     }
797 
798     if (localMatrix && !localMatrix->invert(nullptr)) {
799         return nullptr;
800     }
801     EXPAND_1_COLOR(colorCount);
802 
803     ColorStopOptimizer opt(colors, pos, colorCount, mode);
804 
805     SkGradientShaderBase::Descriptor desc;
806     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
807               localMatrix);
808     return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc);
809 }
810 
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)811 sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
812                                             const SkColor colors[],
813                                             const SkScalar pos[],
814                                             int colorCount,
815                                             SkTileMode mode,
816                                             SkScalar startAngle,
817                                             SkScalar endAngle,
818                                             uint32_t flags,
819                                             const SkMatrix* localMatrix) {
820     ColorConverter converter(colors, colorCount);
821     return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount,
822                      mode, startAngle, endAngle, flags, localMatrix);
823 }
824 
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)825 sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
826                                             const SkColor4f colors[],
827                                             sk_sp<SkColorSpace> colorSpace,
828                                             const SkScalar pos[],
829                                             int colorCount,
830                                             SkTileMode mode,
831                                             SkScalar startAngle,
832                                             SkScalar endAngle,
833                                             uint32_t flags,
834                                             const SkMatrix* localMatrix) {
835     if (!valid_grad(colors, pos, colorCount, mode)) {
836         return nullptr;
837     }
838     if (1 == colorCount) {
839         return SkShaders::Color(colors[0], std::move(colorSpace));
840     }
841     if (!SkScalarIsFinite(startAngle) || !SkScalarIsFinite(endAngle) || startAngle > endAngle) {
842         return nullptr;
843     }
844     if (localMatrix && !localMatrix->invert(nullptr)) {
845         return nullptr;
846     }
847 
848     if (SkScalarNearlyEqual(startAngle, endAngle, kDegenerateThreshold)) {
849         // Degenerate gradient, which should follow default degenerate behavior unless it is
850         // clamped and the angle is greater than 0.
851         if (mode == SkTileMode::kClamp && endAngle > kDegenerateThreshold) {
852             // In this case, the first color is repeated from 0 to the angle, then a hardstop
853             // switches to the last color (all other colors are compressed to the infinitely thin
854             // interpolation region).
855             static constexpr SkScalar clampPos[3] = {0, 1, 1};
856             SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
857             return MakeSweep(cx, cy, reColors, std::move(colorSpace), clampPos, 3, mode, 0,
858                              endAngle, flags, localMatrix);
859         } else {
860             return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
861         }
862     }
863 
864     if (startAngle <= 0 && endAngle >= 360) {
865         // If the t-range includes [0,1], then we can always use clamping (presumably faster).
866         mode = SkTileMode::kClamp;
867     }
868 
869     ColorStopOptimizer opt(colors, pos, colorCount, mode);
870 
871     SkGradientShaderBase::Descriptor desc;
872     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
873               localMatrix);
874 
875     const SkScalar t0 = startAngle / 360,
876                    t1 =   endAngle / 360;
877 
878     return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc);
879 }
880 
RegisterFlattenables()881 void SkGradientShader::RegisterFlattenables() {
882     SK_REGISTER_FLATTENABLE(SkLinearGradient);
883     SK_REGISTER_FLATTENABLE(SkRadialGradient);
884     SK_REGISTER_FLATTENABLE(SkSweepGradient);
885     SK_REGISTER_FLATTENABLE(SkTwoPointConicalGradient);
886 }
887