• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SkRadialGradient_BuildTable()19 void 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 
47 namespace {
48 
49 // GCC doesn't like using static functions as template arguments.  So force these to be non-static.
mirror_tileproc_nonstatic(SkFixed x)50 inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
51     return mirror_tileproc(x);
52 }
53 
repeat_tileproc_nonstatic(SkFixed x)54 inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
55     return repeat_tileproc(x);
56 }
57 
rad_to_unit_matrix(const SkPoint & center,SkScalar radius,SkMatrix * matrix)58 void 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 
66 typedef 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 
shadeSpan16_radial_clamp(SkScalar sfx,SkScalar sdx,SkScalar sfy,SkScalar sdy,uint16_t * SK_RESTRICT dstC,const uint16_t * SK_RESTRICT cache,int toggle,int count)71 void 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 
117 template <SkFixed (*TileProc)(SkFixed)>
shadeSpan16_radial(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,uint16_t * SK_RESTRICT dstC,const uint16_t * SK_RESTRICT cache,int toggle,int count)118 void 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 
shadeSpan16_radial_mirror(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,uint16_t * SK_RESTRICT dstC,const uint16_t * SK_RESTRICT cache,int toggle,int count)132 void 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 
shadeSpan16_radial_repeat(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,uint16_t * SK_RESTRICT dstC,const uint16_t * SK_RESTRICT cache,int toggle,int count)138 void 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 
SkRadialGradient(const SkPoint & center,SkScalar radius,const Descriptor & desc)148 SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
149                                    const Descriptor& desc)
150     : SkGradientShaderBase(desc),
151       fCenter(center),
152       fRadius(radius)
153 {
154     // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
155     SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
156 
157     rad_to_unit_matrix(center, radius, &fPtsToUnit);
158 }
159 
shadeSpan16(int x,int y,uint16_t * dstCParam,int count)160 void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
161                          int count) {
162     SkASSERT(count > 0);
163 
164     uint16_t* SK_RESTRICT dstC = dstCParam;
165 
166     SkPoint             srcPt;
167     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
168     TileProc            proc = fTileProc;
169     const uint16_t* SK_RESTRICT cache = this->getCache16();
170     int                 toggle = init_dither_toggle16(x, y);
171 
172     if (fDstToIndexClass != kPerspective_MatrixClass) {
173         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
174                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
175 
176         SkScalar sdx = fDstToIndex.getScaleX();
177         SkScalar sdy = fDstToIndex.getSkewY();
178 
179         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
180             SkFixed storage[2];
181             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
182                                            &storage[0], &storage[1]);
183             sdx = SkFixedToScalar(storage[0]);
184             sdy = SkFixedToScalar(storage[1]);
185         } else {
186             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
187         }
188 
189         RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
190         if (SkShader::kClamp_TileMode == fTileMode) {
191             shadeProc = shadeSpan16_radial_clamp;
192         } else if (SkShader::kMirror_TileMode == fTileMode) {
193             shadeProc = shadeSpan16_radial_mirror;
194         } else {
195             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
196         }
197         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
198                      cache, toggle, count);
199     } else {    // perspective case
200         SkScalar dstX = SkIntToScalar(x);
201         SkScalar dstY = SkIntToScalar(y);
202         do {
203             dstProc(fDstToIndex, dstX, dstY, &srcPt);
204             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
205             SkASSERT(fi <= 0xFFFF);
206 
207             int index = fi >> (16 - kCache16Bits);
208             *dstC++ = cache[toggle + index];
209             toggle = next_dither_toggle16(toggle);
210 
211             dstX += SK_Scalar1;
212         } while (--count != 0);
213     }
214 }
215 
asABitmap(SkBitmap * bitmap,SkMatrix * matrix,SkShader::TileMode * xy) const216 SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
217     SkMatrix* matrix, SkShader::TileMode* xy) const {
218     if (bitmap) {
219         this->getGradientTableBitmap(bitmap);
220     }
221     if (matrix) {
222         matrix->setScale(SkIntToScalar(kCache32Count),
223                          SkIntToScalar(kCache32Count));
224         matrix->preConcat(fPtsToUnit);
225     }
226     if (xy) {
227         xy[0] = fTileMode;
228         xy[1] = kClamp_TileMode;
229     }
230     return kRadial_BitmapType;
231 }
232 
asAGradient(GradientInfo * info) const233 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
234     if (info) {
235         commonAsAGradient(info);
236         info->fPoint[0] = fCenter;
237         info->fRadius[0] = fRadius;
238     }
239     return kRadial_GradientType;
240 }
241 
SkRadialGradient(SkFlattenableReadBuffer & buffer)242 SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer)
243     : INHERITED(buffer),
244       fCenter(buffer.readPoint()),
245       fRadius(buffer.readScalar()) {
246 }
247 
flatten(SkFlattenableWriteBuffer & buffer) const248 void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
249     this->INHERITED::flatten(buffer);
250     buffer.writePoint(fCenter);
251     buffer.writeScalar(fRadius);
252 }
253 
254 namespace {
255 
radial_completely_pinned(int fx,int dx,int fy,int dy)256 inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
257     // fast, overly-conservative test: checks unit square instead
258     // of unit circle
259     bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
260                     (fx <= -SK_FixedHalf && dx <= 0);
261     bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
262                     (fy <= -SK_FixedHalf && dy <= 0);
263 
264     return xClamped || yClamped;
265 }
266 
267 // Return true if (fx * fy) is always inside the unit circle
268 // SkPin32 is expensive, but so are all the SkFixedMul in this test,
269 // so it shouldn't be run if count is small.
no_need_for_radial_pin(int fx,int dx,int fy,int dy,int count)270 inline bool no_need_for_radial_pin(int fx, int dx,
271                                           int fy, int dy, int count) {
272     SkASSERT(count > 0);
273     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
274         return false;
275     }
276     if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
277         return false;
278     }
279     fx += (count - 1) * dx;
280     fy += (count - 1) * dy;
281     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
282         return false;
283     }
284     return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
285 }
286 
287 #define UNPINNED_RADIAL_STEP \
288     fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
289     *dstC++ = cache[toggle + \
290                     (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
291     toggle = next_dither_toggle(toggle); \
292     fx += dx; \
293     fy += dy;
294 
295 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
296         SkScalar sfy, SkScalar sdy,
297         SkPMColor* dstC, const SkPMColor* cache,
298         int count, int toggle);
299 
300 // On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
shadeSpan_radial_clamp(SkScalar sfx,SkScalar sdx,SkScalar sfy,SkScalar sdy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)301 void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
302         SkScalar sfy, SkScalar sdy,
303         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
304         int count, int toggle) {
305     // Floating point seems to be slower than fixed point,
306     // even when we have float hardware.
307     const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
308     SkFixed fx = SkScalarToFixed(sfx) >> 1;
309     SkFixed dx = SkScalarToFixed(sdx) >> 1;
310     SkFixed fy = SkScalarToFixed(sfy) >> 1;
311     SkFixed dy = SkScalarToFixed(sdy) >> 1;
312     if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
313         unsigned fi = SkGradientShaderBase::kCache32Count - 1;
314         sk_memset32_dither(dstC,
315             cache[toggle + fi],
316             cache[next_dither_toggle(toggle) + fi],
317             count);
318     } else if ((count > 4) &&
319                no_need_for_radial_pin(fx, dx, fy, dy, count)) {
320         unsigned fi;
321         // 4x unroll appears to be no faster than 2x unroll on Linux
322         while (count > 1) {
323             UNPINNED_RADIAL_STEP;
324             UNPINNED_RADIAL_STEP;
325             count -= 2;
326         }
327         if (count) {
328             UNPINNED_RADIAL_STEP;
329         }
330     } else  {
331         // Specializing for dy == 0 gains us 25% on Skia benchmarks
332         if (dy == 0) {
333             unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
334             yy *= yy;
335             do {
336                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
337                 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
338                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
339                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
340                     SkGradientShaderBase::kSqrt32Shift)];
341                 toggle = next_dither_toggle(toggle);
342                 fx += dx;
343             } while (--count != 0);
344         } else {
345             do {
346                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
347                 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
348                 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
349                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
350                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
351                     SkGradientShaderBase::kSqrt32Shift)];
352                 toggle = next_dither_toggle(toggle);
353                 fx += dx;
354                 fy += dy;
355             } while (--count != 0);
356         }
357     }
358 }
359 
360 // Unrolling this loop doesn't seem to help (when float); we're stalling to
361 // get the results of the sqrt (?), and don't have enough extra registers to
362 // have many in flight.
363 template <SkFixed (*TileProc)(SkFixed)>
shadeSpan_radial(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)364 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
365                       SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
366                       int count, int toggle) {
367     do {
368         const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
369         const unsigned fi = TileProc(dist);
370         SkASSERT(fi <= 0xFFFF);
371         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
372         toggle = next_dither_toggle(toggle);
373         fx += dx;
374         fy += dy;
375     } while (--count != 0);
376 }
377 
shadeSpan_radial_mirror(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)378 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
379                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
380                              int count, int toggle) {
381     shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
382 }
383 
shadeSpan_radial_repeat(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)384 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
385                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
386                              int count, int toggle) {
387     shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
388 }
389 
390 }  // namespace
391 
shadeSpan(int x,int y,SkPMColor * SK_RESTRICT dstC,int count)392 void SkRadialGradient::shadeSpan(int x, int y,
393                                 SkPMColor* SK_RESTRICT dstC, int count) {
394     SkASSERT(count > 0);
395 
396     SkPoint             srcPt;
397     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
398     TileProc            proc = fTileProc;
399     const SkPMColor* SK_RESTRICT cache = this->getCache32();
400     int toggle = init_dither_toggle(x, y);
401 
402     if (fDstToIndexClass != kPerspective_MatrixClass) {
403         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
404                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
405         SkScalar sdx = fDstToIndex.getScaleX();
406         SkScalar sdy = fDstToIndex.getSkewY();
407 
408         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
409             SkFixed storage[2];
410             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
411                                            &storage[0], &storage[1]);
412             sdx = SkFixedToScalar(storage[0]);
413             sdy = SkFixedToScalar(storage[1]);
414         } else {
415             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
416         }
417 
418         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
419         if (SkShader::kClamp_TileMode == fTileMode) {
420             shadeProc = shadeSpan_radial_clamp;
421         } else if (SkShader::kMirror_TileMode == fTileMode) {
422             shadeProc = shadeSpan_radial_mirror;
423         } else {
424             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
425         }
426         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
427     } else {    // perspective case
428         SkScalar dstX = SkIntToScalar(x);
429         SkScalar dstY = SkIntToScalar(y);
430         do {
431             dstProc(fDstToIndex, dstX, dstY, &srcPt);
432             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
433             SkASSERT(fi <= 0xFFFF);
434             *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
435             dstX += SK_Scalar1;
436         } while (--count != 0);
437     }
438 }
439 
440 /////////////////////////////////////////////////////////////////////
441 
442 #if SK_SUPPORT_GPU
443 
444 #include "GrTBackendEffectFactory.h"
445 
446 class GrGLRadialGradient : public GrGLGradientEffect {
447 public:
448 
GrGLRadialGradient(const GrBackendEffectFactory & factory,const GrDrawEffect &)449     GrGLRadialGradient(const GrBackendEffectFactory& factory,
450                        const GrDrawEffect&) : INHERITED (factory) { }
~GrGLRadialGradient()451     virtual ~GrGLRadialGradient() { }
452 
453     virtual void emitCode(GrGLShaderBuilder*,
454                           const GrDrawEffect&,
455                           EffectKey,
456                           const char* outputColor,
457                           const char* inputColor,
458                           const TransformedCoordsArray&,
459                           const TextureSamplerArray&) SK_OVERRIDE;
460 
GenKey(const GrDrawEffect & drawEffect,const GrGLCaps &)461     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
462         return GenBaseGradientKey(drawEffect);
463     }
464 
465 private:
466 
467     typedef GrGLGradientEffect INHERITED;
468 
469 };
470 
471 /////////////////////////////////////////////////////////////////////
472 
473 class GrRadialGradient : public GrGradientEffect {
474 public:
Create(GrContext * ctx,const SkRadialGradient & shader,const SkMatrix & matrix,SkShader::TileMode tm)475     static GrEffectRef* Create(GrContext* ctx,
476                                const SkRadialGradient& shader,
477                                const SkMatrix& matrix,
478                                SkShader::TileMode tm) {
479         AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
480         return CreateEffectRef(effect);
481     }
482 
~GrRadialGradient()483     virtual ~GrRadialGradient() { }
484 
Name()485     static const char* Name() { return "Radial Gradient"; }
getFactory() const486     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
487         return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
488     }
489 
490     typedef GrGLRadialGradient GLEffect;
491 
492 private:
GrRadialGradient(GrContext * ctx,const SkRadialGradient & shader,const SkMatrix & matrix,SkShader::TileMode tm)493     GrRadialGradient(GrContext* ctx,
494                      const SkRadialGradient& shader,
495                      const SkMatrix& matrix,
496                      SkShader::TileMode tm)
497         : INHERITED(ctx, shader, matrix, tm) {
498     }
499 
500     GR_DECLARE_EFFECT_TEST;
501 
502     typedef GrGradientEffect INHERITED;
503 };
504 
505 /////////////////////////////////////////////////////////////////////
506 
507 GR_DEFINE_EFFECT_TEST(GrRadialGradient);
508 
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture **)509 GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
510                                           GrContext* context,
511                                           const GrDrawTargetCaps&,
512                                           GrTexture**) {
513     SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
514     SkScalar radius = random->nextUScalar1();
515 
516     SkColor colors[kMaxRandomGradientColors];
517     SkScalar stopsArray[kMaxRandomGradientColors];
518     SkScalar* stops = stopsArray;
519     SkShader::TileMode tm;
520     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
521     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
522                                                                  colors, stops, colorCount,
523                                                                  tm));
524     SkPaint paint;
525     return shader->asNewEffect(context, paint);
526 }
527 
528 /////////////////////////////////////////////////////////////////////
529 
emitCode(GrGLShaderBuilder * builder,const GrDrawEffect &,EffectKey key,const char * outputColor,const char * inputColor,const TransformedCoordsArray & coords,const TextureSamplerArray & samplers)530 void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
531                                   const GrDrawEffect&,
532                                   EffectKey key,
533                                   const char* outputColor,
534                                   const char* inputColor,
535                                   const TransformedCoordsArray& coords,
536                                   const TextureSamplerArray& samplers) {
537     this->emitUniforms(builder, key);
538     SkString t("length(");
539     t.append(builder->ensureFSCoords2D(coords, 0));
540     t.append(")");
541     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
542 }
543 
544 /////////////////////////////////////////////////////////////////////
545 
asNewEffect(GrContext * context,const SkPaint &) const546 GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
547     SkASSERT(NULL != context);
548 
549     SkMatrix matrix;
550     if (!this->getLocalMatrix().invert(&matrix)) {
551         return NULL;
552     }
553     matrix.postConcat(fPtsToUnit);
554     return GrRadialGradient::Create(context, *this, matrix, fTileMode);
555 }
556 
557 #else
558 
asNewEffect(GrContext *,const SkPaint &) const559 GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
560     SkDEBUGFAIL("Should not call in GPU-less build");
561     return NULL;
562 }
563 
564 #endif
565 
566 #ifdef SK_DEVELOPER
toString(SkString * str) const567 void SkRadialGradient::toString(SkString* str) const {
568     str->append("SkRadialGradient: (");
569 
570     str->append("center: (");
571     str->appendScalar(fCenter.fX);
572     str->append(", ");
573     str->appendScalar(fCenter.fY);
574     str->append(") radius: ");
575     str->appendScalar(fRadius);
576     str->append(" ");
577 
578     this->INHERITED::toString(str);
579 
580     str->append(")");
581 }
582 #endif
583