blob: 91e239c4239e22d451c5f41d403a1ca6939e2137 [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();
301 SkPoint srcPt;
302
303 if (fDstToIndexClass != kPerspective_MatrixClass) {
304 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
305 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
306 SkScalar dx, fx = srcPt.fX;
307 SkScalar dy, fy = srcPt.fY;
308
309 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
310 SkFixed storage[2];
311 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
312 &storage[0], &storage[1]);
313 dx = SkFixedToScalar(storage[0]);
314 dy = SkFixedToScalar(storage[1]);
315 } else {
316 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
317 dx = matrix.getScaleX();
318 dy = matrix.getSkewY();
319 }
320
321 for (; count > 0; --count) {
322 *dstC++ = cache[SkATan2_255(fy, fx)];
323 fx += dx;
324 fy += dy;
325 }
326 } else { // perspective case
327 for (int stop = x + count; x < stop; x++) {
328 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
329 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
330 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
331 }
332 }
333}
334
335void SkSweepGradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
336 int count) {
337 SkMatrix::MapXYProc proc = fDstToIndexProc;
338 const SkMatrix& matrix = fDstToIndex;
339 const uint16_t* SK_RESTRICT cache = this->getCache16();
340 int toggle = ((x ^ y) & 1) * kDitherStride16;
341 SkPoint srcPt;
342
343 if (fDstToIndexClass != kPerspective_MatrixClass) {
344 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
345 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
346 SkScalar dx, fx = srcPt.fX;
347 SkScalar dy, fy = srcPt.fY;
348
349 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
350 SkFixed storage[2];
351 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
352 &storage[0], &storage[1]);
353 dx = SkFixedToScalar(storage[0]);
354 dy = SkFixedToScalar(storage[1]);
355 } else {
356 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
357 dx = matrix.getScaleX();
358 dy = matrix.getSkewY();
359 }
360
361 for (; count > 0; --count) {
362 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
363 *dstC++ = cache[toggle + index];
364 toggle ^= kDitherStride16;
365 fx += dx;
366 fy += dy;
367 }
368 } else { // perspective case
369 for (int stop = x + count; x < stop; x++) {
370 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
371 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
372
373 int index = SkATan2_255(srcPt.fY, srcPt.fX);
374 index >>= (8 - kCache16Bits);
375 *dstC++ = cache[toggle + index];
376 toggle ^= kDitherStride16;
377 }
378 }
379}
380
rileya@google.comd7cc6512012-07-27 14:00:39 +0000381/////////////////////////////////////////////////////////////////////
382
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000383#if SK_SUPPORT_GPU
384
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000385#include "GrTBackendEffectFactory.h"
386
bsalomon@google.com0707c292012-10-25 21:45:42 +0000387class GrGLSweepGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000388public:
389
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000390 GrGLSweepGradient(const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000391 const GrEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000392 virtual ~GrGLSweepGradient() { }
393
bsalomon@google.comf78df332012-10-29 12:43:38 +0000394 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000395 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000396 EffectKey,
397 const char* vertexCoords,
398 const char* outputColor,
399 const char* inputColor,
400 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000401
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000402 static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
403 return GenMatrixKey(stage);
404 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000405
406private:
407
bsalomon@google.com0707c292012-10-25 21:45:42 +0000408 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000409
410};
411
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000412/////////////////////////////////////////////////////////////////////
413
414class GrSweepGradient : public GrGradientEffect {
415public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000416 static GrEffectRef* Create(GrContext* ctx,
417 const SkSweepGradient& shader,
418 const SkMatrix& matrix) {
419 SkAutoTUnref<GrEffect> effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix)));
420 return CreateEffectPtr(effect);
421 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000422 virtual ~GrSweepGradient() { }
423
424 static const char* Name() { return "Sweep Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000425 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
426 return GrTBackendEffectFactory<GrSweepGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000427 }
428
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000429 typedef GrGLSweepGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000430
bsalomon@google.comd4726202012-08-03 14:34:46 +0000431private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000432 GrSweepGradient(GrContext* ctx,
433 const SkSweepGradient& shader,
434 const SkMatrix& matrix)
435 : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000436 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000437
438 typedef GrGradientEffect INHERITED;
439};
440
441/////////////////////////////////////////////////////////////////////
442
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000443GR_DEFINE_EFFECT_TEST(GrSweepGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000444
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000445GrEffectRef* GrSweepGradient::TestCreate(SkRandom* random,
446 GrContext* context,
447 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000448 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
449
450 SkColor colors[kMaxRandomGradientColors];
451 SkScalar stopsArray[kMaxRandomGradientColors];
452 SkScalar* stops = stopsArray;
453 SkShader::TileMode tmIgnored;
454 int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
455 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
456 colors, stops, colorCount));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000457 SkPaint paint;
458 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000459}
460
461/////////////////////////////////////////////////////////////////////
462
bsalomon@google.comf78df332012-10-29 12:43:38 +0000463void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000464 const GrEffectStage& stage,
465 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000466 const char* vertexCoords,
467 const char* outputColor,
468 const char* inputColor,
469 const TextureSamplerArray& samplers) {
470 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000471 const char* coords;
472 this->setupMatrix(builder, key, vertexCoords, &coords);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000473 SkString t;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000474 t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords, coords);
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000475 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000476}
477
478/////////////////////////////////////////////////////////////////////
479
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000480GrEffectRef* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000481 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000482 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000483 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000484 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000485 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000486 return GrSweepGradient::Create(context, *this, matrix);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000487}
488
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000489#else
490
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000491GrEffect* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000492 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000493 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000494}
495
496#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000497
498#ifdef SK_DEVELOPER
499void SkSweepGradient::toString(SkString* str) const {
500 str->append("SkSweepGradient: (");
501
502 str->append("center: (");
503 str->appendScalar(fCenter.fX);
504 str->append(", ");
505 str->appendScalar(fCenter.fY);
506 str->append(") ");
507
508 this->INHERITED::toString(str);
509
510 str->append(")");
511}
512#endif