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