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