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