blob: e880df25cbbe59aa8d665467a26a995624b601b4 [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
reed@google.com4ec5c952013-12-19 15:00:18 +000015#if 0
rileya@google.com589708b2012-07-26 20:04:23 +000016
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
commit-bot@chromium.org34150b42013-10-16 18:59:44 +000049// GCC doesn't like using static functions as template arguments. So force these to be non-static.
50inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
51 return mirror_tileproc(x);
52}
53
54inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
55 return repeat_tileproc(x);
56}
57
rileya@google.com589708b2012-07-26 20:04:23 +000058void 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
66typedef 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
71void 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)];
reed@google.com55853db2013-02-01 19:34:59 +0000100 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000101 } 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)];
reed@google.com55853db2013-02-01 19:34:59 +0000112 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000113 } while (--count != 0);
114 }
115}
116
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000117template <SkFixed (*TileProc)(SkFixed)>
118void 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) {
rileya@google.com589708b2012-07-26 20:04:23 +0000121 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000122 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
123 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000124 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 fx += dx;
128 fy += dy;
rileya@google.com589708b2012-07-26 20:04:23 +0000129 } while (--count != 0);
130}
131
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000132void 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);
rileya@google.com589708b2012-07-26 20:04:23 +0000136}
137
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000138void 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
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000146/////////////////////////////////////////////////////////////////////
147
reedaddf2ed2014-08-11 08:28:24 -0700148SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
149 : SkGradientShaderBase(desc)
150 , fCenter(center)
151 , fRadius(radius)
rileya@google.com589708b2012-07-26 20:04:23 +0000152{
153 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
154 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
155
156 rad_to_unit_matrix(center, radius, &fPtsToUnit);
157}
158
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000159size_t SkRadialGradient::contextSize() const {
160 return sizeof(RadialGradientContext);
161}
162
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000163SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000164 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000165}
166
167SkRadialGradient::RadialGradientContext::RadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000168 const SkRadialGradient& shader, const ContextRec& rec)
169 : INHERITED(shader, rec) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000170
171void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
172 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000173 SkASSERT(count > 0);
174
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000175 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
176
rileya@google.com589708b2012-07-26 20:04:23 +0000177 uint16_t* SK_RESTRICT dstC = dstCParam;
178
179 SkPoint srcPt;
180 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000181 TileProc proc = radialGradient.fTileProc;
182 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000183 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000184
185 if (fDstToIndexClass != kPerspective_MatrixClass) {
186 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
187 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
188
189 SkScalar sdx = fDstToIndex.getScaleX();
190 SkScalar sdy = fDstToIndex.getSkewY();
191
192 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
193 SkFixed storage[2];
194 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
195 &storage[0], &storage[1]);
196 sdx = SkFixedToScalar(storage[0]);
197 sdy = SkFixedToScalar(storage[1]);
198 } else {
199 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
200 }
201
202 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000203 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000204 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000205 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000206 shadeProc = shadeSpan16_radial_mirror;
207 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000208 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000209 }
210 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
211 cache, toggle, count);
212 } else { // perspective case
213 SkScalar dstX = SkIntToScalar(x);
214 SkScalar dstY = SkIntToScalar(y);
215 do {
216 dstProc(fDstToIndex, dstX, dstY, &srcPt);
217 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
218 SkASSERT(fi <= 0xFFFF);
219
220 int index = fi >> (16 - kCache16Bits);
221 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000222 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000223
224 dstX += SK_Scalar1;
225 } while (--count != 0);
226 }
227}
228
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000229SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000230 SkMatrix* matrix, SkShader::TileMode* xy) const {
231 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000232 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000233 }
234 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000235 matrix->setScale(SkIntToScalar(kCache32Count),
236 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000237 matrix->preConcat(fPtsToUnit);
238 }
239 if (xy) {
240 xy[0] = fTileMode;
241 xy[1] = kClamp_TileMode;
242 }
243 return kRadial_BitmapType;
244}
245
246SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
247 if (info) {
248 commonAsAGradient(info);
249 info->fPoint[0] = fCenter;
250 info->fRadius[0] = fRadius;
251 }
252 return kRadial_GradientType;
253}
254
reed9fa60da2014-08-21 07:59:51 -0700255SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
256 DescriptorScope desc;
257 if (!desc.unflatten(buffer)) {
258 return NULL;
259 }
260 const SkPoint center = buffer.readPoint();
261 const SkScalar radius = buffer.readScalar();
262 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
263 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
264}
rileya@google.com589708b2012-07-26 20:04:23 +0000265
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000266void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000267 this->INHERITED::flatten(buffer);
268 buffer.writePoint(fCenter);
269 buffer.writeScalar(fRadius);
270}
271
272namespace {
273
274inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
275 // fast, overly-conservative test: checks unit square instead
276 // of unit circle
277 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
278 (fx <= -SK_FixedHalf && dx <= 0);
279 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
280 (fy <= -SK_FixedHalf && dy <= 0);
281
282 return xClamped || yClamped;
283}
284
285// Return true if (fx * fy) is always inside the unit circle
286// SkPin32 is expensive, but so are all the SkFixedMul in this test,
287// so it shouldn't be run if count is small.
288inline bool no_need_for_radial_pin(int fx, int dx,
289 int fy, int dy, int count) {
290 SkASSERT(count > 0);
291 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
292 return false;
293 }
294 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
295 return false;
296 }
297 fx += (count - 1) * dx;
298 fy += (count - 1) * dy;
299 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
300 return false;
301 }
302 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
303}
304
305#define UNPINNED_RADIAL_STEP \
306 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
307 *dstC++ = cache[toggle + \
308 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000309 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000310 fx += dx; \
311 fy += dy;
312
313typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
314 SkScalar sfy, SkScalar sdy,
315 SkPMColor* dstC, const SkPMColor* cache,
316 int count, int toggle);
317
318// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
319void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
320 SkScalar sfy, SkScalar sdy,
321 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
322 int count, int toggle) {
323 // Floating point seems to be slower than fixed point,
324 // even when we have float hardware.
325 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
326 SkFixed fx = SkScalarToFixed(sfx) >> 1;
327 SkFixed dx = SkScalarToFixed(sdx) >> 1;
328 SkFixed fy = SkScalarToFixed(sfy) >> 1;
329 SkFixed dy = SkScalarToFixed(sdy) >> 1;
330 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000331 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000332 sk_memset32_dither(dstC,
333 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000334 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000335 count);
336 } else if ((count > 4) &&
337 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
338 unsigned fi;
339 // 4x unroll appears to be no faster than 2x unroll on Linux
340 while (count > 1) {
341 UNPINNED_RADIAL_STEP;
342 UNPINNED_RADIAL_STEP;
343 count -= 2;
344 }
345 if (count) {
346 UNPINNED_RADIAL_STEP;
347 }
reed@google.com60040292013-02-04 18:21:23 +0000348 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000349 // Specializing for dy == 0 gains us 25% on Skia benchmarks
350 if (dy == 0) {
351 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
352 yy *= yy;
353 do {
354 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
355 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
356 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
357 *dstC++ = cache[toggle + (sqrt_table[fi] >>
358 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000359 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000360 fx += dx;
361 } while (--count != 0);
362 } else {
363 do {
364 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
365 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
366 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
367 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
368 *dstC++ = cache[toggle + (sqrt_table[fi] >>
369 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000370 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000371 fx += dx;
372 fy += dy;
373 } while (--count != 0);
374 }
375 }
376}
377
378// Unrolling this loop doesn't seem to help (when float); we're stalling to
379// get the results of the sqrt (?), and don't have enough extra registers to
380// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000381template <SkFixed (*TileProc)(SkFixed)>
382void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
383 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
384 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000385 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000386 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
387 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000388 SkASSERT(fi <= 0xFFFF);
389 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000390 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000391 fx += dx;
392 fy += dy;
393 } while (--count != 0);
394}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000395
396void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
397 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
398 int count, int toggle) {
399 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000400}
401
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000402void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
403 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
404 int count, int toggle) {
405 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
406}
407
408} // namespace
409
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000410void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
411 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000412 SkASSERT(count > 0);
413
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000414 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
415
rileya@google.com589708b2012-07-26 20:04:23 +0000416 SkPoint srcPt;
417 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000418 TileProc proc = radialGradient.fTileProc;
419 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000420 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000421
422 if (fDstToIndexClass != kPerspective_MatrixClass) {
423 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
424 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
425 SkScalar sdx = fDstToIndex.getScaleX();
426 SkScalar sdy = fDstToIndex.getSkewY();
427
428 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
429 SkFixed storage[2];
430 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
431 &storage[0], &storage[1]);
432 sdx = SkFixedToScalar(storage[0]);
433 sdy = SkFixedToScalar(storage[1]);
434 } else {
435 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
436 }
437
438 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000439 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000440 shadeProc = shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000441 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000442 shadeProc = shadeSpan_radial_mirror;
443 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000444 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000445 }
446 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
447 } else { // perspective case
448 SkScalar dstX = SkIntToScalar(x);
449 SkScalar dstY = SkIntToScalar(y);
450 do {
451 dstProc(fDstToIndex, dstX, dstY, &srcPt);
452 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
453 SkASSERT(fi <= 0xFFFF);
454 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
455 dstX += SK_Scalar1;
456 } while (--count != 0);
457 }
458}
459
rileya@google.comd7cc6512012-07-27 14:00:39 +0000460/////////////////////////////////////////////////////////////////////
461
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000462#if SK_SUPPORT_GPU
463
dandov9de5b512014-06-10 14:38:28 -0700464#include "SkGr.h"
joshualitteb2a6762014-12-04 11:35:33 -0800465#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000466
bsalomon@google.com0707c292012-10-25 21:45:42 +0000467class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000468public:
469
joshualitteb2a6762014-12-04 11:35:33 -0800470 GrGLRadialGradient(const GrProcessor&) {}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000471 virtual ~GrGLRadialGradient() { }
472
joshualitt15988992014-10-09 15:04:05 -0700473 virtual void emitCode(GrGLFPBuilder*,
joshualittb0a8a372014-09-23 09:50:21 -0700474 const GrFragmentProcessor&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000475 const char* outputColor,
476 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000477 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000478 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000479
joshualittb0a8a372014-09-23 09:50:21 -0700480 static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
481 b->add32(GenBaseGradientKey(processor));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000482 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000483
484private:
485
bsalomon@google.com0707c292012-10-25 21:45:42 +0000486 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000487
488};
489
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000490/////////////////////////////////////////////////////////////////////
491
492class GrRadialGradient : public GrGradientEffect {
493public:
joshualittb0a8a372014-09-23 09:50:21 -0700494 static GrFragmentProcessor* Create(GrContext* ctx,
495 const SkRadialGradient& shader,
496 const SkMatrix& matrix,
497 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700498 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000499 }
500
501 virtual ~GrRadialGradient() { }
502
joshualitteb2a6762014-12-04 11:35:33 -0800503 virtual const char* name() const SK_OVERRIDE { return "Radial Gradient"; }
504
505 virtual void getGLProcessorKey(const GrGLCaps& caps,
506 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
507 GrGLRadialGradient::GenKey(*this, caps, b);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000508 }
509
joshualitteb2a6762014-12-04 11:35:33 -0800510 virtual GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
511 return SkNEW_ARGS(GrGLRadialGradient, (*this));
512 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000513
514private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000515 GrRadialGradient(GrContext* ctx,
516 const SkRadialGradient& shader,
517 const SkMatrix& matrix,
518 SkShader::TileMode tm)
519 : INHERITED(ctx, shader, matrix, tm) {
joshualitteb2a6762014-12-04 11:35:33 -0800520 this->initClassID<GrRadialGradient>();
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000521 }
522
joshualittb0a8a372014-09-23 09:50:21 -0700523 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000524
525 typedef GrGradientEffect INHERITED;
526};
527
528/////////////////////////////////////////////////////////////////////
529
joshualittb0a8a372014-09-23 09:50:21 -0700530GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000531
joshualittb0a8a372014-09-23 09:50:21 -0700532GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random,
533 GrContext* context,
534 const GrDrawTargetCaps&,
535 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000536 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
537 SkScalar radius = random->nextUScalar1();
538
539 SkColor colors[kMaxRandomGradientColors];
540 SkScalar stopsArray[kMaxRandomGradientColors];
541 SkScalar* stops = stopsArray;
542 SkShader::TileMode tm;
543 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
544 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
545 colors, stops, colorCount,
546 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000547 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700548 GrColor paintColor;
joshualittb0a8a372014-09-23 09:50:21 -0700549 GrFragmentProcessor* fp;
550 SkAssertResult(shader->asFragmentProcessor(context, paint, NULL, &paintColor, &fp));
551 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000552}
553
554/////////////////////////////////////////////////////////////////////
555
joshualitt15988992014-10-09 15:04:05 -0700556void GrGLRadialGradient::emitCode(GrGLFPBuilder* builder,
joshualitt60030bc2014-11-25 14:21:55 -0800557 const GrFragmentProcessor& fp,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000558 const char* outputColor,
559 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000560 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000561 const TextureSamplerArray& samplers) {
joshualitteb2a6762014-12-04 11:35:33 -0800562 const GrRadialGradient& ge = fp.cast<GrRadialGradient>();
joshualitt60030bc2014-11-25 14:21:55 -0800563 this->emitUniforms(builder, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000564 SkString t("length(");
joshualitt30ba4362014-08-21 20:18:45 -0700565 t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000566 t.append(")");
joshualitt60030bc2014-11-25 14:21:55 -0800567 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000568}
569
rileya@google.comd7cc6512012-07-27 14:00:39 +0000570/////////////////////////////////////////////////////////////////////
571
joshualittb0a8a372014-09-23 09:50:21 -0700572bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
573 const SkMatrix* localMatrix, GrColor* paintColor,
574 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700575 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800576
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000577 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000578 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700579 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000580 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000581 if (localMatrix) {
582 SkMatrix inv;
583 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700584 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000585 }
586 matrix.postConcat(inv);
587 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000588 matrix.postConcat(fPtsToUnit);
mtklein3f3b3d02014-12-01 11:47:08 -0800589
bsalomon83d081a2014-07-08 09:56:10 -0700590 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualittb0a8a372014-09-23 09:50:21 -0700591 *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800592
dandov9de5b512014-06-10 14:38:28 -0700593 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000594}
595
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000596#else
597
joshualittb0a8a372014-09-23 09:50:21 -0700598bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, GrColor*,
599 GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000600 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700601 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000602}
603
604#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000605
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000606#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000607void SkRadialGradient::toString(SkString* str) const {
608 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000609
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000610 str->append("center: (");
611 str->appendScalar(fCenter.fX);
612 str->append(", ");
613 str->appendScalar(fCenter.fY);
614 str->append(") radius: ");
615 str->appendScalar(fRadius);
616 str->append(" ");
617
618 this->INHERITED::toString(str);
619
620 str->append(")");
621}
622#endif