blob: 3a1a21e83c26fffbd48444547a099487b1eec1b4 [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
bungeman99fe8222015-08-20 07:57:51 -070016static_assert(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, "SqrtTableSizesMatch");
mtkleincc695fe2014-12-10 10:29:19 -080017
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 {
halcanary385fe4d2015-08-26 13:07:48 -0700163 return new (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
rileya@google.com589708b2012-07-26 20:04:23 +0000228SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
229 if (info) {
230 commonAsAGradient(info);
231 info->fPoint[0] = fCenter;
232 info->fRadius[0] = fRadius;
233 }
234 return kRadial_GradientType;
235}
236
reed9fa60da2014-08-21 07:59:51 -0700237SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
238 DescriptorScope desc;
239 if (!desc.unflatten(buffer)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700240 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700241 }
242 const SkPoint center = buffer.readPoint();
243 const SkScalar radius = buffer.readScalar();
244 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
245 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
246}
rileya@google.com589708b2012-07-26 20:04:23 +0000247
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000248void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000249 this->INHERITED::flatten(buffer);
250 buffer.writePoint(fCenter);
251 buffer.writeScalar(fRadius);
252}
253
254namespace {
255
mtklein1113da72015-04-27 12:08:01 -0700256inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
257 // fast, overly-conservative test: checks unit square instead of unit circle
258 bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
259 bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
rileya@google.com589708b2012-07-26 20:04:23 +0000260 return xClamped || yClamped;
261}
262
rileya@google.com589708b2012-07-26 20:04:23 +0000263typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
264 SkScalar sfy, SkScalar sdy,
265 SkPMColor* dstC, const SkPMColor* cache,
266 int count, int toggle);
267
mtklein1113da72015-04-27 12:08:01 -0700268static inline Sk4f fast_sqrt(const Sk4f& R) {
mtkleind7c014f2015-04-27 14:22:32 -0700269 // R * R.rsqrt0() is much faster, but it's non-monotonic, which isn't so pretty for gradients.
270 return R * R.rsqrt1();
mtklein1113da72015-04-27 12:08:01 -0700271}
272
273static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
274 return a * a + b * b;
275}
276
277void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
278 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
279 int count, int toggle) {
280 if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
281 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
282 sk_memset32_dither(dstC,
283 cache[toggle + fi],
284 cache[next_dither_toggle(toggle) + fi],
285 count);
286 } else {
287 const Sk4f max(255);
288 const float scale = 255;
289 sfx *= scale;
290 sfy *= scale;
291 sdx *= scale;
292 sdy *= scale;
293 const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
294 const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
295 const Sk4f dx4(sdx * 4);
296 const Sk4f dy4(sdy * 4);
297
298 Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
299 Sk4f tmpdxdy = sum_squares(dx4, dy4);
300 Sk4f R = sum_squares(fx4, fy4);
301 Sk4f dR = tmpxy + tmpxy + tmpdxdy;
302 const Sk4f ddR = tmpdxdy + tmpdxdy;
303
304 for (int i = 0; i < (count >> 2); ++i) {
305 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
mtkleinf2fe0e02015-06-10 08:57:28 -0700306 R = R + dR;
307 dR = dR + ddR;
mtklein1113da72015-04-27 12:08:01 -0700308
309 int fi[4];
310 dist.castTrunc().store(fi);
311
312 for (int i = 0; i < 4; i++) {
313 *dstC++ = cache[toggle + fi[i]];
314 toggle = next_dither_toggle(toggle);
315 }
316 }
317 count &= 3;
318 if (count) {
319 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
320
321 int fi[4];
322 dist.castTrunc().store(fi);
323 for (int i = 0; i < count; i++) {
324 *dstC++ = cache[toggle + fi[i]];
325 toggle = next_dither_toggle(toggle);
326 }
327 }
328 }
329}
330
rileya@google.com589708b2012-07-26 20:04:23 +0000331// Unrolling this loop doesn't seem to help (when float); we're stalling to
332// get the results of the sqrt (?), and don't have enough extra registers to
333// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000334template <SkFixed (*TileProc)(SkFixed)>
335void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
336 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
337 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000338 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000339 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
340 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000341 SkASSERT(fi <= 0xFFFF);
342 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000343 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000344 fx += dx;
345 fy += dy;
346 } while (--count != 0);
347}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000348
349void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
350 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
351 int count, int toggle) {
352 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000353}
354
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000355void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
356 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
357 int count, int toggle) {
358 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
359}
360
361} // namespace
362
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000363void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
364 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000365 SkASSERT(count > 0);
366
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000367 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
368
rileya@google.com589708b2012-07-26 20:04:23 +0000369 SkPoint srcPt;
370 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000371 TileProc proc = radialGradient.fTileProc;
372 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000373 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000374
375 if (fDstToIndexClass != kPerspective_MatrixClass) {
376 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
377 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
378 SkScalar sdx = fDstToIndex.getScaleX();
379 SkScalar sdy = fDstToIndex.getSkewY();
380
381 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
382 SkFixed storage[2];
383 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
384 &storage[0], &storage[1]);
385 sdx = SkFixedToScalar(storage[0]);
386 sdy = SkFixedToScalar(storage[1]);
387 } else {
388 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
389 }
390
391 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000392 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
reed67383fc2015-05-05 07:55:19 -0700393 shadeProc = shadeSpan_radial_clamp2;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000394 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000395 shadeProc = shadeSpan_radial_mirror;
396 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000397 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000398 }
399 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
400 } else { // perspective case
401 SkScalar dstX = SkIntToScalar(x);
402 SkScalar dstY = SkIntToScalar(y);
403 do {
404 dstProc(fDstToIndex, dstX, dstY, &srcPt);
405 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
406 SkASSERT(fi <= 0xFFFF);
407 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
408 dstX += SK_Scalar1;
409 } while (--count != 0);
410 }
411}
412
rileya@google.comd7cc6512012-07-27 14:00:39 +0000413/////////////////////////////////////////////////////////////////////
414
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000415#if SK_SUPPORT_GPU
416
dandov9de5b512014-06-10 14:38:28 -0700417#include "SkGr.h"
joshualitteb2a6762014-12-04 11:35:33 -0800418#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000419
bsalomon@google.com0707c292012-10-25 21:45:42 +0000420class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000421public:
422
joshualitteb2a6762014-12-04 11:35:33 -0800423 GrGLRadialGradient(const GrProcessor&) {}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000424 virtual ~GrGLRadialGradient() { }
425
wangyix7c157a92015-07-22 15:08:53 -0700426 virtual void emitCode(EmitArgs&) override;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000427
jvanverthcfc18862015-04-28 08:48:20 -0700428 static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700429 b->add32(GenBaseGradientKey(processor));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000430 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000431
432private:
433
bsalomon@google.com0707c292012-10-25 21:45:42 +0000434 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000435
436};
437
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000438/////////////////////////////////////////////////////////////////////
439
440class GrRadialGradient : public GrGradientEffect {
441public:
joshualittb0a8a372014-09-23 09:50:21 -0700442 static GrFragmentProcessor* Create(GrContext* ctx,
joshualitt9cc17752015-07-09 06:28:14 -0700443 GrProcessorDataManager* procDataManager,
joshualittb0a8a372014-09-23 09:50:21 -0700444 const SkRadialGradient& shader,
445 const SkMatrix& matrix,
446 SkShader::TileMode tm) {
halcanary385fe4d2015-08-26 13:07:48 -0700447 return new GrRadialGradient(ctx, procDataManager, shader, matrix, tm);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000448 }
449
450 virtual ~GrRadialGradient() { }
451
mtklein36352bf2015-03-25 18:17:31 -0700452 const char* name() const override { return "Radial Gradient"; }
joshualitteb2a6762014-12-04 11:35:33 -0800453
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000454private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000455 GrRadialGradient(GrContext* ctx,
joshualitt9cc17752015-07-09 06:28:14 -0700456 GrProcessorDataManager* procDataManager,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000457 const SkRadialGradient& shader,
458 const SkMatrix& matrix,
459 SkShader::TileMode tm)
joshualitt9cc17752015-07-09 06:28:14 -0700460 : INHERITED(ctx, procDataManager, shader, matrix, tm) {
joshualitteb2a6762014-12-04 11:35:33 -0800461 this->initClassID<GrRadialGradient>();
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000462 }
463
wangyixb1daa862015-08-18 11:29:31 -0700464 GrGLFragmentProcessor* onCreateGLInstance() const override {
halcanary385fe4d2015-08-26 13:07:48 -0700465 return new GrGLRadialGradient(*this);
wangyixb1daa862015-08-18 11:29:31 -0700466 }
467
wangyix4b3050b2015-08-04 07:59:37 -0700468 virtual void onGetGLProcessorKey(const GrGLSLCaps& caps,
469 GrProcessorKeyBuilder* b) const override {
470 GrGLRadialGradient::GenKey(*this, caps, b);
471 }
472
joshualittb0a8a372014-09-23 09:50:21 -0700473 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000474
475 typedef GrGradientEffect INHERITED;
476};
477
478/////////////////////////////////////////////////////////////////////
479
joshualittb0a8a372014-09-23 09:50:21 -0700480GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000481
joshualitt0067ff52015-07-08 14:26:19 -0700482GrFragmentProcessor* GrRadialGradient::TestCreate(GrProcessorTestData* d) {
483 SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
484 SkScalar radius = d->fRandom->nextUScalar1();
bsalomon@google.comd4726202012-08-03 14:34:46 +0000485
486 SkColor colors[kMaxRandomGradientColors];
487 SkScalar stopsArray[kMaxRandomGradientColors];
488 SkScalar* stops = stopsArray;
489 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700490 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000491 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
492 colors, stops, colorCount,
493 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000494 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700495 GrColor paintColor;
joshualittb0a8a372014-09-23 09:50:21 -0700496 GrFragmentProcessor* fp;
joshualitt0067ff52015-07-08 14:26:19 -0700497 SkAssertResult(shader->asFragmentProcessor(d->fContext, paint,
halcanary96fcdcc2015-08-27 07:41:13 -0700498 GrTest::TestMatrix(d->fRandom), nullptr,
joshualitt9cc17752015-07-09 06:28:14 -0700499 &paintColor, d->fProcDataManager, &fp));
joshualittb0a8a372014-09-23 09:50:21 -0700500 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000501}
502
503/////////////////////////////////////////////////////////////////////
504
wangyix7c157a92015-07-22 15:08:53 -0700505void GrGLRadialGradient::emitCode(EmitArgs& args) {
506 const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
507 this->emitUniforms(args.fBuilder, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000508 SkString t("length(");
wangyix7c157a92015-07-22 15:08:53 -0700509 t.append(args.fBuilder->getFragmentShaderBuilder()->ensureFSCoords2D(args.fCoords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000510 t.append(")");
wangyix7c157a92015-07-22 15:08:53 -0700511 this->emitColor(args.fBuilder, ge, t.c_str(), args.fOutputColor, args.fInputColor,
512 args.fSamplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000513}
514
rileya@google.comd7cc6512012-07-27 14:00:39 +0000515/////////////////////////////////////////////////////////////////////
516
joshualittb0a8a372014-09-23 09:50:21 -0700517bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
joshualitt5531d512014-12-17 15:50:11 -0800518 const SkMatrix& viewM,
joshualittb0a8a372014-09-23 09:50:21 -0700519 const SkMatrix* localMatrix, GrColor* paintColor,
joshualitt9cc17752015-07-09 06:28:14 -0700520 GrProcessorDataManager* procDataManager,
joshualittb2456052015-07-08 09:36:59 -0700521 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700522 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800523
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000524 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000525 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700526 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000527 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000528 if (localMatrix) {
529 SkMatrix inv;
530 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700531 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000532 }
533 matrix.postConcat(inv);
534 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000535 matrix.postConcat(fPtsToUnit);
mtklein3f3b3d02014-12-01 11:47:08 -0800536
bsalomon83d081a2014-07-08 09:56:10 -0700537 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualitt9cc17752015-07-09 06:28:14 -0700538 *fp = GrRadialGradient::Create(context, procDataManager, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800539
dandov9de5b512014-06-10 14:38:28 -0700540 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000541}
542
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000543#else
544
joshualitt5531d512014-12-17 15:50:11 -0800545bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
joshualitt9cc17752015-07-09 06:28:14 -0700546 const SkMatrix*, GrColor*, GrProcessorDataManager*,
joshualittb0a8a372014-09-23 09:50:21 -0700547 GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000548 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700549 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000550}
551
552#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000553
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000554#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000555void SkRadialGradient::toString(SkString* str) const {
556 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000557
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000558 str->append("center: (");
559 str->appendScalar(fCenter.fX);
560 str->append(", ");
561 str->appendScalar(fCenter.fY);
562 str->append(") radius: ");
563 str->appendScalar(fRadius);
564 str->append(" ");
565
566 this->INHERITED::toString(str);
567
568 str->append(")");
569}
570#endif