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