blob: e3aaea341f7ccc8644ebcdf49c6ea8cc19b98ef8 [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
rileya@google.com589708b2012-07-26 20:04:23 +0000148SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000149 const Descriptor& desc, const SkMatrix* localMatrix)
150 : SkGradientShaderBase(desc, localMatrix),
rileya@google.com589708b2012-07-26 20:04:23 +0000151 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
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000160size_t SkRadialGradient::contextSize() const {
161 return sizeof(RadialGradientContext);
162}
163
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000164SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000165 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000166}
167
168SkRadialGradient::RadialGradientContext::RadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000169 const SkRadialGradient& shader, const ContextRec& rec)
170 : INHERITED(shader, rec) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000171
172void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
173 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000174 SkASSERT(count > 0);
175
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000176 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
177
rileya@google.com589708b2012-07-26 20:04:23 +0000178 uint16_t* SK_RESTRICT dstC = dstCParam;
179
180 SkPoint srcPt;
181 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000182 TileProc proc = radialGradient.fTileProc;
183 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000184 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000185
186 if (fDstToIndexClass != kPerspective_MatrixClass) {
187 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
188 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
189
190 SkScalar sdx = fDstToIndex.getScaleX();
191 SkScalar sdy = fDstToIndex.getSkewY();
192
193 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
194 SkFixed storage[2];
195 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
196 &storage[0], &storage[1]);
197 sdx = SkFixedToScalar(storage[0]);
198 sdy = SkFixedToScalar(storage[1]);
199 } else {
200 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
201 }
202
203 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000204 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000205 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000206 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000207 shadeProc = shadeSpan16_radial_mirror;
208 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000209 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000210 }
211 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
212 cache, toggle, count);
213 } else { // perspective case
214 SkScalar dstX = SkIntToScalar(x);
215 SkScalar dstY = SkIntToScalar(y);
216 do {
217 dstProc(fDstToIndex, dstX, dstY, &srcPt);
218 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
219 SkASSERT(fi <= 0xFFFF);
220
221 int index = fi >> (16 - kCache16Bits);
222 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000223 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000224
225 dstX += SK_Scalar1;
226 } while (--count != 0);
227 }
228}
229
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000230SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000231 SkMatrix* matrix, SkShader::TileMode* xy) const {
232 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000233 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000234 }
235 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000236 matrix->setScale(SkIntToScalar(kCache32Count),
237 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000238 matrix->preConcat(fPtsToUnit);
239 }
240 if (xy) {
241 xy[0] = fTileMode;
242 xy[1] = kClamp_TileMode;
243 }
244 return kRadial_BitmapType;
245}
246
247SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
248 if (info) {
249 commonAsAGradient(info);
250 info->fPoint[0] = fCenter;
251 info->fRadius[0] = fRadius;
252 }
253 return kRadial_GradientType;
254}
255
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000256SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
rileya@google.com589708b2012-07-26 20:04:23 +0000257 : INHERITED(buffer),
258 fCenter(buffer.readPoint()),
259 fRadius(buffer.readScalar()) {
260}
261
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000262void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000263 this->INHERITED::flatten(buffer);
264 buffer.writePoint(fCenter);
265 buffer.writeScalar(fRadius);
266}
267
268namespace {
269
270inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
271 // fast, overly-conservative test: checks unit square instead
272 // of unit circle
273 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
274 (fx <= -SK_FixedHalf && dx <= 0);
275 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
276 (fy <= -SK_FixedHalf && dy <= 0);
277
278 return xClamped || yClamped;
279}
280
281// Return true if (fx * fy) is always inside the unit circle
282// SkPin32 is expensive, but so are all the SkFixedMul in this test,
283// so it shouldn't be run if count is small.
284inline bool no_need_for_radial_pin(int fx, int dx,
285 int fy, int dy, int count) {
286 SkASSERT(count > 0);
287 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
288 return false;
289 }
290 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
291 return false;
292 }
293 fx += (count - 1) * dx;
294 fy += (count - 1) * dy;
295 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
296 return false;
297 }
298 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
299}
300
301#define UNPINNED_RADIAL_STEP \
302 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
303 *dstC++ = cache[toggle + \
304 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000305 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000306 fx += dx; \
307 fy += dy;
308
309typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
310 SkScalar sfy, SkScalar sdy,
311 SkPMColor* dstC, const SkPMColor* cache,
312 int count, int toggle);
313
314// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
315void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
316 SkScalar sfy, SkScalar sdy,
317 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
318 int count, int toggle) {
319 // Floating point seems to be slower than fixed point,
320 // even when we have float hardware.
321 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
322 SkFixed fx = SkScalarToFixed(sfx) >> 1;
323 SkFixed dx = SkScalarToFixed(sdx) >> 1;
324 SkFixed fy = SkScalarToFixed(sfy) >> 1;
325 SkFixed dy = SkScalarToFixed(sdy) >> 1;
326 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000327 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000328 sk_memset32_dither(dstC,
329 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000330 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000331 count);
332 } else if ((count > 4) &&
333 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
334 unsigned fi;
335 // 4x unroll appears to be no faster than 2x unroll on Linux
336 while (count > 1) {
337 UNPINNED_RADIAL_STEP;
338 UNPINNED_RADIAL_STEP;
339 count -= 2;
340 }
341 if (count) {
342 UNPINNED_RADIAL_STEP;
343 }
reed@google.com60040292013-02-04 18:21:23 +0000344 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000345 // Specializing for dy == 0 gains us 25% on Skia benchmarks
346 if (dy == 0) {
347 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
348 yy *= yy;
349 do {
350 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
351 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
352 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
353 *dstC++ = cache[toggle + (sqrt_table[fi] >>
354 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000355 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000356 fx += dx;
357 } while (--count != 0);
358 } else {
359 do {
360 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
361 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
362 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
363 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
364 *dstC++ = cache[toggle + (sqrt_table[fi] >>
365 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000366 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000367 fx += dx;
368 fy += dy;
369 } while (--count != 0);
370 }
371 }
372}
373
374// Unrolling this loop doesn't seem to help (when float); we're stalling to
375// get the results of the sqrt (?), and don't have enough extra registers to
376// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000377template <SkFixed (*TileProc)(SkFixed)>
378void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
379 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
380 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000381 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000382 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
383 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000384 SkASSERT(fi <= 0xFFFF);
385 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000386 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000387 fx += dx;
388 fy += dy;
389 } while (--count != 0);
390}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000391
392void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
393 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
394 int count, int toggle) {
395 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000396}
397
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000398void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
399 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
400 int count, int toggle) {
401 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
402}
403
404} // namespace
405
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000406void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
407 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000408 SkASSERT(count > 0);
409
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000410 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
411
rileya@google.com589708b2012-07-26 20:04:23 +0000412 SkPoint srcPt;
413 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000414 TileProc proc = radialGradient.fTileProc;
415 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000416 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000417
418 if (fDstToIndexClass != kPerspective_MatrixClass) {
419 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
420 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
421 SkScalar sdx = fDstToIndex.getScaleX();
422 SkScalar sdy = fDstToIndex.getSkewY();
423
424 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
425 SkFixed storage[2];
426 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
427 &storage[0], &storage[1]);
428 sdx = SkFixedToScalar(storage[0]);
429 sdy = SkFixedToScalar(storage[1]);
430 } else {
431 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
432 }
433
434 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000435 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000436 shadeProc = shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000437 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000438 shadeProc = shadeSpan_radial_mirror;
439 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000440 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000441 }
442 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
443 } else { // perspective case
444 SkScalar dstX = SkIntToScalar(x);
445 SkScalar dstY = SkIntToScalar(y);
446 do {
447 dstProc(fDstToIndex, dstX, dstY, &srcPt);
448 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
449 SkASSERT(fi <= 0xFFFF);
450 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
451 dstX += SK_Scalar1;
452 } while (--count != 0);
453 }
454}
455
rileya@google.comd7cc6512012-07-27 14:00:39 +0000456/////////////////////////////////////////////////////////////////////
457
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000458#if SK_SUPPORT_GPU
459
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000460#include "GrTBackendEffectFactory.h"
bsalomon848faf02014-07-11 10:01:02 -0700461#include "gl/GrGLShaderBuilder.h"
dandov9de5b512014-06-10 14:38:28 -0700462#include "SkGr.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000463
bsalomon@google.com0707c292012-10-25 21:45:42 +0000464class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000465public:
466
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000467 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000468 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000469 virtual ~GrGLRadialGradient() { }
470
bsalomon@google.comf78df332012-10-29 12:43:38 +0000471 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000472 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000473 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000474 const char* outputColor,
475 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000476 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000477 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000478
bsalomon@google.comc7818882013-03-20 19:19:53 +0000479 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000480 return GenBaseGradientKey(drawEffect);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000481 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000482
483private:
484
bsalomon@google.com0707c292012-10-25 21:45:42 +0000485 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000486
487};
488
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000489/////////////////////////////////////////////////////////////////////
490
491class GrRadialGradient : public GrGradientEffect {
492public:
bsalomon83d081a2014-07-08 09:56:10 -0700493 static GrEffect* Create(GrContext* ctx,
494 const SkRadialGradient& shader,
495 const SkMatrix& matrix,
496 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700497 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000498 }
499
500 virtual ~GrRadialGradient() { }
501
502 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000503 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
504 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000505 }
506
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000507 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000508
509private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000510 GrRadialGradient(GrContext* ctx,
511 const SkRadialGradient& shader,
512 const SkMatrix& matrix,
513 SkShader::TileMode tm)
514 : INHERITED(ctx, shader, matrix, tm) {
515 }
516
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000517 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000518
519 typedef GrGradientEffect INHERITED;
520};
521
522/////////////////////////////////////////////////////////////////////
523
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000524GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000525
bsalomon83d081a2014-07-08 09:56:10 -0700526GrEffect* GrRadialGradient::TestCreate(SkRandom* random,
527 GrContext* context,
528 const GrDrawTargetCaps&,
529 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000530 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
531 SkScalar radius = random->nextUScalar1();
532
533 SkColor colors[kMaxRandomGradientColors];
534 SkScalar stopsArray[kMaxRandomGradientColors];
535 SkScalar* stops = stopsArray;
536 SkShader::TileMode tm;
537 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
538 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
539 colors, stops, colorCount,
540 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000541 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700542 GrColor paintColor;
543 GrEffect* effect;
544 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
dandov9de5b512014-06-10 14:38:28 -0700545 return effect;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000546}
547
548/////////////////////////////////////////////////////////////////////
549
bsalomon@google.comf78df332012-10-29 12:43:38 +0000550void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000551 const GrDrawEffect&,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000552 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000553 const char* outputColor,
554 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000555 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000556 const TextureSamplerArray& samplers) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000557 this->emitUniforms(builder, key);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000558 SkString t("length(");
bsalomon@google.com77af6802013-10-02 13:04:56 +0000559 t.append(builder->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000560 t.append(")");
bsalomon@google.com82d12232013-09-09 15:36:26 +0000561 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000562}
563
rileya@google.comd7cc6512012-07-27 14:00:39 +0000564/////////////////////////////////////////////////////////////////////
565
dandov9de5b512014-06-10 14:38:28 -0700566bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700567 const SkMatrix* localMatrix, GrColor* paintColor,
568 GrEffect** effect) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000569 SkASSERT(NULL != context);
dandov9de5b512014-06-10 14:38:28 -0700570
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000571 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000572 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700573 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000574 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000575 if (localMatrix) {
576 SkMatrix inv;
577 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700578 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000579 }
580 matrix.postConcat(inv);
581 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000582 matrix.postConcat(fPtsToUnit);
dandov9de5b512014-06-10 14:38:28 -0700583
bsalomon83d081a2014-07-08 09:56:10 -0700584 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
585 *effect = GrRadialGradient::Create(context, *this, matrix, fTileMode);
dandov9de5b512014-06-10 14:38:28 -0700586
587 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000588}
589
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000590#else
591
dandov9de5b512014-06-10 14:38:28 -0700592bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700593 const SkMatrix* localMatrix, GrColor* paintColor,
594 GrEffect** effect) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000595 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700596 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000597}
598
599#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000600
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000601#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000602void SkRadialGradient::toString(SkString* str) const {
603 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000604
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000605 str->append("center: (");
606 str->appendScalar(fCenter.fX);
607 str->append(", ");
608 str->appendScalar(fCenter.fY);
609 str->append(") radius: ");
610 str->appendScalar(fRadius);
611 str->append(" ");
612
613 this->INHERITED::toString(str);
614
615 str->append(")");
616}
617#endif