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