blob: 6d7c7959e5f576d4d025e860f59ae938088c2414 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/effects/SkGradientShader.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkGradientShader.h"
19#include "SkColorPriv.h"
20#include "SkUnitMapper.h"
21#include "SkUtils.h"
22
23/*
24 ToDo
25
26 - not sure we still need the full Rec struct, now that we're using a cache
27 - detect const-alpha (but not opaque) in getFlags()
28*/
29
30/* dither seems to look better, but not stuningly yet, and it slows us down a little
31 so its not on by default yet.
32*/
33#define TEST_GRADIENT_DITHER
34
35///////////////////////////////////////////////////////////////////////////
36
37typedef SkFixed (*TileProc)(SkFixed);
38
reed@android.com41bccf52009-04-03 13:33:51 +000039static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 return SkClampMax(x, 0xFFFF);
41}
42
reed@android.com41bccf52009-04-03 13:33:51 +000043static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 return x & 0xFFFF;
45}
46
reed@android.com41bccf52009-04-03 13:33:51 +000047static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 int s = x << 15 >> 31;
49 return (x ^ s) & 0xFFFF;
50}
51
52static const TileProc gTileProcs[] = {
53 clamp_tileproc,
54 repeat_tileproc,
55 mirror_tileproc
56};
57
58//////////////////////////////////////////////////////////////////////////////
59
reed@android.com41bccf52009-04-03 13:33:51 +000060static inline int repeat_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 return x & 63;
62}
63
reed@android.com41bccf52009-04-03 13:33:51 +000064static inline int mirror_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000065#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
66 if (x & 64)
67 x = ~x;
68 return x & 63;
69#else
70 int s = x << 25 >> 31;
71 return (x ^ s) & 63;
72#endif
73}
74
reed@android.com41bccf52009-04-03 13:33:51 +000075static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 return x & 0xFF;
77}
78
reed@android.com41bccf52009-04-03 13:33:51 +000079static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000080#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000081 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 return x & 255;
85#else
86 int s = x << 23 >> 31;
87 return (x ^ s) & 0xFF;
88#endif
89}
90
91//////////////////////////////////////////////////////////////////////////////
92
93class Gradient_Shader : public SkShader {
94public:
95 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +000096 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 virtual ~Gradient_Shader();
98
99 // overrides
100 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
101 virtual uint32_t getFlags() { return fFlags; }
102
103protected:
104 Gradient_Shader(SkFlattenableReadBuffer& );
105 SkUnitMapper* fMapper;
106 SkMatrix fPtsToUnit; // set by subclass
107 SkMatrix fDstToIndex;
108 SkMatrix::MapXYProc fDstToIndexProc;
109 SkPMColor* fARGB32;
110 TileMode fTileMode;
111 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000112 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 uint8_t fDstToIndexClass;
114 uint8_t fFlags;
115
116 struct Rec {
117 SkFixed fPos; // 0...1
118 uint32_t fScale; // (1 << 24) / range
119 };
120 Rec* fRecs;
121
122 enum {
123 kCache16Bits = 6, // seems like enough for visual accuracy
124 kCache16Count = 1 << kCache16Bits,
125 kCache32Bits = 8, // pretty much should always be 8
126 kCache32Count = 1 << kCache32Bits
127 };
128 virtual void flatten(SkFlattenableWriteBuffer& );
129 const uint16_t* getCache16();
130 const SkPMColor* getCache32();
131
132private:
133 enum {
134 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
135
136 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec))
137 };
138 SkColor fStorage[(kStorageSize + 3) >> 2];
139 SkColor* fOrigColors;
140
141 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
142 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
143
144 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
145 SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
146 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
147
148 typedef SkShader INHERITED;
149};
150
reed@android.com41bccf52009-04-03 13:33:51 +0000151static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 SkASSERT(x >= 0 && x <= SK_Scalar1);
153
154#ifdef SK_SCALAR_IS_FLOAT
155 return (unsigned)(x * 0xFFFF);
156#else
157 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
158#endif
159}
160
reed@android.com41bccf52009-04-03 13:33:51 +0000161Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
162 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 SkASSERT(colorCount > 1);
164
165 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
166
167 fMapper = mapper;
168 mapper->safeRef();
169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
171 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
172 fTileMode = mode;
173 fTileProc = gTileProcs[mode];
reed@android.com41bccf52009-04-03 13:33:51 +0000174
175 fCache16 = fCache16Storage = NULL;
176 fCache32 = fCache32Storage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
reed@android.com41bccf52009-04-03 13:33:51 +0000178 /* Note: we let the caller skip the first and/or last position.
179 i.e. pos[0] = 0.3, pos[1] = 0.7
180 In these cases, we insert dummy entries to ensure that the final data
181 will be bracketed by [0, 1].
182 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
183
184 Thus colorCount (the caller's value, and fColorCount (our value) may
185 differ by up to 2. In the above example:
186 colorCount = 2
187 fColorCount = 4
188 */
189 fColorCount = colorCount;
190 // check if we need to add in dummy start and/or end position/colors
191 bool dummyFirst = false;
192 bool dummyLast = false;
193 if (pos) {
194 dummyFirst = pos[0] != 0;
195 dummyLast = pos[colorCount - 1] != SK_Scalar1;
196 fColorCount += dummyFirst + dummyLast;
197 }
198
199 if (fColorCount > kColorStorageCount) {
200 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
201 fOrigColors = reinterpret_cast<SkColor*>(
202 sk_malloc_throw(size * fColorCount));
203 }
204 else {
205 fOrigColors = fStorage;
206 }
207
208 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 {
reed@android.com41bccf52009-04-03 13:33:51 +0000210 SkColor* origColors = fOrigColors;
211 if (dummyFirst) {
212 *origColors++ = colors[0];
213 }
214 memcpy(origColors, colors, colorCount * sizeof(SkColor));
215 if (dummyLast) {
216 origColors += colorCount;
217 *origColors = colors[colorCount - 1];
218 }
219 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220
reed@android.com41bccf52009-04-03 13:33:51 +0000221 // our premul colors point to the 2nd half of the array
222 // these are assigned each time in setContext
223 fARGB32 = fOrigColors + fColorCount;
224 fRecs = (Rec*)(fARGB32 + fColorCount);
225 if (fColorCount > 2) {
226 Rec* recs = fRecs;
227 recs->fPos = 0;
228 // recs->fScale = 0; // unused;
229 recs += 1;
230 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 /* We need to convert the user's array of relative positions into
232 fixed-point positions and scale factors. We need these results
233 to be strictly monotonic (no two values equal or out of order).
234 Hence this complex loop that just jams a zero for the scale
235 value if it sees a segment out of order, and it assures that
236 we start at 0 and end at 1.0
237 */
238 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000239 int startIndex = dummyFirst ? 0 : 1;
240 int count = colorCount + dummyLast;
241 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 // force the last value to be 1.0
243 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000244 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000246 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 }
reed@android.com41bccf52009-04-03 13:33:51 +0000249 // pin curr withing range
250 if (curr < 0) {
251 curr = 0;
252 } else if (curr > SK_Fixed1) {
253 curr = SK_Fixed1;
254 }
255 recs->fPos = curr;
256 if (curr > prev) {
257 recs->fScale = (1 << 24) / (curr - prev);
258 } else {
259 recs->fScale = 0; // ignore this segment
260 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 // get ready for the next value
262 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000263 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 }
reed@android.com41bccf52009-04-03 13:33:51 +0000265 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkFixed dp = SK_Fixed1 / (colorCount - 1);
267 SkFixed p = dp;
268 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000269 for (int i = 1; i < colorCount; i++) {
270 recs->fPos = p;
271 recs->fScale = scale;
272 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 p += dp;
274 }
275 }
276 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000277 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278}
279
280Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000281 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fCacheAlpha = 256;
283
284 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
285
286 fCache16 = fCache16Storage = NULL;
287 fCache32 = fCache32Storage = NULL;
288
reed@android.com41bccf52009-04-03 13:33:51 +0000289 int colorCount = fColorCount = buffer.readU32();
290 if (colorCount > kColorStorageCount) {
291 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
292 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
293 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000295 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
297 fARGB32 = fOrigColors + colorCount;
298
299 fTileMode = (TileMode)buffer.readU8();
300 fTileProc = gTileProcs[fTileMode];
301 fRecs = (Rec*)(fARGB32 + colorCount);
302 if (colorCount > 2) {
303 Rec* recs = fRecs;
304 recs[0].fPos = 0;
305 for (int i = 1; i < colorCount; i++) {
306 recs[i].fPos = buffer.readS32();
307 recs[i].fScale = buffer.readU32();
308 }
309 }
310 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000311 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312}
313
reed@android.com41bccf52009-04-03 13:33:51 +0000314Gradient_Shader::~Gradient_Shader() {
315 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000317 }
318 if (fCache32Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 sk_free(fCache32Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000320 }
321 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000323 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 fMapper->safeUnref();
325}
326
reed@android.com41bccf52009-04-03 13:33:51 +0000327void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 this->INHERITED::flatten(buffer);
329 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000330 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
332 buffer.write8(fTileMode);
333 if (fColorCount > 2) {
334 Rec* recs = fRecs;
335 for (int i = 1; i < fColorCount; i++) {
336 buffer.write32(recs[i].fPos);
337 buffer.write32(recs[i].fScale);
338 }
339 }
340 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
341}
342
343bool Gradient_Shader::setContext(const SkBitmap& device,
344 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000345 const SkMatrix& matrix) {
346 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349
350 const SkMatrix& inverse = this->getTotalInverse();
351
352 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
353 return false;
354 }
355
356 fDstToIndexProc = fDstToIndex.getMapXYProc();
357 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
358
359 // now convert our colors in to PMColors
360 unsigned paintAlpha = this->getPaintAlpha();
361 unsigned colorAlpha = 0xFF;
362
reed@android.com41bccf52009-04-03 13:33:51 +0000363 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 SkColor src = fOrigColors[i];
365 unsigned sa = SkColorGetA(src);
366 colorAlpha &= sa;
367
368 // now modulate it by the paint for our resulting ARGB32 array
369 sa = SkMulDiv255Round(sa, paintAlpha);
370 fARGB32[i] = SkPreMultiplyARGB(sa, SkColorGetR(src), SkColorGetG(src),
371 SkColorGetB(src));
372 }
373
374 fFlags = this->INHERITED::getFlags();
375 if ((colorAlpha & paintAlpha) == 0xFF) {
376 fFlags |= kOpaqueAlpha_Flag;
377 }
378 // we can do span16 as long as our individual colors are opaque,
379 // regardless of the paint's alpha
380 if (0xFF == colorAlpha) {
381 fFlags |= kHasSpan16_Flag;
382 }
383
384 // if the new alpha differs from the previous time we were called, inval our cache
385 // this will trigger the cache to be rebuilt.
386 // we don't care about the first time, since the cache ptrs will already be NULL
387 if (fCacheAlpha != paintAlpha) {
388 fCache16 = NULL; // inval the cache
389 fCache32 = NULL; // inval the cache
390 fCacheAlpha = paintAlpha; // record the new alpha
391 }
392 return true;
393}
394
reed@android.com41bccf52009-04-03 13:33:51 +0000395static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 SkASSERT(a == SkToU8(a));
397 SkASSERT(b == SkToU8(b));
398 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 return a + ((b - a) * scale >> 8);
400}
401
reed@android.com41bccf52009-04-03 13:33:51 +0000402static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
403 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404#if 0
405 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
406 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
407 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
408 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
409
410 return SkPackARGB32(a, r, g, b);
411#else
412 int otherBlend = 256 - blend;
413
414#if 0
415 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
416 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
417 SkASSERT((t0 & t1) == 0);
418 return t0 | t1;
419#else
420 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
421 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
422#endif
423
424#endif
425}
426
427#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
428
reed@android.com41bccf52009-04-03 13:33:51 +0000429/** We take the original colors, not our premultiplied PMColors, since we can
430 build a 16bit table as long as the original colors are opaque, even if the
431 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432*/
reed@android.com41bccf52009-04-03 13:33:51 +0000433static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
434 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 SkASSERT(count > 1);
436 SkASSERT(SkColorGetA(c0) == 0xFF);
437 SkASSERT(SkColorGetA(c1) == 0xFF);
438
439 SkFixed r = SkColorGetR(c0);
440 SkFixed g = SkColorGetG(c0);
441 SkFixed b = SkColorGetB(c0);
442
443 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
444 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
445 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
446
447 r = SkIntToFixed(r) + 0x8000;
448 g = SkIntToFixed(g) + 0x8000;
449 b = SkIntToFixed(b) + 0x8000;
450
451 do {
452 unsigned rr = r >> 16;
453 unsigned gg = g >> 16;
454 unsigned bb = b >> 16;
455 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
456 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
457 cache += 1;
458 r += dr;
459 g += dg;
460 b += db;
461 } while (--count != 0);
462}
463
reed@android.com41bccf52009-04-03 13:33:51 +0000464static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1,
465 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 SkASSERT(count > 1);
467
468 SkFixed a = SkGetPackedA32(c0);
469 SkFixed r = SkGetPackedR32(c0);
470 SkFixed g = SkGetPackedG32(c0);
471 SkFixed b = SkGetPackedB32(c0);
472
473 SkFixed da = SkIntToFixed(SkGetPackedA32(c1) - a) / (count - 1);
474 SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1);
475 SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1);
476 SkFixed db = SkIntToFixed(SkGetPackedB32(c1) - b) / (count - 1);
477
478 a = SkIntToFixed(a) + 0x8000;
479 r = SkIntToFixed(r) + 0x8000;
480 g = SkIntToFixed(g) + 0x8000;
481 b = SkIntToFixed(b) + 0x8000;
482
483 do {
484 *cache++ = SkPackARGB32(a >> 16, r >> 16, g >> 16, b >> 16);
485 a += da;
486 r += dr;
487 g += dg;
488 b += db;
489 } while (--count != 0);
490}
491
reed@android.com41bccf52009-04-03 13:33:51 +0000492static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 SkASSERT((unsigned)x <= SK_Fixed1);
494 return x - (x >> 16);
495}
496
reed@android.com41bccf52009-04-03 13:33:51 +0000497static inline U16CPU dot6to16(unsigned x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 SkASSERT(x < 64);
499 return (x << 10) | (x << 4) | (x >> 2);
500}
501
reed@android.com41bccf52009-04-03 13:33:51 +0000502const uint16_t* Gradient_Shader::getCache16() {
503 if (fCache16 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 if (fCache16Storage == NULL) // set the storage and our working ptr
505#ifdef TEST_GRADIENT_DITHER
506 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
507#else
508 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
509#endif
510 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000511 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000513 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 Rec* rec = fRecs;
515 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000516 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
518 SkASSERT(nextIndex < kCache16Count);
519
520 if (nextIndex > prevIndex)
521 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
522 prevIndex = nextIndex;
523 }
524 SkASSERT(prevIndex == kCache16Count - 1);
525 }
526
reed@android.com41bccf52009-04-03 13:33:51 +0000527 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528#ifdef TEST_GRADIENT_DITHER
529 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
530#else
531 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
532#endif
533 uint16_t* linear = fCache16; // just computed linear data
534 uint16_t* mapped = fCache16Storage; // storage for mapped data
535 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000536 for (int i = 0; i < 64; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 int index = map->mapUnit16(dot6to16(i)) >> 10;
538 mapped[i] = linear[index];
539#ifdef TEST_GRADIENT_DITHER
540 mapped[i + 64] = linear[index + 64];
541#endif
542 }
543 sk_free(fCache16);
544 fCache16 = fCache16Storage;
545 }
546 }
547 return fCache16;
548}
549
reed@android.com41bccf52009-04-03 13:33:51 +0000550const SkPMColor* Gradient_Shader::getCache32() {
551 if (fCache32 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 if (fCache32Storage == NULL) // set the storage and our working ptr
553 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
554
555 fCache32 = fCache32Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000556 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000558 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 Rec* rec = fRecs;
560 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000561 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
563 SkASSERT(nextIndex < kCache32Count);
564
565 if (nextIndex > prevIndex)
566 build_32bit_cache(fCache32 + prevIndex, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1);
567 prevIndex = nextIndex;
568 }
569 SkASSERT(prevIndex == kCache32Count - 1);
570 }
571
reed@android.com41bccf52009-04-03 13:33:51 +0000572 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
574 SkPMColor* linear = fCache32; // just computed linear data
575 SkPMColor* mapped = fCache32Storage; // storage for mapped data
576 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000577 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000579 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 sk_free(fCache32);
581 fCache32 = fCache32Storage;
582 }
583 }
584 return fCache32;
585}
586
587///////////////////////////////////////////////////////////////////////////
588
reed@android.com41bccf52009-04-03 13:33:51 +0000589static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 SkVector vec = pts[1] - pts[0];
591 SkScalar mag = vec.length();
592 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
593
594 vec.scale(inv);
595 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
596 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
597 matrix->postScale(inv, inv);
598}
599
600///////////////////////////////////////////////////////////////////////////////
601
602class Linear_Gradient : public Gradient_Shader {
603public:
604 Linear_Gradient(const SkPoint pts[2],
605 const SkColor colors[], const SkScalar pos[], int colorCount,
606 SkShader::TileMode mode, SkUnitMapper* mapper)
607 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
608 {
609 pts_to_unit_matrix(pts, &fPtsToUnit);
610 }
611 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
612 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
613 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
614
615 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
616 return SkNEW_ARGS(Linear_Gradient, (buffer));
617 }
618
619protected:
620 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
621 virtual Factory getFactory() { return CreateProc; }
622
623private:
624 typedef Gradient_Shader INHERITED;
625};
626
627// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000628static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629{
630 SkASSERT(count > 0);
631 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
632}
633
634void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
635{
636 SkASSERT(count > 0);
637
638 SkPoint srcPt;
639 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
640 TileProc proc = fTileProc;
641 const SkPMColor* cache = this->getCache32();
642
643 if (fDstToIndexClass != kPerspective_MatrixClass)
644 {
645 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
646 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
647
648 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
649 {
650 SkFixed dxStorage[1];
651 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
652 dx = dxStorage[0];
653 }
654 else
655 {
656 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
657 dx = SkScalarToFixed(fDstToIndex.getScaleX());
658 }
659
660 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
661 {
662 unsigned fi = proc(fx);
663 SkASSERT(fi <= 0xFFFF);
664 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
665 }
666 else if (proc == clamp_tileproc)
667 {
668#if 0
669 if (no_need_for_clamp(fx, dx, count))
670 {
671 unsigned fi;
672 while ((count -= 4) >= 0)
673 {
674 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
675 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
676 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
677 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
678 }
679 SkASSERT(count <= -1 && count >= -4);
680 count += 4;
681 while (--count >= 0)
682 {
683 fi = fx >> 8;
684 SkASSERT(fi <= 0xFF);
685 fx += dx;
686 *dstC++ = cache[fi];
687 }
688 }
689 else
690#endif
691 do {
692 unsigned fi = SkClampMax(fx >> 8, 0xFF);
693 SkASSERT(fi <= 0xFF);
694 fx += dx;
695 *dstC++ = cache[fi];
696 } while (--count != 0);
697 }
698 else if (proc == mirror_tileproc)
699 {
700 do {
701 unsigned fi = mirror_8bits(fx >> 8);
702 SkASSERT(fi <= 0xFF);
703 fx += dx;
704 *dstC++ = cache[fi];
705 } while (--count != 0);
706 }
707 else
708 {
709 SkASSERT(proc == repeat_tileproc);
710 do {
711 unsigned fi = repeat_8bits(fx >> 8);
712 SkASSERT(fi <= 0xFF);
713 fx += dx;
714 *dstC++ = cache[fi];
715 } while (--count != 0);
716 }
717 }
718 else
719 {
720 SkScalar dstX = SkIntToScalar(x);
721 SkScalar dstY = SkIntToScalar(y);
722 do {
723 dstProc(fDstToIndex, dstX, dstY, &srcPt);
724 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
725 SkASSERT(fi <= 0xFFFF);
726 *dstC++ = cache[fi >> (16 - kCache32Bits)];
727 dstX += SK_Scalar1;
728 } while (--count != 0);
729 }
730}
731
732bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
733 TileMode xy[]) {
734 if (bitmap) {
735 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
736 bitmap->allocPixels(); // share with shader???
737 memcpy(bitmap->getPixels(), this->getCache32(), kCache32Count * 4);
738 }
739 if (matrix) {
740 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
741 matrix->preConcat(fPtsToUnit);
742 }
743 if (xy) {
744 xy[0] = fTileMode;
745 xy[1] = kClamp_TileMode;
746 }
747 return true;
748}
749
750#ifdef TEST_GRADIENT_DITHER
751static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
752{
reed@android.com0becfc5b2009-01-13 13:26:44 +0000753 if (reinterpret_cast<uintptr_t>(dst) & 2)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 {
755 *dst++ = value;
756 count -= 1;
757 SkTSwap(value, other);
758 }
759
760 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
761
762 if (count & 1)
763 dst[count - 1] = value;
764}
765#endif
766
767void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
768{
769 SkASSERT(count > 0);
770
771 SkPoint srcPt;
772 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
773 TileProc proc = fTileProc;
774 const uint16_t* cache = this->getCache16();
775#ifdef TEST_GRADIENT_DITHER
776 int toggle = ((x ^ y) & 1) << kCache16Bits;
777#endif
778
779 if (fDstToIndexClass != kPerspective_MatrixClass)
780 {
781 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
782 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
783
784 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
785 {
786 SkFixed dxStorage[1];
787 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
788 dx = dxStorage[0];
789 }
790 else
791 {
792 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
793 dx = SkScalarToFixed(fDstToIndex.getScaleX());
794 }
795
796 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
797 {
798 unsigned fi = proc(fx) >> 10;
799 SkASSERT(fi <= 63);
800#ifdef TEST_GRADIENT_DITHER
801 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
802#else
803 sk_memset16(dstC, cache[fi], count);
804#endif
805 }
806 else if (proc == clamp_tileproc)
807 {
808 do {
809 unsigned fi = SkClampMax(fx >> 10, 63);
810 SkASSERT(fi <= 63);
811 fx += dx;
812#ifdef TEST_GRADIENT_DITHER
813 *dstC++ = cache[toggle + fi];
814 toggle ^= (1 << kCache16Bits);
815#else
816 *dstC++ = cache[fi];
817#endif
818 } while (--count != 0);
819 }
820 else if (proc == mirror_tileproc)
821 {
822 do {
823 unsigned fi = mirror_6bits(fx >> 10);
824 SkASSERT(fi <= 0x3F);
825 fx += dx;
826#ifdef TEST_GRADIENT_DITHER
827 *dstC++ = cache[toggle + fi];
828 toggle ^= (1 << kCache16Bits);
829#else
830 *dstC++ = cache[fi];
831#endif
832 } while (--count != 0);
833 }
834 else
835 {
836 SkASSERT(proc == repeat_tileproc);
837 do {
838 unsigned fi = repeat_6bits(fx >> 10);
839 SkASSERT(fi <= 0x3F);
840 fx += dx;
841#ifdef TEST_GRADIENT_DITHER
842 *dstC++ = cache[toggle + fi];
843 toggle ^= (1 << kCache16Bits);
844#else
845 *dstC++ = cache[fi];
846#endif
847 } while (--count != 0);
848 }
849 }
850 else
851 {
852 SkScalar dstX = SkIntToScalar(x);
853 SkScalar dstY = SkIntToScalar(y);
854 do {
855 dstProc(fDstToIndex, dstX, dstY, &srcPt);
856 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
857 SkASSERT(fi <= 0xFFFF);
858
859 int index = fi >> (16 - kCache16Bits);
860#ifdef TEST_GRADIENT_DITHER
861 *dstC++ = cache[toggle + index];
862 toggle ^= (1 << kCache16Bits);
863#else
864 *dstC++ = cache[index];
865#endif
866
867 dstX += SK_Scalar1;
868 } while (--count != 0);
869 }
870}
871
872///////////////////////////////////////////////////////////////////////////////
873
874#define kSQRT_TABLE_BITS 11
875#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
876
877#include "SkRadialGradient_Table.h"
878
879#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
880
881#include <stdio.h>
882
883void SkRadialGradient_BuildTable()
884{
885 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
886
887 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
888 SkASSERT(file);
889 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
890
891 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
892 {
893 if ((i & 15) == 0)
894 ::fprintf(file, "\t");
895
896 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
897
898 ::fprintf(file, "0x%02X", value);
899 if (i < kSQRT_TABLE_SIZE-1)
900 ::fprintf(file, ", ");
901 if ((i & 15) == 15)
902 ::fprintf(file, "\n");
903 }
904 ::fprintf(file, "};\n");
905 ::fclose(file);
906}
907
908#endif
909
910
911static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
912{
913 SkScalar inv = SkScalarInvert(radius);
914
915 matrix->setTranslate(-center.fX, -center.fY);
916 matrix->postScale(inv, inv);
917}
918
919class Radial_Gradient : public Gradient_Shader {
920public:
921 Radial_Gradient(const SkPoint& center, SkScalar radius,
922 const SkColor colors[], const SkScalar pos[], int colorCount,
923 SkShader::TileMode mode, SkUnitMapper* mapper)
924 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
925 {
926 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
927 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
928
929 rad_to_unit_matrix(center, radius, &fPtsToUnit);
930 }
931 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
932 {
933 SkASSERT(count > 0);
934
935 SkPoint srcPt;
936 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
937 TileProc proc = fTileProc;
938 const SkPMColor* cache = this->getCache32();
939
940 if (fDstToIndexClass != kPerspective_MatrixClass)
941 {
942 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
943 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
944 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
945
946 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
947 {
948 SkFixed storage[2];
949 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
950 dx = storage[0];
951 dy = storage[1];
952 }
953 else
954 {
955 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
956 dx = SkScalarToFixed(fDstToIndex.getScaleX());
957 dy = SkScalarToFixed(fDstToIndex.getSkewY());
958 }
959
960 if (proc == clamp_tileproc)
961 {
962 const uint8_t* sqrt_table = gSqrt8Table;
963 fx >>= 1;
964 dx >>= 1;
965 fy >>= 1;
966 dy >>= 1;
967 do {
968 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
969 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
970 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
971 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
972 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
973 fx += dx;
974 fy += dy;
975 } while (--count != 0);
976 }
977 else if (proc == mirror_tileproc)
978 {
979 do {
980 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
981 unsigned fi = mirror_tileproc(dist);
982 SkASSERT(fi <= 0xFFFF);
983 *dstC++ = cache[fi >> (16 - kCache32Bits)];
984 fx += dx;
985 fy += dy;
986 } while (--count != 0);
987 }
988 else
989 {
990 SkASSERT(proc == repeat_tileproc);
991 do {
992 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
993 unsigned fi = repeat_tileproc(dist);
994 SkASSERT(fi <= 0xFFFF);
995 *dstC++ = cache[fi >> (16 - kCache32Bits)];
996 fx += dx;
997 fy += dy;
998 } while (--count != 0);
999 }
1000 }
1001 else // perspective case
1002 {
1003 SkScalar dstX = SkIntToScalar(x);
1004 SkScalar dstY = SkIntToScalar(y);
1005 do {
1006 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1007 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1008 SkASSERT(fi <= 0xFFFF);
1009 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1010 dstX += SK_Scalar1;
1011 } while (--count != 0);
1012 }
1013 }
1014 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
1015 {
1016 SkASSERT(count > 0);
1017
1018 SkPoint srcPt;
1019 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1020 TileProc proc = fTileProc;
1021 const uint16_t* cache = this->getCache16();
1022#ifdef TEST_GRADIENT_DITHER
1023 int toggle = ((x ^ y) & 1) << kCache16Bits;
1024#endif
1025
1026 if (fDstToIndexClass != kPerspective_MatrixClass)
1027 {
1028 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1029 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1030 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1031
1032 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1033 {
1034 SkFixed storage[2];
1035 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1036 dx = storage[0];
1037 dy = storage[1];
1038 }
1039 else
1040 {
1041 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1042 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1043 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1044 }
1045
1046 if (proc == clamp_tileproc)
1047 {
1048 const uint8_t* sqrt_table = gSqrt8Table;
1049
1050 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1051 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1052 precision, but that appears to be visually OK. If we decide this is OK for
1053 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1054 to avoid having to do these extra shifts each time.
1055 */
1056 fx >>= 1;
1057 dx >>= 1;
1058 fy >>= 1;
1059 dy >>= 1;
1060 if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
1061 {
1062 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1063 fy *= fy;
1064 do {
1065 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1066 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1067 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1068 fx += dx;
1069#ifdef TEST_GRADIENT_DITHER
1070 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1071 toggle ^= (1 << kCache16Bits);
1072#else
1073 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1074#endif
1075 } while (--count != 0);
1076 }
1077 else
1078 {
1079 do {
1080 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1081 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1082 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1083 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1084 fx += dx;
1085 fy += dy;
1086#ifdef TEST_GRADIENT_DITHER
1087 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1088 toggle ^= (1 << kCache16Bits);
1089#else
1090 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1091#endif
1092 } while (--count != 0);
1093 }
1094 }
1095 else if (proc == mirror_tileproc)
1096 {
1097 do {
1098 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1099 unsigned fi = mirror_tileproc(dist);
1100 SkASSERT(fi <= 0xFFFF);
1101 fx += dx;
1102 fy += dy;
1103#ifdef TEST_GRADIENT_DITHER
1104 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1105 toggle ^= (1 << kCache16Bits);
1106#else
1107 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1108#endif
1109 } while (--count != 0);
1110 }
1111 else
1112 {
1113 SkASSERT(proc == repeat_tileproc);
1114 do {
1115 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1116 unsigned fi = repeat_tileproc(dist);
1117 SkASSERT(fi <= 0xFFFF);
1118 fx += dx;
1119 fy += dy;
1120#ifdef TEST_GRADIENT_DITHER
1121 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1122 toggle ^= (1 << kCache16Bits);
1123#else
1124 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1125#endif
1126 } while (--count != 0);
1127 }
1128 }
1129 else // perspective case
1130 {
1131 SkScalar dstX = SkIntToScalar(x);
1132 SkScalar dstY = SkIntToScalar(y);
1133 do {
1134 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1135 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1136 SkASSERT(fi <= 0xFFFF);
1137
1138 int index = fi >> (16 - kCache16Bits);
1139#ifdef TEST_GRADIENT_DITHER
1140 *dstC++ = cache[toggle + index];
1141 toggle ^= (1 << kCache16Bits);
1142#else
1143 *dstC++ = cache[index];
1144#endif
1145
1146 dstX += SK_Scalar1;
1147 } while (--count != 0);
1148 }
1149 }
1150
1151 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1152 return SkNEW_ARGS(Radial_Gradient, (buffer));
1153 }
1154
1155protected:
1156 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1157 virtual Factory getFactory() { return CreateProc; }
1158
1159private:
1160 typedef Gradient_Shader INHERITED;
1161};
1162
1163///////////////////////////////////////////////////////////////////////////////
1164
1165class Sweep_Gradient : public Gradient_Shader {
1166public:
1167 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1168 const SkScalar pos[], int count, SkUnitMapper* mapper)
1169 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1170 {
1171 fPtsToUnit.setTranslate(-cx, -cy);
1172 }
1173 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1174 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1175
1176 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1177 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1178 }
1179
1180protected:
1181 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
1182
1183 virtual Factory getFactory() { return CreateProc; }
1184
1185private:
1186 typedef Gradient_Shader INHERITED;
1187};
1188
1189#ifdef COMPUTE_SWEEP_TABLE
1190#define PI 3.14159265
1191static bool gSweepTableReady;
1192static uint8_t gSweepTable[65];
1193
1194/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1195 We scale the results to [0..32]
1196*/
1197static const uint8_t* build_sweep_table()
1198{
1199 if (!gSweepTableReady)
1200 {
1201 const int N = 65;
1202 const double DENOM = N - 1;
1203
1204 for (int i = 0; i < N; i++)
1205 {
1206 double arg = i / DENOM;
1207 double v = atan(arg);
1208 int iv = (int)round(v * DENOM * 2 / PI);
1209// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1210 printf("%d, ", iv);
1211 gSweepTable[i] = iv;
1212 }
1213 gSweepTableReady = true;
1214 }
1215 return gSweepTable;
1216}
1217#else
1218static const uint8_t gSweepTable[] = {
1219 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1220 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1221 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1222 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1223 32
1224};
1225static const uint8_t* build_sweep_table() { return gSweepTable; }
1226#endif
1227
1228// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1229// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1230// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1231
1232//unsigned div_64(int numer, int denom);
1233static unsigned div_64(int numer, int denom)
1234{
1235 SkASSERT(numer <= denom);
1236 SkASSERT(numer > 0);
1237 SkASSERT(denom > 0);
1238
1239 int nbits = SkCLZ(numer);
1240 int dbits = SkCLZ(denom);
1241 int bits = 6 - nbits + dbits;
1242 SkASSERT(bits <= 6);
1243
1244 if (bits < 0) // detect underflow
1245 return 0;
1246
1247 denom <<= dbits - 1;
1248 numer <<= nbits - 1;
1249
1250 unsigned result = 0;
1251
1252 // do the first one
1253 if ((numer -= denom) >= 0)
1254 result = 1;
1255 else
1256 numer += denom;
1257
1258 // Now fall into our switch statement if there are more bits to compute
1259 if (bits > 0)
1260 {
1261 // make room for the rest of the answer bits
1262 result <<= bits;
1263 switch (bits) {
1264 case 6:
1265 if ((numer = (numer << 1) - denom) >= 0)
1266 result |= 32;
1267 else
1268 numer += denom;
1269 case 5:
1270 if ((numer = (numer << 1) - denom) >= 0)
1271 result |= 16;
1272 else
1273 numer += denom;
1274 case 4:
1275 if ((numer = (numer << 1) - denom) >= 0)
1276 result |= 8;
1277 else
1278 numer += denom;
1279 case 3:
1280 if ((numer = (numer << 1) - denom) >= 0)
1281 result |= 4;
1282 else
1283 numer += denom;
1284 case 2:
1285 if ((numer = (numer << 1) - denom) >= 0)
1286 result |= 2;
1287 else
1288 numer += denom;
1289 case 1:
1290 default: // not strictly need, but makes GCC make better ARM code
1291 if ((numer = (numer << 1) - denom) >= 0)
1292 result |= 1;
1293 else
1294 numer += denom;
1295 }
1296 }
1297 return result;
1298}
1299
1300// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1301static unsigned atan_0_90(SkFixed y, SkFixed x)
1302{
1303#ifdef SK_DEBUG
1304 {
1305 static bool gOnce;
1306 if (!gOnce)
1307 {
1308 gOnce = true;
1309 SkASSERT(div_64(55, 55) == 64);
1310 SkASSERT(div_64(128, 256) == 32);
1311 SkASSERT(div_64(2326528, 4685824) == 31);
1312 SkASSERT(div_64(753664, 5210112) == 9);
1313 SkASSERT(div_64(229376, 4882432) == 3);
1314 SkASSERT(div_64(2, 64) == 2);
1315 SkASSERT(div_64(1, 64) == 1);
1316 // test that we handle underflow correctly
1317 SkASSERT(div_64(12345, 0x54321234) == 0);
1318 }
1319 }
1320#endif
1321
1322 SkASSERT(y > 0 && x > 0);
1323 const uint8_t* table = build_sweep_table();
1324
1325 unsigned result;
1326 bool swap = (x < y);
1327 if (swap)
1328 {
1329 // first part of the atan(v) = PI/2 - atan(1/v) identity
1330 // since our div_64 and table want v <= 1, where v = y/x
1331 SkTSwap<SkFixed>(x, y);
1332 }
1333
1334 result = div_64(y, x);
1335
1336#ifdef SK_DEBUG
1337 {
1338 unsigned result2 = SkDivBits(y, x, 6);
1339 SkASSERT(result2 == result ||
1340 (result == 1 && result2 == 0));
1341 }
1342#endif
1343
1344 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1345 result = table[result];
1346
1347 if (swap)
1348 {
1349 // complete the atan(v) = PI/2 - atan(1/v) identity
1350 result = 64 - result;
1351 // pin to 63
1352 result -= result >> 6;
1353 }
1354
1355 SkASSERT(result <= 63);
1356 return result;
1357}
1358
1359// returns angle in a circle [0..2PI) -> [0..255]
1360static unsigned SkATan2_255(SkFixed y, SkFixed x)
1361{
1362 if (x == 0)
1363 {
1364 if (y == 0)
1365 return 0;
1366 return y < 0 ? 192 : 64;
1367 }
1368 if (y == 0)
1369 return x < 0 ? 128 : 0;
1370
1371 /* Find the right quadrant for x,y
1372 Since atan_0_90 only handles the first quadrant, we rotate x,y
1373 appropriately before calling it, and then add the right amount
1374 to account for the real quadrant.
1375 quadrant 0 : add 0 | x > 0 && y > 0
1376 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1377 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1378 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1379
1380 map x<0 to (1 << 6)
1381 map y<0 to (3 << 6)
1382 add = map_x ^ map_y
1383 */
1384 int xsign = x >> 31;
1385 int ysign = y >> 31;
1386 int add = ((-xsign) ^ (ysign & 3)) << 6;
1387
1388#ifdef SK_DEBUG
1389 if (0 == add)
1390 SkASSERT(x > 0 && y > 0);
1391 else if (64 == add)
1392 SkASSERT(x < 0 && y > 0);
1393 else if (128 == add)
1394 SkASSERT(x < 0 && y < 0);
1395 else if (192 == add)
1396 SkASSERT(x > 0 && y < 0);
1397 else
1398 SkASSERT(!"bad value for add");
1399#endif
1400
1401 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1402 where we need to rotate x,y by 90 or -90
1403 */
1404 x = (x ^ xsign) - xsign;
1405 y = (y ^ ysign) - ysign;
1406 if (add & 64) // quads 1 or 3 need to swap x,y
1407 SkTSwap<SkFixed>(x, y);
1408
1409 unsigned result = add + atan_0_90(y, x);
1410 SkASSERT(result < 256);
1411 return result;
1412}
1413
1414void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1415{
1416 SkMatrix::MapXYProc proc = fDstToIndexProc;
1417 const SkMatrix& matrix = fDstToIndex;
1418 const SkPMColor* cache = this->getCache32();
1419 SkPoint srcPt;
1420
1421 if (fDstToIndexClass != kPerspective_MatrixClass)
1422 {
1423 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1424 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1425 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1426 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1427
1428 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1429 {
1430 SkFixed storage[2];
1431 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1432 &storage[0], &storage[1]);
1433 dx = storage[0];
1434 dy = storage[1];
1435 }
1436 else
1437 {
1438 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1439 dx = SkScalarToFixed(matrix.getScaleX());
1440 dy = SkScalarToFixed(matrix.getSkewY());
1441 }
1442
1443 for (; count > 0; --count)
1444 {
1445 *dstC++ = cache[SkATan2_255(fy, fx)];
1446 fx += dx;
1447 fy += dy;
1448 }
1449 }
1450 else // perspective case
1451 {
1452 for (int stop = x + count; x < stop; x++)
1453 {
1454 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1455 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1456
1457 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1458 SkScalarToFixed(srcPt.fX));
1459 *dstC++ = cache[index];
1460 }
1461 }
1462}
1463
1464void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1465{
1466 SkMatrix::MapXYProc proc = fDstToIndexProc;
1467 const SkMatrix& matrix = fDstToIndex;
1468 const uint16_t* cache = this->getCache16();
1469 int toggle = ((x ^ y) & 1) << kCache16Bits;
1470 SkPoint srcPt;
1471
1472 if (fDstToIndexClass != kPerspective_MatrixClass)
1473 {
1474 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1475 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1476 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1477 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1478
1479 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1480 {
1481 SkFixed storage[2];
1482 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1483 &storage[0], &storage[1]);
1484 dx = storage[0];
1485 dy = storage[1];
1486 }
1487 else
1488 {
1489 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1490 dx = SkScalarToFixed(matrix.getScaleX());
1491 dy = SkScalarToFixed(matrix.getSkewY());
1492 }
1493
1494 for (; count > 0; --count)
1495 {
1496 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1497 *dstC++ = cache[toggle + index];
1498 toggle ^= (1 << kCache16Bits);
1499 fx += dx;
1500 fy += dy;
1501 }
1502 }
1503 else // perspective case
1504 {
1505 for (int stop = x + count; x < stop; x++)
1506 {
1507 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1508 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1509
1510 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1511 SkScalarToFixed(srcPt.fX));
1512 index >>= (8 - kCache16Bits);
1513 *dstC++ = cache[toggle + index];
1514 toggle ^= (1 << kCache16Bits);
1515 }
1516 }
1517}
1518
1519///////////////////////////////////////////////////////////////////////////
1520///////////////////////////////////////////////////////////////////////////
1521
1522// assumes colors is SkColor* and pos is SkScalar*
1523#define EXPAND_1_COLOR(count) \
1524 SkColor tmp[2]; \
1525 do { \
1526 if (1 == count) { \
1527 tmp[0] = tmp[1] = colors[0]; \
1528 colors = tmp; \
1529 pos = NULL; \
1530 count = 2; \
1531 } \
1532 } while (0)
1533
1534SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1535 const SkColor colors[], const SkScalar pos[], int colorCount,
1536 SkShader::TileMode mode, SkUnitMapper* mapper)
1537{
1538 if (NULL == pts || NULL == colors || colorCount < 1) {
1539 return NULL;
1540 }
1541 EXPAND_1_COLOR(colorCount);
1542
reed@android.com41bccf52009-04-03 13:33:51 +00001543 SkScalar posStorage[2];
1544 if (colorCount == 2 && pos == NULL) {
1545 posStorage[0] = SK_Scalar1/4;
1546 posStorage[1] = 3*SK_Scalar1/4;
1547 pos = posStorage;
1548 }
1549
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
1551}
1552
1553SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1554 const SkColor colors[], const SkScalar pos[], int colorCount,
1555 SkShader::TileMode mode, SkUnitMapper* mapper)
1556{
1557 if (radius <= 0 || NULL == colors || colorCount < 1) {
1558 return NULL;
1559 }
1560 EXPAND_1_COLOR(colorCount);
1561
1562 return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper));
1563}
1564
1565SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1566 const SkColor colors[],
1567 const SkScalar pos[],
1568 int count, SkUnitMapper* mapper)
1569{
1570 if (NULL == colors || count < 1) {
1571 return NULL;
1572 }
1573 EXPAND_1_COLOR(count);
1574
1575 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1576}
1577
1578static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1579 Linear_Gradient::CreateProc);
1580
1581static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1582 Radial_Gradient::CreateProc);
1583
1584static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1585 Sweep_Gradient::CreateProc);
1586