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