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 "SkRadialGradient.h"
10#include "SkRadialGradient_Table.h"
11
12#define kSQRT_TABLE_BITS    11
13#define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
14
15#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
16
17#include <stdio.h>
18
19void SkRadialGradient_BuildTable() {
20    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
21
22    FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
23    SkASSERT(file);
24    ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
25
26    for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
27        if ((i & 15) == 0) {
28            ::fprintf(file, "\t");
29        }
30
31        uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
32
33        ::fprintf(file, "0x%02X", value);
34        if (i < kSQRT_TABLE_SIZE-1) {
35            ::fprintf(file, ", ");
36        }
37        if ((i & 15) == 15) {
38            ::fprintf(file, "\n");
39        }
40    }
41    ::fprintf(file, "};\n");
42    ::fclose(file);
43}
44
45#endif
46
47namespace {
48
49void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
50                               SkMatrix* matrix) {
51    SkScalar    inv = SkScalarInvert(radius);
52
53    matrix->setTranslate(-center.fX, -center.fY);
54    matrix->postScale(inv, inv);
55}
56
57typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
58        SkScalar sfy, SkScalar sdy,
59        uint16_t* dstC, const uint16_t* cache,
60        int toggle, int count);
61
62void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
63        SkScalar sfy, SkScalar sdy,
64        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
65        int toggle, int count) {
66    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
67
68    /* knock these down so we can pin against +- 0x7FFF, which is an
69       immediate load, rather than 0xFFFF which is slower. This is a
70       compromise, since it reduces our precision, but that appears
71       to be visually OK. If we decide this is OK for all of our cases,
72       we could (it seems) put this scale-down into fDstToIndex,
73       to avoid having to do these extra shifts each time.
74    */
75    SkFixed fx = SkScalarToFixed(sfx) >> 1;
76    SkFixed dx = SkScalarToFixed(sdx) >> 1;
77    SkFixed fy = SkScalarToFixed(sfy) >> 1;
78    SkFixed dy = SkScalarToFixed(sdy) >> 1;
79    // might perform this check for the other modes,
80    // but the win will be a smaller % of the total
81    if (dy == 0) {
82        fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
83        fy *= fy;
84        do {
85            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
86            unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
87            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
88            fx += dx;
89            *dstC++ = cache[toggle +
90                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
91            toggle = next_dither_toggle16(toggle);
92        } while (--count != 0);
93    } else {
94        do {
95            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
96            unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
97            fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
98            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
99            fx += dx;
100            fy += dy;
101            *dstC++ = cache[toggle +
102                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
103            toggle = next_dither_toggle16(toggle);
104        } while (--count != 0);
105    }
106}
107
108void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
109        SkScalar sfy, SkScalar sdy,
110        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
111        int toggle, int count) {
112    do {
113#ifdef SK_SCALAR_IS_FLOAT
114        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
115        SkFixed dist = SkFloatToFixed(fdist);
116#else
117        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
118            SkFixedSquare(sfy);
119        if (magnitudeSquared < 0) // Overflow.
120            magnitudeSquared = SK_FixedMax;
121        SkFixed dist = SkFixedSqrt(magnitudeSquared);
122#endif
123        unsigned fi = mirror_tileproc(dist);
124        SkASSERT(fi <= 0xFFFF);
125        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
126        toggle = next_dither_toggle16(toggle);
127        sfx += sdx;
128        sfy += sdy;
129    } while (--count != 0);
130}
131
132void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
133        SkScalar sfy, SkScalar sdy,
134        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
135        int toggle, int count) {
136    SkFixed fx = SkScalarToFixed(sfx);
137    SkFixed dx = SkScalarToFixed(sdx);
138    SkFixed fy = SkScalarToFixed(sfy);
139    SkFixed dy = SkScalarToFixed(sdy);
140    do {
141        SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
142        unsigned fi = repeat_tileproc(dist);
143        SkASSERT(fi <= 0xFFFF);
144        fx += dx;
145        fy += dy;
146        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
147        toggle = next_dither_toggle16(toggle);
148    } while (--count != 0);
149}
150
151}
152
153/////////////////////////////////////////////////////////////////////
154
155SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
156                                   const Descriptor& desc)
157    : SkGradientShaderBase(desc),
158      fCenter(center),
159      fRadius(radius)
160{
161    // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
162    SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
163
164    rad_to_unit_matrix(center, radius, &fPtsToUnit);
165}
166
167void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
168                         int count) {
169    SkASSERT(count > 0);
170
171    uint16_t* SK_RESTRICT dstC = dstCParam;
172
173    SkPoint             srcPt;
174    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
175    TileProc            proc = fTileProc;
176    const uint16_t* SK_RESTRICT cache = this->getCache16();
177    int                 toggle = init_dither_toggle16(x, y);
178
179    if (fDstToIndexClass != kPerspective_MatrixClass) {
180        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
181                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
182
183        SkScalar sdx = fDstToIndex.getScaleX();
184        SkScalar sdy = fDstToIndex.getSkewY();
185
186        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
187            SkFixed storage[2];
188            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
189                                           &storage[0], &storage[1]);
190            sdx = SkFixedToScalar(storage[0]);
191            sdy = SkFixedToScalar(storage[1]);
192        } else {
193            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
194        }
195
196        RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
197        if (SkShader::kClamp_TileMode == fTileMode) {
198            shadeProc = shadeSpan16_radial_clamp;
199        } else if (SkShader::kMirror_TileMode == fTileMode) {
200            shadeProc = shadeSpan16_radial_mirror;
201        } else {
202            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
203        }
204        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
205                     cache, toggle, count);
206    } else {    // perspective case
207        SkScalar dstX = SkIntToScalar(x);
208        SkScalar dstY = SkIntToScalar(y);
209        do {
210            dstProc(fDstToIndex, dstX, dstY, &srcPt);
211            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
212            SkASSERT(fi <= 0xFFFF);
213
214            int index = fi >> (16 - kCache16Bits);
215            *dstC++ = cache[toggle + index];
216            toggle = next_dither_toggle16(toggle);
217
218            dstX += SK_Scalar1;
219        } while (--count != 0);
220    }
221}
222
223SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
224    SkMatrix* matrix, SkShader::TileMode* xy) const {
225    if (bitmap) {
226        this->getGradientTableBitmap(bitmap);
227    }
228    if (matrix) {
229        matrix->setScale(SkIntToScalar(kCache32Count),
230                         SkIntToScalar(kCache32Count));
231        matrix->preConcat(fPtsToUnit);
232    }
233    if (xy) {
234        xy[0] = fTileMode;
235        xy[1] = kClamp_TileMode;
236    }
237    return kRadial_BitmapType;
238}
239
240SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
241    if (info) {
242        commonAsAGradient(info);
243        info->fPoint[0] = fCenter;
244        info->fRadius[0] = fRadius;
245    }
246    return kRadial_GradientType;
247}
248
249SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer)
250    : INHERITED(buffer),
251      fCenter(buffer.readPoint()),
252      fRadius(buffer.readScalar()) {
253}
254
255void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
256    this->INHERITED::flatten(buffer);
257    buffer.writePoint(fCenter);
258    buffer.writeScalar(fRadius);
259}
260
261namespace {
262
263inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
264    // fast, overly-conservative test: checks unit square instead
265    // of unit circle
266    bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
267                    (fx <= -SK_FixedHalf && dx <= 0);
268    bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
269                    (fy <= -SK_FixedHalf && dy <= 0);
270
271    return xClamped || yClamped;
272}
273
274// Return true if (fx * fy) is always inside the unit circle
275// SkPin32 is expensive, but so are all the SkFixedMul in this test,
276// so it shouldn't be run if count is small.
277inline bool no_need_for_radial_pin(int fx, int dx,
278                                          int fy, int dy, int count) {
279    SkASSERT(count > 0);
280    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
281        return false;
282    }
283    if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
284        return false;
285    }
286    fx += (count - 1) * dx;
287    fy += (count - 1) * dy;
288    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
289        return false;
290    }
291    return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
292}
293
294#define UNPINNED_RADIAL_STEP \
295    fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
296    *dstC++ = cache[toggle + \
297                    (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
298    toggle = next_dither_toggle(toggle); \
299    fx += dx; \
300    fy += dy;
301
302typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
303        SkScalar sfy, SkScalar sdy,
304        SkPMColor* dstC, const SkPMColor* cache,
305        int count, int toggle);
306
307// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
308void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
309        SkScalar sfy, SkScalar sdy,
310        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
311        int count, int toggle) {
312    // Floating point seems to be slower than fixed point,
313    // even when we have float hardware.
314    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
315    SkFixed fx = SkScalarToFixed(sfx) >> 1;
316    SkFixed dx = SkScalarToFixed(sdx) >> 1;
317    SkFixed fy = SkScalarToFixed(sfy) >> 1;
318    SkFixed dy = SkScalarToFixed(sdy) >> 1;
319    if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
320        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
321        sk_memset32_dither(dstC,
322            cache[toggle + fi],
323            cache[next_dither_toggle(toggle) + fi],
324            count);
325    } else if ((count > 4) &&
326               no_need_for_radial_pin(fx, dx, fy, dy, count)) {
327        unsigned fi;
328        // 4x unroll appears to be no faster than 2x unroll on Linux
329        while (count > 1) {
330            UNPINNED_RADIAL_STEP;
331            UNPINNED_RADIAL_STEP;
332            count -= 2;
333        }
334        if (count) {
335            UNPINNED_RADIAL_STEP;
336        }
337    } else  {
338        // Specializing for dy == 0 gains us 25% on Skia benchmarks
339        if (dy == 0) {
340            unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
341            yy *= yy;
342            do {
343                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
344                unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
345                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
346                *dstC++ = cache[toggle + (sqrt_table[fi] >>
347                    SkGradientShaderBase::kSqrt32Shift)];
348                toggle = next_dither_toggle(toggle);
349                fx += dx;
350            } while (--count != 0);
351        } else {
352            do {
353                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
354                unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
355                fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
356                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
357                *dstC++ = cache[toggle + (sqrt_table[fi] >>
358                    SkGradientShaderBase::kSqrt32Shift)];
359                toggle = next_dither_toggle(toggle);
360                fx += dx;
361                fy += dy;
362            } while (--count != 0);
363        }
364    }
365}
366
367// Unrolling this loop doesn't seem to help (when float); we're stalling to
368// get the results of the sqrt (?), and don't have enough extra registers to
369// have many in flight.
370void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
371        SkScalar sfy, SkScalar sdy,
372        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
373        int count, int toggle) {
374    do {
375#ifdef SK_SCALAR_IS_FLOAT
376        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
377        SkFixed dist = SkFloatToFixed(fdist);
378#else
379        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
380            SkFixedSquare(sfy);
381        if (magnitudeSquared < 0) // Overflow.
382            magnitudeSquared = SK_FixedMax;
383        SkFixed dist = SkFixedSqrt(magnitudeSquared);
384#endif
385        unsigned fi = mirror_tileproc(dist);
386        SkASSERT(fi <= 0xFFFF);
387        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
388        toggle = next_dither_toggle(toggle);
389        sfx += sdx;
390        sfy += sdy;
391    } while (--count != 0);
392}
393
394void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
395        SkScalar sfy, SkScalar sdy,
396        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
397        int count, int toggle) {
398    SkFixed fx = SkScalarToFixed(sfx);
399    SkFixed dx = SkScalarToFixed(sdx);
400    SkFixed fy = SkScalarToFixed(sfy);
401    SkFixed dy = SkScalarToFixed(sdy);
402    do {
403        SkFixed magnitudeSquared = SkFixedSquare(fx) +
404            SkFixedSquare(fy);
405        if (magnitudeSquared < 0) // Overflow.
406            magnitudeSquared = SK_FixedMax;
407        SkFixed dist = SkFixedSqrt(magnitudeSquared);
408        unsigned fi = repeat_tileproc(dist);
409        SkASSERT(fi <= 0xFFFF);
410        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
411        toggle = next_dither_toggle(toggle);
412        fx += dx;
413        fy += dy;
414    } while (--count != 0);
415}
416}
417
418void SkRadialGradient::shadeSpan(int x, int y,
419                                SkPMColor* SK_RESTRICT dstC, int count) {
420    SkASSERT(count > 0);
421
422    SkPoint             srcPt;
423    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
424    TileProc            proc = fTileProc;
425    const SkPMColor* SK_RESTRICT cache = this->getCache32();
426    int toggle = init_dither_toggle(x, y);
427
428    if (fDstToIndexClass != kPerspective_MatrixClass) {
429        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
430                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
431        SkScalar sdx = fDstToIndex.getScaleX();
432        SkScalar sdy = fDstToIndex.getSkewY();
433
434        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
435            SkFixed storage[2];
436            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
437                                           &storage[0], &storage[1]);
438            sdx = SkFixedToScalar(storage[0]);
439            sdy = SkFixedToScalar(storage[1]);
440        } else {
441            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
442        }
443
444        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
445        if (SkShader::kClamp_TileMode == fTileMode) {
446            shadeProc = shadeSpan_radial_clamp;
447        } else if (SkShader::kMirror_TileMode == fTileMode) {
448            shadeProc = shadeSpan_radial_mirror;
449        } else {
450            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
451        }
452        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
453    } else {    // perspective case
454        SkScalar dstX = SkIntToScalar(x);
455        SkScalar dstY = SkIntToScalar(y);
456        do {
457            dstProc(fDstToIndex, dstX, dstY, &srcPt);
458            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
459            SkASSERT(fi <= 0xFFFF);
460            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
461            dstX += SK_Scalar1;
462        } while (--count != 0);
463    }
464}
465
466/////////////////////////////////////////////////////////////////////
467
468#if SK_SUPPORT_GPU
469
470#include "GrTBackendEffectFactory.h"
471
472class GrGLRadialGradient : public GrGLGradientEffect {
473public:
474
475    GrGLRadialGradient(const GrBackendEffectFactory& factory,
476                       const GrDrawEffect&) : INHERITED (factory) { }
477    virtual ~GrGLRadialGradient() { }
478
479    virtual void emitCode(GrGLShaderBuilder*,
480                          const GrDrawEffect&,
481                          EffectKey,
482                          const char* outputColor,
483                          const char* inputColor,
484                          const TextureSamplerArray&) SK_OVERRIDE;
485
486    static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
487        return GenMatrixKey(drawEffect);
488    }
489
490private:
491
492    typedef GrGLGradientEffect INHERITED;
493
494};
495
496/////////////////////////////////////////////////////////////////////
497
498class GrRadialGradient : public GrGradientEffect {
499public:
500    static GrEffectRef* Create(GrContext* ctx,
501                               const SkRadialGradient& shader,
502                               const SkMatrix& matrix,
503                               SkShader::TileMode tm) {
504        AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
505        return CreateEffectRef(effect);
506    }
507
508    virtual ~GrRadialGradient() { }
509
510    static const char* Name() { return "Radial Gradient"; }
511    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
512        return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
513    }
514
515    typedef GrGLRadialGradient GLEffect;
516
517private:
518    GrRadialGradient(GrContext* ctx,
519                     const SkRadialGradient& shader,
520                     const SkMatrix& matrix,
521                     SkShader::TileMode tm)
522        : INHERITED(ctx, shader, matrix, tm) {
523    }
524
525    GR_DECLARE_EFFECT_TEST;
526
527    typedef GrGradientEffect INHERITED;
528};
529
530/////////////////////////////////////////////////////////////////////
531
532GR_DEFINE_EFFECT_TEST(GrRadialGradient);
533
534GrEffectRef* GrRadialGradient::TestCreate(SkMWCRandom* random,
535                                          GrContext* context,
536                                          const GrDrawTargetCaps&,
537                                          GrTexture**) {
538    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
539    SkScalar radius = random->nextUScalar1();
540
541    SkColor colors[kMaxRandomGradientColors];
542    SkScalar stopsArray[kMaxRandomGradientColors];
543    SkScalar* stops = stopsArray;
544    SkShader::TileMode tm;
545    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
546    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
547                                                                 colors, stops, colorCount,
548                                                                 tm));
549    SkPaint paint;
550    return shader->asNewEffect(context, paint);
551}
552
553/////////////////////////////////////////////////////////////////////
554
555void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
556                                  const GrDrawEffect&,
557                                  EffectKey key,
558                                  const char* outputColor,
559                                  const char* inputColor,
560                                  const TextureSamplerArray& samplers) {
561    this->emitYCoordUniform(builder);
562    const char* coords;
563    this->setupMatrix(builder, key, &coords);
564    SkString t("length(");
565    t.append(coords);
566    t.append(")");
567    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
568}
569
570/////////////////////////////////////////////////////////////////////
571
572GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
573    SkASSERT(NULL != context);
574
575    SkMatrix matrix;
576    if (!this->getLocalMatrix().invert(&matrix)) {
577        return NULL;
578    }
579    matrix.postConcat(fPtsToUnit);
580    return GrRadialGradient::Create(context, *this, matrix, fTileMode);
581}
582
583#else
584
585GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
586    SkDEBUGFAIL("Should not call in GPU-less build");
587    return NULL;
588}
589
590#endif
591
592#ifdef SK_DEVELOPER
593void SkRadialGradient::toString(SkString* str) const {
594    str->append("SkRadialGradient: (");
595
596    str->append("center: (");
597    str->appendScalar(fCenter.fX);
598    str->append(", ");
599    str->appendScalar(fCenter.fY);
600    str->append(") radius: ");
601    str->appendScalar(fRadius);
602    str->append(" ");
603
604    this->INHERITED::toString(str);
605
606    str->append(")");
607}
608#endif
609