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