• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Google Inc.
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 "Sk4fLinearGradient.h"
9 #include "SkLinearGradient.h"
10 #include "SkRefCnt.h"
11 
12 // define to test the 4f gradient path
13 // #define FORCE_4F_CONTEXT
14 
15 static const float kInv255Float = 1.0f / 255;
16 
repeat_8bits(int x)17 static inline int repeat_8bits(int x) {
18     return x & 0xFF;
19 }
20 
mirror_8bits(int x)21 static inline int mirror_8bits(int x) {
22     if (x & 256) {
23         x = ~x;
24     }
25     return x & 255;
26 }
27 
pts_to_unit_matrix(const SkPoint pts[2])28 static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
29     SkVector    vec = pts[1] - pts[0];
30     SkScalar    mag = vec.length();
31     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
32 
33     vec.scale(inv);
34     SkMatrix matrix;
35     matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
36     matrix.postTranslate(-pts[0].fX, -pts[0].fY);
37     matrix.postScale(inv, inv);
38     return matrix;
39 }
40 
use_4f_context(const SkShader::ContextRec & rec,uint32_t flags)41 static bool use_4f_context(const SkShader::ContextRec& rec, uint32_t flags) {
42 #ifdef FORCE_4F_CONTEXT
43     return true;
44 #else
45     return rec.fPreferredDstType == SkShader::ContextRec::kPM4f_DstType
46         || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag);
47 #endif
48 }
49 
50 ///////////////////////////////////////////////////////////////////////////////
51 
SkLinearGradient(const SkPoint pts[2],const Descriptor & desc)52 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
53     : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
54     , fStart(pts[0])
55     , fEnd(pts[1]) {
56 }
57 
CreateProc(SkReadBuffer & buffer)58 sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
59     DescriptorScope desc;
60     if (!desc.unflatten(buffer)) {
61         return nullptr;
62     }
63     SkPoint pts[2];
64     pts[0] = buffer.readPoint();
65     pts[1] = buffer.readPoint();
66     return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos,
67                                         desc.fCount, desc.fTileMode, desc.fGradFlags,
68                                         desc.fLocalMatrix);
69 }
70 
flatten(SkWriteBuffer & buffer) const71 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
72     this->INHERITED::flatten(buffer);
73     buffer.writePoint(fStart);
74     buffer.writePoint(fEnd);
75 }
76 
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const77 SkShader::Context* SkLinearGradient::onMakeContext(
78     const ContextRec& rec, SkArenaAlloc* alloc) const
79 {
80     return use_4f_context(rec, fGradFlags)
81            ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec)
82            : CheckedMakeContext<  LinearGradientContext>(alloc, *this, rec);
83 }
84 
85 // For now, only a 2-stop raster pipeline specialization.
86 //
87 // Stages:
88 //
89 //   * matrix (map dst -> grad space)
90 //   * clamp/repeat/mirror (tiling)
91 //   * linear_gradient_2stops (lerp c0/c1)
92 //   * optional premul
93 //
onAppendStages(SkRasterPipeline * p,SkColorSpace * cs,SkArenaAlloc * alloc,const SkMatrix & ctm,const SkPaint &,const SkMatrix * localM) const94 bool SkLinearGradient::onAppendStages(SkRasterPipeline* p,
95                                       SkColorSpace* cs,
96                                       SkArenaAlloc* alloc,
97                                       const SkMatrix& ctm,
98                                       const SkPaint&,
99                                       const SkMatrix* localM) const {
100     if (fColorCount > 2) {
101         return false;
102     }
103 
104     // Local matrix not supported currently.  Remove once we have a generic RP wrapper.
105     if (localM || !getLocalMatrix().isIdentity()) {
106         return false;
107     }
108 
109     SkASSERT(fColorCount == 2);
110     SkASSERT(fOrigPos == nullptr || (fOrigPos[0] == 0 && fOrigPos[1] == 1));
111 
112     SkMatrix dstToPts;
113     if (!ctm.invert(&dstToPts)) {
114         return false;
115     }
116 
117     const auto dstToUnit = SkMatrix::Concat(fPtsToUnit, dstToPts);
118 
119     auto* m = alloc->makeArrayDefault<float>(9);
120     if (dstToUnit.asAffine(m)) {
121         // TODO: mapping y is not needed; split the matrix stages to save some math?
122         p->append(SkRasterPipeline::matrix_2x3, m);
123     } else {
124         dstToUnit.get9(m);
125         p->append(SkRasterPipeline::matrix_perspective, m);
126     }
127 
128     // TODO: clamp/repeat/mirror const 1f stages?
129     auto* limit = alloc->make<float>(1.0f);
130 
131     switch (fTileMode) {
132         case kClamp_TileMode:  p->append(SkRasterPipeline:: clamp_x, limit); break;
133         case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit); break;
134         case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit); break;
135     }
136 
137     const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
138     const SkColor4f c0 = to_colorspace(fOrigColors4f[0], fColorSpace.get(), cs),
139                     c1 = to_colorspace(fOrigColors4f[1], fColorSpace.get(), cs);
140     const SkPM4f  pmc0 = premulGrad ? c0.premul() : SkPM4f::From4f(Sk4f::Load(&c0)),
141                   pmc1 = premulGrad ? c1.premul() : SkPM4f::From4f(Sk4f::Load(&c1));
142 
143     auto* c0_and_dc = alloc->makeArrayDefault<SkPM4f>(2);
144     c0_and_dc[0] = pmc0;
145     c0_and_dc[1] = SkPM4f::From4f(pmc1.to4f() - pmc0.to4f());
146 
147     p->append(SkRasterPipeline::linear_gradient_2stops, c0_and_dc);
148 
149     if (!premulGrad && !this->colorsAreOpaque()) {
150         p->append(SkRasterPipeline::premul);
151     }
152 
153     return true;
154 }
155 
156 // This swizzles SkColor into the same component order as SkPMColor, but does not actually
157 // "pre" multiply the color components.
158 //
159 // This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
160 // SkPMColor from the floats, without having to swizzle each time.
161 //
SkSwizzle_Color_to_PMColor(SkColor c)162 static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
163     return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
164 }
165 
LinearGradientContext(const SkLinearGradient & shader,const ContextRec & ctx)166 SkLinearGradient::LinearGradientContext::LinearGradientContext(
167         const SkLinearGradient& shader, const ContextRec& ctx)
168     : INHERITED(shader, ctx)
169 {
170     // setup for Sk4f
171     const int count = shader.fColorCount;
172     SkASSERT(count > 1);
173 
174     fRecs.setCount(count);
175     Rec* rec = fRecs.begin();
176     if (shader.fOrigPos) {
177         rec[0].fPos = 0;
178         SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;)   // should never get used
179         for (int i = 1; i < count; ++i) {
180             rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
181             float diff = rec[i].fPos - rec[i - 1].fPos;
182             if (diff > 0) {
183                 rec[i].fPosScale = 1.0f / diff;
184             } else {
185                 rec[i].fPosScale = 0;
186             }
187         }
188     } else {
189         // no pos specified, so we compute evenly spaced values
190         const float scale = float(count - 1);
191         const float invScale = 1.0f / scale;
192         for (int i = 0; i < count; ++i) {
193             rec[i].fPos = i * invScale;
194             rec[i].fPosScale = scale;
195         }
196     }
197     rec[count - 1].fPos = 1;    // overwrite the last value just to be sure we end at 1.0
198 
199     fApplyAlphaAfterInterp = true;
200     if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
201         shader.colorsAreOpaque())
202     {
203         fApplyAlphaAfterInterp = false;
204     }
205 
206     if (fApplyAlphaAfterInterp) {
207         // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
208         // interpolate in unpremultiplied space first, and then scale by alpha right before we
209         // convert to SkPMColor bytes.
210         const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
211         const Sk4f scale(1, 1, 1, paintAlpha);
212         for (int i = 0; i < count; ++i) {
213             uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
214             rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale;
215             if (i > 0) {
216                 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
217             }
218         }
219     } else {
220         // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
221         // of converting the floats down to bytes.
222         unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
223         for (int i = 0; i < count; ++i) {
224             SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
225             pmc = SkAlphaMulQ(pmc, alphaScale);
226             rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc));
227             if (i > 0) {
228                 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
229             }
230         }
231     }
232 }
233 
234 #define NO_CHECK_ITER               \
235     do {                            \
236     unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
237     SkASSERT(fi <= 0xFF);           \
238     fx += dx;                       \
239     *dstC++ = cache[toggle + fi];   \
240     toggle = next_dither_toggle(toggle); \
241     } while (0)
242 
243 namespace {
244 
245 typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
246                                 SkPMColor* dstC, const SkPMColor* cache,
247                                 int toggle, int count);
248 
249 // Linear interpolation (lerp) is unnecessary if there are no sharp
250 // discontinuities in the gradient - which must be true if there are
251 // only 2 colors - but it's cheap.
shadeSpan_linear_vertical_lerp(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)252 void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
253                                     SkPMColor* SK_RESTRICT dstC,
254                                     const SkPMColor* SK_RESTRICT cache,
255                                     int toggle, int count) {
256     // We're a vertical gradient, so no change in a span.
257     // If colors change sharply across the gradient, dithering is
258     // insufficient (it subsamples the color space) and we need to lerp.
259     unsigned fullIndex = proc(SkGradFixedToFixed(fx));
260     unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
261     unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
262 
263     int index0 = fi + toggle;
264     int index1 = index0;
265     if (fi < SkGradientShaderBase::kCache32Count - 1) {
266         index1 += 1;
267     }
268     SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
269     index0 ^= SkGradientShaderBase::kDitherStride32;
270     index1 ^= SkGradientShaderBase::kDitherStride32;
271     SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
272     sk_memset32_dither(dstC, lerp, dlerp, count);
273 }
274 
shadeSpan_linear_clamp(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)275 void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
276                             SkPMColor* SK_RESTRICT dstC,
277                             const SkPMColor* SK_RESTRICT cache,
278                             int toggle, int count) {
279     SkClampRange range;
280     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
281     range.validate(count);
282 
283     if ((count = range.fCount0) > 0) {
284         sk_memset32_dither(dstC,
285             cache[toggle + range.fV0],
286             cache[next_dither_toggle(toggle) + range.fV0],
287             count);
288         dstC += count;
289     }
290     if ((count = range.fCount1) > 0) {
291         int unroll = count >> 3;
292         fx = range.fFx1;
293         for (int i = 0; i < unroll; i++) {
294             NO_CHECK_ITER;  NO_CHECK_ITER;
295             NO_CHECK_ITER;  NO_CHECK_ITER;
296             NO_CHECK_ITER;  NO_CHECK_ITER;
297             NO_CHECK_ITER;  NO_CHECK_ITER;
298         }
299         if ((count &= 7) > 0) {
300             do {
301                 NO_CHECK_ITER;
302             } while (--count != 0);
303         }
304     }
305     if ((count = range.fCount2) > 0) {
306         sk_memset32_dither(dstC,
307             cache[toggle + range.fV1],
308             cache[next_dither_toggle(toggle) + range.fV1],
309             count);
310     }
311 }
312 
shadeSpan_linear_mirror(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)313 void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
314                              SkPMColor* SK_RESTRICT dstC,
315                              const SkPMColor* SK_RESTRICT cache,
316                              int toggle, int count) {
317     do {
318         unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
319         SkASSERT(fi <= 0xFF);
320         fx += dx;
321         *dstC++ = cache[toggle + fi];
322         toggle = next_dither_toggle(toggle);
323     } while (--count != 0);
324 }
325 
shadeSpan_linear_repeat(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)326 void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
327         SkPMColor* SK_RESTRICT dstC,
328         const SkPMColor* SK_RESTRICT cache,
329         int toggle, int count) {
330     do {
331         unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
332         SkASSERT(fi <= 0xFF);
333         fx += dx;
334         *dstC++ = cache[toggle + fi];
335         toggle = next_dither_toggle(toggle);
336     } while (--count != 0);
337 }
338 
339 }
340 
shadeSpan(int x,int y,SkPMColor * SK_RESTRICT dstC,int count)341 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
342                                                         int count) {
343     SkASSERT(count > 0);
344     const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
345 
346     if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
347         kLinear_MatrixClass == fDstToIndexClass)
348     {
349         this->shade4_clamp(x, y, dstC, count);
350         return;
351     }
352 
353     SkPoint             srcPt;
354     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
355     TileProc            proc = linearGradient.fTileProc;
356     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
357     int                 toggle = init_dither_toggle(x, y);
358 
359     if (fDstToIndexClass != kPerspective_MatrixClass) {
360         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
361                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
362         SkGradFixed dx, fx = SkScalarPinToGradFixed(srcPt.fX);
363 
364         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
365             const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
366             // todo: do we need a real/high-precision value for dx here?
367             dx = SkScalarPinToGradFixed(step.fX);
368         } else {
369             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
370             dx = SkScalarPinToGradFixed(fDstToIndex.getScaleX());
371         }
372 
373         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
374         if (0 == dx) {
375             shadeProc = shadeSpan_linear_vertical_lerp;
376         } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
377             shadeProc = shadeSpan_linear_clamp;
378         } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
379             shadeProc = shadeSpan_linear_mirror;
380         } else {
381             SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
382         }
383         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
384     } else {
385         SkScalar    dstX = SkIntToScalar(x);
386         SkScalar    dstY = SkIntToScalar(y);
387         do {
388             dstProc(fDstToIndex, dstX, dstY, &srcPt);
389             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
390             SkASSERT(fi <= 0xFFFF);
391             *dstC++ = cache[toggle + (fi >> kCache32Shift)];
392             toggle = next_dither_toggle(toggle);
393             dstX += SK_Scalar1;
394         } while (--count != 0);
395     }
396 }
397 
asAGradient(GradientInfo * info) const398 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
399     if (info) {
400         commonAsAGradient(info);
401         info->fPoint[0] = fStart;
402         info->fPoint[1] = fEnd;
403     }
404     return kLinear_GradientType;
405 }
406 
407 #if SK_SUPPORT_GPU
408 
409 #include "GrColorSpaceXform.h"
410 #include "GrShaderCaps.h"
411 #include "glsl/GrGLSLFragmentShaderBuilder.h"
412 #include "SkGr.h"
413 
414 /////////////////////////////////////////////////////////////////////
415 
416 class GrLinearGradient : public GrGradientEffect {
417 public:
418     class GLSLLinearProcessor;
419 
Make(const CreateArgs & args)420     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
421         return sk_sp<GrFragmentProcessor>(new GrLinearGradient(args));
422     }
423 
~GrLinearGradient()424     ~GrLinearGradient() override {}
425 
name() const426     const char* name() const override { return "Linear Gradient"; }
427 
428 private:
GrLinearGradient(const CreateArgs & args)429     GrLinearGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
430         this->initClassID<GrLinearGradient>();
431     }
432 
433     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
434 
435     virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
436                                        GrProcessorKeyBuilder* b) const override;
437 
438     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
439 
440     typedef GrGradientEffect INHERITED;
441 };
442 
443 /////////////////////////////////////////////////////////////////////
444 
445 class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor {
446 public:
GLSLLinearProcessor(const GrProcessor &)447     GLSLLinearProcessor(const GrProcessor&) {}
448 
~GLSLLinearProcessor()449     ~GLSLLinearProcessor() override {}
450 
451     virtual void emitCode(EmitArgs&) override;
452 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)453     static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
454         b->add32(GenBaseGradientKey(processor));
455     }
456 
457 private:
458     typedef GrGradientEffect::GLSLProcessor INHERITED;
459 };
460 
461 /////////////////////////////////////////////////////////////////////
462 
onCreateGLSLInstance() const463 GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const {
464     return new GrLinearGradient::GLSLLinearProcessor(*this);
465 }
466 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const467 void GrLinearGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
468                                              GrProcessorKeyBuilder* b) const {
469     GrLinearGradient::GLSLLinearProcessor::GenKey(*this, caps, b);
470 }
471 
472 /////////////////////////////////////////////////////////////////////
473 
474 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
475 
476 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)477 sk_sp<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) {
478     SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
479                         {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
480 
481     RandomGradientParams params(d->fRandom);
482     auto shader = params.fUseColors4f ?
483         SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops,
484                                      params.fColorCount, params.fTileMode) :
485         SkGradientShader::MakeLinear(points, params.fColors, params.fStops,
486                                      params.fColorCount, params.fTileMode);
487     GrTest::TestAsFPArgs asFPArgs(d);
488     sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
489     GrAlwaysAssert(fp);
490     return fp;
491 }
492 #endif
493 
494 /////////////////////////////////////////////////////////////////////
495 
emitCode(EmitArgs & args)496 void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) {
497     const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
498     this->emitUniforms(args.fUniformHandler, ge);
499     SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
500     t.append(".x");
501     this->emitColor(args.fFragBuilder,
502                     args.fUniformHandler,
503                     args.fShaderCaps,
504                     ge,
505                     t.c_str(),
506                     args.fOutputColor,
507                     args.fInputColor,
508                     args.fTexSamplers);
509 }
510 
511 /////////////////////////////////////////////////////////////////////
512 
asFragmentProcessor(const AsFPArgs & args) const513 sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs& args) const {
514     SkASSERT(args.fContext);
515 
516     SkMatrix matrix;
517     if (!this->getLocalMatrix().invert(&matrix)) {
518         return nullptr;
519     }
520     if (args.fLocalMatrix) {
521         SkMatrix inv;
522         if (!args.fLocalMatrix->invert(&inv)) {
523             return nullptr;
524         }
525         matrix.postConcat(inv);
526     }
527     matrix.postConcat(fPtsToUnit);
528 
529     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
530                                                                        args.fDstColorSpace);
531     sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make(
532         GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
533                                      std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
534     return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
535 }
536 
537 
538 #endif
539 
540 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const541 void SkLinearGradient::toString(SkString* str) const {
542     str->append("SkLinearGradient (");
543 
544     str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
545     str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
546 
547     this->INHERITED::toString(str);
548 
549     str->append(")");
550 }
551 #endif
552 
553 ///////////////////////////////////////////////////////////////////////////////////////////////////
554 
555 #include "SkNx.h"
556 
557 static const SkLinearGradient::LinearGradientContext::Rec*
find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[],float tiledX)558 find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
559     SkASSERT(tiledX >= 0 && tiledX <= 1);
560 
561     SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
562     SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
563     SkASSERT(rec[0].fPos <= rec[1].fPos);
564     rec += 1;
565     while (rec->fPos < tiledX || rec->fPosScale == 0) {
566         SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
567         SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
568         SkASSERT(rec[0].fPos <= rec[1].fPos);
569         rec += 1;
570     }
571     return rec - 1;
572 }
573 
574 static const SkLinearGradient::LinearGradientContext::Rec*
find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[],float tiledX)575 find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
576     SkASSERT(tiledX >= 0 && tiledX <= 1);
577 
578     SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
579     SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
580     SkASSERT(rec[0].fPos <= rec[1].fPos);
581     while (tiledX < rec->fPos || rec[1].fPosScale == 0) {
582         rec -= 1;
583         SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
584         SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
585         SkASSERT(rec[0].fPos <= rec[1].fPos);
586     }
587     return rec;
588 }
589 
590 // As an optimization, we can apply the dither bias before interpolation -- but only when
591 // operating in premul space (apply_alpha == false).  When apply_alpha == true, we must
592 // defer the bias application until after premul.
593 //
594 // The following two helpers encapsulate this logic: pre_bias is called before interpolation,
595 // and effects the bias when apply_alpha == false, while post_bias is called after premul and
596 // effects the bias for the apply_alpha == true case.
597 
598 template <bool apply_alpha>
pre_bias(const Sk4f & x,const Sk4f & bias)599 Sk4f pre_bias(const Sk4f& x, const Sk4f& bias) {
600     return apply_alpha ? x : x + bias;
601 }
602 
603 template <bool apply_alpha>
post_bias(const Sk4f & x,const Sk4f & bias)604 Sk4f post_bias(const Sk4f& x, const Sk4f& bias) {
605     return apply_alpha ? x + bias : x;
606 }
607 
trunc_from_255(const Sk4f & x,const Sk4f & bias)608 template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x, const Sk4f& bias) {
609     SkPMColor c;
610     Sk4f c4f255 = x;
611     if (apply_alpha) {
612         const float scale = x[SkPM4f::A] * (1 / 255.f);
613         c4f255 *= Sk4f(scale, scale, scale, 1);
614     }
615     SkNx_cast<uint8_t>(post_bias<apply_alpha>(c4f255, bias)).store(&c);
616 
617     return c;
618 }
619 
fill(SkPMColor dst[],int count,const Sk4f & c4,const Sk4f & bias0,const Sk4f & bias1)620 template <bool apply_alpha> void fill(SkPMColor dst[], int count,
621                                       const Sk4f& c4, const Sk4f& bias0, const Sk4f& bias1) {
622     const SkPMColor c0 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias0), bias0);
623     const SkPMColor c1 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias1), bias1);
624     sk_memset32_dither(dst, c0, c1, count);
625 }
626 
fill(SkPMColor dst[],int count,const Sk4f & c4)627 template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
628     // Assumes that c4 does not need to be dithered.
629     sk_memset32(dst, trunc_from_255<apply_alpha>(c4, 0), count);
630 }
631 
632 /*
633  *  TODOs
634  *
635  *  - tilemodes
636  *  - interp before or after premul
637  *  - perspective
638  *  - optimizations
639  *      - use fixed (32bit or 16bit) instead of floats?
640  */
641 
lerp_color(float fx,const SkLinearGradient::LinearGradientContext::Rec * rec)642 static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
643     SkASSERT(fx >= rec[0].fPos);
644     SkASSERT(fx <= rec[1].fPos);
645 
646     const float p0 = rec[0].fPos;
647     const Sk4f c0 = rec[0].fColor;
648     const Sk4f c1 = rec[1].fColor;
649     const Sk4f diffc = c1 - c0;
650     const float scale = rec[1].fPosScale;
651     const float t = (fx - p0) * scale;
652     return c0 + Sk4f(t) * diffc;
653 }
654 
ramp(SkPMColor dstC[],int n,const Sk4f & c,const Sk4f & dc,const Sk4f & dither0,const Sk4f & dither1)655 template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
656                                       const Sk4f& dither0, const Sk4f& dither1) {
657     Sk4f dc2 = dc + dc;
658     Sk4f dc4 = dc2 + dc2;
659     Sk4f cd0 = pre_bias<apply_alpha>(c     , dither0);
660     Sk4f cd1 = pre_bias<apply_alpha>(c + dc, dither1);
661     Sk4f cd2 = cd0 + dc2;
662     Sk4f cd3 = cd1 + dc2;
663     while (n >= 4) {
664         if (!apply_alpha) {
665             Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
666             dstC += 4;
667         } else {
668             *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
669             *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
670             *dstC++ = trunc_from_255<apply_alpha>(cd2, dither0);
671             *dstC++ = trunc_from_255<apply_alpha>(cd3, dither1);
672         }
673         cd0 = cd0 + dc4;
674         cd1 = cd1 + dc4;
675         cd2 = cd2 + dc4;
676         cd3 = cd3 + dc4;
677         n -= 4;
678     }
679     if (n & 2) {
680         *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
681         *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
682         cd0 = cd0 + dc2;
683     }
684     if (n & 1) {
685         *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
686     }
687 }
688 
689 template <bool apply_alpha, bool dx_is_pos>
shade4_dx_clamp(SkPMColor dstC[],int count,float fx,float dx,float invDx,const float dither[2])690 void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
691                                                               float fx, float dx, float invDx,
692                                                               const float dither[2]) {
693     Sk4f dither0(dither[0]);
694     Sk4f dither1(dither[1]);
695     const Rec* rec = fRecs.begin();
696 
697     const Sk4f dx4 = Sk4f(dx);
698     SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
699 
700     if (dx_is_pos) {
701         if (fx < 0) {
702             // count is guaranteed to be positive, but the first arg may overflow int32 after
703             // increment => casting to uint32 ensures correct clamping.
704             int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor(-fx * invDx)) + 1,
705                                      count);
706             SkASSERT(n > 0);
707             fill<apply_alpha>(dstC, n, rec[0].fColor);
708             count -= n;
709             dstC += n;
710             fx += n * dx;
711             SkASSERT(0 == count || fx >= 0);
712             if (n & 1) {
713                 SkTSwap(dither0, dither1);
714             }
715         }
716     } else { // dx < 0
717         if (fx > 1) {
718             // count is guaranteed to be positive, but the first arg may overflow int32 after
719             // increment => casting to uint32 ensures correct clamping.
720             int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor((1 - fx) * invDx)) + 1,
721                                      count);
722             SkASSERT(n > 0);
723             fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
724             count -= n;
725             dstC += n;
726             fx += n * dx;
727             SkASSERT(0 == count || fx <= 1);
728             if (n & 1) {
729                 SkTSwap(dither0, dither1);
730             }
731         }
732     }
733     SkASSERT(count >= 0);
734 
735     const Rec* r;
736     if (dx_is_pos) {
737         r = fRecs.begin();                      // start at the beginning
738     } else {
739         r = fRecs.begin() + fRecs.count() - 2;  // start at the end
740     }
741 
742     while (count > 0) {
743         if (dx_is_pos) {
744             if (fx >= 1) {
745                 fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
746                 return;
747             }
748         } else {    // dx < 0
749             if (fx <= 0) {
750                 fill<apply_alpha>(dstC, count, rec[0].fColor);
751                 return;
752             }
753         }
754 
755         if (dx_is_pos) {
756             r = find_forward(r, fx);
757         } else {
758             r = find_backward(r, fx);
759         }
760         SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
761 
762         const float p0 = r[0].fPos;
763         const Sk4f c0 = r[0].fColor;
764         const float p1 = r[1].fPos;
765         const Sk4f diffc = Sk4f(r[1].fColor) - c0;
766         const float scale = r[1].fPosScale;
767         const float t = (fx - p0) * scale;
768         const Sk4f c = c0 + Sk4f(t) * diffc;
769         const Sk4f dc = diffc * dx4 * Sk4f(scale);
770 
771         int n;
772         if (dx_is_pos) {
773             n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
774         } else {
775             n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
776         }
777 
778         fx += n * dx;
779         // fx should now outside of the p0..p1 interval. However, due to float precision loss,
780         // its possible that fx is slightly too small/large, so we clamp it.
781         if (dx_is_pos) {
782             fx = SkTMax(fx, p1);
783         } else {
784             fx = SkTMin(fx, p0);
785         }
786 
787         ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
788         dstC += n;
789         SkASSERT(dstC <= endDstC);
790 
791         if (n & 1) {
792             SkTSwap(dither0, dither1);
793         }
794 
795         count -= n;
796         SkASSERT(count >= 0);
797     }
798 }
799 
shade4_clamp(int x,int y,SkPMColor dstC[],int count)800 void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
801                                                            int count) {
802     SkASSERT(count > 0);
803     SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
804 
805     SkPoint srcPt;
806     fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
807     float fx = srcPt.x();
808     const float dx = fDstToIndex.getScaleX();
809 
810     // Default our dither bias values to 1/2, (rounding), which is no dithering
811     float dither0 = 0.5f;
812     float dither1 = 0.5f;
813     if (fDither) {
814         const float ditherCell[] = {
815             1/8.0f,   5/8.0f,
816             7/8.0f,   3/8.0f,
817         };
818         const int rowIndex = (y & 1) << 1;
819         dither0 = ditherCell[rowIndex];
820         dither1 = ditherCell[rowIndex + 1];
821         if (x & 1) {
822             SkTSwap(dither0, dither1);
823         }
824     }
825     const float dither[2] = { dither0, dither1 };
826 
827     if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
828         const float pinFx = SkTPin(fx, 0.0f, 1.0f);
829         Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
830         if (fApplyAlphaAfterInterp) {
831             fill<true>(dstC, count, c, dither0, dither1);
832         } else {
833             fill<false>(dstC, count, c, dither0, dither1);
834         }
835         return;
836     }
837 
838     SkASSERT(0.f != dx);
839     const float invDx = 1 / dx;
840     if (dx > 0) {
841         if (fApplyAlphaAfterInterp) {
842             this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
843         } else {
844             this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
845         }
846     } else {
847         if (fApplyAlphaAfterInterp) {
848             this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
849         } else {
850             this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
851         }
852     }
853 }
854