blob: 4ff635ab473e46d8d5b0953225f39cf48d8aa0ab [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
mtklein1113da72015-04-27 12:08:01 -0700273inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
274 // fast, overly-conservative test: checks unit square instead of unit circle
275 bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
276 bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
rileya@google.com589708b2012-07-26 20:04:23 +0000277 return xClamped || yClamped;
278}
279
rileya@google.com589708b2012-07-26 20:04:23 +0000280typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
281 SkScalar sfy, SkScalar sdy,
282 SkPMColor* dstC, const SkPMColor* cache,
283 int count, int toggle);
284
mtklein1113da72015-04-27 12:08:01 -0700285static inline Sk4f fast_sqrt(const Sk4f& R) {
mtkleind7c014f2015-04-27 14:22:32 -0700286 // R * R.rsqrt0() is much faster, but it's non-monotonic, which isn't so pretty for gradients.
287 return R * R.rsqrt1();
mtklein1113da72015-04-27 12:08:01 -0700288}
289
290static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
291 return a * a + b * b;
292}
293
294void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
295 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
296 int count, int toggle) {
297 if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
298 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
299 sk_memset32_dither(dstC,
300 cache[toggle + fi],
301 cache[next_dither_toggle(toggle) + fi],
302 count);
303 } else {
304 const Sk4f max(255);
305 const float scale = 255;
306 sfx *= scale;
307 sfy *= scale;
308 sdx *= scale;
309 sdy *= scale;
310 const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
311 const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
312 const Sk4f dx4(sdx * 4);
313 const Sk4f dy4(sdy * 4);
314
315 Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
316 Sk4f tmpdxdy = sum_squares(dx4, dy4);
317 Sk4f R = sum_squares(fx4, fy4);
318 Sk4f dR = tmpxy + tmpxy + tmpdxdy;
319 const Sk4f ddR = tmpdxdy + tmpdxdy;
320
321 for (int i = 0; i < (count >> 2); ++i) {
322 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
mtkleinf2fe0e02015-06-10 08:57:28 -0700323 R = R + dR;
324 dR = dR + ddR;
mtklein1113da72015-04-27 12:08:01 -0700325
326 int fi[4];
327 dist.castTrunc().store(fi);
328
329 for (int i = 0; i < 4; i++) {
330 *dstC++ = cache[toggle + fi[i]];
331 toggle = next_dither_toggle(toggle);
332 }
333 }
334 count &= 3;
335 if (count) {
336 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
337
338 int fi[4];
339 dist.castTrunc().store(fi);
340 for (int i = 0; i < count; i++) {
341 *dstC++ = cache[toggle + fi[i]];
342 toggle = next_dither_toggle(toggle);
343 }
344 }
345 }
346}
347
rileya@google.com589708b2012-07-26 20:04:23 +0000348// Unrolling this loop doesn't seem to help (when float); we're stalling to
349// get the results of the sqrt (?), and don't have enough extra registers to
350// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000351template <SkFixed (*TileProc)(SkFixed)>
352void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
353 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
354 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000355 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000356 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
357 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000358 SkASSERT(fi <= 0xFFFF);
359 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000360 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000361 fx += dx;
362 fy += dy;
363 } while (--count != 0);
364}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000365
366void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
367 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
368 int count, int toggle) {
369 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000370}
371
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000372void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
373 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
374 int count, int toggle) {
375 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
376}
377
378} // namespace
379
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000380void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
381 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000382 SkASSERT(count > 0);
383
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000384 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
385
rileya@google.com589708b2012-07-26 20:04:23 +0000386 SkPoint srcPt;
387 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000388 TileProc proc = radialGradient.fTileProc;
389 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000390 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000391
392 if (fDstToIndexClass != kPerspective_MatrixClass) {
393 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
394 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
395 SkScalar sdx = fDstToIndex.getScaleX();
396 SkScalar sdy = fDstToIndex.getSkewY();
397
398 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
399 SkFixed storage[2];
400 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
401 &storage[0], &storage[1]);
402 sdx = SkFixedToScalar(storage[0]);
403 sdy = SkFixedToScalar(storage[1]);
404 } else {
405 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
406 }
407
408 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000409 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
reed67383fc2015-05-05 07:55:19 -0700410 shadeProc = shadeSpan_radial_clamp2;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000411 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000412 shadeProc = shadeSpan_radial_mirror;
413 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000414 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000415 }
416 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
417 } else { // perspective case
418 SkScalar dstX = SkIntToScalar(x);
419 SkScalar dstY = SkIntToScalar(y);
420 do {
421 dstProc(fDstToIndex, dstX, dstY, &srcPt);
422 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
423 SkASSERT(fi <= 0xFFFF);
424 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
425 dstX += SK_Scalar1;
426 } while (--count != 0);
427 }
428}
429
rileya@google.comd7cc6512012-07-27 14:00:39 +0000430/////////////////////////////////////////////////////////////////////
431
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000432#if SK_SUPPORT_GPU
433
dandov9de5b512014-06-10 14:38:28 -0700434#include "SkGr.h"
joshualitteb2a6762014-12-04 11:35:33 -0800435#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000436
bsalomon@google.com0707c292012-10-25 21:45:42 +0000437class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000438public:
439
joshualitteb2a6762014-12-04 11:35:33 -0800440 GrGLRadialGradient(const GrProcessor&) {}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000441 virtual ~GrGLRadialGradient() { }
442
wangyix7c157a92015-07-22 15:08:53 -0700443 virtual void emitCode(EmitArgs&) override;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000444
jvanverthcfc18862015-04-28 08:48:20 -0700445 static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700446 b->add32(GenBaseGradientKey(processor));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000447 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000448
449private:
450
bsalomon@google.com0707c292012-10-25 21:45:42 +0000451 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000452
453};
454
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000455/////////////////////////////////////////////////////////////////////
456
457class GrRadialGradient : public GrGradientEffect {
458public:
joshualittb0a8a372014-09-23 09:50:21 -0700459 static GrFragmentProcessor* Create(GrContext* ctx,
joshualitt9cc17752015-07-09 06:28:14 -0700460 GrProcessorDataManager* procDataManager,
joshualittb0a8a372014-09-23 09:50:21 -0700461 const SkRadialGradient& shader,
462 const SkMatrix& matrix,
463 SkShader::TileMode tm) {
joshualitt9cc17752015-07-09 06:28:14 -0700464 return SkNEW_ARGS(GrRadialGradient, (ctx, procDataManager, shader, matrix, tm));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000465 }
466
467 virtual ~GrRadialGradient() { }
468
mtklein36352bf2015-03-25 18:17:31 -0700469 const char* name() const override { return "Radial Gradient"; }
joshualitteb2a6762014-12-04 11:35:33 -0800470
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000471private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000472 GrRadialGradient(GrContext* ctx,
joshualitt9cc17752015-07-09 06:28:14 -0700473 GrProcessorDataManager* procDataManager,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000474 const SkRadialGradient& shader,
475 const SkMatrix& matrix,
476 SkShader::TileMode tm)
joshualitt9cc17752015-07-09 06:28:14 -0700477 : INHERITED(ctx, procDataManager, shader, matrix, tm) {
joshualitteb2a6762014-12-04 11:35:33 -0800478 this->initClassID<GrRadialGradient>();
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000479 }
480
wangyixb1daa862015-08-18 11:29:31 -0700481 GrGLFragmentProcessor* onCreateGLInstance() const override {
482 return SkNEW_ARGS(GrGLRadialGradient, (*this));
483 }
484
wangyix4b3050b2015-08-04 07:59:37 -0700485 virtual void onGetGLProcessorKey(const GrGLSLCaps& caps,
486 GrProcessorKeyBuilder* b) const override {
487 GrGLRadialGradient::GenKey(*this, caps, b);
488 }
489
joshualittb0a8a372014-09-23 09:50:21 -0700490 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000491
492 typedef GrGradientEffect INHERITED;
493};
494
495/////////////////////////////////////////////////////////////////////
496
joshualittb0a8a372014-09-23 09:50:21 -0700497GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000498
joshualitt0067ff52015-07-08 14:26:19 -0700499GrFragmentProcessor* GrRadialGradient::TestCreate(GrProcessorTestData* d) {
500 SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
501 SkScalar radius = d->fRandom->nextUScalar1();
bsalomon@google.comd4726202012-08-03 14:34:46 +0000502
503 SkColor colors[kMaxRandomGradientColors];
504 SkScalar stopsArray[kMaxRandomGradientColors];
505 SkScalar* stops = stopsArray;
506 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700507 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000508 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
509 colors, stops, colorCount,
510 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000511 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700512 GrColor paintColor;
joshualittb0a8a372014-09-23 09:50:21 -0700513 GrFragmentProcessor* fp;
joshualitt0067ff52015-07-08 14:26:19 -0700514 SkAssertResult(shader->asFragmentProcessor(d->fContext, paint,
515 GrTest::TestMatrix(d->fRandom), NULL,
joshualitt9cc17752015-07-09 06:28:14 -0700516 &paintColor, d->fProcDataManager, &fp));
joshualittb0a8a372014-09-23 09:50:21 -0700517 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000518}
519
520/////////////////////////////////////////////////////////////////////
521
wangyix7c157a92015-07-22 15:08:53 -0700522void GrGLRadialGradient::emitCode(EmitArgs& args) {
523 const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
524 this->emitUniforms(args.fBuilder, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000525 SkString t("length(");
wangyix7c157a92015-07-22 15:08:53 -0700526 t.append(args.fBuilder->getFragmentShaderBuilder()->ensureFSCoords2D(args.fCoords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000527 t.append(")");
wangyix7c157a92015-07-22 15:08:53 -0700528 this->emitColor(args.fBuilder, ge, t.c_str(), args.fOutputColor, args.fInputColor,
529 args.fSamplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000530}
531
rileya@google.comd7cc6512012-07-27 14:00:39 +0000532/////////////////////////////////////////////////////////////////////
533
joshualittb0a8a372014-09-23 09:50:21 -0700534bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
joshualitt5531d512014-12-17 15:50:11 -0800535 const SkMatrix& viewM,
joshualittb0a8a372014-09-23 09:50:21 -0700536 const SkMatrix* localMatrix, GrColor* paintColor,
joshualitt9cc17752015-07-09 06:28:14 -0700537 GrProcessorDataManager* procDataManager,
joshualittb2456052015-07-08 09:36:59 -0700538 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700539 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800540
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000541 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000542 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700543 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000544 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000545 if (localMatrix) {
546 SkMatrix inv;
547 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700548 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000549 }
550 matrix.postConcat(inv);
551 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000552 matrix.postConcat(fPtsToUnit);
mtklein3f3b3d02014-12-01 11:47:08 -0800553
bsalomon83d081a2014-07-08 09:56:10 -0700554 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualitt9cc17752015-07-09 06:28:14 -0700555 *fp = GrRadialGradient::Create(context, procDataManager, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800556
dandov9de5b512014-06-10 14:38:28 -0700557 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000558}
559
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000560#else
561
joshualitt5531d512014-12-17 15:50:11 -0800562bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
joshualitt9cc17752015-07-09 06:28:14 -0700563 const SkMatrix*, GrColor*, GrProcessorDataManager*,
joshualittb0a8a372014-09-23 09:50:21 -0700564 GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000565 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700566 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000567}
568
569#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000570
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000571#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000572void SkRadialGradient::toString(SkString* str) const {
573 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000574
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000575 str->append("center: (");
576 str->appendScalar(fCenter.fX);
577 str->append(", ");
578 str->appendScalar(fCenter.fY);
579 str->append(") radius: ");
580 str->appendScalar(fRadius);
581 str->append(" ");
582
583 this->INHERITED::toString(str);
584
585 str->append(")");
586}
587#endif