blob: 047c482384317bd23230f0a83c0d915a794ec087 [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
reed@android.com9b46e772009-06-05 12:24:41 +0000132 // called when we kill our cached colors (to be rebuilt later on demand)
133 virtual void onCacheReset() {}
134
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135private:
136 enum {
137 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
138
139 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec))
140 };
141 SkColor fStorage[(kStorageSize + 3) >> 2];
142 SkColor* fOrigColors;
143
144 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
145 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
146
147 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
148 SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
149 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
150
151 typedef SkShader INHERITED;
152};
153
reed@android.com41bccf52009-04-03 13:33:51 +0000154static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 SkASSERT(x >= 0 && x <= SK_Scalar1);
156
157#ifdef SK_SCALAR_IS_FLOAT
158 return (unsigned)(x * 0xFFFF);
159#else
160 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
161#endif
162}
163
reed@android.com41bccf52009-04-03 13:33:51 +0000164Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
165 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 SkASSERT(colorCount > 1);
167
168 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
169
170 fMapper = mapper;
171 mapper->safeRef();
172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
174 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
175 fTileMode = mode;
176 fTileProc = gTileProcs[mode];
reed@android.com41bccf52009-04-03 13:33:51 +0000177
178 fCache16 = fCache16Storage = NULL;
179 fCache32 = fCache32Storage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
reed@android.com41bccf52009-04-03 13:33:51 +0000181 /* Note: we let the caller skip the first and/or last position.
182 i.e. pos[0] = 0.3, pos[1] = 0.7
183 In these cases, we insert dummy entries to ensure that the final data
184 will be bracketed by [0, 1].
185 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
186
187 Thus colorCount (the caller's value, and fColorCount (our value) may
188 differ by up to 2. In the above example:
189 colorCount = 2
190 fColorCount = 4
191 */
192 fColorCount = colorCount;
193 // check if we need to add in dummy start and/or end position/colors
194 bool dummyFirst = false;
195 bool dummyLast = false;
196 if (pos) {
197 dummyFirst = pos[0] != 0;
198 dummyLast = pos[colorCount - 1] != SK_Scalar1;
199 fColorCount += dummyFirst + dummyLast;
200 }
201
202 if (fColorCount > kColorStorageCount) {
203 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
204 fOrigColors = reinterpret_cast<SkColor*>(
205 sk_malloc_throw(size * fColorCount));
206 }
207 else {
208 fOrigColors = fStorage;
209 }
210
211 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 {
reed@android.com41bccf52009-04-03 13:33:51 +0000213 SkColor* origColors = fOrigColors;
214 if (dummyFirst) {
215 *origColors++ = colors[0];
216 }
217 memcpy(origColors, colors, colorCount * sizeof(SkColor));
218 if (dummyLast) {
219 origColors += colorCount;
220 *origColors = colors[colorCount - 1];
221 }
222 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223
reed@android.com41bccf52009-04-03 13:33:51 +0000224 // our premul colors point to the 2nd half of the array
225 // these are assigned each time in setContext
226 fARGB32 = fOrigColors + fColorCount;
227 fRecs = (Rec*)(fARGB32 + fColorCount);
228 if (fColorCount > 2) {
229 Rec* recs = fRecs;
230 recs->fPos = 0;
231 // recs->fScale = 0; // unused;
232 recs += 1;
233 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 /* We need to convert the user's array of relative positions into
235 fixed-point positions and scale factors. We need these results
236 to be strictly monotonic (no two values equal or out of order).
237 Hence this complex loop that just jams a zero for the scale
238 value if it sees a segment out of order, and it assures that
239 we start at 0 and end at 1.0
240 */
241 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000242 int startIndex = dummyFirst ? 0 : 1;
243 int count = colorCount + dummyLast;
244 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 // force the last value to be 1.0
246 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000247 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000249 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 }
reed@android.com41bccf52009-04-03 13:33:51 +0000252 // pin curr withing range
253 if (curr < 0) {
254 curr = 0;
255 } else if (curr > SK_Fixed1) {
256 curr = SK_Fixed1;
257 }
258 recs->fPos = curr;
259 if (curr > prev) {
260 recs->fScale = (1 << 24) / (curr - prev);
261 } else {
262 recs->fScale = 0; // ignore this segment
263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 // get ready for the next value
265 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000266 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 }
reed@android.com41bccf52009-04-03 13:33:51 +0000268 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 SkFixed dp = SK_Fixed1 / (colorCount - 1);
270 SkFixed p = dp;
271 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000272 for (int i = 1; i < colorCount; i++) {
273 recs->fPos = p;
274 recs->fScale = scale;
275 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 p += dp;
277 }
278 }
279 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000280 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281}
282
283Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000284 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fCacheAlpha = 256;
286
287 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
288
289 fCache16 = fCache16Storage = NULL;
290 fCache32 = fCache32Storage = NULL;
291
reed@android.com41bccf52009-04-03 13:33:51 +0000292 int colorCount = fColorCount = buffer.readU32();
293 if (colorCount > kColorStorageCount) {
294 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
295 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
296 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
300 fARGB32 = fOrigColors + colorCount;
301
302 fTileMode = (TileMode)buffer.readU8();
303 fTileProc = gTileProcs[fTileMode];
304 fRecs = (Rec*)(fARGB32 + colorCount);
305 if (colorCount > 2) {
306 Rec* recs = fRecs;
307 recs[0].fPos = 0;
308 for (int i = 1; i < colorCount; i++) {
309 recs[i].fPos = buffer.readS32();
310 recs[i].fScale = buffer.readU32();
311 }
312 }
313 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000314 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315}
316
reed@android.com41bccf52009-04-03 13:33:51 +0000317Gradient_Shader::~Gradient_Shader() {
318 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000320 }
321 if (fCache32Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 sk_free(fCache32Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000323 }
324 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 fMapper->safeUnref();
328}
329
reed@android.com41bccf52009-04-03 13:33:51 +0000330void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 this->INHERITED::flatten(buffer);
332 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000333 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
335 buffer.write8(fTileMode);
336 if (fColorCount > 2) {
337 Rec* recs = fRecs;
338 for (int i = 1; i < fColorCount; i++) {
339 buffer.write32(recs[i].fPos);
340 buffer.write32(recs[i].fScale);
341 }
342 }
343 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
344}
345
346bool Gradient_Shader::setContext(const SkBitmap& device,
347 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000348 const SkMatrix& matrix) {
349 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000351 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352
353 const SkMatrix& inverse = this->getTotalInverse();
354
355 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
356 return false;
357 }
358
359 fDstToIndexProc = fDstToIndex.getMapXYProc();
360 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
361
362 // now convert our colors in to PMColors
363 unsigned paintAlpha = this->getPaintAlpha();
364 unsigned colorAlpha = 0xFF;
365
reed@android.com41bccf52009-04-03 13:33:51 +0000366 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 SkColor src = fOrigColors[i];
368 unsigned sa = SkColorGetA(src);
369 colorAlpha &= sa;
370
371 // now modulate it by the paint for our resulting ARGB32 array
372 sa = SkMulDiv255Round(sa, paintAlpha);
373 fARGB32[i] = SkPreMultiplyARGB(sa, SkColorGetR(src), SkColorGetG(src),
374 SkColorGetB(src));
375 }
376
377 fFlags = this->INHERITED::getFlags();
378 if ((colorAlpha & paintAlpha) == 0xFF) {
379 fFlags |= kOpaqueAlpha_Flag;
380 }
381 // we can do span16 as long as our individual colors are opaque,
382 // regardless of the paint's alpha
383 if (0xFF == colorAlpha) {
384 fFlags |= kHasSpan16_Flag;
385 }
386
387 // if the new alpha differs from the previous time we were called, inval our cache
388 // this will trigger the cache to be rebuilt.
389 // we don't care about the first time, since the cache ptrs will already be NULL
390 if (fCacheAlpha != paintAlpha) {
391 fCache16 = NULL; // inval the cache
392 fCache32 = NULL; // inval the cache
393 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000394 // inform our subclasses
395 this->onCacheReset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 }
397 return true;
398}
399
reed@android.com41bccf52009-04-03 13:33:51 +0000400static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 SkASSERT(a == SkToU8(a));
402 SkASSERT(b == SkToU8(b));
403 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404 return a + ((b - a) * scale >> 8);
405}
406
reed@android.com41bccf52009-04-03 13:33:51 +0000407static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
408 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409#if 0
410 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
411 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
412 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
413 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
414
415 return SkPackARGB32(a, r, g, b);
416#else
417 int otherBlend = 256 - blend;
418
419#if 0
420 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
421 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
422 SkASSERT((t0 & t1) == 0);
423 return t0 | t1;
424#else
425 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
426 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
427#endif
428
429#endif
430}
431
432#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
433
reed@android.com41bccf52009-04-03 13:33:51 +0000434/** We take the original colors, not our premultiplied PMColors, since we can
435 build a 16bit table as long as the original colors are opaque, even if the
436 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437*/
reed@android.com41bccf52009-04-03 13:33:51 +0000438static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
439 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 SkASSERT(count > 1);
441 SkASSERT(SkColorGetA(c0) == 0xFF);
442 SkASSERT(SkColorGetA(c1) == 0xFF);
443
444 SkFixed r = SkColorGetR(c0);
445 SkFixed g = SkColorGetG(c0);
446 SkFixed b = SkColorGetB(c0);
447
448 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
449 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
450 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
451
452 r = SkIntToFixed(r) + 0x8000;
453 g = SkIntToFixed(g) + 0x8000;
454 b = SkIntToFixed(b) + 0x8000;
455
456 do {
457 unsigned rr = r >> 16;
458 unsigned gg = g >> 16;
459 unsigned bb = b >> 16;
460 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
461 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
462 cache += 1;
463 r += dr;
464 g += dg;
465 b += db;
466 } while (--count != 0);
467}
468
reed@android.com41bccf52009-04-03 13:33:51 +0000469static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1,
470 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 SkASSERT(count > 1);
472
473 SkFixed a = SkGetPackedA32(c0);
474 SkFixed r = SkGetPackedR32(c0);
475 SkFixed g = SkGetPackedG32(c0);
476 SkFixed b = SkGetPackedB32(c0);
477
478 SkFixed da = SkIntToFixed(SkGetPackedA32(c1) - a) / (count - 1);
479 SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1);
480 SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1);
481 SkFixed db = SkIntToFixed(SkGetPackedB32(c1) - b) / (count - 1);
482
483 a = SkIntToFixed(a) + 0x8000;
484 r = SkIntToFixed(r) + 0x8000;
485 g = SkIntToFixed(g) + 0x8000;
486 b = SkIntToFixed(b) + 0x8000;
487
488 do {
489 *cache++ = SkPackARGB32(a >> 16, r >> 16, g >> 16, b >> 16);
490 a += da;
491 r += dr;
492 g += dg;
493 b += db;
494 } while (--count != 0);
495}
496
reed@android.com41bccf52009-04-03 13:33:51 +0000497static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 SkASSERT((unsigned)x <= SK_Fixed1);
499 return x - (x >> 16);
500}
501
reed@android.com41bccf52009-04-03 13:33:51 +0000502static inline U16CPU dot6to16(unsigned x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 SkASSERT(x < 64);
504 return (x << 10) | (x << 4) | (x >> 2);
505}
506
reed@android.com41bccf52009-04-03 13:33:51 +0000507const uint16_t* Gradient_Shader::getCache16() {
508 if (fCache16 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 if (fCache16Storage == NULL) // set the storage and our working ptr
510#ifdef TEST_GRADIENT_DITHER
511 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
512#else
513 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
514#endif
515 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000516 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000518 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 Rec* rec = fRecs;
520 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000521 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
523 SkASSERT(nextIndex < kCache16Count);
524
525 if (nextIndex > prevIndex)
526 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
527 prevIndex = nextIndex;
528 }
529 SkASSERT(prevIndex == kCache16Count - 1);
530 }
531
reed@android.com41bccf52009-04-03 13:33:51 +0000532 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533#ifdef TEST_GRADIENT_DITHER
534 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
535#else
536 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
537#endif
538 uint16_t* linear = fCache16; // just computed linear data
539 uint16_t* mapped = fCache16Storage; // storage for mapped data
540 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000541 for (int i = 0; i < 64; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 int index = map->mapUnit16(dot6to16(i)) >> 10;
543 mapped[i] = linear[index];
544#ifdef TEST_GRADIENT_DITHER
545 mapped[i + 64] = linear[index + 64];
546#endif
547 }
548 sk_free(fCache16);
549 fCache16 = fCache16Storage;
550 }
551 }
552 return fCache16;
553}
554
reed@android.com41bccf52009-04-03 13:33:51 +0000555const SkPMColor* Gradient_Shader::getCache32() {
556 if (fCache32 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 if (fCache32Storage == NULL) // set the storage and our working ptr
558 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
559
560 fCache32 = fCache32Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000561 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000563 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 Rec* rec = fRecs;
565 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000566 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
568 SkASSERT(nextIndex < kCache32Count);
569
570 if (nextIndex > prevIndex)
571 build_32bit_cache(fCache32 + prevIndex, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1);
572 prevIndex = nextIndex;
573 }
574 SkASSERT(prevIndex == kCache32Count - 1);
575 }
576
reed@android.com41bccf52009-04-03 13:33:51 +0000577 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
579 SkPMColor* linear = fCache32; // just computed linear data
580 SkPMColor* mapped = fCache32Storage; // storage for mapped data
581 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000582 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 sk_free(fCache32);
586 fCache32 = fCache32Storage;
587 }
588 }
589 return fCache32;
590}
591
592///////////////////////////////////////////////////////////////////////////
593
reed@android.com41bccf52009-04-03 13:33:51 +0000594static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 SkVector vec = pts[1] - pts[0];
596 SkScalar mag = vec.length();
597 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
598
599 vec.scale(inv);
600 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
601 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
602 matrix->postScale(inv, inv);
603}
604
605///////////////////////////////////////////////////////////////////////////////
606
607class Linear_Gradient : public Gradient_Shader {
608public:
609 Linear_Gradient(const SkPoint pts[2],
610 const SkColor colors[], const SkScalar pos[], int colorCount,
611 SkShader::TileMode mode, SkUnitMapper* mapper)
612 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
613 {
reed@android.com9b46e772009-06-05 12:24:41 +0000614 fCachedBitmap = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 pts_to_unit_matrix(pts, &fPtsToUnit);
616 }
reed@android.com9b46e772009-06-05 12:24:41 +0000617 virtual ~Linear_Gradient() {
618 if (fCachedBitmap) {
619 SkDELETE(fCachedBitmap);
620 }
621 }
622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
624 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
625 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
reed@android.com9b46e772009-06-05 12:24:41 +0000626 virtual void onCacheReset() {
627 if (fCachedBitmap) {
628 SkDELETE(fCachedBitmap);
629 fCachedBitmap = NULL;
630 }
631 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632
633 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
634 return SkNEW_ARGS(Linear_Gradient, (buffer));
635 }
636
637protected:
638 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
639 virtual Factory getFactory() { return CreateProc; }
640
641private:
reed@android.com9b46e772009-06-05 12:24:41 +0000642 SkBitmap* fCachedBitmap; // allocated on demand
643
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 typedef Gradient_Shader INHERITED;
645};
646
647// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000648static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649{
650 SkASSERT(count > 0);
651 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
652}
653
654void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
655{
656 SkASSERT(count > 0);
657
658 SkPoint srcPt;
659 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
660 TileProc proc = fTileProc;
661 const SkPMColor* cache = this->getCache32();
662
663 if (fDstToIndexClass != kPerspective_MatrixClass)
664 {
665 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
666 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
667
668 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
669 {
670 SkFixed dxStorage[1];
671 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
672 dx = dxStorage[0];
673 }
674 else
675 {
676 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
677 dx = SkScalarToFixed(fDstToIndex.getScaleX());
678 }
679
680 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
681 {
682 unsigned fi = proc(fx);
683 SkASSERT(fi <= 0xFFFF);
684 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
685 }
686 else if (proc == clamp_tileproc)
687 {
688#if 0
689 if (no_need_for_clamp(fx, dx, count))
690 {
691 unsigned fi;
692 while ((count -= 4) >= 0)
693 {
694 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
695 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
696 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
697 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
698 }
699 SkASSERT(count <= -1 && count >= -4);
700 count += 4;
701 while (--count >= 0)
702 {
703 fi = fx >> 8;
704 SkASSERT(fi <= 0xFF);
705 fx += dx;
706 *dstC++ = cache[fi];
707 }
708 }
709 else
710#endif
711 do {
712 unsigned fi = SkClampMax(fx >> 8, 0xFF);
713 SkASSERT(fi <= 0xFF);
714 fx += dx;
715 *dstC++ = cache[fi];
716 } while (--count != 0);
717 }
718 else if (proc == mirror_tileproc)
719 {
720 do {
721 unsigned fi = mirror_8bits(fx >> 8);
722 SkASSERT(fi <= 0xFF);
723 fx += dx;
724 *dstC++ = cache[fi];
725 } while (--count != 0);
726 }
727 else
728 {
729 SkASSERT(proc == repeat_tileproc);
730 do {
731 unsigned fi = repeat_8bits(fx >> 8);
732 SkASSERT(fi <= 0xFF);
733 fx += dx;
734 *dstC++ = cache[fi];
735 } while (--count != 0);
736 }
737 }
738 else
739 {
740 SkScalar dstX = SkIntToScalar(x);
741 SkScalar dstY = SkIntToScalar(y);
742 do {
743 dstProc(fDstToIndex, dstX, dstY, &srcPt);
744 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
745 SkASSERT(fi <= 0xFFFF);
746 *dstC++ = cache[fi >> (16 - kCache32Bits)];
747 dstX += SK_Scalar1;
748 } while (--count != 0);
749 }
750}
751
752bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
753 TileMode xy[]) {
reed@android.com9b46e772009-06-05 12:24:41 +0000754 // we cache our "bitmap", so it's generationID will be const on subsequent
755 // calls to asABitmap
756 if (NULL == fCachedBitmap) {
757 fCachedBitmap = SkNEW(SkBitmap);
758 fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
759 fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
760 }
761
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 if (bitmap) {
reed@android.com9b46e772009-06-05 12:24:41 +0000763 *bitmap = *fCachedBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 }
765 if (matrix) {
766 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
767 matrix->preConcat(fPtsToUnit);
768 }
769 if (xy) {
770 xy[0] = fTileMode;
771 xy[1] = kClamp_TileMode;
772 }
773 return true;
774}
775
776#ifdef TEST_GRADIENT_DITHER
777static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
778{
reed@android.com0becfc52009-01-13 13:26:44 +0000779 if (reinterpret_cast<uintptr_t>(dst) & 2)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 {
781 *dst++ = value;
782 count -= 1;
783 SkTSwap(value, other);
784 }
785
786 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
787
788 if (count & 1)
789 dst[count - 1] = value;
790}
791#endif
792
793void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
794{
795 SkASSERT(count > 0);
796
797 SkPoint srcPt;
798 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
799 TileProc proc = fTileProc;
800 const uint16_t* cache = this->getCache16();
801#ifdef TEST_GRADIENT_DITHER
802 int toggle = ((x ^ y) & 1) << kCache16Bits;
803#endif
804
805 if (fDstToIndexClass != kPerspective_MatrixClass)
806 {
807 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
808 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
809
810 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
811 {
812 SkFixed dxStorage[1];
813 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
814 dx = dxStorage[0];
815 }
816 else
817 {
818 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
819 dx = SkScalarToFixed(fDstToIndex.getScaleX());
820 }
821
822 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
823 {
824 unsigned fi = proc(fx) >> 10;
825 SkASSERT(fi <= 63);
826#ifdef TEST_GRADIENT_DITHER
827 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
828#else
829 sk_memset16(dstC, cache[fi], count);
830#endif
831 }
832 else if (proc == clamp_tileproc)
833 {
834 do {
835 unsigned fi = SkClampMax(fx >> 10, 63);
836 SkASSERT(fi <= 63);
837 fx += dx;
838#ifdef TEST_GRADIENT_DITHER
839 *dstC++ = cache[toggle + fi];
840 toggle ^= (1 << kCache16Bits);
841#else
842 *dstC++ = cache[fi];
843#endif
844 } while (--count != 0);
845 }
846 else if (proc == mirror_tileproc)
847 {
848 do {
849 unsigned fi = mirror_6bits(fx >> 10);
850 SkASSERT(fi <= 0x3F);
851 fx += dx;
852#ifdef TEST_GRADIENT_DITHER
853 *dstC++ = cache[toggle + fi];
854 toggle ^= (1 << kCache16Bits);
855#else
856 *dstC++ = cache[fi];
857#endif
858 } while (--count != 0);
859 }
860 else
861 {
862 SkASSERT(proc == repeat_tileproc);
863 do {
864 unsigned fi = repeat_6bits(fx >> 10);
865 SkASSERT(fi <= 0x3F);
866 fx += dx;
867#ifdef TEST_GRADIENT_DITHER
868 *dstC++ = cache[toggle + fi];
869 toggle ^= (1 << kCache16Bits);
870#else
871 *dstC++ = cache[fi];
872#endif
873 } while (--count != 0);
874 }
875 }
876 else
877 {
878 SkScalar dstX = SkIntToScalar(x);
879 SkScalar dstY = SkIntToScalar(y);
880 do {
881 dstProc(fDstToIndex, dstX, dstY, &srcPt);
882 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
883 SkASSERT(fi <= 0xFFFF);
884
885 int index = fi >> (16 - kCache16Bits);
886#ifdef TEST_GRADIENT_DITHER
887 *dstC++ = cache[toggle + index];
888 toggle ^= (1 << kCache16Bits);
889#else
890 *dstC++ = cache[index];
891#endif
892
893 dstX += SK_Scalar1;
894 } while (--count != 0);
895 }
896}
897
898///////////////////////////////////////////////////////////////////////////////
899
900#define kSQRT_TABLE_BITS 11
901#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
902
903#include "SkRadialGradient_Table.h"
904
905#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
906
907#include <stdio.h>
908
909void SkRadialGradient_BuildTable()
910{
911 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
912
913 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
914 SkASSERT(file);
915 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
916
917 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
918 {
919 if ((i & 15) == 0)
920 ::fprintf(file, "\t");
921
922 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
923
924 ::fprintf(file, "0x%02X", value);
925 if (i < kSQRT_TABLE_SIZE-1)
926 ::fprintf(file, ", ");
927 if ((i & 15) == 15)
928 ::fprintf(file, "\n");
929 }
930 ::fprintf(file, "};\n");
931 ::fclose(file);
932}
933
934#endif
935
936
937static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
938{
939 SkScalar inv = SkScalarInvert(radius);
940
941 matrix->setTranslate(-center.fX, -center.fY);
942 matrix->postScale(inv, inv);
943}
944
945class Radial_Gradient : public Gradient_Shader {
946public:
947 Radial_Gradient(const SkPoint& center, SkScalar radius,
948 const SkColor colors[], const SkScalar pos[], int colorCount,
949 SkShader::TileMode mode, SkUnitMapper* mapper)
950 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
951 {
952 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
953 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
954
955 rad_to_unit_matrix(center, radius, &fPtsToUnit);
956 }
957 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
958 {
959 SkASSERT(count > 0);
960
961 SkPoint srcPt;
962 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
963 TileProc proc = fTileProc;
964 const SkPMColor* cache = this->getCache32();
965
966 if (fDstToIndexClass != kPerspective_MatrixClass)
967 {
968 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
969 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
970 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
971
972 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
973 {
974 SkFixed storage[2];
975 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
976 dx = storage[0];
977 dy = storage[1];
978 }
979 else
980 {
981 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
982 dx = SkScalarToFixed(fDstToIndex.getScaleX());
983 dy = SkScalarToFixed(fDstToIndex.getSkewY());
984 }
985
986 if (proc == clamp_tileproc)
987 {
988 const uint8_t* sqrt_table = gSqrt8Table;
989 fx >>= 1;
990 dx >>= 1;
991 fy >>= 1;
992 dy >>= 1;
993 do {
994 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
995 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
996 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
997 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
998 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
999 fx += dx;
1000 fy += dy;
1001 } while (--count != 0);
1002 }
1003 else if (proc == mirror_tileproc)
1004 {
1005 do {
1006 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1007 unsigned fi = mirror_tileproc(dist);
1008 SkASSERT(fi <= 0xFFFF);
1009 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1010 fx += dx;
1011 fy += dy;
1012 } while (--count != 0);
1013 }
1014 else
1015 {
1016 SkASSERT(proc == repeat_tileproc);
1017 do {
1018 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1019 unsigned fi = repeat_tileproc(dist);
1020 SkASSERT(fi <= 0xFFFF);
1021 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1022 fx += dx;
1023 fy += dy;
1024 } while (--count != 0);
1025 }
1026 }
1027 else // perspective case
1028 {
1029 SkScalar dstX = SkIntToScalar(x);
1030 SkScalar dstY = SkIntToScalar(y);
1031 do {
1032 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1033 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1034 SkASSERT(fi <= 0xFFFF);
1035 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1036 dstX += SK_Scalar1;
1037 } while (--count != 0);
1038 }
1039 }
1040 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
1041 {
1042 SkASSERT(count > 0);
1043
1044 SkPoint srcPt;
1045 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1046 TileProc proc = fTileProc;
1047 const uint16_t* cache = this->getCache16();
1048#ifdef TEST_GRADIENT_DITHER
1049 int toggle = ((x ^ y) & 1) << kCache16Bits;
1050#endif
1051
1052 if (fDstToIndexClass != kPerspective_MatrixClass)
1053 {
1054 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1055 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1056 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1057
1058 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1059 {
1060 SkFixed storage[2];
1061 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1062 dx = storage[0];
1063 dy = storage[1];
1064 }
1065 else
1066 {
1067 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1068 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1069 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1070 }
1071
1072 if (proc == clamp_tileproc)
1073 {
1074 const uint8_t* sqrt_table = gSqrt8Table;
1075
1076 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1077 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1078 precision, but that appears to be visually OK. If we decide this is OK for
1079 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1080 to avoid having to do these extra shifts each time.
1081 */
1082 fx >>= 1;
1083 dx >>= 1;
1084 fy >>= 1;
1085 dy >>= 1;
1086 if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
1087 {
1088 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1089 fy *= fy;
1090 do {
1091 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1092 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1093 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1094 fx += dx;
1095#ifdef TEST_GRADIENT_DITHER
1096 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1097 toggle ^= (1 << kCache16Bits);
1098#else
1099 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1100#endif
1101 } while (--count != 0);
1102 }
1103 else
1104 {
1105 do {
1106 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1107 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1108 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1109 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1110 fx += dx;
1111 fy += dy;
1112#ifdef TEST_GRADIENT_DITHER
1113 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1114 toggle ^= (1 << kCache16Bits);
1115#else
1116 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1117#endif
1118 } while (--count != 0);
1119 }
1120 }
1121 else if (proc == mirror_tileproc)
1122 {
1123 do {
1124 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1125 unsigned fi = mirror_tileproc(dist);
1126 SkASSERT(fi <= 0xFFFF);
1127 fx += dx;
1128 fy += dy;
1129#ifdef TEST_GRADIENT_DITHER
1130 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1131 toggle ^= (1 << kCache16Bits);
1132#else
1133 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1134#endif
1135 } while (--count != 0);
1136 }
1137 else
1138 {
1139 SkASSERT(proc == repeat_tileproc);
1140 do {
1141 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1142 unsigned fi = repeat_tileproc(dist);
1143 SkASSERT(fi <= 0xFFFF);
1144 fx += dx;
1145 fy += dy;
1146#ifdef TEST_GRADIENT_DITHER
1147 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1148 toggle ^= (1 << kCache16Bits);
1149#else
1150 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1151#endif
1152 } while (--count != 0);
1153 }
1154 }
1155 else // perspective case
1156 {
1157 SkScalar dstX = SkIntToScalar(x);
1158 SkScalar dstY = SkIntToScalar(y);
1159 do {
1160 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1161 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1162 SkASSERT(fi <= 0xFFFF);
1163
1164 int index = fi >> (16 - kCache16Bits);
1165#ifdef TEST_GRADIENT_DITHER
1166 *dstC++ = cache[toggle + index];
1167 toggle ^= (1 << kCache16Bits);
1168#else
1169 *dstC++ = cache[index];
1170#endif
1171
1172 dstX += SK_Scalar1;
1173 } while (--count != 0);
1174 }
1175 }
1176
1177 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1178 return SkNEW_ARGS(Radial_Gradient, (buffer));
1179 }
1180
1181protected:
1182 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1183 virtual Factory getFactory() { return CreateProc; }
1184
1185private:
1186 typedef Gradient_Shader INHERITED;
1187};
1188
1189///////////////////////////////////////////////////////////////////////////////
1190
1191class Sweep_Gradient : public Gradient_Shader {
1192public:
1193 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1194 const SkScalar pos[], int count, SkUnitMapper* mapper)
1195 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1196 {
1197 fPtsToUnit.setTranslate(-cx, -cy);
1198 }
1199 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1200 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1201
1202 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1203 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1204 }
1205
1206protected:
1207 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
1208
1209 virtual Factory getFactory() { return CreateProc; }
1210
1211private:
1212 typedef Gradient_Shader INHERITED;
1213};
1214
1215#ifdef COMPUTE_SWEEP_TABLE
1216#define PI 3.14159265
1217static bool gSweepTableReady;
1218static uint8_t gSweepTable[65];
1219
1220/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1221 We scale the results to [0..32]
1222*/
1223static const uint8_t* build_sweep_table()
1224{
1225 if (!gSweepTableReady)
1226 {
1227 const int N = 65;
1228 const double DENOM = N - 1;
1229
1230 for (int i = 0; i < N; i++)
1231 {
1232 double arg = i / DENOM;
1233 double v = atan(arg);
1234 int iv = (int)round(v * DENOM * 2 / PI);
1235// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1236 printf("%d, ", iv);
1237 gSweepTable[i] = iv;
1238 }
1239 gSweepTableReady = true;
1240 }
1241 return gSweepTable;
1242}
1243#else
1244static const uint8_t gSweepTable[] = {
1245 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1246 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1247 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1248 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1249 32
1250};
1251static const uint8_t* build_sweep_table() { return gSweepTable; }
1252#endif
1253
1254// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1255// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1256// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1257
1258//unsigned div_64(int numer, int denom);
1259static unsigned div_64(int numer, int denom)
1260{
1261 SkASSERT(numer <= denom);
1262 SkASSERT(numer > 0);
1263 SkASSERT(denom > 0);
1264
1265 int nbits = SkCLZ(numer);
1266 int dbits = SkCLZ(denom);
1267 int bits = 6 - nbits + dbits;
1268 SkASSERT(bits <= 6);
1269
1270 if (bits < 0) // detect underflow
1271 return 0;
1272
1273 denom <<= dbits - 1;
1274 numer <<= nbits - 1;
1275
1276 unsigned result = 0;
1277
1278 // do the first one
1279 if ((numer -= denom) >= 0)
1280 result = 1;
1281 else
1282 numer += denom;
1283
1284 // Now fall into our switch statement if there are more bits to compute
1285 if (bits > 0)
1286 {
1287 // make room for the rest of the answer bits
1288 result <<= bits;
1289 switch (bits) {
1290 case 6:
1291 if ((numer = (numer << 1) - denom) >= 0)
1292 result |= 32;
1293 else
1294 numer += denom;
1295 case 5:
1296 if ((numer = (numer << 1) - denom) >= 0)
1297 result |= 16;
1298 else
1299 numer += denom;
1300 case 4:
1301 if ((numer = (numer << 1) - denom) >= 0)
1302 result |= 8;
1303 else
1304 numer += denom;
1305 case 3:
1306 if ((numer = (numer << 1) - denom) >= 0)
1307 result |= 4;
1308 else
1309 numer += denom;
1310 case 2:
1311 if ((numer = (numer << 1) - denom) >= 0)
1312 result |= 2;
1313 else
1314 numer += denom;
1315 case 1:
1316 default: // not strictly need, but makes GCC make better ARM code
1317 if ((numer = (numer << 1) - denom) >= 0)
1318 result |= 1;
1319 else
1320 numer += denom;
1321 }
1322 }
1323 return result;
1324}
1325
1326// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1327static unsigned atan_0_90(SkFixed y, SkFixed x)
1328{
1329#ifdef SK_DEBUG
1330 {
1331 static bool gOnce;
1332 if (!gOnce)
1333 {
1334 gOnce = true;
1335 SkASSERT(div_64(55, 55) == 64);
1336 SkASSERT(div_64(128, 256) == 32);
1337 SkASSERT(div_64(2326528, 4685824) == 31);
1338 SkASSERT(div_64(753664, 5210112) == 9);
1339 SkASSERT(div_64(229376, 4882432) == 3);
1340 SkASSERT(div_64(2, 64) == 2);
1341 SkASSERT(div_64(1, 64) == 1);
1342 // test that we handle underflow correctly
1343 SkASSERT(div_64(12345, 0x54321234) == 0);
1344 }
1345 }
1346#endif
1347
1348 SkASSERT(y > 0 && x > 0);
1349 const uint8_t* table = build_sweep_table();
1350
1351 unsigned result;
1352 bool swap = (x < y);
1353 if (swap)
1354 {
1355 // first part of the atan(v) = PI/2 - atan(1/v) identity
1356 // since our div_64 and table want v <= 1, where v = y/x
1357 SkTSwap<SkFixed>(x, y);
1358 }
1359
1360 result = div_64(y, x);
1361
1362#ifdef SK_DEBUG
1363 {
1364 unsigned result2 = SkDivBits(y, x, 6);
1365 SkASSERT(result2 == result ||
1366 (result == 1 && result2 == 0));
1367 }
1368#endif
1369
1370 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1371 result = table[result];
1372
1373 if (swap)
1374 {
1375 // complete the atan(v) = PI/2 - atan(1/v) identity
1376 result = 64 - result;
1377 // pin to 63
1378 result -= result >> 6;
1379 }
1380
1381 SkASSERT(result <= 63);
1382 return result;
1383}
1384
1385// returns angle in a circle [0..2PI) -> [0..255]
1386static unsigned SkATan2_255(SkFixed y, SkFixed x)
1387{
1388 if (x == 0)
1389 {
1390 if (y == 0)
1391 return 0;
1392 return y < 0 ? 192 : 64;
1393 }
1394 if (y == 0)
1395 return x < 0 ? 128 : 0;
1396
1397 /* Find the right quadrant for x,y
1398 Since atan_0_90 only handles the first quadrant, we rotate x,y
1399 appropriately before calling it, and then add the right amount
1400 to account for the real quadrant.
1401 quadrant 0 : add 0 | x > 0 && y > 0
1402 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1403 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1404 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1405
1406 map x<0 to (1 << 6)
1407 map y<0 to (3 << 6)
1408 add = map_x ^ map_y
1409 */
1410 int xsign = x >> 31;
1411 int ysign = y >> 31;
1412 int add = ((-xsign) ^ (ysign & 3)) << 6;
1413
1414#ifdef SK_DEBUG
1415 if (0 == add)
1416 SkASSERT(x > 0 && y > 0);
1417 else if (64 == add)
1418 SkASSERT(x < 0 && y > 0);
1419 else if (128 == add)
1420 SkASSERT(x < 0 && y < 0);
1421 else if (192 == add)
1422 SkASSERT(x > 0 && y < 0);
1423 else
1424 SkASSERT(!"bad value for add");
1425#endif
1426
1427 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1428 where we need to rotate x,y by 90 or -90
1429 */
1430 x = (x ^ xsign) - xsign;
1431 y = (y ^ ysign) - ysign;
1432 if (add & 64) // quads 1 or 3 need to swap x,y
1433 SkTSwap<SkFixed>(x, y);
1434
1435 unsigned result = add + atan_0_90(y, x);
1436 SkASSERT(result < 256);
1437 return result;
1438}
1439
1440void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1441{
1442 SkMatrix::MapXYProc proc = fDstToIndexProc;
1443 const SkMatrix& matrix = fDstToIndex;
1444 const SkPMColor* cache = this->getCache32();
1445 SkPoint srcPt;
1446
1447 if (fDstToIndexClass != kPerspective_MatrixClass)
1448 {
1449 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1450 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1451 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1452 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1453
1454 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1455 {
1456 SkFixed storage[2];
1457 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1458 &storage[0], &storage[1]);
1459 dx = storage[0];
1460 dy = storage[1];
1461 }
1462 else
1463 {
1464 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1465 dx = SkScalarToFixed(matrix.getScaleX());
1466 dy = SkScalarToFixed(matrix.getSkewY());
1467 }
1468
1469 for (; count > 0; --count)
1470 {
1471 *dstC++ = cache[SkATan2_255(fy, fx)];
1472 fx += dx;
1473 fy += dy;
1474 }
1475 }
1476 else // perspective case
1477 {
1478 for (int stop = x + count; x < stop; x++)
1479 {
1480 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1481 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1482
1483 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1484 SkScalarToFixed(srcPt.fX));
1485 *dstC++ = cache[index];
1486 }
1487 }
1488}
1489
1490void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1491{
1492 SkMatrix::MapXYProc proc = fDstToIndexProc;
1493 const SkMatrix& matrix = fDstToIndex;
1494 const uint16_t* cache = this->getCache16();
1495 int toggle = ((x ^ y) & 1) << kCache16Bits;
1496 SkPoint srcPt;
1497
1498 if (fDstToIndexClass != kPerspective_MatrixClass)
1499 {
1500 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1501 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1502 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1503 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1504
1505 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1506 {
1507 SkFixed storage[2];
1508 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1509 &storage[0], &storage[1]);
1510 dx = storage[0];
1511 dy = storage[1];
1512 }
1513 else
1514 {
1515 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1516 dx = SkScalarToFixed(matrix.getScaleX());
1517 dy = SkScalarToFixed(matrix.getSkewY());
1518 }
1519
1520 for (; count > 0; --count)
1521 {
1522 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1523 *dstC++ = cache[toggle + index];
1524 toggle ^= (1 << kCache16Bits);
1525 fx += dx;
1526 fy += dy;
1527 }
1528 }
1529 else // perspective case
1530 {
1531 for (int stop = x + count; x < stop; x++)
1532 {
1533 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1534 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1535
1536 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1537 SkScalarToFixed(srcPt.fX));
1538 index >>= (8 - kCache16Bits);
1539 *dstC++ = cache[toggle + index];
1540 toggle ^= (1 << kCache16Bits);
1541 }
1542 }
1543}
1544
1545///////////////////////////////////////////////////////////////////////////
1546///////////////////////////////////////////////////////////////////////////
1547
1548// assumes colors is SkColor* and pos is SkScalar*
1549#define EXPAND_1_COLOR(count) \
1550 SkColor tmp[2]; \
1551 do { \
1552 if (1 == count) { \
1553 tmp[0] = tmp[1] = colors[0]; \
1554 colors = tmp; \
1555 pos = NULL; \
1556 count = 2; \
1557 } \
1558 } while (0)
1559
1560SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1561 const SkColor colors[], const SkScalar pos[], int colorCount,
1562 SkShader::TileMode mode, SkUnitMapper* mapper)
1563{
1564 if (NULL == pts || NULL == colors || colorCount < 1) {
1565 return NULL;
1566 }
1567 EXPAND_1_COLOR(colorCount);
1568
reed@android.com41bccf52009-04-03 13:33:51 +00001569 SkScalar posStorage[2];
1570 if (colorCount == 2 && pos == NULL) {
1571 posStorage[0] = SK_Scalar1/4;
1572 posStorage[1] = 3*SK_Scalar1/4;
1573 pos = posStorage;
1574 }
1575
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576 return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
1577}
1578
1579SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1580 const SkColor colors[], const SkScalar pos[], int colorCount,
1581 SkShader::TileMode mode, SkUnitMapper* mapper)
1582{
1583 if (radius <= 0 || NULL == colors || colorCount < 1) {
1584 return NULL;
1585 }
1586 EXPAND_1_COLOR(colorCount);
1587
1588 return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper));
1589}
1590
1591SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1592 const SkColor colors[],
1593 const SkScalar pos[],
1594 int count, SkUnitMapper* mapper)
1595{
1596 if (NULL == colors || count < 1) {
1597 return NULL;
1598 }
1599 EXPAND_1_COLOR(count);
1600
1601 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1602}
1603
1604static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1605 Linear_Gradient::CreateProc);
1606
1607static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1608 Radial_Gradient::CreateProc);
1609
1610static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1611 Sweep_Gradient::CreateProc);
1612