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 "SkLinearGradient.h"
9
10static inline int repeat_bits(int x, const int bits) {
11    return x & ((1 << bits) - 1);
12}
13
14static inline int repeat_8bits(int x) {
15    return x & 0xFF;
16}
17
18// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
19// See http://code.google.com/p/skia/issues/detail?id=472
20#if defined(_MSC_VER) && (_MSC_VER >= 1600)
21#pragma optimize("", off)
22#endif
23
24static inline int mirror_bits(int x, const int bits) {
25    if (x & (1 << bits)) {
26        x = ~x;
27    }
28    return x & ((1 << bits) - 1);
29}
30
31static inline int mirror_8bits(int x) {
32    if (x & 256) {
33        x = ~x;
34    }
35    return x & 255;
36}
37
38#if defined(_MSC_VER) && (_MSC_VER >= 1600)
39#pragma optimize("", on)
40#endif
41
42static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
43    SkVector    vec = pts[1] - pts[0];
44    SkScalar    mag = vec.length();
45    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
46
47    vec.scale(inv);
48    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
49    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
50    matrix->postScale(inv, inv);
51}
52
53///////////////////////////////////////////////////////////////////////////////
54
55SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
56    : SkGradientShaderBase(desc)
57    , fStart(pts[0])
58    , fEnd(pts[1]) {
59    pts_to_unit_matrix(pts, &fPtsToUnit);
60}
61
62SkLinearGradient::SkLinearGradient(SkFlattenableReadBuffer& buffer)
63    : INHERITED(buffer)
64    , fStart(buffer.readPoint())
65    , fEnd(buffer.readPoint()) {
66}
67
68void SkLinearGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
69    this->INHERITED::flatten(buffer);
70    buffer.writePoint(fStart);
71    buffer.writePoint(fEnd);
72}
73
74bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint,
75                                 const SkMatrix& matrix) {
76    if (!this->INHERITED::setContext(device, paint, matrix)) {
77        return false;
78    }
79
80    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
81    if ((fDstToIndex.getType() & ~mask) == 0) {
82        // when we dither, we are (usually) not const-in-Y
83        if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
84            // only claim this if we do have a 16bit mode (i.e. none of our
85            // colors have alpha), and if we are not dithering (which obviously
86            // is not const in Y).
87            fFlags |= SkShader::kConstInY16_Flag;
88        }
89    }
90    return true;
91}
92
93#define NO_CHECK_ITER               \
94    do {                            \
95    unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \
96    SkASSERT(fi <= 0xFF);           \
97    fx += dx;                       \
98    *dstC++ = cache[toggle + fi];   \
99    toggle = next_dither_toggle(toggle); \
100    } while (0)
101
102namespace {
103
104typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
105                                SkPMColor* dstC, const SkPMColor* cache,
106                                int toggle, int count);
107
108// Linear interpolation (lerp) is unnecessary if there are no sharp
109// discontinuities in the gradient - which must be true if there are
110// only 2 colors - but it's cheap.
111void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
112                                    SkPMColor* SK_RESTRICT dstC,
113                                    const SkPMColor* SK_RESTRICT cache,
114                                    int toggle, int count) {
115    // We're a vertical gradient, so no change in a span.
116    // If colors change sharply across the gradient, dithering is
117    // insufficient (it subsamples the color space) and we need to lerp.
118    unsigned fullIndex = proc(fx);
119    unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
120    unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
121
122    int index0 = fi + toggle;
123    int index1 = index0;
124    if (fi < SkGradientShaderBase::kCache32Count - 1) {
125        index1 += 1;
126    }
127    SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
128    index0 ^= SkGradientShaderBase::kDitherStride32;
129    index1 ^= SkGradientShaderBase::kDitherStride32;
130    SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
131    sk_memset32_dither(dstC, lerp, dlerp, count);
132}
133
134void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
135                            SkPMColor* SK_RESTRICT dstC,
136                            const SkPMColor* SK_RESTRICT cache,
137                            int toggle, int count) {
138    SkClampRange range;
139    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
140
141    if ((count = range.fCount0) > 0) {
142        sk_memset32_dither(dstC,
143            cache[toggle + range.fV0],
144            cache[next_dither_toggle(toggle) + range.fV0],
145            count);
146        dstC += count;
147    }
148    if ((count = range.fCount1) > 0) {
149        int unroll = count >> 3;
150        fx = range.fFx1;
151        for (int i = 0; i < unroll; i++) {
152            NO_CHECK_ITER;  NO_CHECK_ITER;
153            NO_CHECK_ITER;  NO_CHECK_ITER;
154            NO_CHECK_ITER;  NO_CHECK_ITER;
155            NO_CHECK_ITER;  NO_CHECK_ITER;
156        }
157        if ((count &= 7) > 0) {
158            do {
159                NO_CHECK_ITER;
160            } while (--count != 0);
161        }
162    }
163    if ((count = range.fCount2) > 0) {
164        sk_memset32_dither(dstC,
165            cache[toggle + range.fV1],
166            cache[next_dither_toggle(toggle) + range.fV1],
167            count);
168    }
169}
170
171void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
172                             SkPMColor* SK_RESTRICT dstC,
173                             const SkPMColor* SK_RESTRICT cache,
174                             int toggle, int count) {
175    do {
176        unsigned fi = mirror_8bits(fx >> 8);
177        SkASSERT(fi <= 0xFF);
178        fx += dx;
179        *dstC++ = cache[toggle + fi];
180        toggle = next_dither_toggle(toggle);
181    } while (--count != 0);
182}
183
184void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
185        SkPMColor* SK_RESTRICT dstC,
186        const SkPMColor* SK_RESTRICT cache,
187        int toggle, int count) {
188    do {
189        unsigned fi = repeat_8bits(fx >> 8);
190        SkASSERT(fi <= 0xFF);
191        fx += dx;
192        *dstC++ = cache[toggle + fi];
193        toggle = next_dither_toggle(toggle);
194    } while (--count != 0);
195}
196
197}
198
199void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
200                                int count) {
201    SkASSERT(count > 0);
202
203    SkPoint             srcPt;
204    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
205    TileProc            proc = fTileProc;
206    const SkPMColor* SK_RESTRICT cache = this->getCache32();
207    int                 toggle = init_dither_toggle(x, y);
208
209    if (fDstToIndexClass != kPerspective_MatrixClass) {
210        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
211                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
212        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
213
214        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
215            SkFixed dxStorage[1];
216            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
217            dx = dxStorage[0];
218        } else {
219            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
220            dx = SkScalarToFixed(fDstToIndex.getScaleX());
221        }
222
223        LinearShadeProc shadeProc = shadeSpan_linear_repeat;
224        if (0 == dx) {
225            shadeProc = shadeSpan_linear_vertical_lerp;
226        } else if (SkShader::kClamp_TileMode == fTileMode) {
227            shadeProc = shadeSpan_linear_clamp;
228        } else if (SkShader::kMirror_TileMode == fTileMode) {
229            shadeProc = shadeSpan_linear_mirror;
230        } else {
231            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
232        }
233        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
234    } else {
235        SkScalar    dstX = SkIntToScalar(x);
236        SkScalar    dstY = SkIntToScalar(y);
237        do {
238            dstProc(fDstToIndex, dstX, dstY, &srcPt);
239            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
240            SkASSERT(fi <= 0xFFFF);
241            *dstC++ = cache[toggle + (fi >> kCache32Shift)];
242            toggle = next_dither_toggle(toggle);
243            dstX += SK_Scalar1;
244        } while (--count != 0);
245    }
246}
247
248SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap,
249                                                SkMatrix* matrix,
250                                                TileMode xy[]) const {
251    if (bitmap) {
252        this->getGradientTableBitmap(bitmap);
253    }
254    if (matrix) {
255        matrix->preConcat(fPtsToUnit);
256    }
257    if (xy) {
258        xy[0] = fTileMode;
259        xy[1] = kClamp_TileMode;
260    }
261    return kLinear_BitmapType;
262}
263
264SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
265    if (info) {
266        commonAsAGradient(info);
267        info->fPoint[0] = fStart;
268        info->fPoint[1] = fEnd;
269    }
270    return kLinear_GradientType;
271}
272
273static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
274                            int count) {
275    if (reinterpret_cast<uintptr_t>(dst) & 2) {
276        *dst++ = value;
277        count -= 1;
278        SkTSwap(value, other);
279    }
280
281    sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
282
283    if (count & 1) {
284        dst[count - 1] = value;
285    }
286}
287
288#define NO_CHECK_ITER_16                \
289    do {                                \
290    unsigned fi = fx >> SkGradientShaderBase::kCache16Shift;  \
291    SkASSERT(fi < SkGradientShaderBase::kCache16Count);       \
292    fx += dx;                           \
293    *dstC++ = cache[toggle + fi];       \
294    toggle = next_dither_toggle16(toggle);            \
295    } while (0)
296
297namespace {
298
299typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
300                                  uint16_t* dstC, const uint16_t* cache,
301                                  int toggle, int count);
302
303void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
304                                 uint16_t* SK_RESTRICT dstC,
305                                 const uint16_t* SK_RESTRICT cache,
306                                 int toggle, int count) {
307    // we're a vertical gradient, so no change in a span
308    unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
309    SkASSERT(fi < SkGradientShaderBase::kCache16Count);
310    dither_memset16(dstC, cache[toggle + fi],
311        cache[next_dither_toggle16(toggle) + fi], count);
312}
313
314void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
315                              uint16_t* SK_RESTRICT dstC,
316                              const uint16_t* SK_RESTRICT cache,
317                              int toggle, int count) {
318    SkClampRange range;
319    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
320
321    if ((count = range.fCount0) > 0) {
322        dither_memset16(dstC,
323            cache[toggle + range.fV0],
324            cache[next_dither_toggle16(toggle) + range.fV0],
325            count);
326        dstC += count;
327    }
328    if ((count = range.fCount1) > 0) {
329        int unroll = count >> 3;
330        fx = range.fFx1;
331        for (int i = 0; i < unroll; i++) {
332            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
333            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
334            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
335            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
336        }
337        if ((count &= 7) > 0) {
338            do {
339                NO_CHECK_ITER_16;
340            } while (--count != 0);
341        }
342    }
343    if ((count = range.fCount2) > 0) {
344        dither_memset16(dstC,
345            cache[toggle + range.fV1],
346            cache[next_dither_toggle16(toggle) + range.fV1],
347            count);
348    }
349}
350
351void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
352                               uint16_t* SK_RESTRICT dstC,
353                               const uint16_t* SK_RESTRICT cache,
354                               int toggle, int count) {
355    do {
356        unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift,
357                                        SkGradientShaderBase::kCache16Bits);
358        SkASSERT(fi < SkGradientShaderBase::kCache16Count);
359        fx += dx;
360        *dstC++ = cache[toggle + fi];
361        toggle = next_dither_toggle16(toggle);
362    } while (--count != 0);
363}
364
365void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
366                               uint16_t* SK_RESTRICT dstC,
367                               const uint16_t* SK_RESTRICT cache,
368                               int toggle, int count) {
369    do {
370        unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift,
371                                  SkGradientShaderBase::kCache16Bits);
372        SkASSERT(fi < SkGradientShaderBase::kCache16Count);
373        fx += dx;
374        *dstC++ = cache[toggle + fi];
375        toggle = next_dither_toggle16(toggle);
376    } while (--count != 0);
377}
378}
379
380void SkLinearGradient::shadeSpan16(int x, int y,
381                                  uint16_t* SK_RESTRICT dstC, int count) {
382    SkASSERT(count > 0);
383
384    SkPoint             srcPt;
385    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
386    TileProc            proc = fTileProc;
387    const uint16_t* SK_RESTRICT cache = this->getCache16();
388    int                 toggle = init_dither_toggle16(x, y);
389
390    if (fDstToIndexClass != kPerspective_MatrixClass) {
391        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
392                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
393        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
394
395        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
396            SkFixed dxStorage[1];
397            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
398            dx = dxStorage[0];
399        } else {
400            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
401            dx = SkScalarToFixed(fDstToIndex.getScaleX());
402        }
403
404        LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
405        if (SkFixedNearlyZero(dx)) {
406            shadeProc = shadeSpan16_linear_vertical;
407        } else if (SkShader::kClamp_TileMode == fTileMode) {
408            shadeProc = shadeSpan16_linear_clamp;
409        } else if (SkShader::kMirror_TileMode == fTileMode) {
410            shadeProc = shadeSpan16_linear_mirror;
411        } else {
412            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
413        }
414        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
415    } else {
416        SkScalar    dstX = SkIntToScalar(x);
417        SkScalar    dstY = SkIntToScalar(y);
418        do {
419            dstProc(fDstToIndex, dstX, dstY, &srcPt);
420            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
421            SkASSERT(fi <= 0xFFFF);
422
423            int index = fi >> kCache16Shift;
424            *dstC++ = cache[toggle + index];
425            toggle = next_dither_toggle16(toggle);
426
427            dstX += SK_Scalar1;
428        } while (--count != 0);
429    }
430}
431
432#if SK_SUPPORT_GPU
433
434#include "GrTBackendEffectFactory.h"
435
436/////////////////////////////////////////////////////////////////////
437
438class GrGLLinearGradient : public GrGLGradientEffect {
439public:
440
441    GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrDrawEffect&)
442                       : INHERITED (factory) { }
443
444    virtual ~GrGLLinearGradient() { }
445
446    virtual void emitCode(GrGLShaderBuilder*,
447                          const GrDrawEffect&,
448                          EffectKey,
449                          const char* outputColor,
450                          const char* inputColor,
451                          const TransformedCoordsArray&,
452                          const TextureSamplerArray&) SK_OVERRIDE;
453
454    static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
455        return GenBaseGradientKey(drawEffect);
456    }
457
458private:
459
460    typedef GrGLGradientEffect INHERITED;
461};
462
463/////////////////////////////////////////////////////////////////////
464
465class GrLinearGradient : public GrGradientEffect {
466public:
467
468    static GrEffectRef* Create(GrContext* ctx,
469                               const SkLinearGradient& shader,
470                               const SkMatrix& matrix,
471                               SkShader::TileMode tm) {
472        AutoEffectUnref effect(SkNEW_ARGS(GrLinearGradient, (ctx, shader, matrix, tm)));
473        return CreateEffectRef(effect);
474    }
475
476    virtual ~GrLinearGradient() { }
477
478    static const char* Name() { return "Linear Gradient"; }
479    const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
480        return GrTBackendEffectFactory<GrLinearGradient>::getInstance();
481    }
482
483    typedef GrGLLinearGradient GLEffect;
484
485private:
486    GrLinearGradient(GrContext* ctx,
487                     const SkLinearGradient& shader,
488                     const SkMatrix& matrix,
489                     SkShader::TileMode tm)
490        : INHERITED(ctx, shader, matrix, tm) { }
491    GR_DECLARE_EFFECT_TEST;
492
493    typedef GrGradientEffect INHERITED;
494};
495
496/////////////////////////////////////////////////////////////////////
497
498GR_DEFINE_EFFECT_TEST(GrLinearGradient);
499
500GrEffectRef* GrLinearGradient::TestCreate(SkRandom* random,
501                                          GrContext* context,
502                                          const GrDrawTargetCaps&,
503                                          GrTexture**) {
504    SkPoint points[] = {{random->nextUScalar1(), random->nextUScalar1()},
505                        {random->nextUScalar1(), random->nextUScalar1()}};
506
507    SkColor colors[kMaxRandomGradientColors];
508    SkScalar stopsArray[kMaxRandomGradientColors];
509    SkScalar* stops = stopsArray;
510    SkShader::TileMode tm;
511    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
512    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
513                                                                 colors, stops, colorCount,
514                                                                 tm));
515    SkPaint paint;
516    return shader->asNewEffect(context, paint);
517}
518
519/////////////////////////////////////////////////////////////////////
520
521void GrGLLinearGradient::emitCode(GrGLShaderBuilder* builder,
522                                  const GrDrawEffect&,
523                                  EffectKey key,
524                                  const char* outputColor,
525                                  const char* inputColor,
526                                  const TransformedCoordsArray& coords,
527                                  const TextureSamplerArray& samplers) {
528    this->emitUniforms(builder, key);
529    SkString t = builder->ensureFSCoords2D(coords, 0);
530    t.append(".x");
531    this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
532}
533
534/////////////////////////////////////////////////////////////////////
535
536GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const {
537    SkASSERT(NULL != context);
538    SkMatrix matrix;
539    if (!this->getLocalMatrix().invert(&matrix)) {
540        return NULL;
541    }
542    matrix.postConcat(fPtsToUnit);
543    return GrLinearGradient::Create(context, *this, matrix, fTileMode);
544}
545
546#else
547
548GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const {
549    SkDEBUGFAIL("Should not call in GPU-less build");
550    return NULL;
551}
552
553#endif
554
555#ifdef SK_DEVELOPER
556void SkLinearGradient::toString(SkString* str) const {
557    str->append("SkLinearGradient (");
558
559    str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
560    str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
561
562    this->INHERITED::toString(str);
563
564    str->append(")");
565}
566#endif
567