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 "SkColorSpace_XYZ.h"
11 #include "SkColorSpaceXformer.h"
12 #include "SkFloatBits.h"
13 #include "SkGradientBitmapCache.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 #include "../../jumper/SkJumper.h"
24 
25 
26 enum GradientSerializationFlags {
27     // Bits 29:31 used for various boolean flags
28     kHasPosition_GSF    = 0x80000000,
29     kHasLocalMatrix_GSF = 0x40000000,
30     kHasColorSpace_GSF  = 0x20000000,
31 
32     // Bits 12:28 unused
33 
34     // Bits 8:11 for fTileMode
35     kTileModeShift_GSF  = 8,
36     kTileModeMask_GSF   = 0xF,
37 
38     // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
39     kGradFlagsShift_GSF = 0,
40     kGradFlagsMask_GSF  = 0xFF,
41 };
42 
flatten(SkWriteBuffer & buffer) const43 void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
44     uint32_t flags = 0;
45     if (fPos) {
46         flags |= kHasPosition_GSF;
47     }
48     if (fLocalMatrix) {
49         flags |= kHasLocalMatrix_GSF;
50     }
51     sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
52     if (colorSpaceData) {
53         flags |= kHasColorSpace_GSF;
54     }
55     SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
56     flags |= (fTileMode << kTileModeShift_GSF);
57     SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
58     flags |= (fGradFlags << kGradFlagsShift_GSF);
59 
60     buffer.writeUInt(flags);
61 
62     buffer.writeColor4fArray(fColors, fCount);
63     if (colorSpaceData) {
64         buffer.writeDataAsByteArray(colorSpaceData.get());
65     }
66     if (fPos) {
67         buffer.writeScalarArray(fPos, fCount);
68     }
69     if (fLocalMatrix) {
70         buffer.writeMatrix(*fLocalMatrix);
71     }
72 }
73 
unflatten(SkReadBuffer & buffer)74 bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
75     // New gradient format. Includes floating point color, color space, densely packed flags
76     uint32_t flags = buffer.readUInt();
77 
78     fTileMode = (SkShader::TileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
79     fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
80 
81     fCount = buffer.getArrayCount();
82     if (fCount > kStorageCount) {
83         size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
84         fDynamicStorage.reset(allocSize);
85         fColors = (SkColor4f*)fDynamicStorage.get();
86         fPos = (SkScalar*)(fColors + fCount);
87     } else {
88         fColors = fColorStorage;
89         fPos = fPosStorage;
90     }
91     if (!buffer.readColor4fArray(mutableColors(), fCount)) {
92         return false;
93     }
94     if (SkToBool(flags & kHasColorSpace_GSF)) {
95         sk_sp<SkData> data = buffer.readByteArrayAsData();
96         fColorSpace = SkColorSpace::Deserialize(data->data(), data->size());
97     } else {
98         fColorSpace = nullptr;
99     }
100     if (SkToBool(flags & kHasPosition_GSF)) {
101         if (!buffer.readScalarArray(mutablePos(), fCount)) {
102             return false;
103         }
104     } else {
105         fPos = nullptr;
106     }
107     if (SkToBool(flags & kHasLocalMatrix_GSF)) {
108         fLocalMatrix = &fLocalMatrixStorage;
109         buffer.readMatrix(&fLocalMatrixStorage);
110     } else {
111         fLocalMatrix = nullptr;
112     }
113     return buffer.isValid();
114 }
115 
116 ////////////////////////////////////////////////////////////////////////////////////////////
117 
SkGradientShaderBase(const Descriptor & desc,const SkMatrix & ptsToUnit)118 SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
119     : INHERITED(desc.fLocalMatrix)
120     , fPtsToUnit(ptsToUnit)
121     , fColorsAreOpaque(true)
122 {
123     fPtsToUnit.getType();  // Precache so reads are threadsafe.
124     SkASSERT(desc.fCount > 1);
125 
126     fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
127 
128     SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount);
129     fTileMode = desc.fTileMode;
130 
131     /*  Note: we let the caller skip the first and/or last position.
132         i.e. pos[0] = 0.3, pos[1] = 0.7
133         In these cases, we insert dummy entries to ensure that the final data
134         will be bracketed by [0, 1].
135         i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
136 
137         Thus colorCount (the caller's value, and fColorCount (our value) may
138         differ by up to 2. In the above example:
139             colorCount = 2
140             fColorCount = 4
141      */
142     fColorCount = desc.fCount;
143     // check if we need to add in dummy start and/or end position/colors
144     bool dummyFirst = false;
145     bool dummyLast = false;
146     if (desc.fPos) {
147         dummyFirst = desc.fPos[0] != 0;
148         dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
149         fColorCount += dummyFirst + dummyLast;
150     }
151 
152     size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0));
153     fOrigColors4f      = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
154     fOrigPos           = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount)
155                                    : nullptr;
156 
157     // Now copy over the colors, adding the dummies as needed
158     SkColor4f* origColors = fOrigColors4f;
159     if (dummyFirst) {
160         *origColors++ = desc.fColors[0];
161     }
162     for (int i = 0; i < desc.fCount; ++i) {
163         origColors[i] = desc.fColors[i];
164         fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
165     }
166     if (dummyLast) {
167         origColors += desc.fCount;
168         *origColors = desc.fColors[desc.fCount - 1];
169     }
170 
171     if (!desc.fColorSpace) {
172         // This happens if we were constructed from SkColors, so our colors are really sRGB
173         fColorSpace = SkColorSpace::MakeSRGBLinear();
174     } else {
175         // The color space refers to the float colors, so it must be linear gamma
176         // TODO: GPU code no longer requires this (see GrGradientEffect). Remove this restriction?
177         SkASSERT(desc.fColorSpace->gammaIsLinear());
178         fColorSpace = desc.fColorSpace;
179     }
180 
181     if (desc.fPos) {
182         SkScalar prev = 0;
183         SkScalar* origPosPtr = fOrigPos;
184         *origPosPtr++ = prev; // force the first pos to 0
185 
186         int startIndex = dummyFirst ? 0 : 1;
187         int count = desc.fCount + dummyLast;
188 
189         bool uniformStops = true;
190         const SkScalar uniformStep = desc.fPos[startIndex] - prev;
191         for (int i = startIndex; i < count; i++) {
192             // Pin the last value to 1.0, and make sure pos is monotonic.
193             auto curr = (i == desc.fCount) ? 1 : SkScalarPin(desc.fPos[i], prev, 1);
194             uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
195 
196             *origPosPtr++ = prev = curr;
197         }
198 
199         // If the stops are uniform, treat them as implicit.
200         if (uniformStops) {
201             fOrigPos = nullptr;
202         }
203     }
204 }
205 
~SkGradientShaderBase()206 SkGradientShaderBase::~SkGradientShaderBase() {}
207 
flatten(SkWriteBuffer & buffer) const208 void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
209     Descriptor desc;
210     desc.fColors = fOrigColors4f;
211     desc.fColorSpace = fColorSpace;
212     desc.fPos = fOrigPos;
213     desc.fCount = fColorCount;
214     desc.fTileMode = fTileMode;
215     desc.fGradFlags = fGradFlags;
216 
217     const SkMatrix& m = this->getLocalMatrix();
218     desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
219     desc.flatten(buffer);
220 }
221 
add_stop_color(SkJumper_GradientCtx * ctx,size_t stop,SkPM4f Fs,SkPM4f Bs)222 static void add_stop_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f Fs, SkPM4f Bs) {
223     (ctx->fs[0])[stop] = Fs.r();
224     (ctx->fs[1])[stop] = Fs.g();
225     (ctx->fs[2])[stop] = Fs.b();
226     (ctx->fs[3])[stop] = Fs.a();
227     (ctx->bs[0])[stop] = Bs.r();
228     (ctx->bs[1])[stop] = Bs.g();
229     (ctx->bs[2])[stop] = Bs.b();
230     (ctx->bs[3])[stop] = Bs.a();
231 }
232 
add_const_color(SkJumper_GradientCtx * ctx,size_t stop,SkPM4f color)233 static void add_const_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f color) {
234     add_stop_color(ctx, stop, SkPM4f::FromPremulRGBA(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(SkJumper_GradientCtx * ctx,float gapCount,size_t stop,SkPM4f c_l,SkPM4f c_r)239 static void init_stop_evenly(
240     SkJumper_GradientCtx* ctx, float gapCount, size_t stop, SkPM4f c_l, SkPM4f c_r) {
241     // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
242     SkPM4f Fs = {{
243         (c_r.r() - c_l.r()) * gapCount,
244         (c_r.g() - c_l.g()) * gapCount,
245         (c_r.b() - c_l.b()) * gapCount,
246         (c_r.a() - c_l.a()) * gapCount,
247     }};
248     SkPM4f Bs = {{
249         c_l.r() - Fs.r()*(stop/gapCount),
250         c_l.g() - Fs.g()*(stop/gapCount),
251         c_l.b() - Fs.b()*(stop/gapCount),
252         c_l.a() - Fs.a()*(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(SkJumper_GradientCtx * ctx,size_t stop,float t_l,float t_r,SkPM4f c_l,SkPM4f c_r)259 static void init_stop_pos(
260     SkJumper_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPM4f c_l, SkPM4f c_r) {
261     // See note about Clankium's old compiler in init_stop_evenly().
262     SkPM4f Fs = {{
263         (c_r.r() - c_l.r()) / (t_r - t_l),
264         (c_r.g() - c_l.g()) / (t_r - t_l),
265         (c_r.b() - c_l.b()) / (t_r - t_l),
266         (c_r.a() - c_l.a()) / (t_r - t_l),
267     }};
268     SkPM4f Bs = {{
269         c_l.r() - Fs.r()*t_l,
270         c_l.g() - Fs.g()*t_l,
271         c_l.b() - Fs.b()*t_l,
272         c_l.a() - Fs.a()*t_l,
273     }};
274     ctx->ts[stop] = t_l;
275     add_stop_color(ctx, stop, Fs, Bs);
276 }
277 
onAppendStages(const StageRec & rec) const278 bool SkGradientShaderBase::onAppendStages(const StageRec& rec) const {
279     SkRasterPipeline* p = rec.fPipeline;
280     SkArenaAlloc* alloc = rec.fAlloc;
281     SkColorSpace* dstCS = rec.fDstCS;
282     SkJumper_DecalTileCtx* decal_ctx = nullptr;
283 
284     SkMatrix matrix;
285     if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
286         return false;
287     }
288     matrix.postConcat(fPtsToUnit);
289 
290     SkRasterPipeline_<256> postPipeline;
291 
292     p->append_seed_shader();
293     p->append_matrix(alloc, matrix);
294     this->appendGradientStages(alloc, p, &postPipeline);
295 
296     switch(fTileMode) {
297         case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x_1); break;
298         case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x_1); break;
299         case kDecal_TileMode:
300             decal_ctx = alloc->make<SkJumper_DecalTileCtx>();
301             decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
302             // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
303             p->append(SkRasterPipeline::decal_x, decal_ctx);
304             // fall-through to clamp
305         case kClamp_TileMode:
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     auto prepareColor = [premulGrad, dstCS, this](int i) {
318         SkColor4f c = this->getXformedColor(i, dstCS);
319         return premulGrad ? c.premul()
320                           : SkPM4f::From4f(Sk4f::Load(&c));
321     };
322 
323     // The two-stop case with stops at 0 and 1.
324     if (fColorCount == 2 && fOrigPos == nullptr) {
325         const SkPM4f c_l = prepareColor(0),
326                      c_r = prepareColor(1);
327 
328         // See F and B below.
329         auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
330         f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
331         f_and_b[1] = c_l;
332 
333         p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, f_and_b);
334     } else {
335         auto* ctx = alloc->make<SkJumper_GradientCtx>();
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             SkPM4f c_l = prepareColor(0);
352             for (size_t i = 0; i < stopCount - 1; i++) {
353                 SkPM4f 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             SkPM4f 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                 SkPM4f 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() != SkShader::kDecal_TileMode);
420 }
421 
onIsRasterPipelineOnly(const SkMatrix & ctm) const422 bool SkGradientShaderBase::onIsRasterPipelineOnly(const SkMatrix& ctm) const {
423     if (this->getTileMode() == SkShader::kDecal_TileMode) {
424         return true;
425     }
426     return this->INHERITED::onIsRasterPipelineOnly(ctm);
427 }
428 
rounded_divide(unsigned numer,unsigned denom)429 static unsigned rounded_divide(unsigned numer, unsigned denom) {
430     return (numer + (denom >> 1)) / denom;
431 }
432 
onAsLuminanceColor(SkColor * lum) const433 bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
434     // we just compute an average color.
435     // possibly we could weight this based on the proportional width for each color
436     //   assuming they are not evenly distributed in the fPos array.
437     int r = 0;
438     int g = 0;
439     int b = 0;
440     const int n = fColorCount;
441     // TODO: use linear colors?
442     for (int i = 0; i < n; ++i) {
443         SkColor c = this->getLegacyColor(i);
444         r += SkColorGetR(c);
445         g += SkColorGetG(c);
446         b += SkColorGetB(c);
447     }
448     *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
449     return true;
450 }
451 
AutoXformColors(const SkGradientShaderBase & grad,SkColorSpaceXformer * xformer)452 SkGradientShaderBase::AutoXformColors::AutoXformColors(const SkGradientShaderBase& grad,
453                                                        SkColorSpaceXformer* xformer)
454     : fColors(grad.fColorCount) {
455     // TODO: stay in 4f to preserve precision?
456 
457     SkAutoSTMalloc<8, SkColor> origColors(grad.fColorCount);
458     for (int i = 0; i < grad.fColorCount; ++i) {
459         origColors[i] = grad.getLegacyColor(i);
460     }
461 
462     xformer->apply(fColors.get(), origColors.get(), grad.fColorCount);
463 }
464 
465 static constexpr int kGradientTextureSize = 256;
466 
initLinearBitmap(SkBitmap * bitmap,GradientBitmapType bitmapType) const467 void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType bitmapType) const {
468     const bool interpInPremul = SkToBool(fGradFlags &
469                                          SkGradientShader::kInterpolateColorsInPremul_Flag);
470     SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels());
471     uint32_t* pixels32 = reinterpret_cast<uint32_t*>(bitmap->getPixels());
472 
473     typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t;
474 
475     pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) {
476         Sk4h c = SkFloatToHalf_finite_ftz(x);
477         pixelsF16[4*index+0] = c[0];
478         pixelsF16[4*index+1] = c[1];
479         pixelsF16[4*index+2] = c[2];
480         pixelsF16[4*index+3] = c[3];
481     };
482     pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) {
483         pixels32[index] = Sk4f_toS32(c);
484     };
485     pixelWriteFn_t writeL32Pixel = [&](const Sk4f& c, int index) {
486         pixels32[index] = Sk4f_toL32(c);
487     };
488 
489     pixelWriteFn_t writeSizedPixel =
490         (bitmapType == GradientBitmapType::kHalfFloat) ? writeF16Pixel :
491         (bitmapType == GradientBitmapType::kSRGB     ) ? writeS32Pixel : writeL32Pixel;
492     pixelWriteFn_t writeUnpremulPixel = [&](const Sk4f& c, int index) {
493         writeSizedPixel(c * Sk4f(c[3], c[3], c[3], 1.0f), index);
494     };
495 
496     pixelWriteFn_t writePixel = interpInPremul ? writeSizedPixel : writeUnpremulPixel;
497 
498     // When not in legacy mode, we just want the original 4f colors - so we pass in
499     // our own CS for identity/no transform.
500     auto* cs = bitmapType != GradientBitmapType::kLegacy ? fColorSpace.get() : nullptr;
501 
502     int prevIndex = 0;
503     for (int i = 1; i < fColorCount; i++) {
504         // Historically, stops have been mapped to [0, 256], with 256 then nudged to the
505         // next smaller value, then truncate for the texture index. This seems to produce
506         // the best results for some common distributions, so we preserve the behavior.
507         int nextIndex = SkTMin(this->getPos(i) * kGradientTextureSize,
508                                SkIntToScalar(kGradientTextureSize - 1));
509 
510         if (nextIndex > prevIndex) {
511             SkColor4f color0 = this->getXformedColor(i - 1, cs),
512                       color1 = this->getXformedColor(i    , cs);
513             Sk4f          c0 = Sk4f::Load(color0.vec()),
514                           c1 = Sk4f::Load(color1.vec());
515 
516             if (interpInPremul) {
517                 c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f);
518                 c1 = c1 * Sk4f(c1[3], c1[3], c1[3], 1.0f);
519             }
520 
521             Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex));
522             Sk4f delta = (c1 - c0) * step;
523 
524             for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) {
525                 writePixel(c0, curIndex);
526                 c0 += delta;
527             }
528         }
529         prevIndex = nextIndex;
530     }
531     SkASSERT(prevIndex == kGradientTextureSize - 1);
532 }
533 
getXformedColor(size_t i,SkColorSpace * dstCS) const534 SkColor4f SkGradientShaderBase::getXformedColor(size_t i, SkColorSpace* dstCS) const {
535     if (dstCS) {
536         return to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS);
537     }
538 
539     // Legacy/srgb color.
540     // We quantize upfront to ensure stable SkColor round-trips.
541     auto rgb255 = sk_linear_to_srgb(Sk4f::Load(fOrigColors4f[i].vec()));
542     auto rgb    = SkNx_cast<float>(rgb255) * (1/255.0f);
543     return { rgb[0], rgb[1], rgb[2], fOrigColors4f[i].fA };
544 }
545 
546 SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex);
547 /*
548  *  Because our caller might rebuild the same (logically the same) gradient
549  *  over and over, we'd like to return exactly the same "bitmap" if possible,
550  *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
551  *  To do that, we maintain a private cache of built-bitmaps, based on our
552  *  colors and positions.
553  */
getGradientTableBitmap(SkBitmap * bitmap,GradientBitmapType bitmapType) const554 void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
555                                                   GradientBitmapType bitmapType) const {
556     // build our key: [numColors + colors[] + {positions[]} + flags + colorType ]
557     static_assert(sizeof(SkColor4f) % sizeof(int32_t) == 0, "");
558     const int colorsAsIntCount = fColorCount * sizeof(SkColor4f) / sizeof(int32_t);
559     int count = 1 + colorsAsIntCount + 1 + 1;
560     if (fColorCount > 2) {
561         count += fColorCount - 1;
562     }
563 
564     SkAutoSTMalloc<64, int32_t> storage(count);
565     int32_t* buffer = storage.get();
566 
567     *buffer++ = fColorCount;
568     memcpy(buffer, fOrigColors4f, fColorCount * sizeof(SkColor4f));
569     buffer += colorsAsIntCount;
570     if (fColorCount > 2) {
571         for (int i = 1; i < fColorCount; i++) {
572             *buffer++ = SkFloat2Bits(this->getPos(i));
573         }
574     }
575     *buffer++ = fGradFlags;
576     *buffer++ = static_cast<int32_t>(bitmapType);
577     SkASSERT(buffer - storage.get() == count);
578 
579     ///////////////////////////////////
580 
581     static SkGradientBitmapCache* gCache;
582     // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp
583     static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
584     SkAutoMutexAcquire ama(gGradientCacheMutex);
585 
586     if (nullptr == gCache) {
587         gCache = new SkGradientBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
588     }
589     size_t size = count * sizeof(int32_t);
590 
591     if (!gCache->find(storage.get(), size, bitmap)) {
592         // For these cases we use the bitmap cache, but not the GradientShaderCache. So just
593         // allocate and populate the bitmap's data directly.
594 
595         SkImageInfo info;
596         switch (bitmapType) {
597         case GradientBitmapType::kLegacy:
598             info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_8888_SkColorType,
599                                      kPremul_SkAlphaType);
600             break;
601         case GradientBitmapType::kSRGB:
602             info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_8888_SkColorType,
603                                      kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
604             break;
605         case GradientBitmapType::kHalfFloat:
606             info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_F16_SkColorType,
607                                      kPremul_SkAlphaType, SkColorSpace::MakeSRGBLinear());
608             break;
609         }
610 
611         bitmap->allocPixels(info);
612         this->initLinearBitmap(bitmap, bitmapType);
613         bitmap->setImmutable();
614         gCache->add(storage.get(), size, *bitmap);
615     }
616 }
617 
commonAsAGradient(GradientInfo * info) const618 void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
619     if (info) {
620         if (info->fColorCount >= fColorCount) {
621             if (info->fColors) {
622                 for (int i = 0; i < fColorCount; ++i) {
623                     info->fColors[i] = this->getLegacyColor(i);
624                 }
625             }
626             if (info->fColorOffsets) {
627                 for (int i = 0; i < fColorCount; ++i) {
628                     info->fColorOffsets[i] = this->getPos(i);
629                 }
630             }
631         }
632         info->fColorCount = fColorCount;
633         info->fTileMode = fTileMode;
634         info->fGradientFlags = fGradFlags;
635     }
636 }
637 
638 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const639 void SkGradientShaderBase::toString(SkString* str) const {
640 
641     str->appendf("%d colors: ", fColorCount);
642 
643     for (int i = 0; i < fColorCount; ++i) {
644         str->appendHex(this->getLegacyColor(i), 8);
645         if (i < fColorCount-1) {
646             str->append(", ");
647         }
648     }
649 
650     if (fColorCount > 2) {
651         str->append(" points: (");
652         for (int i = 0; i < fColorCount; ++i) {
653             str->appendScalar(this->getPos(i));
654             if (i < fColorCount-1) {
655                 str->append(", ");
656             }
657         }
658         str->append(")");
659     }
660 
661     static const char* gTileModeName[SkShader::kTileModeCount] = {
662         "clamp", "repeat", "mirror", "decal",
663     };
664 
665     str->append(" ");
666     str->append(gTileModeName[fTileMode]);
667 
668     this->INHERITED::toString(str);
669 }
670 #endif
671 
672 ///////////////////////////////////////////////////////////////////////////////
673 ///////////////////////////////////////////////////////////////////////////////
674 
675 // Return true if these parameters are valid/legal/safe to construct a gradient
676 //
valid_grad(const SkColor4f colors[],const SkScalar pos[],int count,unsigned tileMode)677 static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
678                        unsigned tileMode) {
679     return nullptr != colors && count >= 1 && tileMode < (unsigned)SkShader::kTileModeCount;
680 }
681 
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)682 static void desc_init(SkGradientShaderBase::Descriptor* desc,
683                       const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
684                       const SkScalar pos[], int colorCount,
685                       SkShader::TileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
686     SkASSERT(colorCount > 1);
687 
688     desc->fColors       = colors;
689     desc->fColorSpace   = std::move(colorSpace);
690     desc->fPos          = pos;
691     desc->fCount        = colorCount;
692     desc->fTileMode     = mode;
693     desc->fGradFlags    = flags;
694     desc->fLocalMatrix  = localMatrix;
695 }
696 
697 // assumes colors is SkColor4f* and pos is SkScalar*
698 #define EXPAND_1_COLOR(count)                \
699      SkColor4f tmp[2];                       \
700      do {                                    \
701          if (1 == count) {                   \
702              tmp[0] = tmp[1] = colors[0];    \
703              colors = tmp;                   \
704              pos = nullptr;                  \
705              count = 2;                      \
706          }                                   \
707      } while (0)
708 
709 struct ColorStopOptimizer {
ColorStopOptimizerColorStopOptimizer710     ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos,
711                        int count, SkShader::TileMode mode)
712         : fColors(colors)
713         , fPos(pos)
714         , fCount(count) {
715 
716             if (!pos || count != 3) {
717                 return;
718             }
719 
720             if (SkScalarNearlyEqual(pos[0], 0.0f) &&
721                 SkScalarNearlyEqual(pos[1], 0.0f) &&
722                 SkScalarNearlyEqual(pos[2], 1.0f)) {
723 
724                 if (SkShader::kRepeat_TileMode == mode ||
725                     SkShader::kMirror_TileMode == mode ||
726                     colors[0] == colors[1]) {
727 
728                     // Ignore the leftmost color/pos.
729                     fColors += 1;
730                     fPos    += 1;
731                     fCount   = 2;
732                 }
733             } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
734                        SkScalarNearlyEqual(pos[1], 1.0f) &&
735                        SkScalarNearlyEqual(pos[2], 1.0f)) {
736 
737                 if (SkShader::kRepeat_TileMode == mode ||
738                     SkShader::kMirror_TileMode == mode ||
739                     colors[1] == colors[2]) {
740 
741                     // Ignore the rightmost color/pos.
742                     fCount  = 2;
743                 }
744             }
745     }
746 
747     const SkColor4f* fColors;
748     const SkScalar*  fPos;
749     int              fCount;
750 };
751 
752 struct ColorConverter {
ColorConverterColorConverter753     ColorConverter(const SkColor* colors, int count) {
754         for (int i = 0; i < count; ++i) {
755             fColors4f.push_back(SkColor4f::FromColor(colors[i]));
756         }
757     }
758 
759     SkSTArray<2, SkColor4f, true> fColors4f;
760 };
761 
MakeLinear(const SkPoint pts[2],const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,uint32_t flags,const SkMatrix * localMatrix)762 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
763                                              const SkColor colors[],
764                                              const SkScalar pos[], int colorCount,
765                                              SkShader::TileMode mode,
766                                              uint32_t flags,
767                                              const SkMatrix* localMatrix) {
768     ColorConverter converter(colors, colorCount);
769     return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
770                       localMatrix);
771 }
772 
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)773 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
774                                              const SkColor4f colors[],
775                                              sk_sp<SkColorSpace> colorSpace,
776                                              const SkScalar pos[], int colorCount,
777                                              SkShader::TileMode mode,
778                                              uint32_t flags,
779                                              const SkMatrix* localMatrix) {
780     if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
781         return nullptr;
782     }
783     if (!valid_grad(colors, pos, colorCount, mode)) {
784         return nullptr;
785     }
786     if (1 == colorCount) {
787         return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
788     }
789     if (localMatrix && !localMatrix->invert(nullptr)) {
790         return nullptr;
791     }
792 
793     ColorStopOptimizer opt(colors, pos, colorCount, mode);
794 
795     SkGradientShaderBase::Descriptor desc;
796     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
797               localMatrix);
798     return sk_make_sp<SkLinearGradient>(pts, desc);
799 }
800 
MakeRadial(const SkPoint & center,SkScalar radius,const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,uint32_t flags,const SkMatrix * localMatrix)801 sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
802                                              const SkColor colors[],
803                                              const SkScalar pos[], int colorCount,
804                                              SkShader::TileMode mode,
805                                              uint32_t flags,
806                                              const SkMatrix* localMatrix) {
807     ColorConverter converter(colors, colorCount);
808     return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
809                       flags, localMatrix);
810 }
811 
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)812 sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
813                                              const SkColor4f colors[],
814                                              sk_sp<SkColorSpace> colorSpace,
815                                              const SkScalar pos[], int colorCount,
816                                              SkShader::TileMode mode,
817                                              uint32_t flags,
818                                              const SkMatrix* localMatrix) {
819     if (radius <= 0) {
820         return nullptr;
821     }
822     if (!valid_grad(colors, pos, colorCount, mode)) {
823         return nullptr;
824     }
825     if (1 == colorCount) {
826         return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
827     }
828     if (localMatrix && !localMatrix->invert(nullptr)) {
829         return nullptr;
830     }
831 
832     ColorStopOptimizer opt(colors, pos, colorCount, mode);
833 
834     SkGradientShaderBase::Descriptor desc;
835     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
836               localMatrix);
837     return sk_make_sp<SkRadialGradient>(center, radius, desc);
838 }
839 
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)840 sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
841                                                       SkScalar startRadius,
842                                                       const SkPoint& end,
843                                                       SkScalar endRadius,
844                                                       const SkColor colors[],
845                                                       const SkScalar pos[],
846                                                       int colorCount,
847                                                       SkShader::TileMode mode,
848                                                       uint32_t flags,
849                                                       const SkMatrix* localMatrix) {
850     ColorConverter converter(colors, colorCount);
851     return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
852                                nullptr, pos, colorCount, mode, flags, localMatrix);
853 }
854 
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)855 sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
856                                                       SkScalar startRadius,
857                                                       const SkPoint& end,
858                                                       SkScalar endRadius,
859                                                       const SkColor4f colors[],
860                                                       sk_sp<SkColorSpace> colorSpace,
861                                                       const SkScalar pos[],
862                                                       int colorCount,
863                                                       SkShader::TileMode mode,
864                                                       uint32_t flags,
865                                                       const SkMatrix* localMatrix) {
866     if (startRadius < 0 || endRadius < 0) {
867         return nullptr;
868     }
869     if (SkScalarNearlyZero((start - end).length()) && SkScalarNearlyZero(startRadius)) {
870         // We can treat this gradient as radial, which is faster.
871         return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
872                           mode, flags, localMatrix);
873     }
874     if (!valid_grad(colors, pos, colorCount, mode)) {
875         return nullptr;
876     }
877     if (startRadius == endRadius) {
878         if (start == end || startRadius == 0) {
879             return SkShader::MakeEmptyShader();
880         }
881     }
882     if (localMatrix && !localMatrix->invert(nullptr)) {
883         return nullptr;
884     }
885     EXPAND_1_COLOR(colorCount);
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     return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc);
893 }
894 
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)895 sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
896                                             const SkColor colors[],
897                                             const SkScalar pos[],
898                                             int colorCount,
899                                             SkShader::TileMode mode,
900                                             SkScalar startAngle,
901                                             SkScalar endAngle,
902                                             uint32_t flags,
903                                             const SkMatrix* localMatrix) {
904     ColorConverter converter(colors, colorCount);
905     return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount,
906                      mode, startAngle, endAngle, flags, localMatrix);
907 }
908 
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)909 sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
910                                             const SkColor4f colors[],
911                                             sk_sp<SkColorSpace> colorSpace,
912                                             const SkScalar pos[],
913                                             int colorCount,
914                                             SkShader::TileMode mode,
915                                             SkScalar startAngle,
916                                             SkScalar endAngle,
917                                             uint32_t flags,
918                                             const SkMatrix* localMatrix) {
919     if (!valid_grad(colors, pos, colorCount, mode)) {
920         return nullptr;
921     }
922     if (1 == colorCount) {
923         return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
924     }
925     if (startAngle >= endAngle) {
926         return nullptr;
927     }
928     if (localMatrix && !localMatrix->invert(nullptr)) {
929         return nullptr;
930     }
931 
932     if (startAngle <= 0 && endAngle >= 360) {
933         // If the t-range includes [0,1], then we can always use clamping (presumably faster).
934         mode = SkShader::kClamp_TileMode;
935     }
936 
937     ColorStopOptimizer opt(colors, pos, colorCount, mode);
938 
939     SkGradientShaderBase::Descriptor desc;
940     desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
941               localMatrix);
942 
943     const SkScalar t0 = startAngle / 360,
944                    t1 =   endAngle / 360;
945 
946     return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc);
947 }
948 
949 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient)950     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient)
951     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialGradient)
952     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSweepGradient)
953     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointConicalGradient)
954 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
955 
956 ///////////////////////////////////////////////////////////////////////////////
957 
958 #if SK_SUPPORT_GPU
959 
960 #include "GrColorSpaceXform.h"
961 #include "GrContext.h"
962 #include "GrContextPriv.h"
963 #include "GrShaderCaps.h"
964 #include "GrTextureStripAtlas.h"
965 #include "gl/GrGLContext.h"
966 #include "glsl/GrGLSLFragmentShaderBuilder.h"
967 #include "glsl/GrGLSLProgramDataManager.h"
968 #include "glsl/GrGLSLUniformHandler.h"
969 #include "SkGr.h"
970 
971 void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler,
972                                                    const GrGradientEffect& ge) {
973     switch (ge.fStrategy) {
974         case GrGradientEffect::InterpolationStrategy::kThreshold:
975         case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
976         case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
977             fThresholdUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
978                                                        kFloat_GrSLType,
979                                                        kHigh_GrSLPrecision,
980                                                        "Threshold");
981             // fall through
982         case GrGradientEffect::InterpolationStrategy::kSingle:
983             fIntervalsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
984                                                             kHalf4_GrSLType,
985                                                             "Intervals",
986                                                             ge.fIntervals.count());
987             break;
988         case GrGradientEffect::InterpolationStrategy::kTexture:
989             fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
990                                                  "GradientYCoordFS");
991             break;
992     }
993 }
994 
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & processor)995 void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
996                                                 const GrFragmentProcessor& processor) {
997     const GrGradientEffect& e = processor.cast<GrGradientEffect>();
998 
999     switch (e.fStrategy) {
1000         case GrGradientEffect::InterpolationStrategy::kThreshold:
1001         case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
1002         case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
1003             pdman.set1f(fThresholdUni, e.fThreshold);
1004             // fall through
1005         case GrGradientEffect::InterpolationStrategy::kSingle:
1006             pdman.set4fv(fIntervalsUni, e.fIntervals.count(),
1007                          reinterpret_cast<const float*>(e.fIntervals.begin()));
1008             break;
1009         case GrGradientEffect::InterpolationStrategy::kTexture:
1010             if (e.fYCoord != fCachedYCoord) {
1011                 pdman.set1f(fFSYUni, e.fYCoord);
1012                 fCachedYCoord = e.fYCoord;
1013             }
1014             break;
1015     }
1016 }
1017 
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const1018 void GrGradientEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
1019     b->add32(GLSLProcessor::GenBaseGradientKey(*this));
1020 }
1021 
GenBaseGradientKey(const GrProcessor & processor)1022 uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) {
1023     const GrGradientEffect& e = processor.cast<GrGradientEffect>();
1024 
1025     // Build a key using the following bit allocation:
1026                 static constexpr uint32_t kStrategyBits = 3;
1027                 static constexpr uint32_t kPremulBits   = 1;
1028     SkDEBUGCODE(static constexpr uint32_t kWrapModeBits = 2;)
1029 
1030     uint32_t key = static_cast<uint32_t>(e.fStrategy);
1031     SkASSERT(key < (1 << kStrategyBits));
1032 
1033     // This is already baked into the table for texture gradients,
1034     // and only changes behavior for analytical gradients.
1035     if (e.fStrategy != InterpolationStrategy::kTexture &&
1036         e.fPremulType == GrGradientEffect::kBeforeInterp_PremulType) {
1037         key |= 1 << kStrategyBits;
1038         SkASSERT(key < (1 << (kStrategyBits + kPremulBits)));
1039     }
1040 
1041     key |= static_cast<uint32_t>(e.fWrapMode) << (kStrategyBits + kPremulBits);
1042     SkASSERT(key < (1 << (kStrategyBits + kPremulBits + kWrapModeBits)));
1043 
1044     return key;
1045 }
1046 
emitAnalyticalColor(GrGLSLFPFragmentBuilder * fragBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps * shaderCaps,const GrGradientEffect & ge,const char * t,const char * outputColor,const char * inputColor)1047 void GrGradientEffect::GLSLProcessor::emitAnalyticalColor(GrGLSLFPFragmentBuilder* fragBuilder,
1048                                                           GrGLSLUniformHandler* uniformHandler,
1049                                                           const GrShaderCaps* shaderCaps,
1050                                                           const GrGradientEffect& ge,
1051                                                           const char* t,
1052                                                           const char* outputColor,
1053                                                           const char* inputColor) {
1054     // First, apply tiling rules.
1055     switch (ge.fWrapMode) {
1056         case GrSamplerState::WrapMode::kClamp:
1057             switch (ge.fStrategy) {
1058                 case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
1059                     // allow t > 1, in order to hit the clamp interval (1, inf)
1060                     fragBuilder->codeAppendf("half tiled_t = max(%s, 0.0);", t);
1061                     break;
1062                 case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
1063                     // allow t < 0, in order to hit the clamp interval (-inf, 0)
1064                     fragBuilder->codeAppendf("half tiled_t = min(%s, 1.0);", t);
1065                     break;
1066                 default:
1067                     // regular [0, 1] clamping
1068                     fragBuilder->codeAppendf("half tiled_t = clamp(%s, 0.0, 1.0);", t);
1069             }
1070             break;
1071         case GrSamplerState::WrapMode::kRepeat:
1072             fragBuilder->codeAppendf("half tiled_t = fract(%s);", t);
1073             break;
1074         case GrSamplerState::WrapMode::kMirrorRepeat:
1075             fragBuilder->codeAppendf("half t_1 = %s - 1.0;", t);
1076             fragBuilder->codeAppendf("half tiled_t = t_1 - 2.0 * floor(t_1 * 0.5) - 1.0;");
1077             if (shaderCaps->mustDoOpBetweenFloorAndAbs()) {
1078                 // At this point the expected value of tiled_t should between -1 and 1, so this
1079                 // clamp has no effect other than to break up the floor and abs calls and make sure
1080                 // the compiler doesn't merge them back together.
1081                 fragBuilder->codeAppendf("tiled_t = clamp(tiled_t, -1.0, 1.0);");
1082             }
1083             fragBuilder->codeAppendf("tiled_t = abs(tiled_t);");
1084             break;
1085     }
1086 
1087     // Calculate the color.
1088     const char* intervals = uniformHandler->getUniformCStr(fIntervalsUni);
1089 
1090     switch (ge.fStrategy) {
1091         case GrGradientEffect::InterpolationStrategy::kSingle:
1092             SkASSERT(ge.fIntervals.count() == 2);
1093             fragBuilder->codeAppendf(
1094                 "half4 color_scale = %s[0],"
1095                 "      color_bias  = %s[1];"
1096                 , intervals, intervals
1097             );
1098             break;
1099         case GrGradientEffect::InterpolationStrategy::kThreshold:
1100         case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
1101         case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
1102         {
1103             SkASSERT(ge.fIntervals.count() == 4);
1104             const char* threshold = uniformHandler->getUniformCStr(fThresholdUni);
1105             fragBuilder->codeAppendf(
1106                 "half4 color_scale, color_bias;"
1107                 "if (tiled_t < %s) {"
1108                 "    color_scale = %s[0];"
1109                 "    color_bias  = %s[1];"
1110                 "} else {"
1111                 "    color_scale = %s[2];"
1112                 "    color_bias  = %s[3];"
1113                 "}"
1114                 , threshold, intervals, intervals, intervals, intervals
1115             );
1116         }   break;
1117         default:
1118             SkASSERT(false);
1119             break;
1120     }
1121 
1122     fragBuilder->codeAppend("half4 colorTemp = tiled_t * color_scale + color_bias;");
1123 
1124     // We could skip this step if all colors are known to be opaque. Two considerations:
1125     // The gradient SkShader reporting opaque is more restrictive than necessary in the two
1126     // pt case. Make sure the key reflects this optimization (and note that it can use the
1127     // same shader as the kBeforeInterp case).
1128     if (ge.fPremulType == GrGradientEffect::kAfterInterp_PremulType) {
1129         fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
1130     }
1131 
1132     // If the input colors were floats, or there was a color space xform, we may end up out of
1133     // range. The simplest solution is to always clamp our (premul) value here. We only need to
1134     // clamp RGB, but that causes hangs on the Tegra3 Nexus7. Clamping RGBA avoids the problem.
1135     fragBuilder->codeAppend("colorTemp = clamp(colorTemp, 0, colorTemp.a);");
1136 
1137     fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
1138 }
1139 
emitColor(GrGLSLFPFragmentBuilder * fragBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps * shaderCaps,const GrGradientEffect & ge,const char * gradientTValue,const char * outputColor,const char * inputColor,const TextureSamplers & texSamplers)1140 void GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
1141                                                 GrGLSLUniformHandler* uniformHandler,
1142                                                 const GrShaderCaps* shaderCaps,
1143                                                 const GrGradientEffect& ge,
1144                                                 const char* gradientTValue,
1145                                                 const char* outputColor,
1146                                                 const char* inputColor,
1147                                                 const TextureSamplers& texSamplers) {
1148     if (ge.fStrategy != InterpolationStrategy::kTexture) {
1149         this->emitAnalyticalColor(fragBuilder, uniformHandler, shaderCaps, ge, gradientTValue,
1150                                   outputColor, inputColor);
1151         return;
1152     }
1153 
1154     const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni);
1155 
1156     fragBuilder->codeAppendf("half2 coord = half2(%s, %s);", gradientTValue, fsyuni);
1157     fragBuilder->codeAppendf("%s = ", outputColor);
1158     fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord",
1159                                                 kFloat2_GrSLType);
1160     fragBuilder->codeAppend(";");
1161 }
1162 
1163 /////////////////////////////////////////////////////////////////////
1164 
OptFlags(bool isOpaque)1165 inline GrFragmentProcessor::OptimizationFlags GrGradientEffect::OptFlags(bool isOpaque) {
1166     return isOpaque
1167                    ? kPreservesOpaqueInput_OptimizationFlag |
1168                              kCompatibleWithCoverageAsAlpha_OptimizationFlag
1169                    : kCompatibleWithCoverageAsAlpha_OptimizationFlag;
1170 }
1171 
addInterval(const SkGradientShaderBase & shader,size_t idx0,size_t idx1,SkColorSpace * dstCS)1172 void GrGradientEffect::addInterval(const SkGradientShaderBase& shader, size_t idx0, size_t idx1,
1173                                    SkColorSpace* dstCS) {
1174     SkASSERT(idx0 <= idx1);
1175     const auto  c4f0 = shader.getXformedColor(idx0, dstCS),
1176                 c4f1 = shader.getXformedColor(idx1, dstCS);
1177     const auto    c0 = (fPremulType == kBeforeInterp_PremulType)
1178                      ? c4f0.premul().to4f() :  Sk4f::Load(c4f0.vec()),
1179                   c1 = (fPremulType == kBeforeInterp_PremulType)
1180                      ? c4f1.premul().to4f() :  Sk4f::Load(c4f1.vec());
1181     const auto    t0 = shader.getPos(idx0),
1182                   t1 = shader.getPos(idx1),
1183                   dt = t1 - t0;
1184     SkASSERT(dt >= 0);
1185     // dt can be 0 for clamp intervals => in this case we want a scale == 0
1186     const auto scale = SkScalarNearlyZero(dt) ? 0 : (c1 - c0) / dt,
1187                 bias = c0 - t0 * scale;
1188 
1189     // Intervals are stored as (scale, bias) tuples.
1190     SkASSERT(!(fIntervals.count() & 1));
1191     fIntervals.emplace_back(scale[0], scale[1], scale[2], scale[3]);
1192     fIntervals.emplace_back( bias[0],  bias[1],  bias[2],  bias[3]);
1193 }
1194 
GrGradientEffect(ClassID classID,const CreateArgs & args,bool isOpaque)1195 GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool isOpaque)
1196     : INHERITED(classID, OptFlags(isOpaque))
1197     , fWrapMode(args.fWrapMode)
1198     , fRow(-1)
1199     , fIsOpaque(args.fShader->isOpaque())
1200     , fStrategy(InterpolationStrategy::kTexture)
1201     , fThreshold(0) {
1202 
1203     const SkGradientShaderBase& shader(*args.fShader);
1204 
1205     fPremulType = (args.fShader->getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag)
1206                 ? kBeforeInterp_PremulType : kAfterInterp_PremulType;
1207 
1208     // First, determine the interpolation strategy and params.
1209     switch (shader.fColorCount) {
1210         case 2:
1211             SkASSERT(!shader.fOrigPos);
1212             fStrategy = InterpolationStrategy::kSingle;
1213             this->addInterval(shader, 0, 1, args.fDstColorSpace);
1214             break;
1215         case 3:
1216             fThreshold = shader.getPos(1);
1217 
1218             if (shader.fOrigPos) {
1219                 SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[0], 0));
1220                 SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[2], 1));
1221                 if (SkScalarNearlyEqual(shader.fOrigPos[1], 0)) {
1222                     // hard stop on the left edge.
1223                     if (fWrapMode == GrSamplerState::WrapMode::kClamp) {
1224                         fStrategy = InterpolationStrategy::kThresholdClamp1;
1225                         // Clamp interval (scale == 0, bias == colors[0]).
1226                         this->addInterval(shader, 0, 0, args.fDstColorSpace);
1227                     } else {
1228                         // We can ignore the hard stop when not clamping.
1229                         fStrategy = InterpolationStrategy::kSingle;
1230                     }
1231                     this->addInterval(shader, 1, 2, args.fDstColorSpace);
1232                     break;
1233                 }
1234 
1235                 if (SkScalarNearlyEqual(shader.fOrigPos[1], 1)) {
1236                     // hard stop on the right edge.
1237                     this->addInterval(shader, 0, 1, args.fDstColorSpace);
1238                     if (fWrapMode == GrSamplerState::WrapMode::kClamp) {
1239                         fStrategy = InterpolationStrategy::kThresholdClamp0;
1240                         // Clamp interval (scale == 0, bias == colors[2]).
1241                         this->addInterval(shader, 2, 2, args.fDstColorSpace);
1242                     } else {
1243                         // We can ignore the hard stop when not clamping.
1244                         fStrategy = InterpolationStrategy::kSingle;
1245                     }
1246                     break;
1247                 }
1248             }
1249 
1250             // Two arbitrary interpolation intervals.
1251             fStrategy = InterpolationStrategy::kThreshold;
1252             this->addInterval(shader, 0, 1, args.fDstColorSpace);
1253             this->addInterval(shader, 1, 2, args.fDstColorSpace);
1254             break;
1255         case 4:
1256             if (shader.fOrigPos && SkScalarNearlyEqual(shader.fOrigPos[1], shader.fOrigPos[2])) {
1257                 SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[0], 0));
1258                 SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[3], 1));
1259 
1260                 // Single hard stop => two arbitrary interpolation intervals.
1261                 fStrategy = InterpolationStrategy::kThreshold;
1262                 fThreshold = shader.getPos(1);
1263                 this->addInterval(shader, 0, 1, args.fDstColorSpace);
1264                 this->addInterval(shader, 2, 3, args.fDstColorSpace);
1265             }
1266             break;
1267         default:
1268             break;
1269     }
1270 
1271     // Now that we've locked down a strategy, adjust any dependent params.
1272     if (fStrategy != InterpolationStrategy::kTexture) {
1273         // Analytical cases.
1274         fCoordTransform.reset(*args.fMatrix);
1275     } else {
1276         SkGradientShaderBase::GradientBitmapType bitmapType =
1277             SkGradientShaderBase::GradientBitmapType::kLegacy;
1278         if (args.fDstColorSpace) {
1279             // Try to use F16 if we can
1280             if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
1281                 bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat;
1282             } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
1283                 bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB;
1284             } else {
1285                 // This can happen, but only if someone explicitly creates an unsupported
1286                 // (eg sRGB) surface. Just fall back to legacy behavior.
1287             }
1288         }
1289 
1290         SkBitmap bitmap;
1291         shader.getGradientTableBitmap(&bitmap, bitmapType);
1292         SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
1293 
1294 
1295         GrTextureStripAtlas::Desc desc;
1296         desc.fWidth  = bitmap.width();
1297         desc.fHeight = 32;
1298         desc.fRowHeight = bitmap.height(); // always 1 here
1299         desc.fContext = args.fContext;
1300         desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps());
1301         fAtlas = GrTextureStripAtlas::GetAtlas(desc);
1302         SkASSERT(fAtlas);
1303 
1304         // We always filter the gradient table. Each table is one row of a texture, always
1305         // y-clamp.
1306         GrSamplerState samplerState(args.fWrapMode, GrSamplerState::Filter::kBilerp);
1307 
1308         fRow = fAtlas->lockRow(bitmap);
1309         if (-1 != fRow) {
1310             fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
1311             // This is 1/2 places where auto-normalization is disabled
1312             fCoordTransform.reset(*args.fMatrix, fAtlas->asTextureProxyRef().get(), false);
1313             fTextureSampler.reset(fAtlas->asTextureProxyRef(), samplerState);
1314         } else {
1315             // In this instance we know the samplerState state is:
1316             //   clampY, bilerp
1317             // and the proxy is:
1318             //   exact fit, power of two in both dimensions
1319             // Only the x-tileMode is unknown. However, given all the other knowns we know
1320             // that GrMakeCachedImageProxy is sufficient (i.e., it won't need to be
1321             // extracted to a subset or mipmapped).
1322 
1323             SkASSERT(bitmap.isImmutable());
1324             sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap);
1325             if (!srcImage) {
1326                 return;
1327             }
1328 
1329             sk_sp<GrTextureProxy> proxy = GrMakeCachedImageProxy(
1330                                                      args.fContext->contextPriv().proxyProvider(),
1331                                                      std::move(srcImage));
1332             if (!proxy) {
1333                 SkDebugf("Gradient won't draw. Could not create texture.");
1334                 return;
1335             }
1336             // This is 2/2 places where auto-normalization is disabled
1337             fCoordTransform.reset(*args.fMatrix, proxy.get(), false);
1338             fTextureSampler.reset(std::move(proxy), samplerState);
1339             fYCoord = SK_ScalarHalf;
1340         }
1341 
1342         this->addTextureSampler(&fTextureSampler);
1343     }
1344 
1345     this->addCoordTransform(&fCoordTransform);
1346 }
1347 
GrGradientEffect(const GrGradientEffect & that)1348 GrGradientEffect::GrGradientEffect(const GrGradientEffect& that)
1349         : INHERITED(that.classID(), OptFlags(that.fIsOpaque))
1350         , fIntervals(that.fIntervals)
1351         , fWrapMode(that.fWrapMode)
1352         , fCoordTransform(that.fCoordTransform)
1353         , fTextureSampler(that.fTextureSampler)
1354         , fYCoord(that.fYCoord)
1355         , fAtlas(that.fAtlas)
1356         , fRow(that.fRow)
1357         , fIsOpaque(that.fIsOpaque)
1358         , fStrategy(that.fStrategy)
1359         , fThreshold(that.fThreshold)
1360         , fPremulType(that.fPremulType) {
1361     this->addCoordTransform(&fCoordTransform);
1362     if (fStrategy == InterpolationStrategy::kTexture) {
1363         this->addTextureSampler(&fTextureSampler);
1364     }
1365     if (this->useAtlas()) {
1366         fAtlas->lockRow(fRow);
1367     }
1368 }
1369 
~GrGradientEffect()1370 GrGradientEffect::~GrGradientEffect() {
1371     if (this->useAtlas()) {
1372         fAtlas->unlockRow(fRow);
1373     }
1374 }
1375 
onIsEqual(const GrFragmentProcessor & processor) const1376 bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
1377     const GrGradientEffect& ge = processor.cast<GrGradientEffect>();
1378 
1379     if (fWrapMode != ge.fWrapMode || fStrategy != ge.fStrategy) {
1380         return false;
1381     }
1382 
1383     SkASSERT(this->useAtlas() == ge.useAtlas());
1384     if (fStrategy == InterpolationStrategy::kTexture) {
1385         if (fYCoord != ge.fYCoord) {
1386             return false;
1387         }
1388     } else {
1389         if (fThreshold != ge.fThreshold ||
1390             fIntervals != ge.fIntervals ||
1391             fPremulType != ge.fPremulType) {
1392             return false;
1393         }
1394     }
1395     return true;
1396 }
1397 
1398 #if GR_TEST_UTILS
RandomGradientParams(SkRandom * random)1399 GrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) {
1400     // Set color count to min of 2 so that we don't trigger the const color optimization and make
1401     // a non-gradient processor.
1402     fColorCount = random->nextRangeU(2, kMaxRandomGradientColors);
1403     fUseColors4f = random->nextBool();
1404 
1405     // if one color, omit stops, otherwise randomly decide whether or not to
1406     if (fColorCount == 1 || (fColorCount >= 2 && random->nextBool())) {
1407         fStops = nullptr;
1408     } else {
1409         fStops = fStopStorage;
1410     }
1411 
1412     // if using SkColor4f, attach a random (possibly null) color space (with linear gamma)
1413     if (fUseColors4f) {
1414         fColorSpace = GrTest::TestColorSpace(random);
1415         if (fColorSpace) {
1416             fColorSpace = fColorSpace->makeLinearGamma();
1417         }
1418     }
1419 
1420     SkScalar stop = 0.f;
1421     for (int i = 0; i < fColorCount; ++i) {
1422         if (fUseColors4f) {
1423             fColors4f[i].fR = random->nextUScalar1();
1424             fColors4f[i].fG = random->nextUScalar1();
1425             fColors4f[i].fB = random->nextUScalar1();
1426             fColors4f[i].fA = random->nextUScalar1();
1427         } else {
1428             fColors[i] = random->nextU();
1429         }
1430         if (fStops) {
1431             fStops[i] = stop;
1432             stop = i < fColorCount - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f;
1433         }
1434     }
1435     fTileMode = static_cast<SkShader::TileMode>(random->nextULessThan(SkShader::kTileModeCount));
1436 }
1437 #endif
1438 
1439 #endif
1440