1589708bf7c706348b763e8277004cb160b202bdbrileya@google.com/*
2589708bf7c706348b763e8277004cb160b202bdbrileya@google.com * Copyright 2012 Google Inc.
3589708bf7c706348b763e8277004cb160b202bdbrileya@google.com *
4589708bf7c706348b763e8277004cb160b202bdbrileya@google.com * Use of this source code is governed by a BSD-style license that can be
5589708bf7c706348b763e8277004cb160b202bdbrileya@google.com * found in the LICENSE file.
6589708bf7c706348b763e8277004cb160b202bdbrileya@google.com */
7589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
8bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita#include "Sk4fLinearGradient.h"
9589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#include "SkLinearGradient.h"
10589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
11bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita// define to test the 4f gradient path
1255430a620762e882c2b3018c57f9a7d51cf16071fmalita// #define FORCE_4F_CONTEXT
13bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
14f3182ebc72db2bf2e24119d5cea05f270473a491reedstatic const float kInv255Float = 1.0f / 255;
15f3182ebc72db2bf2e24119d5cea05f270473a491reed
16589708bf7c706348b763e8277004cb160b202bdbrileya@google.comstatic inline int repeat_8bits(int x) {
17589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return x & 0xFF;
18589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
19589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
20589708bf7c706348b763e8277004cb160b202bdbrileya@google.com// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
21589708bf7c706348b763e8277004cb160b202bdbrileya@google.com// See http://code.google.com/p/skia/issues/detail?id=472
22589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#if defined(_MSC_VER) && (_MSC_VER >= 1600)
23589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#pragma optimize("", off)
24589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#endif
25589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
26589708bf7c706348b763e8277004cb160b202bdbrileya@google.comstatic inline int mirror_8bits(int x) {
27589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (x & 256) {
28589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        x = ~x;
29589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
30589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return x & 255;
31589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
32589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
33589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#if defined(_MSC_VER) && (_MSC_VER >= 1600)
34589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#pragma optimize("", on)
35589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#endif
36589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
37cc695fee81613dc92746c1c6bb27f45cfc6ce73emtkleinstatic SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
38589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkVector    vec = pts[1] - pts[0];
39589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkScalar    mag = vec.length();
40589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
41589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
42589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    vec.scale(inv);
43cc695fee81613dc92746c1c6bb27f45cfc6ce73emtklein    SkMatrix matrix;
44cc695fee81613dc92746c1c6bb27f45cfc6ce73emtklein    matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
45cc695fee81613dc92746c1c6bb27f45cfc6ce73emtklein    matrix.postTranslate(-pts[0].fX, -pts[0].fY);
46cc695fee81613dc92746c1c6bb27f45cfc6ce73emtklein    matrix.postScale(inv, inv);
47cc695fee81613dc92746c1c6bb27f45cfc6ce73emtklein    return matrix;
48589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
49589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
5055430a620762e882c2b3018c57f9a7d51cf16071fmalitastatic bool use_4f_context(const SkShader::ContextRec& rec, uint32_t flags) {
5155430a620762e882c2b3018c57f9a7d51cf16071fmalita#ifdef FORCE_4F_CONTEXT
52bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita    return true;
53bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita#else
5455430a620762e882c2b3018c57f9a7d51cf16071fmalita    // Perspective not supported in 4f yet.
5555430a620762e882c2b3018c57f9a7d51cf16071fmalita    if (rec.fMatrix->hasPerspective()
5655430a620762e882c2b3018c57f9a7d51cf16071fmalita        || (rec.fLocalMatrix && rec.fLocalMatrix->hasPerspective())) {
5755430a620762e882c2b3018c57f9a7d51cf16071fmalita        return false;
5855430a620762e882c2b3018c57f9a7d51cf16071fmalita    }
5955430a620762e882c2b3018c57f9a7d51cf16071fmalita
6055430a620762e882c2b3018c57f9a7d51cf16071fmalita    return rec.fPreferredDstType == SkShader::ContextRec::kPM4f_DstType
6155430a620762e882c2b3018c57f9a7d51cf16071fmalita        || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag);
62bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita#endif
63bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita}
64bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
65589708bf7c706348b763e8277004cb160b202bdbrileya@google.com///////////////////////////////////////////////////////////////////////////////
66589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
67addf2edf3da20f053daa3897cfe2c52d7369a7b1reedSkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
68cc695fee81613dc92746c1c6bb27f45cfc6ce73emtklein    : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
69589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    , fStart(pts[0])
70cc695fee81613dc92746c1c6bb27f45cfc6ce73emtklein    , fEnd(pts[1]) {
71589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
72589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
739fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reedSkFlattenable* SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
749fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    DescriptorScope desc;
759fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    if (!desc.unflatten(buffer)) {
7696fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
779fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    }
789fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    SkPoint pts[2];
799fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    pts[0] = buffer.readPoint();
809fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    pts[1] = buffer.readPoint();
819fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    return SkGradientShader::CreateLinear(pts, desc.fColors, desc.fPos, desc.fCount,
829fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed                                          desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
839fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed}
84589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
858b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.orgvoid SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
86589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    this->INHERITED::flatten(buffer);
87589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    buffer.writePoint(fStart);
88589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    buffer.writePoint(fEnd);
89589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
90589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
9155430a620762e882c2b3018c57f9a7d51cf16071fmalitasize_t SkLinearGradient::contextSize(const ContextRec& rec) const {
9255430a620762e882c2b3018c57f9a7d51cf16071fmalita    return use_4f_context(rec, fGradFlags)
93bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita        ? sizeof(LinearGradient4fContext)
94bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita        : sizeof(LinearGradientContext);
9587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org}
9687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
97ce56d965069c1649afe14319cb239e6ad670682acommit-bot@chromium.orgSkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void* storage) const {
9855430a620762e882c2b3018c57f9a7d51cf16071fmalita    return use_4f_context(rec, fGradFlags)
99bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita        ? static_cast<SkShader::Context*>(new (storage) LinearGradient4fContext(*this, rec))
100bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita        : static_cast<SkShader::Context*>(new (storage) LinearGradientContext(*this, rec));
10187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org}
10287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
103f3182ebc72db2bf2e24119d5cea05f270473a491reed// This swizzles SkColor into the same component order as SkPMColor, but does not actually
104f3182ebc72db2bf2e24119d5cea05f270473a491reed// "pre" multiply the color components.
105f3182ebc72db2bf2e24119d5cea05f270473a491reed//
106f3182ebc72db2bf2e24119d5cea05f270473a491reed// This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
107f3182ebc72db2bf2e24119d5cea05f270473a491reed// SkPMColor from the floats, without having to swizzle each time.
108f3182ebc72db2bf2e24119d5cea05f270473a491reed//
109f3182ebc72db2bf2e24119d5cea05f270473a491reedstatic uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
110f3182ebc72db2bf2e24119d5cea05f270473a491reed    return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
111f3182ebc72db2bf2e24119d5cea05f270473a491reed}
112f3182ebc72db2bf2e24119d5cea05f270473a491reed
11387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgSkLinearGradient::LinearGradientContext::LinearGradientContext(
114f3182ebc72db2bf2e24119d5cea05f270473a491reed        const SkLinearGradient& shader, const ContextRec& ctx)
115f3182ebc72db2bf2e24119d5cea05f270473a491reed    : INHERITED(shader, ctx)
11687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org{
117f3182ebc72db2bf2e24119d5cea05f270473a491reed    // setup for Sk4f
118f3182ebc72db2bf2e24119d5cea05f270473a491reed    int count = shader.fColorCount;
119f3182ebc72db2bf2e24119d5cea05f270473a491reed    fRecs.setCount(count);
120f3182ebc72db2bf2e24119d5cea05f270473a491reed    Rec* rec = fRecs.begin();
121f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (shader.fOrigPos) {
122f3182ebc72db2bf2e24119d5cea05f270473a491reed        rec[0].fPos = 0;
123f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;)   // should never get used
124f3182ebc72db2bf2e24119d5cea05f270473a491reed        for (int i = 1; i < count; ++i) {
125f3182ebc72db2bf2e24119d5cea05f270473a491reed            rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
126f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman            float diff = rec[i].fPos - rec[i - 1].fPos;
127f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman            if (diff > 0) {
128f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman                rec[i].fPosScale = 1.0f / diff;
129f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman            } else {
130f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman                rec[i].fPosScale = 0;
131f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman            }
132f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
133f3182ebc72db2bf2e24119d5cea05f270473a491reed        rec[count - 1].fPos = 1;    // overwrite the last value just to be sure we end at 1.0
134f3182ebc72db2bf2e24119d5cea05f270473a491reed    } else {
135f3182ebc72db2bf2e24119d5cea05f270473a491reed        // no pos specified, so we compute evenly spaced values
136f3182ebc72db2bf2e24119d5cea05f270473a491reed        const float scale = float(count - 1);
137f3182ebc72db2bf2e24119d5cea05f270473a491reed        float invScale = 1.0f / scale;
138f3182ebc72db2bf2e24119d5cea05f270473a491reed        for (int i = 0; i < count; ++i) {
139f3182ebc72db2bf2e24119d5cea05f270473a491reed            rec[i].fPos = i * invScale;
140f3182ebc72db2bf2e24119d5cea05f270473a491reed            rec[i].fPosScale = scale;
141f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
142f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
143f3182ebc72db2bf2e24119d5cea05f270473a491reed
144f3182ebc72db2bf2e24119d5cea05f270473a491reed    fApplyAlphaAfterInterp = true;
145f3182ebc72db2bf2e24119d5cea05f270473a491reed    if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
146f3182ebc72db2bf2e24119d5cea05f270473a491reed        shader.colorsAreOpaque())
147f3182ebc72db2bf2e24119d5cea05f270473a491reed    {
148f3182ebc72db2bf2e24119d5cea05f270473a491reed        fApplyAlphaAfterInterp = false;
149f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
150f3182ebc72db2bf2e24119d5cea05f270473a491reed
151f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (fApplyAlphaAfterInterp) {
152f3182ebc72db2bf2e24119d5cea05f270473a491reed        // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
153f3182ebc72db2bf2e24119d5cea05f270473a491reed        // interpolate in unpremultiplied space first, and then scale by alpha right before we
154f3182ebc72db2bf2e24119d5cea05f270473a491reed        // convert to SkPMColor bytes.
155f3182ebc72db2bf2e24119d5cea05f270473a491reed        const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
156f3182ebc72db2bf2e24119d5cea05f270473a491reed        const Sk4f scale(1, 1, 1, paintAlpha);
157f3182ebc72db2bf2e24119d5cea05f270473a491reed        for (int i = 0; i < count; ++i) {
158f3182ebc72db2bf2e24119d5cea05f270473a491reed            uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
159507ef6d68115ae9e6d884bb36436a1463523d893mtklein            rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale;
160f3182ebc72db2bf2e24119d5cea05f270473a491reed            if (i > 0) {
161f3182ebc72db2bf2e24119d5cea05f270473a491reed                SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
162f3182ebc72db2bf2e24119d5cea05f270473a491reed            }
163f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
164f3182ebc72db2bf2e24119d5cea05f270473a491reed    } else {
165f3182ebc72db2bf2e24119d5cea05f270473a491reed        // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
166f3182ebc72db2bf2e24119d5cea05f270473a491reed        // of converting the floats down to bytes.
167f3182ebc72db2bf2e24119d5cea05f270473a491reed        unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
168f3182ebc72db2bf2e24119d5cea05f270473a491reed        for (int i = 0; i < count; ++i) {
169f3182ebc72db2bf2e24119d5cea05f270473a491reed            SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
170f3182ebc72db2bf2e24119d5cea05f270473a491reed            pmc = SkAlphaMulQ(pmc, alphaScale);
171507ef6d68115ae9e6d884bb36436a1463523d893mtklein            rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc));
172f3182ebc72db2bf2e24119d5cea05f270473a491reed            if (i > 0) {
173f3182ebc72db2bf2e24119d5cea05f270473a491reed                SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
174f3182ebc72db2bf2e24119d5cea05f270473a491reed            }
175f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
176f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
177589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
178589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
179589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#define NO_CHECK_ITER               \
180589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    do {                            \
181caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreed    unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
182589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkASSERT(fi <= 0xFF);           \
183589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fx += dx;                       \
184589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    *dstC++ = cache[toggle + fi];   \
18555853db3cce9539746fe202519a534c85ecdf62creed@google.com    toggle = next_dither_toggle(toggle); \
186589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    } while (0)
187589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
188589708bf7c706348b763e8277004cb160b202bdbrileya@google.comnamespace {
189589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
190caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreedtypedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
191589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                                SkPMColor* dstC, const SkPMColor* cache,
192589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                                int toggle, int count);
193589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
194589708bf7c706348b763e8277004cb160b202bdbrileya@google.com// Linear interpolation (lerp) is unnecessary if there are no sharp
195589708bf7c706348b763e8277004cb160b202bdbrileya@google.com// discontinuities in the gradient - which must be true if there are
196589708bf7c706348b763e8277004cb160b202bdbrileya@google.com// only 2 colors - but it's cheap.
197caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreedvoid shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
198589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                                    SkPMColor* SK_RESTRICT dstC,
199589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                                    const SkPMColor* SK_RESTRICT cache,
200589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                                    int toggle, int count) {
201589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    // We're a vertical gradient, so no change in a span.
202589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    // If colors change sharply across the gradient, dithering is
203589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    // insufficient (it subsamples the color space) and we need to lerp.
204caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreed    unsigned fullIndex = proc(SkGradFixedToFixed(fx));
2053c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
2063c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
2079dde018753c4d16cdc60c008c32bb0b573a82944skia.committer@gmail.com
2083c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    int index0 = fi + toggle;
2093c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    int index1 = index0;
2103c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    if (fi < SkGradientShaderBase::kCache32Count - 1) {
2113c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com        index1 += 1;
2123c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    }
2133c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
2143c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    index0 ^= SkGradientShaderBase::kDitherStride32;
2153c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    index1 ^= SkGradientShaderBase::kDitherStride32;
2163c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
217589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    sk_memset32_dither(dstC, lerp, dlerp, count);
218589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
219589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
220caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreedvoid shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
221589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                            SkPMColor* SK_RESTRICT dstC,
222589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                            const SkPMColor* SK_RESTRICT cache,
223589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                            int toggle, int count) {
224589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkClampRange range;
2253c2102c2d2cdad328a0d87329e1a973f12775836reed@google.com    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
2269d91eb313623dca16bc988bad2e8b01712e4c578reed    range.validate(count);
227589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
228589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if ((count = range.fCount0) > 0) {
229589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        sk_memset32_dither(dstC,
230589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            cache[toggle + range.fV0],
23155853db3cce9539746fe202519a534c85ecdf62creed@google.com            cache[next_dither_toggle(toggle) + range.fV0],
232589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            count);
233589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        dstC += count;
234589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
235589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if ((count = range.fCount1) > 0) {
236589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        int unroll = count >> 3;
237589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        fx = range.fFx1;
238589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        for (int i = 0; i < unroll; i++) {
239589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            NO_CHECK_ITER;  NO_CHECK_ITER;
240589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            NO_CHECK_ITER;  NO_CHECK_ITER;
241589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            NO_CHECK_ITER;  NO_CHECK_ITER;
242589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            NO_CHECK_ITER;  NO_CHECK_ITER;
243589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
244589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if ((count &= 7) > 0) {
245589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            do {
246589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                NO_CHECK_ITER;
247589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            } while (--count != 0);
248589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
249589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
250589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if ((count = range.fCount2) > 0) {
251589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        sk_memset32_dither(dstC,
252589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            cache[toggle + range.fV1],
25355853db3cce9539746fe202519a534c85ecdf62creed@google.com            cache[next_dither_toggle(toggle) + range.fV1],
254589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            count);
255589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
256589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
257589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
258caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreedvoid shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
259589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                             SkPMColor* SK_RESTRICT dstC,
260589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                             const SkPMColor* SK_RESTRICT cache,
261589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                             int toggle, int count) {
262589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    do {
263caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreed        unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
264589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkASSERT(fi <= 0xFF);
265589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        fx += dx;
266589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        *dstC++ = cache[toggle + fi];
26755853db3cce9539746fe202519a534c85ecdf62creed@google.com        toggle = next_dither_toggle(toggle);
268589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    } while (--count != 0);
269589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
270589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
271caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreedvoid shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
272589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkPMColor* SK_RESTRICT dstC,
273589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        const SkPMColor* SK_RESTRICT cache,
274589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        int toggle, int count) {
275589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    do {
276caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreed        unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
277589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkASSERT(fi <= 0xFF);
278589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        fx += dx;
279589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        *dstC++ = cache[toggle + fi];
28055853db3cce9539746fe202519a534c85ecdf62creed@google.com        toggle = next_dither_toggle(toggle);
281589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    } while (--count != 0);
282589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
283589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
284589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
285589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
28687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgvoid SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
28787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org                                                        int count) {
288589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkASSERT(count > 0);
28987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
29087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
291c2e0ac4fce5bfe5bac9f2cb1ebd3aea3a8900fb1fmalita// Only use the Sk4f impl when known to be fast.
292c2e0ac4fce5bfe5bac9f2cb1ebd3aea3a8900fb1fmalita#if defined(SKNX_IS_FAST)
293f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
294f3182ebc72db2bf2e24119d5cea05f270473a491reed        kLinear_MatrixClass == fDstToIndexClass)
295f3182ebc72db2bf2e24119d5cea05f270473a491reed    {
296f3182ebc72db2bf2e24119d5cea05f270473a491reed        this->shade4_clamp(x, y, dstC, count);
297f3182ebc72db2bf2e24119d5cea05f270473a491reed        return;
298f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
299f3182ebc72db2bf2e24119d5cea05f270473a491reed#endif
300f3182ebc72db2bf2e24119d5cea05f270473a491reed
301589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkPoint             srcPt;
302589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
30387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    TileProc            proc = linearGradient.fTileProc;
30487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
30555853db3cce9539746fe202519a534c85ecdf62creed@google.com    int                 toggle = init_dither_toggle(x, y);
306589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
307589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (fDstToIndexClass != kPerspective_MatrixClass) {
308589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
309589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
310caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreed        SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX);
311589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
312589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
3138e17556349132a46c02fbabcaf74b63f521528e0benjaminwagner            const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
314caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreed            // todo: do we need a real/high-precision value for dx here?
3158e17556349132a46c02fbabcaf74b63f521528e0benjaminwagner            dx = SkScalarToGradFixed(step.fX);
316589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } else {
317589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
318caf7e9313b52f78b53ff7d478f9cc41a1f6a85ffreed            dx = SkScalarToGradFixed(fDstToIndex.getScaleX());
319589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
320589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
321589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        LinearShadeProc shadeProc = shadeSpan_linear_repeat;
32253009ba7019cbe86a6ab2aa5b7ae5893e4efffbdreed@google.com        if (0 == dx) {
323589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            shadeProc = shadeSpan_linear_vertical_lerp;
32487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
325589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            shadeProc = shadeSpan_linear_clamp;
32687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
327589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            shadeProc = shadeSpan_linear_mirror;
328589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } else {
32987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org            SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
330589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
331589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
332589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    } else {
333589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkScalar    dstX = SkIntToScalar(x);
334589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkScalar    dstY = SkIntToScalar(y);
335589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        do {
336589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            dstProc(fDstToIndex, dstX, dstY, &srcPt);
337589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
338589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkASSERT(fi <= 0xFFFF);
339589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            *dstC++ = cache[toggle + (fi >> kCache32Shift)];
34055853db3cce9539746fe202519a534c85ecdf62creed@google.com            toggle = next_dither_toggle(toggle);
341589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            dstX += SK_Scalar1;
342589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } while (--count != 0);
343589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
344589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
345589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
346589708bf7c706348b763e8277004cb160b202bdbrileya@google.comSkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
347589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (info) {
348589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        commonAsAGradient(info);
349589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        info->fPoint[0] = fStart;
350589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        info->fPoint[1] = fEnd;
351589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
352589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return kLinear_GradientType;
353589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
354589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
355cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com#if SK_SUPPORT_GPU
356cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com
3577ea439b2203855db97330b25945b87dd4b170b8begdaniel#include "glsl/GrGLSLCaps.h"
3587ea439b2203855db97330b25945b87dd4b170b8begdaniel#include "glsl/GrGLSLFragmentShaderBuilder.h"
3599de5b514d38c5b36066bcdc14fba2f7e5196d372dandov#include "SkGr.h"
3602eaaefd7e6a58339b3f93333f1e9cc92252cc303bsalomon@google.com
361d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com/////////////////////////////////////////////////////////////////////
362d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
3630707c29413a5a3cc1c2d14b8c65b3692af5c7411bsalomon@google.comclass GrGLLinearGradient : public GrGLGradientEffect {
364d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.compublic:
365d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
366eb2a6761654307e8aeeeaabdd63c6bf9ab0411e9joshualitt    GrGLLinearGradient(const GrProcessor&) {}
367d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
368d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com    virtual ~GrGLLinearGradient() { }
369d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
3707c157a988845fb00f9024d6db6dda142c3458033wangyix    virtual void emitCode(EmitArgs&) override;
371f78df33efc72167f94da1b0476c9a86ba18a5f2cbsalomon@google.com
372cfc18867d982119d9dc2888bf09f1093012daaddjvanverth    static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
373b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt        b->add32(GenBaseGradientKey(processor));
374d8b5faca043100d7a1e4594b4d10e462532af390bsalomon@google.com    }
375d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
376d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.comprivate:
377d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
3780707c29413a5a3cc1c2d14b8c65b3692af5c7411bsalomon@google.com    typedef GrGLGradientEffect INHERITED;
379d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com};
380d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
38198e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com/////////////////////////////////////////////////////////////////////
38298e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com
38398e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.comclass GrLinearGradient : public GrGradientEffect {
38498e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.compublic:
38598e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com
386b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt    static GrFragmentProcessor* Create(GrContext* ctx,
387b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt                                       const SkLinearGradient& shader,
388b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt                                       const SkMatrix& matrix,
389b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt                                       SkShader::TileMode tm) {
3904a339529612a43871d021877e58698e067d6c4cdbsalomon        return new GrLinearGradient(ctx, shader, matrix, tm);
3910ac6af49975c54c2debf41e9200af416ecd2d973bsalomon@google.com    }
3920ac6af49975c54c2debf41e9200af416ecd2d973bsalomon@google.com
39398e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com    virtual ~GrLinearGradient() { }
39498e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com
39536352bf5e38f45a70ee4f4fc132a38048d38206dmtklein    const char* name() const override { return "Linear Gradient"; }
396eb2a6761654307e8aeeeaabdd63c6bf9ab0411e9joshualitt
39798e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.comprivate:
3980ac6af49975c54c2debf41e9200af416ecd2d973bsalomon@google.com    GrLinearGradient(GrContext* ctx,
3990ac6af49975c54c2debf41e9200af416ecd2d973bsalomon@google.com                     const SkLinearGradient& shader,
4000ac6af49975c54c2debf41e9200af416ecd2d973bsalomon@google.com                     const SkMatrix& matrix,
4010ac6af49975c54c2debf41e9200af416ecd2d973bsalomon@google.com                     SkShader::TileMode tm)
4024a339529612a43871d021877e58698e067d6c4cdbsalomon        : INHERITED(ctx, shader, matrix, tm) {
403eb2a6761654307e8aeeeaabdd63c6bf9ab0411e9joshualitt        this->initClassID<GrLinearGradient>();
404eb2a6761654307e8aeeeaabdd63c6bf9ab0411e9joshualitt    }
4054b3050b410254d0cb38df9a30ae2e209124fa1a2wangyix
40657d3b039c635945e1dc2fcbac3462ed8bfedb068egdaniel    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
407385fe4d4b62d7d1dd76116dd570df3290a2f487bhalcanary        return new GrGLLinearGradient(*this);
408b1daa86732fe70aa4630c89d75ff0fd619d77c77wangyix    }
409b1daa86732fe70aa4630c89d75ff0fd619d77c77wangyix
41057d3b039c635945e1dc2fcbac3462ed8bfedb068egdaniel    virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
41157d3b039c635945e1dc2fcbac3462ed8bfedb068egdaniel                                       GrProcessorKeyBuilder* b) const override {
4124b3050b410254d0cb38df9a30ae2e209124fa1a2wangyix        GrGLLinearGradient::GenKey(*this, caps, b);
4134b3050b410254d0cb38df9a30ae2e209124fa1a2wangyix    }
4144b3050b410254d0cb38df9a30ae2e209124fa1a2wangyix
415b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
41698e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com
41798e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com    typedef GrGradientEffect INHERITED;
41898e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com};
41998e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com
42098e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com/////////////////////////////////////////////////////////////////////
42198e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com
422b0a8a377f832c59cee939ad721e1f87d378b7142joshualittGR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
423d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com
424c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomonconst GrFragmentProcessor* GrLinearGradient::TestCreate(GrProcessorTestData* d) {
4250067ff5e0f85084dd2b5ad9886b526482b89a116joshualitt    SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
4260067ff5e0f85084dd2b5ad9886b526482b89a116joshualitt                        {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
427d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com
428d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com    SkColor colors[kMaxRandomGradientColors];
429d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com    SkScalar stopsArray[kMaxRandomGradientColors];
430d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com    SkScalar* stops = stopsArray;
431d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com    SkShader::TileMode tm;
4320067ff5e0f85084dd2b5ad9886b526482b89a116joshualitt    int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
433d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
434d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com                                                                 colors, stops, colorCount,
435d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com                                                                 tm));
436c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon    const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
4374a339529612a43871d021877e58698e067d6c4cdbsalomon        GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
438c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon    GrAlwaysAssert(fp);
439b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt    return fp;
440d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com}
441d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com
442d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com/////////////////////////////////////////////////////////////////////
443d472620458e2383e6dd949f4e1aaf61160717ffebsalomon@google.com
4447c157a988845fb00f9024d6db6dda142c3458033wangyixvoid GrGLLinearGradient::emitCode(EmitArgs& args) {
4457c157a988845fb00f9024d6db6dda142c3458033wangyix    const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
4467ea439b2203855db97330b25945b87dd4b170b8begdaniel    this->emitUniforms(args.fUniformHandler, ge);
4474ca2e6034365ad280ec64473f7f1d72ebd8335e4egdaniel    SkString t = args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0);
448d8b5faca043100d7a1e4594b4d10e462532af390bsalomon@google.com    t.append(".x");
4497ea439b2203855db97330b25945b87dd4b170b8begdaniel    this->emitColor(args.fFragBuilder,
4507ea439b2203855db97330b25945b87dd4b170b8begdaniel                    args.fUniformHandler,
451a2e3e0f7f8ceed2ab152428d7ee2812ad8c842c3egdaniel                    args.fGLSLCaps,
4524ca2e6034365ad280ec64473f7f1d72ebd8335e4egdaniel                    ge, t.c_str(),
4534ca2e6034365ad280ec64473f7f1d72ebd8335e4egdaniel                    args.fOutputColor,
4544ca2e6034365ad280ec64473f7f1d72ebd8335e4egdaniel                    args.fInputColor,
4557c157a988845fb00f9024d6db6dda142c3458033wangyix                    args.fSamplers);
456d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com}
457d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
458d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com/////////////////////////////////////////////////////////////////////
459d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
460c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomonconst GrFragmentProcessor* SkLinearGradient::asFragmentProcessor(
461c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon                                                 GrContext* context,
462c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon                                                 const SkMatrix& viewm,
463c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon                                                 const SkMatrix* localMatrix,
4644a339529612a43871d021877e58698e067d6c4cdbsalomon                                                 SkFilterQuality) const {
46549f085dddff10473b6ebf832a974288300224e60bsalomon    SkASSERT(context);
4663f3b3d003527861dc0bd89733857576408906431mtklein
467dfdb7e5240276493077b7c6e1f3cc8b8a0e195babsalomon@google.com    SkMatrix matrix;
468f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com    if (!this->getLocalMatrix().invert(&matrix)) {
469c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon        return nullptr;
470dfdb7e5240276493077b7c6e1f3cc8b8a0e195babsalomon@google.com    }
47196fb7489ba46909c3f81bb2d94755e7d4ccb5fadcommit-bot@chromium.org    if (localMatrix) {
47296fb7489ba46909c3f81bb2d94755e7d4ccb5fadcommit-bot@chromium.org        SkMatrix inv;
47396fb7489ba46909c3f81bb2d94755e7d4ccb5fadcommit-bot@chromium.org        if (!localMatrix->invert(&inv)) {
474c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon            return nullptr;
47596fb7489ba46909c3f81bb2d94755e7d4ccb5fadcommit-bot@chromium.org        }
47696fb7489ba46909c3f81bb2d94755e7d4ccb5fadcommit-bot@chromium.org        matrix.postConcat(inv);
47796fb7489ba46909c3f81bb2d94755e7d4ccb5fadcommit-bot@chromium.org    }
478f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com    matrix.postConcat(fPtsToUnit);
4793f3b3d003527861dc0bd89733857576408906431mtklein
480c21b09eec91c9e263cb0b88467ea44e348ed4962bsalomon    SkAutoTUnref<const GrFragmentProcessor> inner(
4814a339529612a43871d021877e58698e067d6c4cdbsalomon        GrLinearGradient::Create(context, *this, matrix, fTileMode));
482f1b7a1d82860e106ed7d3e0e876419e65783fb84bsalomon    return GrFragmentProcessor::MulOutputByInputAlpha(inner);
483d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com}
484d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
485cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com
486cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com#endif
48776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
4880f10f7bf1fb43ca6346dc220a076773b1f19a367commit-bot@chromium.org#ifndef SK_IGNORE_TO_STRING
48976f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.comvoid SkLinearGradient::toString(SkString* str) const {
49076f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append("SkLinearGradient (");
49176f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
49276f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
49376f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
49476f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
49576f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    this->INHERITED::toString(str);
49676f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
49776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(")");
49876f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com}
49976f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com#endif
500f3182ebc72db2bf2e24119d5cea05f270473a491reed
501f3182ebc72db2bf2e24119d5cea05f270473a491reed///////////////////////////////////////////////////////////////////////////////////////////////////
502f3182ebc72db2bf2e24119d5cea05f270473a491reed
503f3182ebc72db2bf2e24119d5cea05f270473a491reed#include "SkNx.h"
504f3182ebc72db2bf2e24119d5cea05f270473a491reed
505f3182ebc72db2bf2e24119d5cea05f270473a491reedstatic const SkLinearGradient::LinearGradientContext::Rec*
506f3182ebc72db2bf2e24119d5cea05f270473a491reedfind_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
507f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(tiledX >= 0 && tiledX <= 1);
508f3182ebc72db2bf2e24119d5cea05f270473a491reed
509f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
510f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
511f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(rec[0].fPos <= rec[1].fPos);
512f3182ebc72db2bf2e24119d5cea05f270473a491reed    rec += 1;
513f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman    while (rec->fPos < tiledX || rec->fPosScale == 0) {
514f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
515f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
516f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(rec[0].fPos <= rec[1].fPos);
517f3182ebc72db2bf2e24119d5cea05f270473a491reed        rec += 1;
518f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
519f3182ebc72db2bf2e24119d5cea05f270473a491reed    return rec - 1;
520f3182ebc72db2bf2e24119d5cea05f270473a491reed}
521f3182ebc72db2bf2e24119d5cea05f270473a491reed
522f3182ebc72db2bf2e24119d5cea05f270473a491reedstatic const SkLinearGradient::LinearGradientContext::Rec*
523f3182ebc72db2bf2e24119d5cea05f270473a491reedfind_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
524f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(tiledX >= 0 && tiledX <= 1);
525f3182ebc72db2bf2e24119d5cea05f270473a491reed
526f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
527f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
528f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(rec[0].fPos <= rec[1].fPos);
529f2b8662b5c73e03648ed1a154b717e354753a0e1lsalzman    while (tiledX < rec->fPos || rec[1].fPosScale == 0) {
530f3182ebc72db2bf2e24119d5cea05f270473a491reed        rec -= 1;
531f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
532f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
533f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(rec[0].fPos <= rec[1].fPos);
534f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
535f3182ebc72db2bf2e24119d5cea05f270473a491reed    return rec;
536f3182ebc72db2bf2e24119d5cea05f270473a491reed}
537f3182ebc72db2bf2e24119d5cea05f270473a491reed
538f3182ebc72db2bf2e24119d5cea05f270473a491reedtemplate <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x) {
539f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkPMColor c;
540507ef6d68115ae9e6d884bb36436a1463523d893mtklein    SkNx_cast<uint8_t>(x).store(&c);
541f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (apply_alpha) {
542f3182ebc72db2bf2e24119d5cea05f270473a491reed        c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c),
543f3182ebc72db2bf2e24119d5cea05f270473a491reed                              SkGetPackedG32(c), SkGetPackedB32(c));
544f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
545f3182ebc72db2bf2e24119d5cea05f270473a491reed    return c;
546f3182ebc72db2bf2e24119d5cea05f270473a491reed}
547f3182ebc72db2bf2e24119d5cea05f270473a491reed
548f3182ebc72db2bf2e24119d5cea05f270473a491reedtemplate <bool apply_alpha> void fill(SkPMColor dst[], int count,
549f3182ebc72db2bf2e24119d5cea05f270473a491reed                                      const Sk4f& c4, const Sk4f& c4other) {
550f3182ebc72db2bf2e24119d5cea05f270473a491reed    sk_memset32_dither(dst, trunc_from_255<apply_alpha>(c4),
551f3182ebc72db2bf2e24119d5cea05f270473a491reed                       trunc_from_255<apply_alpha>(c4other), count);
552f3182ebc72db2bf2e24119d5cea05f270473a491reed}
553f3182ebc72db2bf2e24119d5cea05f270473a491reed
554f3182ebc72db2bf2e24119d5cea05f270473a491reedtemplate <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
555f3182ebc72db2bf2e24119d5cea05f270473a491reed    // Assumes that c4 does not need to be dithered.
556f3182ebc72db2bf2e24119d5cea05f270473a491reed    sk_memset32(dst, trunc_from_255<apply_alpha>(c4), count);
557f3182ebc72db2bf2e24119d5cea05f270473a491reed}
558f3182ebc72db2bf2e24119d5cea05f270473a491reed
559f3182ebc72db2bf2e24119d5cea05f270473a491reed/*
560f3182ebc72db2bf2e24119d5cea05f270473a491reed *  TODOs
561f3182ebc72db2bf2e24119d5cea05f270473a491reed *
562f3182ebc72db2bf2e24119d5cea05f270473a491reed *  - tilemodes
563f3182ebc72db2bf2e24119d5cea05f270473a491reed *  - interp before or after premul
564f3182ebc72db2bf2e24119d5cea05f270473a491reed *  - perspective
565f3182ebc72db2bf2e24119d5cea05f270473a491reed *  - optimizations
566f3182ebc72db2bf2e24119d5cea05f270473a491reed *      - use fixed (32bit or 16bit) instead of floats?
567f3182ebc72db2bf2e24119d5cea05f270473a491reed */
568f3182ebc72db2bf2e24119d5cea05f270473a491reed
569f3182ebc72db2bf2e24119d5cea05f270473a491reedstatic Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
570de3aac8cea8a7113f31591e6e02c51fe0ac45280reed    SkASSERT(fx >= rec[0].fPos);
571de3aac8cea8a7113f31591e6e02c51fe0ac45280reed    SkASSERT(fx <= rec[1].fPos);
572de3aac8cea8a7113f31591e6e02c51fe0ac45280reed
573f3182ebc72db2bf2e24119d5cea05f270473a491reed    const float p0 = rec[0].fPos;
574f3182ebc72db2bf2e24119d5cea05f270473a491reed    const Sk4f c0 = rec[0].fColor;
575f3182ebc72db2bf2e24119d5cea05f270473a491reed    const Sk4f c1 = rec[1].fColor;
576f3182ebc72db2bf2e24119d5cea05f270473a491reed    const Sk4f diffc = c1 - c0;
577f3182ebc72db2bf2e24119d5cea05f270473a491reed    const float scale = rec[1].fPosScale;
578f3182ebc72db2bf2e24119d5cea05f270473a491reed    const float t = (fx - p0) * scale;
579f3182ebc72db2bf2e24119d5cea05f270473a491reed    return c0 + Sk4f(t) * diffc;
580f3182ebc72db2bf2e24119d5cea05f270473a491reed}
581f3182ebc72db2bf2e24119d5cea05f270473a491reed
582f3182ebc72db2bf2e24119d5cea05f270473a491reedtemplate <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
583f3182ebc72db2bf2e24119d5cea05f270473a491reed                                      const Sk4f& dither0, const Sk4f& dither1) {
584f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f dc2 = dc + dc;
585f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f dc4 = dc2 + dc2;
586f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f cd0 = c + dither0;
587f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f cd1 = c + dc + dither1;
588f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f cd2 = cd0 + dc2;
589f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f cd3 = cd1 + dc2;
590f3182ebc72db2bf2e24119d5cea05f270473a491reed    while (n >= 4) {
5919db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein        if (!apply_alpha) {
5926f37b4a4757ea3eb00c76162cc37f8a56c3b8bdbmtklein            Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
5939db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein            dstC += 4;
5949db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein        } else {
5959db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein            *dstC++ = trunc_from_255<apply_alpha>(cd0);
5969db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein            *dstC++ = trunc_from_255<apply_alpha>(cd1);
5979db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein            *dstC++ = trunc_from_255<apply_alpha>(cd2);
5989db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein            *dstC++ = trunc_from_255<apply_alpha>(cd3);
5999db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein        }
600f3182ebc72db2bf2e24119d5cea05f270473a491reed        cd0 = cd0 + dc4;
601f3182ebc72db2bf2e24119d5cea05f270473a491reed        cd1 = cd1 + dc4;
602f3182ebc72db2bf2e24119d5cea05f270473a491reed        cd2 = cd2 + dc4;
603f3182ebc72db2bf2e24119d5cea05f270473a491reed        cd3 = cd3 + dc4;
604f3182ebc72db2bf2e24119d5cea05f270473a491reed        n -= 4;
605f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
606f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (n & 2) {
607f3182ebc72db2bf2e24119d5cea05f270473a491reed        *dstC++ = trunc_from_255<apply_alpha>(cd0);
608f3182ebc72db2bf2e24119d5cea05f270473a491reed        *dstC++ = trunc_from_255<apply_alpha>(cd1);
609f3182ebc72db2bf2e24119d5cea05f270473a491reed        cd0 = cd0 + dc2;
610f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
611f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (n & 1) {
612f3182ebc72db2bf2e24119d5cea05f270473a491reed        *dstC++ = trunc_from_255<apply_alpha>(cd0);
613f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
614f3182ebc72db2bf2e24119d5cea05f270473a491reed}
615f3182ebc72db2bf2e24119d5cea05f270473a491reed
616f3182ebc72db2bf2e24119d5cea05f270473a491reedtemplate <bool apply_alpha, bool dx_is_pos>
617f3182ebc72db2bf2e24119d5cea05f270473a491reedvoid SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
618f3182ebc72db2bf2e24119d5cea05f270473a491reed                                                              float fx, float dx, float invDx,
619f3182ebc72db2bf2e24119d5cea05f270473a491reed                                                              const float dither[2]) {
620f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f dither0(dither[0]);
621f3182ebc72db2bf2e24119d5cea05f270473a491reed    Sk4f dither1(dither[1]);
622f3182ebc72db2bf2e24119d5cea05f270473a491reed    const Rec* rec = fRecs.begin();
623f3182ebc72db2bf2e24119d5cea05f270473a491reed
624f3182ebc72db2bf2e24119d5cea05f270473a491reed    const Sk4f dx4 = Sk4f(dx);
625f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
626f3182ebc72db2bf2e24119d5cea05f270473a491reed
627f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (dx_is_pos) {
628f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (fx < 0) {
629f3182ebc72db2bf2e24119d5cea05f270473a491reed            int n = SkTMin(SkFloatToIntFloor(-fx * invDx) + 1, count);
630f3182ebc72db2bf2e24119d5cea05f270473a491reed            fill<apply_alpha>(dstC, n, rec[0].fColor);
631f3182ebc72db2bf2e24119d5cea05f270473a491reed            count -= n;
632f3182ebc72db2bf2e24119d5cea05f270473a491reed            dstC += n;
633f3182ebc72db2bf2e24119d5cea05f270473a491reed            fx += n * dx;
634f3182ebc72db2bf2e24119d5cea05f270473a491reed            SkASSERT(0 == count || fx >= 0);
635f3182ebc72db2bf2e24119d5cea05f270473a491reed            if (n & 1) {
636f3182ebc72db2bf2e24119d5cea05f270473a491reed                SkTSwap(dither0, dither1);
637f3182ebc72db2bf2e24119d5cea05f270473a491reed            }
638f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
639f3182ebc72db2bf2e24119d5cea05f270473a491reed    } else { // dx < 0
640f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (fx > 1) {
641f3182ebc72db2bf2e24119d5cea05f270473a491reed            int n = SkTMin(SkFloatToIntFloor((1 - fx) * invDx) + 1, count);
642f3182ebc72db2bf2e24119d5cea05f270473a491reed            fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
643f3182ebc72db2bf2e24119d5cea05f270473a491reed            count -= n;
644f3182ebc72db2bf2e24119d5cea05f270473a491reed            dstC += n;
645f3182ebc72db2bf2e24119d5cea05f270473a491reed            fx += n * dx;
646f3182ebc72db2bf2e24119d5cea05f270473a491reed            SkASSERT(0 == count || fx <= 1);
647f3182ebc72db2bf2e24119d5cea05f270473a491reed            if (n & 1) {
648f3182ebc72db2bf2e24119d5cea05f270473a491reed                SkTSwap(dither0, dither1);
649f3182ebc72db2bf2e24119d5cea05f270473a491reed            }
650f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
651f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
652f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(count >= 0);
653f3182ebc72db2bf2e24119d5cea05f270473a491reed
654f3182ebc72db2bf2e24119d5cea05f270473a491reed    const Rec* r;
655f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (dx_is_pos) {
656f3182ebc72db2bf2e24119d5cea05f270473a491reed        r = fRecs.begin();                      // start at the beginning
657f3182ebc72db2bf2e24119d5cea05f270473a491reed    } else {
658f3182ebc72db2bf2e24119d5cea05f270473a491reed        r = fRecs.begin() + fRecs.count() - 2;  // start at the end
659f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
660f3182ebc72db2bf2e24119d5cea05f270473a491reed
661f3182ebc72db2bf2e24119d5cea05f270473a491reed    while (count > 0) {
662f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (dx_is_pos) {
663f3182ebc72db2bf2e24119d5cea05f270473a491reed            if (fx >= 1) {
664f3182ebc72db2bf2e24119d5cea05f270473a491reed                fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
665f3182ebc72db2bf2e24119d5cea05f270473a491reed                return;
666f3182ebc72db2bf2e24119d5cea05f270473a491reed            }
667f3182ebc72db2bf2e24119d5cea05f270473a491reed        } else {    // dx < 0
668f3182ebc72db2bf2e24119d5cea05f270473a491reed            if (fx <= 0) {
669f3182ebc72db2bf2e24119d5cea05f270473a491reed                fill<apply_alpha>(dstC, count, rec[0].fColor);
670f3182ebc72db2bf2e24119d5cea05f270473a491reed                return;
671f3182ebc72db2bf2e24119d5cea05f270473a491reed            }
672f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
673f3182ebc72db2bf2e24119d5cea05f270473a491reed
674f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (dx_is_pos) {
675f3182ebc72db2bf2e24119d5cea05f270473a491reed            r = find_forward(r, fx);
676f3182ebc72db2bf2e24119d5cea05f270473a491reed        } else {
677f3182ebc72db2bf2e24119d5cea05f270473a491reed            r = find_backward(r, fx);
678f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
679f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
680f3182ebc72db2bf2e24119d5cea05f270473a491reed
681f3182ebc72db2bf2e24119d5cea05f270473a491reed        const float p0 = r[0].fPos;
682f3182ebc72db2bf2e24119d5cea05f270473a491reed        const Sk4f c0 = r[0].fColor;
683f3182ebc72db2bf2e24119d5cea05f270473a491reed        const float p1 = r[1].fPos;
684f3182ebc72db2bf2e24119d5cea05f270473a491reed        const Sk4f diffc = Sk4f(r[1].fColor) - c0;
685f3182ebc72db2bf2e24119d5cea05f270473a491reed        const float scale = r[1].fPosScale;
686f3182ebc72db2bf2e24119d5cea05f270473a491reed        const float t = (fx - p0) * scale;
687f3182ebc72db2bf2e24119d5cea05f270473a491reed        const Sk4f c = c0 + Sk4f(t) * diffc;
688f3182ebc72db2bf2e24119d5cea05f270473a491reed        const Sk4f dc = diffc * dx4 * Sk4f(scale);
689f3182ebc72db2bf2e24119d5cea05f270473a491reed
690f3182ebc72db2bf2e24119d5cea05f270473a491reed        int n;
691f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (dx_is_pos) {
692f3182ebc72db2bf2e24119d5cea05f270473a491reed            n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
693f3182ebc72db2bf2e24119d5cea05f270473a491reed        } else {
694f3182ebc72db2bf2e24119d5cea05f270473a491reed            n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
695f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
696f3182ebc72db2bf2e24119d5cea05f270473a491reed
697f3182ebc72db2bf2e24119d5cea05f270473a491reed        fx += n * dx;
698aeab8ea4d7492dc996fa72de7298de81f8deed93reed        // fx should now outside of the p0..p1 interval. However, due to float precision loss,
699aeab8ea4d7492dc996fa72de7298de81f8deed93reed        // its possible that fx is slightly too small/large, so we clamp it.
700f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (dx_is_pos) {
701aeab8ea4d7492dc996fa72de7298de81f8deed93reed            fx = SkTMax(fx, p1);
702f3182ebc72db2bf2e24119d5cea05f270473a491reed        } else {
703aeab8ea4d7492dc996fa72de7298de81f8deed93reed            fx = SkTMin(fx, p0);
704f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
705f3182ebc72db2bf2e24119d5cea05f270473a491reed
706f3182ebc72db2bf2e24119d5cea05f270473a491reed        ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
707f3182ebc72db2bf2e24119d5cea05f270473a491reed        dstC += n;
708f3182ebc72db2bf2e24119d5cea05f270473a491reed        SkASSERT(dstC <= endDstC);
7099db43ac4ee1a83a4f7b332fe6c00f592b6237349mtklein
710f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (n & 1) {
711f3182ebc72db2bf2e24119d5cea05f270473a491reed            SkTSwap(dither0, dither1);
712f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
713aeab8ea4d7492dc996fa72de7298de81f8deed93reed
714aeab8ea4d7492dc996fa72de7298de81f8deed93reed        count -= n;
715aeab8ea4d7492dc996fa72de7298de81f8deed93reed        SkASSERT(count >= 0);
716f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
717f3182ebc72db2bf2e24119d5cea05f270473a491reed}
718f3182ebc72db2bf2e24119d5cea05f270473a491reed
719f3182ebc72db2bf2e24119d5cea05f270473a491reedvoid SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
720f3182ebc72db2bf2e24119d5cea05f270473a491reed                                                           int count) {
721f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(count > 0);
722f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
723f3182ebc72db2bf2e24119d5cea05f270473a491reed
724f3182ebc72db2bf2e24119d5cea05f270473a491reed    SkPoint srcPt;
725f3182ebc72db2bf2e24119d5cea05f270473a491reed    fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
726f3182ebc72db2bf2e24119d5cea05f270473a491reed    float fx = srcPt.x();
727f3182ebc72db2bf2e24119d5cea05f270473a491reed    const float dx = fDstToIndex.getScaleX();
728f3182ebc72db2bf2e24119d5cea05f270473a491reed
729f3182ebc72db2bf2e24119d5cea05f270473a491reed    // Default our dither bias values to 1/2, (rounding), which is no dithering
730f3182ebc72db2bf2e24119d5cea05f270473a491reed    float dither0 = 0.5f;
731f3182ebc72db2bf2e24119d5cea05f270473a491reed    float dither1 = 0.5f;
732f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (fDither) {
733f3182ebc72db2bf2e24119d5cea05f270473a491reed        const float ditherCell[] = {
734f3182ebc72db2bf2e24119d5cea05f270473a491reed            1/8.0f,   5/8.0f,
735f3182ebc72db2bf2e24119d5cea05f270473a491reed            7/8.0f,   3/8.0f,
736f3182ebc72db2bf2e24119d5cea05f270473a491reed        };
737f3182ebc72db2bf2e24119d5cea05f270473a491reed        const int rowIndex = (y & 1) << 1;
738f3182ebc72db2bf2e24119d5cea05f270473a491reed        dither0 = ditherCell[rowIndex];
739f3182ebc72db2bf2e24119d5cea05f270473a491reed        dither1 = ditherCell[rowIndex + 1];
740f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (x & 1) {
741f3182ebc72db2bf2e24119d5cea05f270473a491reed            SkTSwap(dither0, dither1);
742f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
743f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
744f3182ebc72db2bf2e24119d5cea05f270473a491reed    const float dither[2] = { dither0, dither1 };
745f3182ebc72db2bf2e24119d5cea05f270473a491reed    const float invDx = 1 / dx;
746f3182ebc72db2bf2e24119d5cea05f270473a491reed
7472b46913c99319de9567390e8a543057be0c162e3fmalita    if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
748de3aac8cea8a7113f31591e6e02c51fe0ac45280reed        const float pinFx = SkTPin(fx, 0.0f, 1.0f);
749de3aac8cea8a7113f31591e6e02c51fe0ac45280reed        Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
750f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (fApplyAlphaAfterInterp) {
751f3182ebc72db2bf2e24119d5cea05f270473a491reed            fill<true>(dstC, count, c + dither0, c + dither1);
752f3182ebc72db2bf2e24119d5cea05f270473a491reed        } else {
753f3182ebc72db2bf2e24119d5cea05f270473a491reed            fill<false>(dstC, count, c + dither0, c + dither1);
754f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
755f3182ebc72db2bf2e24119d5cea05f270473a491reed        return;
756f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
757f3182ebc72db2bf2e24119d5cea05f270473a491reed
758f3182ebc72db2bf2e24119d5cea05f270473a491reed    if (dx > 0) {
759f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (fApplyAlphaAfterInterp) {
760f3182ebc72db2bf2e24119d5cea05f270473a491reed            this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
761f3182ebc72db2bf2e24119d5cea05f270473a491reed        } else {
762f3182ebc72db2bf2e24119d5cea05f270473a491reed            this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
763f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
764f3182ebc72db2bf2e24119d5cea05f270473a491reed    } else {
765f3182ebc72db2bf2e24119d5cea05f270473a491reed        if (fApplyAlphaAfterInterp) {
766f3182ebc72db2bf2e24119d5cea05f270473a491reed            this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
767f3182ebc72db2bf2e24119d5cea05f270473a491reed        } else {
768f3182ebc72db2bf2e24119d5cea05f270473a491reed            this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
769f3182ebc72db2bf2e24119d5cea05f270473a491reed        }
770f3182ebc72db2bf2e24119d5cea05f270473a491reed    }
771f3182ebc72db2bf2e24119d5cea05f270473a491reed}
772