blob: ff3a2aa75c19ce45d437ee234b64e8f905ca4264 [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();
reed@google.com55853db2013-02-01 19:34:59 +0000427 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000428
429 if (fDstToIndexClass != kPerspective_MatrixClass) {
430 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
431 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
432 SkScalar sdx = fDstToIndex.getScaleX();
433 SkScalar sdy = fDstToIndex.getSkewY();
434
435 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
436 SkFixed storage[2];
437 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
438 &storage[0], &storage[1]);
439 sdx = SkFixedToScalar(storage[0]);
440 sdy = SkFixedToScalar(storage[1]);
441 } else {
442 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
443 }
444
445 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
446 if (SkShader::kClamp_TileMode == fTileMode) {
447 shadeProc = shadeSpan_radial_clamp;
448 } else if (SkShader::kMirror_TileMode == fTileMode) {
449 shadeProc = shadeSpan_radial_mirror;
450 } else {
451 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
452 }
453 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
454 } else { // perspective case
455 SkScalar dstX = SkIntToScalar(x);
456 SkScalar dstY = SkIntToScalar(y);
457 do {
458 dstProc(fDstToIndex, dstX, dstY, &srcPt);
459 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
460 SkASSERT(fi <= 0xFFFF);
461 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
462 dstX += SK_Scalar1;
463 } while (--count != 0);
464 }
465}
466
rileya@google.comd7cc6512012-07-27 14:00:39 +0000467/////////////////////////////////////////////////////////////////////
468
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000469#if SK_SUPPORT_GPU
470
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000471#include "GrTBackendEffectFactory.h"
472
bsalomon@google.com0707c292012-10-25 21:45:42 +0000473class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000474public:
475
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000476 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.com6340a412013-01-22 19:55:59 +0000477 const GrEffectRef&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000478 virtual ~GrGLRadialGradient() { }
479
bsalomon@google.comf78df332012-10-29 12:43:38 +0000480 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000481 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000482 EffectKey,
483 const char* vertexCoords,
484 const char* outputColor,
485 const char* inputColor,
486 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000487
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000488 static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
489 return GenMatrixKey(stage);
490 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000491
492private:
493
bsalomon@google.com0707c292012-10-25 21:45:42 +0000494 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000495
496};
497
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000498/////////////////////////////////////////////////////////////////////
499
500class GrRadialGradient : public GrGradientEffect {
501public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000502 static GrEffectRef* Create(GrContext* ctx,
503 const SkRadialGradient& shader,
504 const SkMatrix& matrix,
505 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000506 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000507 return CreateEffectRef(effect);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000508 }
509
510 virtual ~GrRadialGradient() { }
511
512 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000513 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
514 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000515 }
516
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000517 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000518
519private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000520 GrRadialGradient(GrContext* ctx,
521 const SkRadialGradient& shader,
522 const SkMatrix& matrix,
523 SkShader::TileMode tm)
524 : INHERITED(ctx, shader, matrix, tm) {
525 }
526
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000527 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000528
529 typedef GrGradientEffect INHERITED;
530};
531
532/////////////////////////////////////////////////////////////////////
533
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000534GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000535
bsalomon@google.com73a96942013-02-13 16:31:19 +0000536GrEffectRef* GrRadialGradient::TestCreate(SkMWCRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000537 GrContext* context,
538 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000539 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
540 SkScalar radius = random->nextUScalar1();
541
542 SkColor colors[kMaxRandomGradientColors];
543 SkScalar stopsArray[kMaxRandomGradientColors];
544 SkScalar* stops = stopsArray;
545 SkShader::TileMode tm;
546 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
547 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
548 colors, stops, colorCount,
549 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000550 SkPaint paint;
551 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000552}
553
554/////////////////////////////////////////////////////////////////////
555
bsalomon@google.comf78df332012-10-29 12:43:38 +0000556void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000557 const GrEffectStage& stage,
558 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000559 const char* vertexCoords,
560 const char* outputColor,
561 const char* inputColor,
562 const TextureSamplerArray& samplers) {
563 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000564 const char* coords;
565 this->setupMatrix(builder, key, vertexCoords, &coords);
566 SkString t("length(");
567 t.append(coords);
568 t.append(")");
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000569 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000570}
571
rileya@google.comd7cc6512012-07-27 14:00:39 +0000572/////////////////////////////////////////////////////////////////////
573
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000574GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000575 SkASSERT(NULL != context);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000576
577 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000578 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000579 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000580 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000581 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000582 return GrRadialGradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000583}
584
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000585#else
586
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000587GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000588 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000589 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000590}
591
592#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000593
594#ifdef SK_DEVELOPER
595void SkRadialGradient::toString(SkString* str) const {
596 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000597
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000598 str->append("center: (");
599 str->appendScalar(fCenter.fX);
600 str->append(", ");
601 str->appendScalar(fCenter.fY);
602 str->append(") radius: ");
603 str->appendScalar(fRadius);
604 str->append(" ");
605
606 this->INHERITED::toString(str);
607
608 str->append(")");
609}
610#endif