blob: b9c4e3b1ddccd5d274dc73c139534d34d0122796 [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)];
91 toggle ^= SkGradientShaderBase::kDitherStride16;
92 } 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)];
103 toggle ^= SkGradientShaderBase::kDitherStride16;
104 } 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)];
126 toggle ^= SkGradientShaderBase::kDitherStride16;
127 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)];
147 toggle ^= SkGradientShaderBase::kDitherStride16;
148 } 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();
178 int toggle = ((x ^ y) & 1) * kDitherStride16;
179
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];
217 toggle ^= kDitherStride16;
218
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) {
230 matrix->setScale(SkIntToScalar(kGradient32Length),
231 SkIntToScalar(kGradient32Length));
232 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)]; \
299 toggle ^= SkGradientShaderBase::kDitherStride32; \
300 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)) {
321 unsigned fi = SkGradientShaderBase::kGradient32Length;
322 sk_memset32_dither(dstC,
323 cache[toggle + fi],
324 cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi],
325 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 }
338 }
339 else {
340 // Specializing for dy == 0 gains us 25% on Skia benchmarks
341 if (dy == 0) {
342 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
343 yy *= yy;
344 do {
345 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
346 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
347 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
348 *dstC++ = cache[toggle + (sqrt_table[fi] >>
349 SkGradientShaderBase::kSqrt32Shift)];
350 toggle ^= SkGradientShaderBase::kDitherStride32;
351 fx += dx;
352 } while (--count != 0);
353 } else {
354 do {
355 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
356 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
357 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
358 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
359 *dstC++ = cache[toggle + (sqrt_table[fi] >>
360 SkGradientShaderBase::kSqrt32Shift)];
361 toggle ^= SkGradientShaderBase::kDitherStride32;
362 fx += dx;
363 fy += dy;
364 } while (--count != 0);
365 }
366 }
367}
368
369// Unrolling this loop doesn't seem to help (when float); we're stalling to
370// get the results of the sqrt (?), and don't have enough extra registers to
371// have many in flight.
372void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
373 SkScalar sfy, SkScalar sdy,
374 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
375 int count, int toggle) {
376 do {
377#ifdef SK_SCALAR_IS_FLOAT
378 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
379 SkFixed dist = SkFloatToFixed(fdist);
380#else
381 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
382 SkFixedSquare(sfy);
383 if (magnitudeSquared < 0) // Overflow.
384 magnitudeSquared = SK_FixedMax;
385 SkFixed dist = SkFixedSqrt(magnitudeSquared);
386#endif
387 unsigned fi = mirror_tileproc(dist);
388 SkASSERT(fi <= 0xFFFF);
389 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
390 toggle ^= SkGradientShaderBase::kDitherStride32;
391 sfx += sdx;
392 sfy += sdy;
393 } while (--count != 0);
394}
395
396void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
397 SkScalar sfy, SkScalar sdy,
398 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
399 int count, int toggle) {
400 SkFixed fx = SkScalarToFixed(sfx);
401 SkFixed dx = SkScalarToFixed(sdx);
402 SkFixed fy = SkScalarToFixed(sfy);
403 SkFixed dy = SkScalarToFixed(sdy);
404 do {
405 SkFixed magnitudeSquared = SkFixedSquare(fx) +
406 SkFixedSquare(fy);
407 if (magnitudeSquared < 0) // Overflow.
408 magnitudeSquared = SK_FixedMax;
409 SkFixed dist = SkFixedSqrt(magnitudeSquared);
410 unsigned fi = repeat_tileproc(dist);
411 SkASSERT(fi <= 0xFFFF);
412 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
413 toggle ^= SkGradientShaderBase::kDitherStride32;
414 fx += dx;
415 fy += dy;
416 } while (--count != 0);
417}
418}
419
420void SkRadialGradient::shadeSpan(int x, int y,
421 SkPMColor* SK_RESTRICT dstC, int count) {
422 SkASSERT(count > 0);
423
424 SkPoint srcPt;
425 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
426 TileProc proc = fTileProc;
427 const SkPMColor* SK_RESTRICT cache = this->getCache32();
428#ifdef USE_DITHER_32BIT_GRADIENT
429 int toggle = ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride32;
430#else
431 int toggle = 0;
432#endif
433
434 if (fDstToIndexClass != kPerspective_MatrixClass) {
435 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
436 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
437 SkScalar sdx = fDstToIndex.getScaleX();
438 SkScalar sdy = fDstToIndex.getSkewY();
439
440 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
441 SkFixed storage[2];
442 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
443 &storage[0], &storage[1]);
444 sdx = SkFixedToScalar(storage[0]);
445 sdy = SkFixedToScalar(storage[1]);
446 } else {
447 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
448 }
449
450 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
451 if (SkShader::kClamp_TileMode == fTileMode) {
452 shadeProc = shadeSpan_radial_clamp;
453 } else if (SkShader::kMirror_TileMode == fTileMode) {
454 shadeProc = shadeSpan_radial_mirror;
455 } else {
456 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
457 }
458 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
459 } else { // perspective case
460 SkScalar dstX = SkIntToScalar(x);
461 SkScalar dstY = SkIntToScalar(y);
462 do {
463 dstProc(fDstToIndex, dstX, dstY, &srcPt);
464 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
465 SkASSERT(fi <= 0xFFFF);
466 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
467 dstX += SK_Scalar1;
468 } while (--count != 0);
469 }
470}
471
rileya@google.comd7cc6512012-07-27 14:00:39 +0000472/////////////////////////////////////////////////////////////////////
473
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000474#if SK_SUPPORT_GPU
475
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000476#include "GrTBackendEffectFactory.h"
477
bsalomon@google.com0707c292012-10-25 21:45:42 +0000478class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000479public:
480
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000481 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000482 const GrEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000483 virtual ~GrGLRadialGradient() { }
484
bsalomon@google.comf78df332012-10-29 12:43:38 +0000485 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000486 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000487 EffectKey,
488 const char* vertexCoords,
489 const char* outputColor,
490 const char* inputColor,
491 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000492
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000493 static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
494 return GenMatrixKey(stage);
495 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000496
497private:
498
bsalomon@google.com0707c292012-10-25 21:45:42 +0000499 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000500
501};
502
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000503/////////////////////////////////////////////////////////////////////
504
505class GrRadialGradient : public GrGradientEffect {
506public:
507
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000508 GrRadialGradient(GrContext* ctx,
509 const SkRadialGradient& shader,
510 const SkMatrix& matrix,
511 SkShader::TileMode tm)
512 : INHERITED(ctx, shader, matrix, tm) {
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000513 }
514
515 virtual ~GrRadialGradient() { }
516
517 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000518 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
519 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000520 }
521
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000522 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000523
524private:
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000525 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000526
527 typedef GrGradientEffect INHERITED;
528};
529
530/////////////////////////////////////////////////////////////////////
531
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000532GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000533
bsalomon@google.coma469c282012-10-24 18:28:34 +0000534GrEffect* GrRadialGradient::TestCreate(SkRandom* random,
535 GrContext* context,
536 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000537 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
538 SkScalar radius = random->nextUScalar1();
539
540 SkColor colors[kMaxRandomGradientColors];
541 SkScalar stopsArray[kMaxRandomGradientColors];
542 SkScalar* stops = stopsArray;
543 SkShader::TileMode tm;
544 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
545 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
546 colors, stops, colorCount,
547 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000548 SkPaint paint;
549 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000550}
551
552/////////////////////////////////////////////////////////////////////
553
bsalomon@google.comf78df332012-10-29 12:43:38 +0000554void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000555 const GrEffectStage& stage,
556 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000557 const char* vertexCoords,
558 const char* outputColor,
559 const char* inputColor,
560 const TextureSamplerArray& samplers) {
561 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000562 const char* coords;
563 this->setupMatrix(builder, key, vertexCoords, &coords);
564 SkString t("length(");
565 t.append(coords);
566 t.append(")");
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000567 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000568}
569
rileya@google.comd7cc6512012-07-27 14:00:39 +0000570/////////////////////////////////////////////////////////////////////
571
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000572GrEffect* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000573 SkASSERT(NULL != context);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000574
575 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000576 if (!this->getLocalMatrix().invert(&matrix)) {
577 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000578 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000579 matrix.postConcat(fPtsToUnit);
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000580 return SkNEW_ARGS(GrRadialGradient, (context, *this, matrix, fTileMode));
rileya@google.comd7cc6512012-07-27 14:00:39 +0000581}
582
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000583#else
584
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000585GrEffect* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000586 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000587 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000588}
589
590#endif