blob: fdca340f3abe0cd1f0151782b691903cefae4636 [file] [log] [blame]
rileya@google.com589708b2012-07-26 20:04:23 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "SkRadialGradient.h"
10#include "SkRadialGradient_Table.h"
11
12#define kSQRT_TABLE_BITS 11
13#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
14
15#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
16
17#include <stdio.h>
18
19void SkRadialGradient_BuildTable() {
20 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
21
22 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
23 SkASSERT(file);
24 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
25
26 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
27 if ((i & 15) == 0) {
28 ::fprintf(file, "\t");
29 }
30
31 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
32
33 ::fprintf(file, "0x%02X", value);
34 if (i < kSQRT_TABLE_SIZE-1) {
35 ::fprintf(file, ", ");
36 }
37 if ((i & 15) == 15) {
38 ::fprintf(file, "\n");
39 }
40 }
41 ::fprintf(file, "};\n");
42 ::fclose(file);
43}
44
45#endif
46
47namespace {
48
49void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
50 SkMatrix* matrix) {
51 SkScalar inv = SkScalarInvert(radius);
52
53 matrix->setTranslate(-center.fX, -center.fY);
54 matrix->postScale(inv, inv);
55}
56
57typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
58 SkScalar sfy, SkScalar sdy,
59 uint16_t* dstC, const uint16_t* cache,
60 int toggle, int count);
61
62void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
63 SkScalar sfy, SkScalar sdy,
64 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
65 int toggle, int count) {
66 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
67
68 /* knock these down so we can pin against +- 0x7FFF, which is an
69 immediate load, rather than 0xFFFF which is slower. This is a
70 compromise, since it reduces our precision, but that appears
71 to be visually OK. If we decide this is OK for all of our cases,
72 we could (it seems) put this scale-down into fDstToIndex,
73 to avoid having to do these extra shifts each time.
74 */
75 SkFixed fx = SkScalarToFixed(sfx) >> 1;
76 SkFixed dx = SkScalarToFixed(sdx) >> 1;
77 SkFixed fy = SkScalarToFixed(sfy) >> 1;
78 SkFixed dy = SkScalarToFixed(sdy) >> 1;
79 // might perform this check for the other modes,
80 // but the win will be a smaller % of the total
81 if (dy == 0) {
82 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
83 fy *= fy;
84 do {
85 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
86 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
87 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
88 fx += dx;
89 *dstC++ = cache[toggle +
90 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +000091 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +000092 } while (--count != 0);
93 } else {
94 do {
95 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
96 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
97 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
98 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
99 fx += dx;
100 fy += dy;
101 *dstC++ = cache[toggle +
102 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000103 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000104 } while (--count != 0);
105 }
106}
107
108void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
109 SkScalar sfy, SkScalar sdy,
110 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
111 int toggle, int count) {
112 do {
113#ifdef SK_SCALAR_IS_FLOAT
114 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
115 SkFixed dist = SkFloatToFixed(fdist);
116#else
117 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
118 SkFixedSquare(sfy);
119 if (magnitudeSquared < 0) // Overflow.
120 magnitudeSquared = SK_FixedMax;
121 SkFixed dist = SkFixedSqrt(magnitudeSquared);
122#endif
123 unsigned fi = mirror_tileproc(dist);
124 SkASSERT(fi <= 0xFFFF);
125 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000126 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000127 sfx += sdx;
128 sfy += sdy;
129 } while (--count != 0);
130}
131
132void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
133 SkScalar sfy, SkScalar sdy,
134 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
135 int toggle, int count) {
136 SkFixed fx = SkScalarToFixed(sfx);
137 SkFixed dx = SkScalarToFixed(sdx);
138 SkFixed fy = SkScalarToFixed(sfy);
139 SkFixed dy = SkScalarToFixed(sdy);
140 do {
141 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
142 unsigned fi = repeat_tileproc(dist);
143 SkASSERT(fi <= 0xFFFF);
144 fx += dx;
145 fy += dy;
146 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000147 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000148 } while (--count != 0);
149}
150
151}
152
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000153/////////////////////////////////////////////////////////////////////
154
rileya@google.com589708b2012-07-26 20:04:23 +0000155SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
156 const SkColor colors[], const SkScalar pos[], int colorCount,
157 SkShader::TileMode mode, SkUnitMapper* mapper)
158 : SkGradientShaderBase(colors, pos, colorCount, mode, mapper),
159 fCenter(center),
160 fRadius(radius)
161{
162 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
163 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
164
165 rad_to_unit_matrix(center, radius, &fPtsToUnit);
166}
167
168void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
169 int count) {
170 SkASSERT(count > 0);
171
172 uint16_t* SK_RESTRICT dstC = dstCParam;
173
174 SkPoint srcPt;
175 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
176 TileProc proc = fTileProc;
177 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000178 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000179
180 if (fDstToIndexClass != kPerspective_MatrixClass) {
181 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
182 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
183
184 SkScalar sdx = fDstToIndex.getScaleX();
185 SkScalar sdy = fDstToIndex.getSkewY();
186
187 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
188 SkFixed storage[2];
189 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
190 &storage[0], &storage[1]);
191 sdx = SkFixedToScalar(storage[0]);
192 sdy = SkFixedToScalar(storage[1]);
193 } else {
194 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
195 }
196
197 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
198 if (SkShader::kClamp_TileMode == fTileMode) {
199 shadeProc = shadeSpan16_radial_clamp;
200 } else if (SkShader::kMirror_TileMode == fTileMode) {
201 shadeProc = shadeSpan16_radial_mirror;
202 } else {
203 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
204 }
205 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
206 cache, toggle, count);
207 } else { // perspective case
208 SkScalar dstX = SkIntToScalar(x);
209 SkScalar dstY = SkIntToScalar(y);
210 do {
211 dstProc(fDstToIndex, dstX, dstY, &srcPt);
212 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
213 SkASSERT(fi <= 0xFFFF);
214
215 int index = fi >> (16 - kCache16Bits);
216 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000217 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000218
219 dstX += SK_Scalar1;
220 } while (--count != 0);
221 }
222}
223
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000224SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000225 SkMatrix* matrix, SkShader::TileMode* xy) const {
226 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000227 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000228 }
229 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000230 matrix->setScale(SkIntToScalar(kCache32Count),
231 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000232 matrix->preConcat(fPtsToUnit);
233 }
234 if (xy) {
235 xy[0] = fTileMode;
236 xy[1] = kClamp_TileMode;
237 }
238 return kRadial_BitmapType;
239}
240
241SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
242 if (info) {
243 commonAsAGradient(info);
244 info->fPoint[0] = fCenter;
245 info->fRadius[0] = fRadius;
246 }
247 return kRadial_GradientType;
248}
249
rileya@google.com589708b2012-07-26 20:04:23 +0000250SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer)
251 : INHERITED(buffer),
252 fCenter(buffer.readPoint()),
253 fRadius(buffer.readScalar()) {
254}
255
256void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
257 this->INHERITED::flatten(buffer);
258 buffer.writePoint(fCenter);
259 buffer.writeScalar(fRadius);
260}
261
262namespace {
263
264inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
265 // fast, overly-conservative test: checks unit square instead
266 // of unit circle
267 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
268 (fx <= -SK_FixedHalf && dx <= 0);
269 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
270 (fy <= -SK_FixedHalf && dy <= 0);
271
272 return xClamped || yClamped;
273}
274
275// Return true if (fx * fy) is always inside the unit circle
276// SkPin32 is expensive, but so are all the SkFixedMul in this test,
277// so it shouldn't be run if count is small.
278inline bool no_need_for_radial_pin(int fx, int dx,
279 int fy, int dy, int count) {
280 SkASSERT(count > 0);
281 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
282 return false;
283 }
284 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
285 return false;
286 }
287 fx += (count - 1) * dx;
288 fy += (count - 1) * dy;
289 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
290 return false;
291 }
292 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
293}
294
295#define UNPINNED_RADIAL_STEP \
296 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
297 *dstC++ = cache[toggle + \
298 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000299 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000300 fx += dx; \
301 fy += dy;
302
303typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
304 SkScalar sfy, SkScalar sdy,
305 SkPMColor* dstC, const SkPMColor* cache,
306 int count, int toggle);
307
308// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
309void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
310 SkScalar sfy, SkScalar sdy,
311 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
312 int count, int toggle) {
313 // Floating point seems to be slower than fixed point,
314 // even when we have float hardware.
315 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
316 SkFixed fx = SkScalarToFixed(sfx) >> 1;
317 SkFixed dx = SkScalarToFixed(sdx) >> 1;
318 SkFixed fy = SkScalarToFixed(sfy) >> 1;
319 SkFixed dy = SkScalarToFixed(sdy) >> 1;
320 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000321 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000322 sk_memset32_dither(dstC,
323 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000324 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000325 count);
326 } else if ((count > 4) &&
327 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
328 unsigned fi;
329 // 4x unroll appears to be no faster than 2x unroll on Linux
330 while (count > 1) {
331 UNPINNED_RADIAL_STEP;
332 UNPINNED_RADIAL_STEP;
333 count -= 2;
334 }
335 if (count) {
336 UNPINNED_RADIAL_STEP;
337 }
reed@google.com60040292013-02-04 18:21:23 +0000338 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000339 // Specializing for dy == 0 gains us 25% on Skia benchmarks
340 if (dy == 0) {
341 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
342 yy *= yy;
343 do {
344 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
345 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
346 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
347 *dstC++ = cache[toggle + (sqrt_table[fi] >>
348 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000349 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000350 fx += dx;
351 } while (--count != 0);
352 } else {
353 do {
354 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
355 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
356 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
357 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
358 *dstC++ = cache[toggle + (sqrt_table[fi] >>
359 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000360 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000361 fx += dx;
362 fy += dy;
363 } while (--count != 0);
364 }
365 }
366}
367
368// Unrolling this loop doesn't seem to help (when float); we're stalling to
369// get the results of the sqrt (?), and don't have enough extra registers to
370// have many in flight.
371void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
372 SkScalar sfy, SkScalar sdy,
373 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
374 int count, int toggle) {
375 do {
376#ifdef SK_SCALAR_IS_FLOAT
377 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
378 SkFixed dist = SkFloatToFixed(fdist);
379#else
380 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
381 SkFixedSquare(sfy);
382 if (magnitudeSquared < 0) // Overflow.
383 magnitudeSquared = SK_FixedMax;
384 SkFixed dist = SkFixedSqrt(magnitudeSquared);
385#endif
386 unsigned fi = mirror_tileproc(dist);
387 SkASSERT(fi <= 0xFFFF);
388 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000389 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000390 sfx += sdx;
391 sfy += sdy;
392 } while (--count != 0);
393}
394
395void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
396 SkScalar sfy, SkScalar sdy,
397 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
398 int count, int toggle) {
399 SkFixed fx = SkScalarToFixed(sfx);
400 SkFixed dx = SkScalarToFixed(sdx);
401 SkFixed fy = SkScalarToFixed(sfy);
402 SkFixed dy = SkScalarToFixed(sdy);
403 do {
404 SkFixed magnitudeSquared = SkFixedSquare(fx) +
405 SkFixedSquare(fy);
406 if (magnitudeSquared < 0) // Overflow.
407 magnitudeSquared = SK_FixedMax;
408 SkFixed dist = SkFixedSqrt(magnitudeSquared);
409 unsigned fi = repeat_tileproc(dist);
410 SkASSERT(fi <= 0xFFFF);
411 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000412 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000413 fx += dx;
414 fy += dy;
415 } while (--count != 0);
416}
417}
418
419void SkRadialGradient::shadeSpan(int x, int y,
420 SkPMColor* SK_RESTRICT dstC, int count) {
421 SkASSERT(count > 0);
422
423 SkPoint srcPt;
424 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
425 TileProc proc = fTileProc;
426 const SkPMColor* SK_RESTRICT cache = this->getCache32();
427#ifdef USE_DITHER_32BIT_GRADIENT
reed@google.com55853db2013-02-01 19:34:59 +0000428 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000429#else
430 int toggle = 0;
431#endif
432
433 if (fDstToIndexClass != kPerspective_MatrixClass) {
434 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
435 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
436 SkScalar sdx = fDstToIndex.getScaleX();
437 SkScalar sdy = fDstToIndex.getSkewY();
438
439 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
440 SkFixed storage[2];
441 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
442 &storage[0], &storage[1]);
443 sdx = SkFixedToScalar(storage[0]);
444 sdy = SkFixedToScalar(storage[1]);
445 } else {
446 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
447 }
448
449 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
450 if (SkShader::kClamp_TileMode == fTileMode) {
451 shadeProc = shadeSpan_radial_clamp;
452 } else if (SkShader::kMirror_TileMode == fTileMode) {
453 shadeProc = shadeSpan_radial_mirror;
454 } else {
455 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
456 }
457 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
458 } else { // perspective case
459 SkScalar dstX = SkIntToScalar(x);
460 SkScalar dstY = SkIntToScalar(y);
461 do {
462 dstProc(fDstToIndex, dstX, dstY, &srcPt);
463 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
464 SkASSERT(fi <= 0xFFFF);
465 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
466 dstX += SK_Scalar1;
467 } while (--count != 0);
468 }
469}
470
rileya@google.comd7cc6512012-07-27 14:00:39 +0000471/////////////////////////////////////////////////////////////////////
472
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000473#if SK_SUPPORT_GPU
474
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000475#include "GrTBackendEffectFactory.h"
476
bsalomon@google.com0707c292012-10-25 21:45:42 +0000477class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000478public:
479
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000480 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.com6340a412013-01-22 19:55:59 +0000481 const GrEffectRef&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000482 virtual ~GrGLRadialGradient() { }
483
bsalomon@google.comf78df332012-10-29 12:43:38 +0000484 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000485 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000486 EffectKey,
487 const char* vertexCoords,
488 const char* outputColor,
489 const char* inputColor,
490 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000491
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000492 static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
493 return GenMatrixKey(stage);
494 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000495
496private:
497
bsalomon@google.com0707c292012-10-25 21:45:42 +0000498 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000499
500};
501
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000502/////////////////////////////////////////////////////////////////////
503
504class GrRadialGradient : public GrGradientEffect {
505public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000506 static GrEffectRef* Create(GrContext* ctx,
507 const SkRadialGradient& shader,
508 const SkMatrix& matrix,
509 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000510 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000511 return CreateEffectRef(effect);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000512 }
513
514 virtual ~GrRadialGradient() { }
515
516 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000517 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
518 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000519 }
520
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000521 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000522
523private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000524 GrRadialGradient(GrContext* ctx,
525 const SkRadialGradient& shader,
526 const SkMatrix& matrix,
527 SkShader::TileMode tm)
528 : INHERITED(ctx, shader, matrix, tm) {
529 }
530
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000531 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000532
533 typedef GrGradientEffect INHERITED;
534};
535
536/////////////////////////////////////////////////////////////////////
537
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000538GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000539
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000540GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
541 GrContext* context,
542 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000543 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
544 SkScalar radius = random->nextUScalar1();
545
546 SkColor colors[kMaxRandomGradientColors];
547 SkScalar stopsArray[kMaxRandomGradientColors];
548 SkScalar* stops = stopsArray;
549 SkShader::TileMode tm;
550 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
551 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
552 colors, stops, colorCount,
553 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000554 SkPaint paint;
555 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000556}
557
558/////////////////////////////////////////////////////////////////////
559
bsalomon@google.comf78df332012-10-29 12:43:38 +0000560void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000561 const GrEffectStage& stage,
562 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000563 const char* vertexCoords,
564 const char* outputColor,
565 const char* inputColor,
566 const TextureSamplerArray& samplers) {
567 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000568 const char* coords;
569 this->setupMatrix(builder, key, vertexCoords, &coords);
570 SkString t("length(");
571 t.append(coords);
572 t.append(")");
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000573 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000574}
575
rileya@google.comd7cc6512012-07-27 14:00:39 +0000576/////////////////////////////////////////////////////////////////////
577
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000578GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000579 SkASSERT(NULL != context);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000580
581 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000582 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000583 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000584 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000585 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000586 return GrRadialGradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000587}
588
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000589#else
590
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000591GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000592 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000593 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000594}
595
596#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000597
598#ifdef SK_DEVELOPER
599void SkRadialGradient::toString(SkString* str) const {
600 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000601
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000602 str->append("center: (");
603 str->appendScalar(fCenter.fX);
604 str->append(", ");
605 str->appendScalar(fCenter.fY);
606 str->append(") radius: ");
607 str->appendScalar(fRadius);
608 str->append(" ");
609
610 this->INHERITED::toString(str);
611
612 str->append(")");
613}
614#endif