blob: fb1d40a7ab0d9c92b66169aca5c785138fbf2a73 [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 -0700255#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
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}
reed9fa60da2014-08-21 07:59:51 -0700261#endif
262
263SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
264 DescriptorScope desc;
265 if (!desc.unflatten(buffer)) {
266 return NULL;
267 }
268 const SkPoint center = buffer.readPoint();
269 const SkScalar radius = buffer.readScalar();
270 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
271 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
272}
rileya@google.com589708b2012-07-26 20:04:23 +0000273
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000274void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000275 this->INHERITED::flatten(buffer);
276 buffer.writePoint(fCenter);
277 buffer.writeScalar(fRadius);
278}
279
280namespace {
281
282inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
283 // fast, overly-conservative test: checks unit square instead
284 // of unit circle
285 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
286 (fx <= -SK_FixedHalf && dx <= 0);
287 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
288 (fy <= -SK_FixedHalf && dy <= 0);
289
290 return xClamped || yClamped;
291}
292
293// Return true if (fx * fy) is always inside the unit circle
294// SkPin32 is expensive, but so are all the SkFixedMul in this test,
295// so it shouldn't be run if count is small.
296inline bool no_need_for_radial_pin(int fx, int dx,
297 int fy, int dy, int count) {
298 SkASSERT(count > 0);
299 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
300 return false;
301 }
302 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
303 return false;
304 }
305 fx += (count - 1) * dx;
306 fy += (count - 1) * dy;
307 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
308 return false;
309 }
310 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
311}
312
313#define UNPINNED_RADIAL_STEP \
314 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
315 *dstC++ = cache[toggle + \
316 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000317 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000318 fx += dx; \
319 fy += dy;
320
321typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
322 SkScalar sfy, SkScalar sdy,
323 SkPMColor* dstC, const SkPMColor* cache,
324 int count, int toggle);
325
326// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
327void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
328 SkScalar sfy, SkScalar sdy,
329 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
330 int count, int toggle) {
331 // Floating point seems to be slower than fixed point,
332 // even when we have float hardware.
333 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
334 SkFixed fx = SkScalarToFixed(sfx) >> 1;
335 SkFixed dx = SkScalarToFixed(sdx) >> 1;
336 SkFixed fy = SkScalarToFixed(sfy) >> 1;
337 SkFixed dy = SkScalarToFixed(sdy) >> 1;
338 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000339 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000340 sk_memset32_dither(dstC,
341 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000342 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000343 count);
344 } else if ((count > 4) &&
345 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
346 unsigned fi;
347 // 4x unroll appears to be no faster than 2x unroll on Linux
348 while (count > 1) {
349 UNPINNED_RADIAL_STEP;
350 UNPINNED_RADIAL_STEP;
351 count -= 2;
352 }
353 if (count) {
354 UNPINNED_RADIAL_STEP;
355 }
reed@google.com60040292013-02-04 18:21:23 +0000356 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000357 // Specializing for dy == 0 gains us 25% on Skia benchmarks
358 if (dy == 0) {
359 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
360 yy *= yy;
361 do {
362 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
363 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
364 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
365 *dstC++ = cache[toggle + (sqrt_table[fi] >>
366 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000367 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000368 fx += dx;
369 } while (--count != 0);
370 } else {
371 do {
372 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
373 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
374 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
375 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
376 *dstC++ = cache[toggle + (sqrt_table[fi] >>
377 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000378 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000379 fx += dx;
380 fy += dy;
381 } while (--count != 0);
382 }
383 }
384}
385
386// Unrolling this loop doesn't seem to help (when float); we're stalling to
387// get the results of the sqrt (?), and don't have enough extra registers to
388// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000389template <SkFixed (*TileProc)(SkFixed)>
390void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
391 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
392 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000393 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000394 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
395 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000396 SkASSERT(fi <= 0xFFFF);
397 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000398 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000399 fx += dx;
400 fy += dy;
401 } while (--count != 0);
402}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000403
404void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
405 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
406 int count, int toggle) {
407 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000408}
409
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000410void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
411 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
412 int count, int toggle) {
413 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
414}
415
416} // namespace
417
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000418void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
419 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000420 SkASSERT(count > 0);
421
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000422 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
423
rileya@google.com589708b2012-07-26 20:04:23 +0000424 SkPoint srcPt;
425 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000426 TileProc proc = radialGradient.fTileProc;
427 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000428 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000429
430 if (fDstToIndexClass != kPerspective_MatrixClass) {
431 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
432 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
433 SkScalar sdx = fDstToIndex.getScaleX();
434 SkScalar sdy = fDstToIndex.getSkewY();
435
436 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
437 SkFixed storage[2];
438 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
439 &storage[0], &storage[1]);
440 sdx = SkFixedToScalar(storage[0]);
441 sdy = SkFixedToScalar(storage[1]);
442 } else {
443 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
444 }
445
446 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000447 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000448 shadeProc = shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000449 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000450 shadeProc = shadeSpan_radial_mirror;
451 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000452 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000453 }
454 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
455 } else { // perspective case
456 SkScalar dstX = SkIntToScalar(x);
457 SkScalar dstY = SkIntToScalar(y);
458 do {
459 dstProc(fDstToIndex, dstX, dstY, &srcPt);
460 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
461 SkASSERT(fi <= 0xFFFF);
462 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
463 dstX += SK_Scalar1;
464 } while (--count != 0);
465 }
466}
467
rileya@google.comd7cc6512012-07-27 14:00:39 +0000468/////////////////////////////////////////////////////////////////////
469
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000470#if SK_SUPPORT_GPU
471
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000472#include "GrTBackendEffectFactory.h"
bsalomon848faf02014-07-11 10:01:02 -0700473#include "gl/GrGLShaderBuilder.h"
dandov9de5b512014-06-10 14:38:28 -0700474#include "SkGr.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000475
bsalomon@google.com0707c292012-10-25 21:45:42 +0000476class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000477public:
478
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000479 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000480 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000481 virtual ~GrGLRadialGradient() { }
482
bsalomon@google.comf78df332012-10-29 12:43:38 +0000483 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000484 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700485 const GrEffectKey&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000486 const char* outputColor,
487 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000488 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000489 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000490
bsalomon63e99f72014-07-21 08:03:14 -0700491 static void GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&, GrEffectKeyBuilder* b) {
492 b->add32(GenBaseGradientKey(drawEffect));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000493 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000494
495private:
496
bsalomon@google.com0707c292012-10-25 21:45:42 +0000497 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000498
499};
500
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000501/////////////////////////////////////////////////////////////////////
502
503class GrRadialGradient : public GrGradientEffect {
504public:
bsalomon83d081a2014-07-08 09:56:10 -0700505 static GrEffect* Create(GrContext* ctx,
506 const SkRadialGradient& shader,
507 const SkMatrix& matrix,
508 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700509 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000510 }
511
512 virtual ~GrRadialGradient() { }
513
514 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000515 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
516 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000517 }
518
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000519 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000520
521private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000522 GrRadialGradient(GrContext* ctx,
523 const SkRadialGradient& shader,
524 const SkMatrix& matrix,
525 SkShader::TileMode tm)
526 : INHERITED(ctx, shader, matrix, tm) {
527 }
528
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000529 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000530
531 typedef GrGradientEffect INHERITED;
532};
533
534/////////////////////////////////////////////////////////////////////
535
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000536GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000537
bsalomon83d081a2014-07-08 09:56:10 -0700538GrEffect* GrRadialGradient::TestCreate(SkRandom* random,
539 GrContext* context,
540 const GrDrawTargetCaps&,
541 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000542 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
543 SkScalar radius = random->nextUScalar1();
544
545 SkColor colors[kMaxRandomGradientColors];
546 SkScalar stopsArray[kMaxRandomGradientColors];
547 SkScalar* stops = stopsArray;
548 SkShader::TileMode tm;
549 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
550 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
551 colors, stops, colorCount,
552 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000553 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700554 GrColor paintColor;
555 GrEffect* effect;
556 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
dandov9de5b512014-06-10 14:38:28 -0700557 return effect;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000558}
559
560/////////////////////////////////////////////////////////////////////
561
bsalomon@google.comf78df332012-10-29 12:43:38 +0000562void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000563 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700564 const GrEffectKey& key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000565 const char* outputColor,
566 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000567 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000568 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -0700569 uint32_t baseKey = key.get32(0);
570 this->emitUniforms(builder, baseKey);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000571 SkString t("length(");
bsalomon@google.com77af6802013-10-02 13:04:56 +0000572 t.append(builder->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000573 t.append(")");
bsalomon63e99f72014-07-21 08:03:14 -0700574 this->emitColor(builder, t.c_str(), baseKey, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000575}
576
rileya@google.comd7cc6512012-07-27 14:00:39 +0000577/////////////////////////////////////////////////////////////////////
578
dandov9de5b512014-06-10 14:38:28 -0700579bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700580 const SkMatrix* localMatrix, GrColor* paintColor,
581 GrEffect** effect) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000582 SkASSERT(NULL != context);
dandov9de5b512014-06-10 14:38:28 -0700583
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000584 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000585 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700586 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000587 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000588 if (localMatrix) {
589 SkMatrix inv;
590 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700591 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000592 }
593 matrix.postConcat(inv);
594 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000595 matrix.postConcat(fPtsToUnit);
dandov9de5b512014-06-10 14:38:28 -0700596
bsalomon83d081a2014-07-08 09:56:10 -0700597 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
598 *effect = GrRadialGradient::Create(context, *this, matrix, fTileMode);
dandov9de5b512014-06-10 14:38:28 -0700599
600 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000601}
602
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000603#else
604
dandov9de5b512014-06-10 14:38:28 -0700605bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700606 const SkMatrix* localMatrix, GrColor* paintColor,
607 GrEffect** effect) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000608 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700609 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000610}
611
612#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000613
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000614#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000615void SkRadialGradient::toString(SkString* str) const {
616 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000617
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000618 str->append("center: (");
619 str->appendScalar(fCenter.fX);
620 str->append(", ");
621 str->appendScalar(fCenter.fY);
622 str->append(") radius: ");
623 str->appendScalar(fRadius);
624 str->append(" ");
625
626 this->INHERITED::toString(str);
627
628 str->append(")");
629}
630#endif