blob: bf3c821f6b7a463cdb0eab7a31f7aea2c5ebeece [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"
mtklein1113da72015-04-27 12:08:01 -070011#include "SkNx.h"
rileya@google.com589708b2012-07-26 20:04:23 +000012
13#define kSQRT_TABLE_BITS 11
14#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
15
mtkleincc695fe2014-12-10 10:29:19 -080016SK_COMPILE_ASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, SqrtTableSizesMatch);
17
reed@google.com4ec5c952013-12-19 15:00:18 +000018#if 0
rileya@google.com589708b2012-07-26 20:04:23 +000019
20#include <stdio.h>
21
22void SkRadialGradient_BuildTable() {
23 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
24
25 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
26 SkASSERT(file);
27 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
28
29 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
30 if ((i & 15) == 0) {
31 ::fprintf(file, "\t");
32 }
33
34 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
35
36 ::fprintf(file, "0x%02X", value);
37 if (i < kSQRT_TABLE_SIZE-1) {
38 ::fprintf(file, ", ");
39 }
40 if ((i & 15) == 15) {
41 ::fprintf(file, "\n");
42 }
43 }
44 ::fprintf(file, "};\n");
45 ::fclose(file);
46}
47
48#endif
49
50namespace {
51
commit-bot@chromium.org34150b42013-10-16 18:59:44 +000052// GCC doesn't like using static functions as template arguments. So force these to be non-static.
53inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
54 return mirror_tileproc(x);
55}
56
57inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
58 return repeat_tileproc(x);
59}
60
mtkleincc695fe2014-12-10 10:29:19 -080061SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
rileya@google.com589708b2012-07-26 20:04:23 +000062 SkScalar inv = SkScalarInvert(radius);
63
mtkleincc695fe2014-12-10 10:29:19 -080064 SkMatrix matrix;
65 matrix.setTranslate(-center.fX, -center.fY);
66 matrix.postScale(inv, inv);
67 return matrix;
rileya@google.com589708b2012-07-26 20:04:23 +000068}
69
70typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
71 SkScalar sfy, SkScalar sdy,
72 uint16_t* dstC, const uint16_t* cache,
73 int toggle, int count);
74
75void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
76 SkScalar sfy, SkScalar sdy,
77 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
78 int toggle, int count) {
79 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
80
81 /* knock these down so we can pin against +- 0x7FFF, which is an
82 immediate load, rather than 0xFFFF which is slower. This is a
83 compromise, since it reduces our precision, but that appears
84 to be visually OK. If we decide this is OK for all of our cases,
85 we could (it seems) put this scale-down into fDstToIndex,
86 to avoid having to do these extra shifts each time.
87 */
88 SkFixed fx = SkScalarToFixed(sfx) >> 1;
89 SkFixed dx = SkScalarToFixed(sdx) >> 1;
90 SkFixed fy = SkScalarToFixed(sfy) >> 1;
91 SkFixed dy = SkScalarToFixed(sdy) >> 1;
92 // might perform this check for the other modes,
93 // but the win will be a smaller % of the total
94 if (dy == 0) {
95 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
96 fy *= fy;
97 do {
98 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
99 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
100 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
101 fx += dx;
102 *dstC++ = cache[toggle +
103 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000104 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000105 } while (--count != 0);
106 } else {
107 do {
108 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
109 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
110 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
111 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
112 fx += dx;
113 fy += dy;
114 *dstC++ = cache[toggle +
115 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000116 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000117 } while (--count != 0);
118 }
119}
120
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000121template <SkFixed (*TileProc)(SkFixed)>
122void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
123 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
124 int toggle, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000125 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000126 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
127 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000128 SkASSERT(fi <= 0xFFFF);
129 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000130 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000131 fx += dx;
132 fy += dy;
rileya@google.com589708b2012-07-26 20:04:23 +0000133 } while (--count != 0);
134}
135
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000136void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
137 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
138 int toggle, int count) {
139 shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
rileya@google.com589708b2012-07-26 20:04:23 +0000140}
141
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000142void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
143 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
144 int toggle, int count) {
145 shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
146}
147
148} // namespace
149
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000150/////////////////////////////////////////////////////////////////////
151
reedaddf2ed2014-08-11 08:28:24 -0700152SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
mtkleincc695fe2014-12-10 10:29:19 -0800153 : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
reedaddf2ed2014-08-11 08:28:24 -0700154 , fCenter(center)
mtkleincc695fe2014-12-10 10:29:19 -0800155 , fRadius(radius) {
rileya@google.com589708b2012-07-26 20:04:23 +0000156}
157
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000158size_t SkRadialGradient::contextSize() const {
159 return sizeof(RadialGradientContext);
160}
161
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000162SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000163 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000164}
165
166SkRadialGradient::RadialGradientContext::RadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000167 const SkRadialGradient& shader, const ContextRec& rec)
168 : INHERITED(shader, rec) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000169
170void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
171 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000172 SkASSERT(count > 0);
173
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000174 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
175
rileya@google.com589708b2012-07-26 20:04:23 +0000176 uint16_t* SK_RESTRICT dstC = dstCParam;
177
178 SkPoint srcPt;
179 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000180 TileProc proc = radialGradient.fTileProc;
181 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000182 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000183
184 if (fDstToIndexClass != kPerspective_MatrixClass) {
185 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
186 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
187
188 SkScalar sdx = fDstToIndex.getScaleX();
189 SkScalar sdy = fDstToIndex.getSkewY();
190
191 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
192 SkFixed storage[2];
193 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
194 &storage[0], &storage[1]);
195 sdx = SkFixedToScalar(storage[0]);
196 sdy = SkFixedToScalar(storage[1]);
197 } else {
198 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
199 }
200
201 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000202 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000203 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000204 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000205 shadeProc = shadeSpan16_radial_mirror;
206 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000207 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000208 }
209 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
210 cache, toggle, count);
211 } else { // perspective case
212 SkScalar dstX = SkIntToScalar(x);
213 SkScalar dstY = SkIntToScalar(y);
214 do {
215 dstProc(fDstToIndex, dstX, dstY, &srcPt);
216 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
217 SkASSERT(fi <= 0xFFFF);
218
219 int index = fi >> (16 - kCache16Bits);
220 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000221 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000222
223 dstX += SK_Scalar1;
224 } while (--count != 0);
225 }
226}
227
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000228SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000229 SkMatrix* matrix, SkShader::TileMode* xy) const {
230 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000231 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000232 }
233 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000234 matrix->setScale(SkIntToScalar(kCache32Count),
235 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000236 matrix->preConcat(fPtsToUnit);
237 }
238 if (xy) {
239 xy[0] = fTileMode;
240 xy[1] = kClamp_TileMode;
241 }
242 return kRadial_BitmapType;
243}
244
245SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
246 if (info) {
247 commonAsAGradient(info);
248 info->fPoint[0] = fCenter;
249 info->fRadius[0] = fRadius;
250 }
251 return kRadial_GradientType;
252}
253
reed9fa60da2014-08-21 07:59:51 -0700254SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
255 DescriptorScope desc;
256 if (!desc.unflatten(buffer)) {
257 return NULL;
258 }
259 const SkPoint center = buffer.readPoint();
260 const SkScalar radius = buffer.readScalar();
261 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
262 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
263}
rileya@google.com589708b2012-07-26 20:04:23 +0000264
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000265void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000266 this->INHERITED::flatten(buffer);
267 buffer.writePoint(fCenter);
268 buffer.writeScalar(fRadius);
269}
270
271namespace {
272
273inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
mtklein1113da72015-04-27 12:08:01 -0700274 // fast, overly-conservative test: checks unit square instead of unit circle
275 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) || (fx <= -SK_FixedHalf && dx <= 0);
276 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) || (fy <= -SK_FixedHalf && dy <= 0);
277 return xClamped || yClamped;
278}
rileya@google.com589708b2012-07-26 20:04:23 +0000279
mtklein1113da72015-04-27 12:08:01 -0700280inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
281 // fast, overly-conservative test: checks unit square instead of unit circle
282 bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
283 bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
rileya@google.com589708b2012-07-26 20:04:23 +0000284 return xClamped || yClamped;
285}
286
287// Return true if (fx * fy) is always inside the unit circle
288// SkPin32 is expensive, but so are all the SkFixedMul in this test,
289// so it shouldn't be run if count is small.
290inline bool no_need_for_radial_pin(int fx, int dx,
291 int fy, int dy, int count) {
292 SkASSERT(count > 0);
293 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
294 return false;
295 }
296 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
297 return false;
298 }
299 fx += (count - 1) * dx;
300 fy += (count - 1) * dy;
301 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
302 return false;
303 }
304 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
305}
306
307#define UNPINNED_RADIAL_STEP \
308 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
309 *dstC++ = cache[toggle + \
310 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000311 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000312 fx += dx; \
313 fy += dy;
314
315typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
316 SkScalar sfy, SkScalar sdy,
317 SkPMColor* dstC, const SkPMColor* cache,
318 int count, int toggle);
319
320// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
321void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
322 SkScalar sfy, SkScalar sdy,
323 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
324 int count, int toggle) {
325 // Floating point seems to be slower than fixed point,
326 // even when we have float hardware.
327 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
328 SkFixed fx = SkScalarToFixed(sfx) >> 1;
329 SkFixed dx = SkScalarToFixed(sdx) >> 1;
330 SkFixed fy = SkScalarToFixed(sfy) >> 1;
331 SkFixed dy = SkScalarToFixed(sdy) >> 1;
332 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000333 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000334 sk_memset32_dither(dstC,
335 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000336 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000337 count);
338 } else if ((count > 4) &&
339 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
340 unsigned fi;
341 // 4x unroll appears to be no faster than 2x unroll on Linux
342 while (count > 1) {
343 UNPINNED_RADIAL_STEP;
344 UNPINNED_RADIAL_STEP;
345 count -= 2;
346 }
347 if (count) {
348 UNPINNED_RADIAL_STEP;
349 }
reed@google.com60040292013-02-04 18:21:23 +0000350 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000351 // Specializing for dy == 0 gains us 25% on Skia benchmarks
352 if (dy == 0) {
353 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
354 yy *= yy;
355 do {
356 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
357 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
358 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
359 *dstC++ = cache[toggle + (sqrt_table[fi] >>
360 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000361 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000362 fx += dx;
363 } while (--count != 0);
364 } else {
365 do {
366 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
367 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
368 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
369 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
370 *dstC++ = cache[toggle + (sqrt_table[fi] >>
371 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000372 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000373 fx += dx;
374 fy += dy;
375 } while (--count != 0);
376 }
377 }
378}
379
mtklein1113da72015-04-27 12:08:01 -0700380// TODO: can we get away with 0th approximatino of inverse-sqrt (i.e. faster than rsqrt)?
381// seems like ~10bits is more than enough for our use, since we want a byte-index
382static inline Sk4f fast_sqrt(const Sk4f& R) {
383 return R * R.rsqrt();
384}
385
386static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
387 return a * a + b * b;
388}
389
390void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
391 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
392 int count, int toggle) {
393 if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
394 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
395 sk_memset32_dither(dstC,
396 cache[toggle + fi],
397 cache[next_dither_toggle(toggle) + fi],
398 count);
399 } else {
400 const Sk4f max(255);
401 const float scale = 255;
402 sfx *= scale;
403 sfy *= scale;
404 sdx *= scale;
405 sdy *= scale;
406 const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
407 const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
408 const Sk4f dx4(sdx * 4);
409 const Sk4f dy4(sdy * 4);
410
411 Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
412 Sk4f tmpdxdy = sum_squares(dx4, dy4);
413 Sk4f R = sum_squares(fx4, fy4);
414 Sk4f dR = tmpxy + tmpxy + tmpdxdy;
415 const Sk4f ddR = tmpdxdy + tmpdxdy;
416
417 for (int i = 0; i < (count >> 2); ++i) {
418 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
419 R += dR;
420 dR += ddR;
421
422 int fi[4];
423 dist.castTrunc().store(fi);
424
425 for (int i = 0; i < 4; i++) {
426 *dstC++ = cache[toggle + fi[i]];
427 toggle = next_dither_toggle(toggle);
428 }
429 }
430 count &= 3;
431 if (count) {
432 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
433
434 int fi[4];
435 dist.castTrunc().store(fi);
436 for (int i = 0; i < count; i++) {
437 *dstC++ = cache[toggle + fi[i]];
438 toggle = next_dither_toggle(toggle);
439 }
440 }
441 }
442}
443
rileya@google.com589708b2012-07-26 20:04:23 +0000444// Unrolling this loop doesn't seem to help (when float); we're stalling to
445// get the results of the sqrt (?), and don't have enough extra registers to
446// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000447template <SkFixed (*TileProc)(SkFixed)>
448void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
449 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
450 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000451 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000452 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
453 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000454 SkASSERT(fi <= 0xFFFF);
455 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000456 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000457 fx += dx;
458 fy += dy;
459 } while (--count != 0);
460}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000461
462void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
463 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
464 int count, int toggle) {
465 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000466}
467
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000468void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
469 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
470 int count, int toggle) {
471 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
472}
473
474} // namespace
475
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000476void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
477 SkPMColor* SK_RESTRICT dstC, int count) {
mtklein1113da72015-04-27 12:08:01 -0700478#ifdef SK_SUPPORT_LEGACY_RADIAL_GRADIENT_SQRT
479 const bool use_new_proc = false;
480#else
481 const bool use_new_proc = true;
482#endif
rileya@google.com589708b2012-07-26 20:04:23 +0000483 SkASSERT(count > 0);
484
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000485 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
486
rileya@google.com589708b2012-07-26 20:04:23 +0000487 SkPoint srcPt;
488 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000489 TileProc proc = radialGradient.fTileProc;
490 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000491 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000492
493 if (fDstToIndexClass != kPerspective_MatrixClass) {
494 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
495 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
496 SkScalar sdx = fDstToIndex.getScaleX();
497 SkScalar sdy = fDstToIndex.getSkewY();
498
499 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
500 SkFixed storage[2];
501 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
502 &storage[0], &storage[1]);
503 sdx = SkFixedToScalar(storage[0]);
504 sdy = SkFixedToScalar(storage[1]);
505 } else {
506 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
507 }
508
509 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000510 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
mtklein1113da72015-04-27 12:08:01 -0700511 shadeProc = use_new_proc ? shadeSpan_radial_clamp2 : shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000512 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000513 shadeProc = shadeSpan_radial_mirror;
514 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000515 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000516 }
517 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
518 } else { // perspective case
519 SkScalar dstX = SkIntToScalar(x);
520 SkScalar dstY = SkIntToScalar(y);
521 do {
522 dstProc(fDstToIndex, dstX, dstY, &srcPt);
523 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
524 SkASSERT(fi <= 0xFFFF);
525 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
526 dstX += SK_Scalar1;
527 } while (--count != 0);
528 }
529}
530
rileya@google.comd7cc6512012-07-27 14:00:39 +0000531/////////////////////////////////////////////////////////////////////
532
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000533#if SK_SUPPORT_GPU
534
dandov9de5b512014-06-10 14:38:28 -0700535#include "SkGr.h"
joshualitteb2a6762014-12-04 11:35:33 -0800536#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000537
bsalomon@google.com0707c292012-10-25 21:45:42 +0000538class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000539public:
540
joshualitteb2a6762014-12-04 11:35:33 -0800541 GrGLRadialGradient(const GrProcessor&) {}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000542 virtual ~GrGLRadialGradient() { }
543
joshualitt15988992014-10-09 15:04:05 -0700544 virtual void emitCode(GrGLFPBuilder*,
joshualittb0a8a372014-09-23 09:50:21 -0700545 const GrFragmentProcessor&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000546 const char* outputColor,
547 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000548 const TransformedCoordsArray&,
mtklein36352bf2015-03-25 18:17:31 -0700549 const TextureSamplerArray&) override;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000550
joshualittb0a8a372014-09-23 09:50:21 -0700551 static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
552 b->add32(GenBaseGradientKey(processor));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000553 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000554
555private:
556
bsalomon@google.com0707c292012-10-25 21:45:42 +0000557 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000558
559};
560
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000561/////////////////////////////////////////////////////////////////////
562
563class GrRadialGradient : public GrGradientEffect {
564public:
joshualittb0a8a372014-09-23 09:50:21 -0700565 static GrFragmentProcessor* Create(GrContext* ctx,
566 const SkRadialGradient& shader,
567 const SkMatrix& matrix,
568 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700569 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000570 }
571
572 virtual ~GrRadialGradient() { }
573
mtklein36352bf2015-03-25 18:17:31 -0700574 const char* name() const override { return "Radial Gradient"; }
joshualitteb2a6762014-12-04 11:35:33 -0800575
576 virtual void getGLProcessorKey(const GrGLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700577 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800578 GrGLRadialGradient::GenKey(*this, caps, b);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000579 }
580
mtklein36352bf2015-03-25 18:17:31 -0700581 GrGLFragmentProcessor* createGLInstance() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800582 return SkNEW_ARGS(GrGLRadialGradient, (*this));
583 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000584
585private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000586 GrRadialGradient(GrContext* ctx,
587 const SkRadialGradient& shader,
588 const SkMatrix& matrix,
589 SkShader::TileMode tm)
590 : INHERITED(ctx, shader, matrix, tm) {
joshualitteb2a6762014-12-04 11:35:33 -0800591 this->initClassID<GrRadialGradient>();
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000592 }
593
joshualittb0a8a372014-09-23 09:50:21 -0700594 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000595
596 typedef GrGradientEffect INHERITED;
597};
598
599/////////////////////////////////////////////////////////////////////
600
joshualittb0a8a372014-09-23 09:50:21 -0700601GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000602
joshualittb0a8a372014-09-23 09:50:21 -0700603GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random,
604 GrContext* context,
605 const GrDrawTargetCaps&,
606 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000607 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
608 SkScalar radius = random->nextUScalar1();
609
610 SkColor colors[kMaxRandomGradientColors];
611 SkScalar stopsArray[kMaxRandomGradientColors];
612 SkScalar* stops = stopsArray;
613 SkShader::TileMode tm;
614 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
615 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
616 colors, stops, colorCount,
617 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000618 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700619 GrColor paintColor;
joshualittb0a8a372014-09-23 09:50:21 -0700620 GrFragmentProcessor* fp;
joshualitt5531d512014-12-17 15:50:11 -0800621 SkAssertResult(shader->asFragmentProcessor(context, paint,
622 GrProcessorUnitTest::TestMatrix(random), NULL,
623 &paintColor, &fp));
joshualittb0a8a372014-09-23 09:50:21 -0700624 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000625}
626
627/////////////////////////////////////////////////////////////////////
628
joshualitt15988992014-10-09 15:04:05 -0700629void GrGLRadialGradient::emitCode(GrGLFPBuilder* builder,
joshualitt60030bc2014-11-25 14:21:55 -0800630 const GrFragmentProcessor& fp,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000631 const char* outputColor,
632 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000633 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000634 const TextureSamplerArray& samplers) {
joshualitteb2a6762014-12-04 11:35:33 -0800635 const GrRadialGradient& ge = fp.cast<GrRadialGradient>();
joshualitt60030bc2014-11-25 14:21:55 -0800636 this->emitUniforms(builder, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000637 SkString t("length(");
joshualitt30ba4362014-08-21 20:18:45 -0700638 t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000639 t.append(")");
joshualitt60030bc2014-11-25 14:21:55 -0800640 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000641}
642
rileya@google.comd7cc6512012-07-27 14:00:39 +0000643/////////////////////////////////////////////////////////////////////
644
joshualittb0a8a372014-09-23 09:50:21 -0700645bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
joshualitt5531d512014-12-17 15:50:11 -0800646 const SkMatrix& viewM,
joshualittb0a8a372014-09-23 09:50:21 -0700647 const SkMatrix* localMatrix, GrColor* paintColor,
648 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700649 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800650
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000651 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000652 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700653 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000654 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000655 if (localMatrix) {
656 SkMatrix inv;
657 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700658 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000659 }
660 matrix.postConcat(inv);
661 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000662 matrix.postConcat(fPtsToUnit);
mtklein3f3b3d02014-12-01 11:47:08 -0800663
bsalomon83d081a2014-07-08 09:56:10 -0700664 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualittb0a8a372014-09-23 09:50:21 -0700665 *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800666
dandov9de5b512014-06-10 14:38:28 -0700667 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000668}
669
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000670#else
671
joshualitt5531d512014-12-17 15:50:11 -0800672bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
673 const SkMatrix*, GrColor*,
joshualittb0a8a372014-09-23 09:50:21 -0700674 GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000675 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700676 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000677}
678
679#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000680
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000681#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000682void SkRadialGradient::toString(SkString* str) const {
683 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000684
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000685 str->append("center: (");
686 str->appendScalar(fCenter.fX);
687 str->append(", ");
688 str->appendScalar(fCenter.fY);
689 str->append(") radius: ");
690 str->appendScalar(fRadius);
691 str->append(" ");
692
693 this->INHERITED::toString(str);
694
695 str->append(")");
696}
697#endif