blob: 458ddbc590e1dc395f824693b9a9e3d288886245 [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 "SkSweepGradient.h"
10
11SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const SkColor colors[],
12 const SkScalar pos[], int count, SkUnitMapper* mapper)
13: SkGradientShaderBase(colors, pos, count, SkShader::kClamp_TileMode, mapper),
14 fCenter(SkPoint::Make(cx, cy))
15{
16 fPtsToUnit.setTranslate(-cx, -cy);
17}
18
19SkShader::BitmapType SkSweepGradient::asABitmap(SkBitmap* bitmap,
20 SkMatrix* matrix, SkShader::TileMode* xy) const {
21 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +000022 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +000023 }
24 if (matrix) {
25 *matrix = fPtsToUnit;
26 }
27 if (xy) {
28 xy[0] = fTileMode;
29 xy[1] = kClamp_TileMode;
30 }
31 return kSweep_BitmapType;
32}
33
34SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
35 if (info) {
36 commonAsAGradient(info);
37 info->fPoint[0] = fCenter;
38 }
39 return kSweep_GradientType;
40}
41
rileya@google.com589708b2012-07-26 20:04:23 +000042SkSweepGradient::SkSweepGradient(SkFlattenableReadBuffer& buffer)
43 : INHERITED(buffer),
44 fCenter(buffer.readPoint()) {
45}
46
47void SkSweepGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
48 this->INHERITED::flatten(buffer);
49 buffer.writePoint(fCenter);
50}
51
52#ifndef SK_SCALAR_IS_FLOAT
53#ifdef COMPUTE_SWEEP_TABLE
54#define PI 3.14159265
55static bool gSweepTableReady;
56static uint8_t gSweepTable[65];
57
58/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
59 We scale the results to [0..32]
60*/
61static const uint8_t* build_sweep_table() {
62 if (!gSweepTableReady) {
63 const int N = 65;
64 const double DENOM = N - 1;
65
66 for (int i = 0; i < N; i++)
67 {
68 double arg = i / DENOM;
69 double v = atan(arg);
70 int iv = (int)round(v * DENOM * 2 / PI);
71// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
72 printf("%d, ", iv);
73 gSweepTable[i] = iv;
74 }
75 gSweepTableReady = true;
76 }
77 return gSweepTable;
78}
79#else
80static const uint8_t gSweepTable[] = {
81 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
82 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
83 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
84 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
85 32
86};
87static const uint8_t* build_sweep_table() { return gSweepTable; }
88#endif
89#endif
90
91// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
92// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
93// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
94
95//unsigned div_64(int numer, int denom);
96#ifndef SK_SCALAR_IS_FLOAT
97static unsigned div_64(int numer, int denom) {
98 SkASSERT(numer <= denom);
99 SkASSERT(numer > 0);
100 SkASSERT(denom > 0);
101
102 int nbits = SkCLZ(numer);
103 int dbits = SkCLZ(denom);
104 int bits = 6 - nbits + dbits;
105 SkASSERT(bits <= 6);
106
107 if (bits < 0) { // detect underflow
108 return 0;
109 }
110
111 denom <<= dbits - 1;
112 numer <<= nbits - 1;
113
114 unsigned result = 0;
115
116 // do the first one
117 if ((numer -= denom) >= 0) {
118 result = 1;
119 } else {
120 numer += denom;
121 }
122
123 // Now fall into our switch statement if there are more bits to compute
124 if (bits > 0) {
125 // make room for the rest of the answer bits
126 result <<= bits;
127 switch (bits) {
128 case 6:
129 if ((numer = (numer << 1) - denom) >= 0)
130 result |= 32;
131 else
132 numer += denom;
133 case 5:
134 if ((numer = (numer << 1) - denom) >= 0)
135 result |= 16;
136 else
137 numer += denom;
138 case 4:
139 if ((numer = (numer << 1) - denom) >= 0)
140 result |= 8;
141 else
142 numer += denom;
143 case 3:
144 if ((numer = (numer << 1) - denom) >= 0)
145 result |= 4;
146 else
147 numer += denom;
148 case 2:
149 if ((numer = (numer << 1) - denom) >= 0)
150 result |= 2;
151 else
152 numer += denom;
153 case 1:
154 default: // not strictly need, but makes GCC make better ARM code
155 if ((numer = (numer << 1) - denom) >= 0)
156 result |= 1;
157 else
158 numer += denom;
159 }
160 }
161 return result;
162}
163#endif
164
165// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
166#ifndef SK_SCALAR_IS_FLOAT
167static unsigned atan_0_90(SkFixed y, SkFixed x) {
168#ifdef SK_DEBUG
169 {
170 static bool gOnce;
171 if (!gOnce) {
172 gOnce = true;
173 SkASSERT(div_64(55, 55) == 64);
174 SkASSERT(div_64(128, 256) == 32);
175 SkASSERT(div_64(2326528, 4685824) == 31);
176 SkASSERT(div_64(753664, 5210112) == 9);
177 SkASSERT(div_64(229376, 4882432) == 3);
178 SkASSERT(div_64(2, 64) == 2);
179 SkASSERT(div_64(1, 64) == 1);
180 // test that we handle underflow correctly
181 SkASSERT(div_64(12345, 0x54321234) == 0);
182 }
183 }
184#endif
185
186 SkASSERT(y > 0 && x > 0);
187 const uint8_t* table = build_sweep_table();
188
189 unsigned result;
190 bool swap = (x < y);
191 if (swap) {
192 // first part of the atan(v) = PI/2 - atan(1/v) identity
193 // since our div_64 and table want v <= 1, where v = y/x
194 SkTSwap<SkFixed>(x, y);
195 }
196
197 result = div_64(y, x);
198
199#ifdef SK_DEBUG
200 {
201 unsigned result2 = SkDivBits(y, x, 6);
202 SkASSERT(result2 == result ||
203 (result == 1 && result2 == 0));
204 }
205#endif
206
207 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
208 result = table[result];
209
210 if (swap) {
211 // complete the atan(v) = PI/2 - atan(1/v) identity
212 result = 64 - result;
213 // pin to 63
214 result -= result >> 6;
215 }
216
217 SkASSERT(result <= 63);
218 return result;
219}
220#endif
221
222// returns angle in a circle [0..2PI) -> [0..255]
223#ifdef SK_SCALAR_IS_FLOAT
224static unsigned SkATan2_255(float y, float x) {
225 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
226 static const float g255Over2PI = 40.584510488433314f;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000227
rileya@google.com589708b2012-07-26 20:04:23 +0000228 float result = sk_float_atan2(y, x);
229 if (result < 0) {
230 result += 2 * SK_ScalarPI;
231 }
232 SkASSERT(result >= 0);
233 // since our value is always >= 0, we can cast to int, which is faster than
234 // calling floorf()
235 int ir = (int)(result * g255Over2PI);
236 SkASSERT(ir >= 0 && ir <= 255);
237 return ir;
238}
239#else
240static unsigned SkATan2_255(SkFixed y, SkFixed x) {
241 if (x == 0) {
242 if (y == 0) {
243 return 0;
244 }
245 return y < 0 ? 192 : 64;
246 }
247 if (y == 0) {
248 return x < 0 ? 128 : 0;
249 }
250
251 /* Find the right quadrant for x,y
252 Since atan_0_90 only handles the first quadrant, we rotate x,y
253 appropriately before calling it, and then add the right amount
254 to account for the real quadrant.
255 quadrant 0 : add 0 | x > 0 && y > 0
256 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
257 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
258 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
259
260 map x<0 to (1 << 6)
261 map y<0 to (3 << 6)
262 add = map_x ^ map_y
263 */
264 int xsign = x >> 31;
265 int ysign = y >> 31;
266 int add = ((-xsign) ^ (ysign & 3)) << 6;
267
268#ifdef SK_DEBUG
269 if (0 == add)
270 SkASSERT(x > 0 && y > 0);
271 else if (64 == add)
272 SkASSERT(x < 0 && y > 0);
273 else if (128 == add)
274 SkASSERT(x < 0 && y < 0);
275 else if (192 == add)
276 SkASSERT(x > 0 && y < 0);
277 else
278 SkDEBUGFAIL("bad value for add");
279#endif
280
281 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
282 where we need to rotate x,y by 90 or -90
283 */
284 x = (x ^ xsign) - xsign;
285 y = (y ^ ysign) - ysign;
286 if (add & 64) { // quads 1 or 3 need to swap x,y
287 SkTSwap<SkFixed>(x, y);
288 }
289
290 unsigned result = add + atan_0_90(y, x);
291 SkASSERT(result < 256);
292 return result;
293}
294#endif
295
296void SkSweepGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
297 int count) {
298 SkMatrix::MapXYProc proc = fDstToIndexProc;
299 const SkMatrix& matrix = fDstToIndex;
300 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com60040292013-02-04 18:21:23 +0000301#ifndef SK_IGNORE_GRADIENT_DITHER_FIX
302 int toggle = init_dither_toggle(x, y);
303#else
304 int toggle = 0;
305#endif
rileya@google.com589708b2012-07-26 20:04:23 +0000306 SkPoint srcPt;
307
308 if (fDstToIndexClass != kPerspective_MatrixClass) {
309 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
310 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
311 SkScalar dx, fx = srcPt.fX;
312 SkScalar dy, fy = srcPt.fY;
313
314 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
315 SkFixed storage[2];
316 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
317 &storage[0], &storage[1]);
318 dx = SkFixedToScalar(storage[0]);
319 dy = SkFixedToScalar(storage[1]);
320 } else {
321 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
322 dx = matrix.getScaleX();
323 dy = matrix.getSkewY();
324 }
325
326 for (; count > 0; --count) {
reed@google.com60040292013-02-04 18:21:23 +0000327 *dstC++ = cache[toggle + SkATan2_255(fy, fx)];
rileya@google.com589708b2012-07-26 20:04:23 +0000328 fx += dx;
329 fy += dy;
reed@google.com60040292013-02-04 18:21:23 +0000330#ifndef SK_IGNORE_GRADIENT_DITHER_FIX
331 toggle = next_dither_toggle(toggle);
332#endif
rileya@google.com589708b2012-07-26 20:04:23 +0000333 }
334 } else { // perspective case
335 for (int stop = x + count; x < stop; x++) {
336 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
337 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com60040292013-02-04 18:21:23 +0000338 *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)];
339#ifndef SK_IGNORE_GRADIENT_DITHER_FIX
340 toggle = next_dither_toggle(toggle);
341#endif
rileya@google.com589708b2012-07-26 20:04:23 +0000342 }
343 }
344}
345
346void SkSweepGradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
347 int count) {
348 SkMatrix::MapXYProc proc = fDstToIndexProc;
349 const SkMatrix& matrix = fDstToIndex;
350 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000351 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000352 SkPoint srcPt;
353
354 if (fDstToIndexClass != kPerspective_MatrixClass) {
355 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
356 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
357 SkScalar dx, fx = srcPt.fX;
358 SkScalar dy, fy = srcPt.fY;
359
360 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
361 SkFixed storage[2];
362 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
363 &storage[0], &storage[1]);
364 dx = SkFixedToScalar(storage[0]);
365 dy = SkFixedToScalar(storage[1]);
366 } else {
367 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
368 dx = matrix.getScaleX();
369 dy = matrix.getSkewY();
370 }
371
372 for (; count > 0; --count) {
373 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
374 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000375 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000376 fx += dx;
377 fy += dy;
378 }
379 } else { // perspective case
380 for (int stop = x + count; x < stop; x++) {
381 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
382 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
383
384 int index = SkATan2_255(srcPt.fY, srcPt.fX);
385 index >>= (8 - kCache16Bits);
386 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000387 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000388 }
389 }
390}
391
rileya@google.comd7cc6512012-07-27 14:00:39 +0000392/////////////////////////////////////////////////////////////////////
393
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000394#if SK_SUPPORT_GPU
395
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000396#include "GrTBackendEffectFactory.h"
397
bsalomon@google.com0707c292012-10-25 21:45:42 +0000398class GrGLSweepGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000399public:
400
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000401 GrGLSweepGradient(const GrBackendEffectFactory& factory,
bsalomon@google.com6340a412013-01-22 19:55:59 +0000402 const GrEffectRef&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000403 virtual ~GrGLSweepGradient() { }
404
bsalomon@google.comf78df332012-10-29 12:43:38 +0000405 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000406 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000407 EffectKey,
408 const char* vertexCoords,
409 const char* outputColor,
410 const char* inputColor,
411 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000412
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000413 static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
414 return GenMatrixKey(stage);
415 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000416
417private:
418
bsalomon@google.com0707c292012-10-25 21:45:42 +0000419 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000420
421};
422
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000423/////////////////////////////////////////////////////////////////////
424
425class GrSweepGradient : public GrGradientEffect {
426public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000427 static GrEffectRef* Create(GrContext* ctx,
428 const SkSweepGradient& shader,
429 const SkMatrix& matrix) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000430 AutoEffectUnref effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000431 return CreateEffectRef(effect);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000432 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000433 virtual ~GrSweepGradient() { }
434
435 static const char* Name() { return "Sweep Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000436 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
437 return GrTBackendEffectFactory<GrSweepGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000438 }
439
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000440 typedef GrGLSweepGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000441
bsalomon@google.comd4726202012-08-03 14:34:46 +0000442private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000443 GrSweepGradient(GrContext* ctx,
444 const SkSweepGradient& shader,
445 const SkMatrix& matrix)
446 : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000447 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000448
449 typedef GrGradientEffect INHERITED;
450};
451
452/////////////////////////////////////////////////////////////////////
453
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000454GR_DEFINE_EFFECT_TEST(GrSweepGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000455
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000456GrEffectRef* GrSweepGradient::TestCreate(SkRandom* random,
457 GrContext* context,
458 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000459 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
460
461 SkColor colors[kMaxRandomGradientColors];
462 SkScalar stopsArray[kMaxRandomGradientColors];
463 SkScalar* stops = stopsArray;
464 SkShader::TileMode tmIgnored;
465 int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
466 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
467 colors, stops, colorCount));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000468 SkPaint paint;
469 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000470}
471
472/////////////////////////////////////////////////////////////////////
473
bsalomon@google.comf78df332012-10-29 12:43:38 +0000474void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000475 const GrEffectStage& stage,
476 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000477 const char* vertexCoords,
478 const char* outputColor,
479 const char* inputColor,
480 const TextureSamplerArray& samplers) {
481 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000482 const char* coords;
483 this->setupMatrix(builder, key, vertexCoords, &coords);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000484 SkString t;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000485 t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords, coords);
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000486 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000487}
488
489/////////////////////////////////////////////////////////////////////
490
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000491GrEffectRef* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000492 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000493 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000494 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000495 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000496 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000497 return GrSweepGradient::Create(context, *this, matrix);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000498}
499
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000500#else
501
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000502GrEffectRef* SkSweepGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000503 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000504 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000505}
506
507#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000508
509#ifdef SK_DEVELOPER
510void SkSweepGradient::toString(SkString* str) const {
511 str->append("SkSweepGradient: (");
512
513 str->append("center: (");
514 str->appendScalar(fCenter.fX);
515 str->append(", ");
516 str->appendScalar(fCenter.fY);
517 str->append(") ");
518
519 this->INHERITED::toString(str);
520
521 str->append(")");
522}
523#endif