SkSweepGradient.cpp revision eaa674987f99687c0bfa337eaa27f00ffe3faa4b
1
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "SkSweepGradient.h"
10
11SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy,
12                                 const Descriptor& desc, const SkMatrix* localMatrix)
13    : SkGradientShaderBase(desc, localMatrix)
14    , fCenter(SkPoint::Make(cx, cy))
15{
16    fPtsToUnit.setTranslate(-cx, -cy);
17
18    // overwrite the tilemode to a canonical value (since sweep ignores it)
19    fTileMode = SkShader::kClamp_TileMode;
20}
21
22SkShader::BitmapType SkSweepGradient::asABitmap(SkBitmap* bitmap,
23    SkMatrix* matrix, SkShader::TileMode* xy) const {
24    if (bitmap) {
25        this->getGradientTableBitmap(bitmap);
26    }
27    if (matrix) {
28        *matrix = fPtsToUnit;
29    }
30    if (xy) {
31        xy[0] = fTileMode;
32        xy[1] = kClamp_TileMode;
33    }
34    return kSweep_BitmapType;
35}
36
37SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
38    if (info) {
39        commonAsAGradient(info);
40        info->fPoint[0] = fCenter;
41    }
42    return kSweep_GradientType;
43}
44
45SkSweepGradient::SkSweepGradient(SkReadBuffer& buffer)
46    : INHERITED(buffer),
47      fCenter(buffer.readPoint()) {
48}
49
50void SkSweepGradient::flatten(SkWriteBuffer& buffer) const {
51    this->INHERITED::flatten(buffer);
52    buffer.writePoint(fCenter);
53}
54
55size_t SkSweepGradient::contextSize() const {
56    return sizeof(SweepGradientContext);
57}
58
59SkShader::Context* SkSweepGradient::onCreateContext(const ContextRec& rec, void* storage) const {
60    return SkNEW_PLACEMENT_ARGS(storage, SweepGradientContext, (*this, rec));
61}
62
63SkSweepGradient::SweepGradientContext::SweepGradientContext(
64        const SkSweepGradient& shader, const ContextRec& rec)
65    : INHERITED(shader, rec) {}
66
67//  returns angle in a circle [0..2PI) -> [0..255]
68static unsigned SkATan2_255(float y, float x) {
69    //    static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
70    static const float g255Over2PI = 40.584510488433314f;
71
72    float result = sk_float_atan2(y, x);
73    if (result < 0) {
74        result += 2 * SK_ScalarPI;
75    }
76    SkASSERT(result >= 0);
77    // since our value is always >= 0, we can cast to int, which is faster than
78    // calling floorf()
79    int ir = (int)(result * g255Over2PI);
80    SkASSERT(ir >= 0 && ir <= 255);
81    return ir;
82}
83
84void SkSweepGradient::SweepGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
85                                                      int count) {
86    SkMatrix::MapXYProc proc = fDstToIndexProc;
87    const SkMatrix&     matrix = fDstToIndex;
88    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
89    int                 toggle = init_dither_toggle(x, y);
90    SkPoint             srcPt;
91
92    if (fDstToIndexClass != kPerspective_MatrixClass) {
93        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
94                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
95        SkScalar dx, fx = srcPt.fX;
96        SkScalar dy, fy = srcPt.fY;
97
98        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
99            SkFixed storage[2];
100            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
101                                      &storage[0], &storage[1]);
102            dx = SkFixedToScalar(storage[0]);
103            dy = SkFixedToScalar(storage[1]);
104        } else {
105            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
106            dx = matrix.getScaleX();
107            dy = matrix.getSkewY();
108        }
109
110        for (; count > 0; --count) {
111            *dstC++ = cache[toggle + SkATan2_255(fy, fx)];
112            fx += dx;
113            fy += dy;
114            toggle = next_dither_toggle(toggle);
115        }
116    } else {  // perspective case
117        for (int stop = x + count; x < stop; x++) {
118            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
119                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
120            *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)];
121            toggle = next_dither_toggle(toggle);
122        }
123    }
124}
125
126void SkSweepGradient::SweepGradientContext::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
127                                                        int count) {
128    SkMatrix::MapXYProc proc = fDstToIndexProc;
129    const SkMatrix&     matrix = fDstToIndex;
130    const uint16_t* SK_RESTRICT cache = fCache->getCache16();
131    int                 toggle = init_dither_toggle16(x, y);
132    SkPoint             srcPt;
133
134    if (fDstToIndexClass != kPerspective_MatrixClass) {
135        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
136                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
137        SkScalar dx, fx = srcPt.fX;
138        SkScalar dy, fy = srcPt.fY;
139
140        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
141            SkFixed storage[2];
142            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
143                                      &storage[0], &storage[1]);
144            dx = SkFixedToScalar(storage[0]);
145            dy = SkFixedToScalar(storage[1]);
146        } else {
147            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
148            dx = matrix.getScaleX();
149            dy = matrix.getSkewY();
150        }
151
152        for (; count > 0; --count) {
153            int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
154            *dstC++ = cache[toggle + index];
155            toggle = next_dither_toggle16(toggle);
156            fx += dx;
157            fy += dy;
158        }
159    } else {  // perspective case
160        for (int stop = x + count; x < stop; x++) {
161            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
162                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
163
164            int index = SkATan2_255(srcPt.fY, srcPt.fX);
165            index >>= (8 - kCache16Bits);
166            *dstC++ = cache[toggle + index];
167            toggle = next_dither_toggle16(toggle);
168        }
169    }
170}
171
172/////////////////////////////////////////////////////////////////////
173
174#if SK_SUPPORT_GPU
175
176#include "GrTBackendEffectFactory.h"
177
178class GrGLSweepGradient : public GrGLGradientEffect {
179public:
180
181    GrGLSweepGradient(const GrBackendEffectFactory& factory,
182                      const GrDrawEffect&) : INHERITED (factory) { }
183    virtual ~GrGLSweepGradient() { }
184
185    virtual void emitCode(GrGLShaderBuilder*,
186                          const GrDrawEffect&,
187                          EffectKey,
188                          const char* outputColor,
189                          const char* inputColor,
190                          const TransformedCoordsArray&,
191                          const TextureSamplerArray&) SK_OVERRIDE;
192
193    static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
194        return GenBaseGradientKey(drawEffect);
195    }
196
197private:
198
199    typedef GrGLGradientEffect INHERITED;
200
201};
202
203/////////////////////////////////////////////////////////////////////
204
205class GrSweepGradient : public GrGradientEffect {
206public:
207    static GrEffectRef* Create(GrContext* ctx,
208                               const SkSweepGradient& shader,
209                               const SkMatrix& matrix) {
210        AutoEffectUnref effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix)));
211        return CreateEffectRef(effect);
212    }
213    virtual ~GrSweepGradient() { }
214
215    static const char* Name() { return "Sweep Gradient"; }
216    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
217        return GrTBackendEffectFactory<GrSweepGradient>::getInstance();
218    }
219
220    typedef GrGLSweepGradient GLEffect;
221
222private:
223    GrSweepGradient(GrContext* ctx,
224                    const SkSweepGradient& shader,
225                    const SkMatrix& matrix)
226    : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
227    GR_DECLARE_EFFECT_TEST;
228
229    typedef GrGradientEffect INHERITED;
230};
231
232/////////////////////////////////////////////////////////////////////
233
234GR_DEFINE_EFFECT_TEST(GrSweepGradient);
235
236GrEffectRef* GrSweepGradient::TestCreate(SkRandom* random,
237                                         GrContext* context,
238                                         const GrDrawTargetCaps&,
239                                         GrTexture**) {
240    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
241
242    SkColor colors[kMaxRandomGradientColors];
243    SkScalar stopsArray[kMaxRandomGradientColors];
244    SkScalar* stops = stopsArray;
245    SkShader::TileMode tmIgnored;
246    int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
247    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
248                                                                colors, stops, colorCount));
249    SkPaint paint;
250    return shader->asNewEffect(context, paint, NULL);
251}
252
253/////////////////////////////////////////////////////////////////////
254
255void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
256                                 const GrDrawEffect&,
257                                 EffectKey key,
258                                 const char* outputColor,
259                                 const char* inputColor,
260                                 const TransformedCoordsArray& coords,
261                                 const TextureSamplerArray& samplers) {
262    this->emitUniforms(builder, key);
263    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
264    const GrGLContextInfo ctxInfo = builder->ctxInfo();
265    SkString t;
266    // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
267    // On Intel GPU there is an issue where it reads the second arguement to atan "- %s.x" as an int
268    // thus must us -1.0 * %s.x to work correctly
269    if (kIntel_GrGLVendor != ctxInfo.vendor()){
270        t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5",
271                 coords2D.c_str(), coords2D.c_str());
272    } else {
273        t.printf("atan(- %s.y, -1.0 * %s.x) * 0.1591549430918 + 0.5",
274                 coords2D.c_str(), coords2D.c_str());
275    }
276    this->emitColor(builder, t.c_str(), key,
277                          outputColor, inputColor, samplers);
278}
279
280/////////////////////////////////////////////////////////////////////
281
282GrEffectRef* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&,
283                                          const SkMatrix* localMatrix) const {
284    SkMatrix matrix;
285    if (!this->getLocalMatrix().invert(&matrix)) {
286        return NULL;
287    }
288    if (localMatrix) {
289        SkMatrix inv;
290        if (!localMatrix->invert(&inv)) {
291            return NULL;
292        }
293        matrix.postConcat(inv);
294    }
295    matrix.postConcat(fPtsToUnit);
296    return GrSweepGradient::Create(context, *this, matrix);
297}
298
299#else
300
301GrEffectRef* SkSweepGradient::asNewEffect(GrContext*, const SkPaint&, const SkMatrix*) const {
302    SkDEBUGFAIL("Should not call in GPU-less build");
303    return NULL;
304}
305
306#endif
307
308#ifndef SK_IGNORE_TO_STRING
309void SkSweepGradient::toString(SkString* str) const {
310    str->append("SkSweepGradient: (");
311
312    str->append("center: (");
313    str->appendScalar(fCenter.fX);
314    str->append(", ");
315    str->appendScalar(fCenter.fY);
316    str->append(") ");
317
318    this->INHERITED::toString(str);
319
320    str->append(")");
321}
322#endif
323