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