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#include "SkGr.h"
178
179class GrGLSweepGradient : public GrGLGradientEffect {
180public:
181
182    GrGLSweepGradient(const GrBackendEffectFactory& factory,
183                      const GrDrawEffect&) : INHERITED (factory) { }
184    virtual ~GrGLSweepGradient() { }
185
186    virtual void emitCode(GrGLShaderBuilder*,
187                          const GrDrawEffect&,
188                          EffectKey,
189                          const char* outputColor,
190                          const char* inputColor,
191                          const TransformedCoordsArray&,
192                          const TextureSamplerArray&) SK_OVERRIDE;
193
194    static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
195        return GenBaseGradientKey(drawEffect);
196    }
197
198private:
199
200    typedef GrGLGradientEffect INHERITED;
201
202};
203
204/////////////////////////////////////////////////////////////////////
205
206class GrSweepGradient : public GrGradientEffect {
207public:
208    static GrEffectRef* Create(GrContext* ctx,
209                               const SkSweepGradient& shader,
210                               const SkMatrix& matrix) {
211        AutoEffectUnref effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix)));
212        return CreateEffectRef(effect);
213    }
214    virtual ~GrSweepGradient() { }
215
216    static const char* Name() { return "Sweep Gradient"; }
217    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
218        return GrTBackendEffectFactory<GrSweepGradient>::getInstance();
219    }
220
221    typedef GrGLSweepGradient GLEffect;
222
223private:
224    GrSweepGradient(GrContext* ctx,
225                    const SkSweepGradient& shader,
226                    const SkMatrix& matrix)
227    : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
228    GR_DECLARE_EFFECT_TEST;
229
230    typedef GrGradientEffect INHERITED;
231};
232
233/////////////////////////////////////////////////////////////////////
234
235GR_DEFINE_EFFECT_TEST(GrSweepGradient);
236
237GrEffectRef* GrSweepGradient::TestCreate(SkRandom* random,
238                                         GrContext* context,
239                                         const GrDrawTargetCaps&,
240                                         GrTexture**) {
241    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
242
243    SkColor colors[kMaxRandomGradientColors];
244    SkScalar stopsArray[kMaxRandomGradientColors];
245    SkScalar* stops = stopsArray;
246    SkShader::TileMode tmIgnored;
247    int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
248    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
249                                                                colors, stops, colorCount));
250    SkPaint paint;
251    GrEffectRef* effect;
252    GrColor grColor;
253    shader->asNewEffect(context, paint, NULL, &grColor, &effect);
254    return effect;
255}
256
257/////////////////////////////////////////////////////////////////////
258
259void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
260                                 const GrDrawEffect&,
261                                 EffectKey key,
262                                 const char* outputColor,
263                                 const char* inputColor,
264                                 const TransformedCoordsArray& coords,
265                                 const TextureSamplerArray& samplers) {
266    this->emitUniforms(builder, key);
267    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
268    const GrGLContextInfo ctxInfo = builder->ctxInfo();
269    SkString t;
270    // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
271    // On Intel GPU there is an issue where it reads the second arguement to atan "- %s.x" as an int
272    // thus must us -1.0 * %s.x to work correctly
273    if (kIntel_GrGLVendor != ctxInfo.vendor()){
274        t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5",
275                 coords2D.c_str(), coords2D.c_str());
276    } else {
277        t.printf("atan(- %s.y, -1.0 * %s.x) * 0.1591549430918 + 0.5",
278                 coords2D.c_str(), coords2D.c_str());
279    }
280    this->emitColor(builder, t.c_str(), key,
281                          outputColor, inputColor, samplers);
282}
283
284/////////////////////////////////////////////////////////////////////
285
286bool SkSweepGradient::asNewEffect(GrContext* context, const SkPaint& paint,
287                                  const SkMatrix* localMatrix, GrColor* grColor,
288                                  GrEffectRef** grEffect)  const {
289
290    SkMatrix matrix;
291    if (!this->getLocalMatrix().invert(&matrix)) {
292        return false;
293    }
294    if (localMatrix) {
295        SkMatrix inv;
296        if (!localMatrix->invert(&inv)) {
297            return false;
298        }
299        matrix.postConcat(inv);
300    }
301    matrix.postConcat(fPtsToUnit);
302
303    *grEffect = GrSweepGradient::Create(context, *this, matrix);
304    *grColor = SkColor2GrColorJustAlpha(paint.getColor());
305
306    return true;
307}
308
309#else
310
311bool SkSweepGradient::asNewEffect(GrContext* context, const SkPaint& paint,
312                                  const SkMatrix* localMatrix, GrColor* grColor,
313                                  GrEffectRef** grEffect)  const {
314    SkDEBUGFAIL("Should not call in GPU-less build");
315    return false;
316}
317
318#endif
319
320#ifndef SK_IGNORE_TO_STRING
321void SkSweepGradient::toString(SkString* str) const {
322    str->append("SkSweepGradient: (");
323
324    str->append("center: (");
325    str->appendScalar(fCenter.fX);
326    str->append(", ");
327    str->appendScalar(fCenter.fY);
328    str->append(") ");
329
330    this->INHERITED::toString(str);
331
332    str->append(")");
333}
334#endif
335