blob: 696def6993c63aefa6d8554f5730554253352fd4 [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)
reed@android.comec3d6e52009-06-05 14:43:55 +0000133 virtual void onCacheReset() = 0;
reed@android.com9b46e772009-06-05 12:24:41 +0000134
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:
reed@android.comec3d6e52009-06-05 14:43:55 +0000638 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {
639 fCachedBitmap = NULL;
640 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 virtual Factory getFactory() { return CreateProc; }
642
643private:
reed@android.com9b46e772009-06-05 12:24:41 +0000644 SkBitmap* fCachedBitmap; // allocated on demand
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 typedef Gradient_Shader INHERITED;
647};
648
649// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000650static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651{
652 SkASSERT(count > 0);
653 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
654}
655
656void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
657{
658 SkASSERT(count > 0);
659
660 SkPoint srcPt;
661 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
662 TileProc proc = fTileProc;
663 const SkPMColor* cache = this->getCache32();
664
665 if (fDstToIndexClass != kPerspective_MatrixClass)
666 {
667 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
668 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
669
670 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
671 {
672 SkFixed dxStorage[1];
673 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
674 dx = dxStorage[0];
675 }
676 else
677 {
678 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
679 dx = SkScalarToFixed(fDstToIndex.getScaleX());
680 }
681
682 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
683 {
684 unsigned fi = proc(fx);
685 SkASSERT(fi <= 0xFFFF);
686 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
687 }
688 else if (proc == clamp_tileproc)
689 {
690#if 0
691 if (no_need_for_clamp(fx, dx, count))
692 {
693 unsigned fi;
694 while ((count -= 4) >= 0)
695 {
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 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
699 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
700 }
701 SkASSERT(count <= -1 && count >= -4);
702 count += 4;
703 while (--count >= 0)
704 {
705 fi = fx >> 8;
706 SkASSERT(fi <= 0xFF);
707 fx += dx;
708 *dstC++ = cache[fi];
709 }
710 }
711 else
712#endif
713 do {
714 unsigned fi = SkClampMax(fx >> 8, 0xFF);
715 SkASSERT(fi <= 0xFF);
716 fx += dx;
717 *dstC++ = cache[fi];
718 } while (--count != 0);
719 }
720 else if (proc == mirror_tileproc)
721 {
722 do {
723 unsigned fi = mirror_8bits(fx >> 8);
724 SkASSERT(fi <= 0xFF);
725 fx += dx;
726 *dstC++ = cache[fi];
727 } while (--count != 0);
728 }
729 else
730 {
731 SkASSERT(proc == repeat_tileproc);
732 do {
733 unsigned fi = repeat_8bits(fx >> 8);
734 SkASSERT(fi <= 0xFF);
735 fx += dx;
736 *dstC++ = cache[fi];
737 } while (--count != 0);
738 }
739 }
740 else
741 {
742 SkScalar dstX = SkIntToScalar(x);
743 SkScalar dstY = SkIntToScalar(y);
744 do {
745 dstProc(fDstToIndex, dstX, dstY, &srcPt);
746 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
747 SkASSERT(fi <= 0xFFFF);
748 *dstC++ = cache[fi >> (16 - kCache32Bits)];
749 dstX += SK_Scalar1;
750 } while (--count != 0);
751 }
752}
753
754bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
755 TileMode xy[]) {
reed@android.com9b46e772009-06-05 12:24:41 +0000756 // we cache our "bitmap", so it's generationID will be const on subsequent
757 // calls to asABitmap
758 if (NULL == fCachedBitmap) {
759 fCachedBitmap = SkNEW(SkBitmap);
760 fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
761 fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
762 }
763
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 if (bitmap) {
reed@android.com9b46e772009-06-05 12:24:41 +0000765 *bitmap = *fCachedBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 }
767 if (matrix) {
768 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
769 matrix->preConcat(fPtsToUnit);
770 }
771 if (xy) {
772 xy[0] = fTileMode;
773 xy[1] = kClamp_TileMode;
774 }
775 return true;
776}
777
778#ifdef TEST_GRADIENT_DITHER
779static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
780{
reed@android.com0becfc5b2009-01-13 13:26:44 +0000781 if (reinterpret_cast<uintptr_t>(dst) & 2)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 {
783 *dst++ = value;
784 count -= 1;
785 SkTSwap(value, other);
786 }
787
788 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
789
790 if (count & 1)
791 dst[count - 1] = value;
792}
793#endif
794
795void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
796{
797 SkASSERT(count > 0);
798
799 SkPoint srcPt;
800 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
801 TileProc proc = fTileProc;
802 const uint16_t* cache = this->getCache16();
803#ifdef TEST_GRADIENT_DITHER
804 int toggle = ((x ^ y) & 1) << kCache16Bits;
805#endif
806
807 if (fDstToIndexClass != kPerspective_MatrixClass)
808 {
809 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
810 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
811
812 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
813 {
814 SkFixed dxStorage[1];
815 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
816 dx = dxStorage[0];
817 }
818 else
819 {
820 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
821 dx = SkScalarToFixed(fDstToIndex.getScaleX());
822 }
823
824 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
825 {
826 unsigned fi = proc(fx) >> 10;
827 SkASSERT(fi <= 63);
828#ifdef TEST_GRADIENT_DITHER
829 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
830#else
831 sk_memset16(dstC, cache[fi], count);
832#endif
833 }
834 else if (proc == clamp_tileproc)
835 {
836 do {
837 unsigned fi = SkClampMax(fx >> 10, 63);
838 SkASSERT(fi <= 63);
839 fx += dx;
840#ifdef TEST_GRADIENT_DITHER
841 *dstC++ = cache[toggle + fi];
842 toggle ^= (1 << kCache16Bits);
843#else
844 *dstC++ = cache[fi];
845#endif
846 } while (--count != 0);
847 }
848 else if (proc == mirror_tileproc)
849 {
850 do {
851 unsigned fi = mirror_6bits(fx >> 10);
852 SkASSERT(fi <= 0x3F);
853 fx += dx;
854#ifdef TEST_GRADIENT_DITHER
855 *dstC++ = cache[toggle + fi];
856 toggle ^= (1 << kCache16Bits);
857#else
858 *dstC++ = cache[fi];
859#endif
860 } while (--count != 0);
861 }
862 else
863 {
864 SkASSERT(proc == repeat_tileproc);
865 do {
866 unsigned fi = repeat_6bits(fx >> 10);
867 SkASSERT(fi <= 0x3F);
868 fx += dx;
869#ifdef TEST_GRADIENT_DITHER
870 *dstC++ = cache[toggle + fi];
871 toggle ^= (1 << kCache16Bits);
872#else
873 *dstC++ = cache[fi];
874#endif
875 } while (--count != 0);
876 }
877 }
878 else
879 {
880 SkScalar dstX = SkIntToScalar(x);
881 SkScalar dstY = SkIntToScalar(y);
882 do {
883 dstProc(fDstToIndex, dstX, dstY, &srcPt);
884 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
885 SkASSERT(fi <= 0xFFFF);
886
887 int index = fi >> (16 - kCache16Bits);
888#ifdef TEST_GRADIENT_DITHER
889 *dstC++ = cache[toggle + index];
890 toggle ^= (1 << kCache16Bits);
891#else
892 *dstC++ = cache[index];
893#endif
894
895 dstX += SK_Scalar1;
896 } while (--count != 0);
897 }
898}
899
900///////////////////////////////////////////////////////////////////////////////
901
902#define kSQRT_TABLE_BITS 11
903#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
904
905#include "SkRadialGradient_Table.h"
906
907#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
908
909#include <stdio.h>
910
911void SkRadialGradient_BuildTable()
912{
913 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
914
915 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
916 SkASSERT(file);
917 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
918
919 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
920 {
921 if ((i & 15) == 0)
922 ::fprintf(file, "\t");
923
924 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
925
926 ::fprintf(file, "0x%02X", value);
927 if (i < kSQRT_TABLE_SIZE-1)
928 ::fprintf(file, ", ");
929 if ((i & 15) == 15)
930 ::fprintf(file, "\n");
931 }
932 ::fprintf(file, "};\n");
933 ::fclose(file);
934}
935
936#endif
937
938
939static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
940{
941 SkScalar inv = SkScalarInvert(radius);
942
943 matrix->setTranslate(-center.fX, -center.fY);
944 matrix->postScale(inv, inv);
945}
946
947class Radial_Gradient : public Gradient_Shader {
948public:
949 Radial_Gradient(const SkPoint& center, SkScalar radius,
950 const SkColor colors[], const SkScalar pos[], int colorCount,
951 SkShader::TileMode mode, SkUnitMapper* mapper)
952 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
953 {
954 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
955 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
956
957 rad_to_unit_matrix(center, radius, &fPtsToUnit);
958 }
959 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
960 {
961 SkASSERT(count > 0);
962
963 SkPoint srcPt;
964 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
965 TileProc proc = fTileProc;
966 const SkPMColor* cache = this->getCache32();
967
968 if (fDstToIndexClass != kPerspective_MatrixClass)
969 {
970 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
971 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
972 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
973
974 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
975 {
976 SkFixed storage[2];
977 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
978 dx = storage[0];
979 dy = storage[1];
980 }
981 else
982 {
983 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
984 dx = SkScalarToFixed(fDstToIndex.getScaleX());
985 dy = SkScalarToFixed(fDstToIndex.getSkewY());
986 }
987
988 if (proc == clamp_tileproc)
989 {
990 const uint8_t* sqrt_table = gSqrt8Table;
991 fx >>= 1;
992 dx >>= 1;
993 fy >>= 1;
994 dy >>= 1;
995 do {
996 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
997 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
998 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
999 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1000 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1001 fx += dx;
1002 fy += dy;
1003 } while (--count != 0);
1004 }
1005 else if (proc == mirror_tileproc)
1006 {
1007 do {
1008 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1009 unsigned fi = mirror_tileproc(dist);
1010 SkASSERT(fi <= 0xFFFF);
1011 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1012 fx += dx;
1013 fy += dy;
1014 } while (--count != 0);
1015 }
1016 else
1017 {
1018 SkASSERT(proc == repeat_tileproc);
1019 do {
1020 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1021 unsigned fi = repeat_tileproc(dist);
1022 SkASSERT(fi <= 0xFFFF);
1023 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1024 fx += dx;
1025 fy += dy;
1026 } while (--count != 0);
1027 }
1028 }
1029 else // perspective case
1030 {
1031 SkScalar dstX = SkIntToScalar(x);
1032 SkScalar dstY = SkIntToScalar(y);
1033 do {
1034 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1035 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1036 SkASSERT(fi <= 0xFFFF);
1037 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1038 dstX += SK_Scalar1;
1039 } while (--count != 0);
1040 }
1041 }
1042 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
1043 {
1044 SkASSERT(count > 0);
1045
1046 SkPoint srcPt;
1047 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1048 TileProc proc = fTileProc;
1049 const uint16_t* cache = this->getCache16();
1050#ifdef TEST_GRADIENT_DITHER
1051 int toggle = ((x ^ y) & 1) << kCache16Bits;
1052#endif
1053
1054 if (fDstToIndexClass != kPerspective_MatrixClass)
1055 {
1056 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1057 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1058 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1059
1060 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1061 {
1062 SkFixed storage[2];
1063 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1064 dx = storage[0];
1065 dy = storage[1];
1066 }
1067 else
1068 {
1069 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1070 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1071 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1072 }
1073
1074 if (proc == clamp_tileproc)
1075 {
1076 const uint8_t* sqrt_table = gSqrt8Table;
1077
1078 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1079 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1080 precision, but that appears to be visually OK. If we decide this is OK for
1081 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1082 to avoid having to do these extra shifts each time.
1083 */
1084 fx >>= 1;
1085 dx >>= 1;
1086 fy >>= 1;
1087 dy >>= 1;
1088 if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
1089 {
1090 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1091 fy *= fy;
1092 do {
1093 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1094 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1095 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1096 fx += dx;
1097#ifdef TEST_GRADIENT_DITHER
1098 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1099 toggle ^= (1 << kCache16Bits);
1100#else
1101 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1102#endif
1103 } while (--count != 0);
1104 }
1105 else
1106 {
1107 do {
1108 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1109 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1110 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1111 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1112 fx += dx;
1113 fy += dy;
1114#ifdef TEST_GRADIENT_DITHER
1115 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1116 toggle ^= (1 << kCache16Bits);
1117#else
1118 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1119#endif
1120 } while (--count != 0);
1121 }
1122 }
1123 else if (proc == mirror_tileproc)
1124 {
1125 do {
1126 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1127 unsigned fi = mirror_tileproc(dist);
1128 SkASSERT(fi <= 0xFFFF);
1129 fx += dx;
1130 fy += dy;
1131#ifdef TEST_GRADIENT_DITHER
1132 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1133 toggle ^= (1 << kCache16Bits);
1134#else
1135 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1136#endif
1137 } while (--count != 0);
1138 }
1139 else
1140 {
1141 SkASSERT(proc == repeat_tileproc);
1142 do {
1143 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1144 unsigned fi = repeat_tileproc(dist);
1145 SkASSERT(fi <= 0xFFFF);
1146 fx += dx;
1147 fy += dy;
1148#ifdef TEST_GRADIENT_DITHER
1149 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1150 toggle ^= (1 << kCache16Bits);
1151#else
1152 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1153#endif
1154 } while (--count != 0);
1155 }
1156 }
1157 else // perspective case
1158 {
1159 SkScalar dstX = SkIntToScalar(x);
1160 SkScalar dstY = SkIntToScalar(y);
1161 do {
1162 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1163 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1164 SkASSERT(fi <= 0xFFFF);
1165
1166 int index = fi >> (16 - kCache16Bits);
1167#ifdef TEST_GRADIENT_DITHER
1168 *dstC++ = cache[toggle + index];
1169 toggle ^= (1 << kCache16Bits);
1170#else
1171 *dstC++ = cache[index];
1172#endif
1173
1174 dstX += SK_Scalar1;
1175 } while (--count != 0);
1176 }
1177 }
1178
1179 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1180 return SkNEW_ARGS(Radial_Gradient, (buffer));
1181 }
1182
1183protected:
1184 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1185 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001186 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187
1188private:
1189 typedef Gradient_Shader INHERITED;
1190};
1191
1192///////////////////////////////////////////////////////////////////////////////
1193
1194class Sweep_Gradient : public Gradient_Shader {
1195public:
1196 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1197 const SkScalar pos[], int count, SkUnitMapper* mapper)
1198 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1199 {
1200 fPtsToUnit.setTranslate(-cx, -cy);
1201 }
1202 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1203 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1204
1205 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1206 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1207 }
1208
1209protected:
1210 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001212 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213
1214private:
1215 typedef Gradient_Shader INHERITED;
1216};
1217
1218#ifdef COMPUTE_SWEEP_TABLE
1219#define PI 3.14159265
1220static bool gSweepTableReady;
1221static uint8_t gSweepTable[65];
1222
1223/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1224 We scale the results to [0..32]
1225*/
1226static const uint8_t* build_sweep_table()
1227{
1228 if (!gSweepTableReady)
1229 {
1230 const int N = 65;
1231 const double DENOM = N - 1;
1232
1233 for (int i = 0; i < N; i++)
1234 {
1235 double arg = i / DENOM;
1236 double v = atan(arg);
1237 int iv = (int)round(v * DENOM * 2 / PI);
1238// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1239 printf("%d, ", iv);
1240 gSweepTable[i] = iv;
1241 }
1242 gSweepTableReady = true;
1243 }
1244 return gSweepTable;
1245}
1246#else
1247static const uint8_t gSweepTable[] = {
1248 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1249 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1250 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1251 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1252 32
1253};
1254static const uint8_t* build_sweep_table() { return gSweepTable; }
1255#endif
1256
1257// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1258// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1259// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1260
1261//unsigned div_64(int numer, int denom);
1262static unsigned div_64(int numer, int denom)
1263{
1264 SkASSERT(numer <= denom);
1265 SkASSERT(numer > 0);
1266 SkASSERT(denom > 0);
1267
1268 int nbits = SkCLZ(numer);
1269 int dbits = SkCLZ(denom);
1270 int bits = 6 - nbits + dbits;
1271 SkASSERT(bits <= 6);
1272
1273 if (bits < 0) // detect underflow
1274 return 0;
1275
1276 denom <<= dbits - 1;
1277 numer <<= nbits - 1;
1278
1279 unsigned result = 0;
1280
1281 // do the first one
1282 if ((numer -= denom) >= 0)
1283 result = 1;
1284 else
1285 numer += denom;
1286
1287 // Now fall into our switch statement if there are more bits to compute
1288 if (bits > 0)
1289 {
1290 // make room for the rest of the answer bits
1291 result <<= bits;
1292 switch (bits) {
1293 case 6:
1294 if ((numer = (numer << 1) - denom) >= 0)
1295 result |= 32;
1296 else
1297 numer += denom;
1298 case 5:
1299 if ((numer = (numer << 1) - denom) >= 0)
1300 result |= 16;
1301 else
1302 numer += denom;
1303 case 4:
1304 if ((numer = (numer << 1) - denom) >= 0)
1305 result |= 8;
1306 else
1307 numer += denom;
1308 case 3:
1309 if ((numer = (numer << 1) - denom) >= 0)
1310 result |= 4;
1311 else
1312 numer += denom;
1313 case 2:
1314 if ((numer = (numer << 1) - denom) >= 0)
1315 result |= 2;
1316 else
1317 numer += denom;
1318 case 1:
1319 default: // not strictly need, but makes GCC make better ARM code
1320 if ((numer = (numer << 1) - denom) >= 0)
1321 result |= 1;
1322 else
1323 numer += denom;
1324 }
1325 }
1326 return result;
1327}
1328
1329// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1330static unsigned atan_0_90(SkFixed y, SkFixed x)
1331{
1332#ifdef SK_DEBUG
1333 {
1334 static bool gOnce;
1335 if (!gOnce)
1336 {
1337 gOnce = true;
1338 SkASSERT(div_64(55, 55) == 64);
1339 SkASSERT(div_64(128, 256) == 32);
1340 SkASSERT(div_64(2326528, 4685824) == 31);
1341 SkASSERT(div_64(753664, 5210112) == 9);
1342 SkASSERT(div_64(229376, 4882432) == 3);
1343 SkASSERT(div_64(2, 64) == 2);
1344 SkASSERT(div_64(1, 64) == 1);
1345 // test that we handle underflow correctly
1346 SkASSERT(div_64(12345, 0x54321234) == 0);
1347 }
1348 }
1349#endif
1350
1351 SkASSERT(y > 0 && x > 0);
1352 const uint8_t* table = build_sweep_table();
1353
1354 unsigned result;
1355 bool swap = (x < y);
1356 if (swap)
1357 {
1358 // first part of the atan(v) = PI/2 - atan(1/v) identity
1359 // since our div_64 and table want v <= 1, where v = y/x
1360 SkTSwap<SkFixed>(x, y);
1361 }
1362
1363 result = div_64(y, x);
1364
1365#ifdef SK_DEBUG
1366 {
1367 unsigned result2 = SkDivBits(y, x, 6);
1368 SkASSERT(result2 == result ||
1369 (result == 1 && result2 == 0));
1370 }
1371#endif
1372
1373 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1374 result = table[result];
1375
1376 if (swap)
1377 {
1378 // complete the atan(v) = PI/2 - atan(1/v) identity
1379 result = 64 - result;
1380 // pin to 63
1381 result -= result >> 6;
1382 }
1383
1384 SkASSERT(result <= 63);
1385 return result;
1386}
1387
1388// returns angle in a circle [0..2PI) -> [0..255]
1389static unsigned SkATan2_255(SkFixed y, SkFixed x)
1390{
1391 if (x == 0)
1392 {
1393 if (y == 0)
1394 return 0;
1395 return y < 0 ? 192 : 64;
1396 }
1397 if (y == 0)
1398 return x < 0 ? 128 : 0;
1399
1400 /* Find the right quadrant for x,y
1401 Since atan_0_90 only handles the first quadrant, we rotate x,y
1402 appropriately before calling it, and then add the right amount
1403 to account for the real quadrant.
1404 quadrant 0 : add 0 | x > 0 && y > 0
1405 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1406 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1407 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1408
1409 map x<0 to (1 << 6)
1410 map y<0 to (3 << 6)
1411 add = map_x ^ map_y
1412 */
1413 int xsign = x >> 31;
1414 int ysign = y >> 31;
1415 int add = ((-xsign) ^ (ysign & 3)) << 6;
1416
1417#ifdef SK_DEBUG
1418 if (0 == add)
1419 SkASSERT(x > 0 && y > 0);
1420 else if (64 == add)
1421 SkASSERT(x < 0 && y > 0);
1422 else if (128 == add)
1423 SkASSERT(x < 0 && y < 0);
1424 else if (192 == add)
1425 SkASSERT(x > 0 && y < 0);
1426 else
1427 SkASSERT(!"bad value for add");
1428#endif
1429
1430 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1431 where we need to rotate x,y by 90 or -90
1432 */
1433 x = (x ^ xsign) - xsign;
1434 y = (y ^ ysign) - ysign;
1435 if (add & 64) // quads 1 or 3 need to swap x,y
1436 SkTSwap<SkFixed>(x, y);
1437
1438 unsigned result = add + atan_0_90(y, x);
1439 SkASSERT(result < 256);
1440 return result;
1441}
1442
1443void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1444{
1445 SkMatrix::MapXYProc proc = fDstToIndexProc;
1446 const SkMatrix& matrix = fDstToIndex;
1447 const SkPMColor* cache = this->getCache32();
1448 SkPoint srcPt;
1449
1450 if (fDstToIndexClass != kPerspective_MatrixClass)
1451 {
1452 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1453 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1454 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1455 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1456
1457 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1458 {
1459 SkFixed storage[2];
1460 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1461 &storage[0], &storage[1]);
1462 dx = storage[0];
1463 dy = storage[1];
1464 }
1465 else
1466 {
1467 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1468 dx = SkScalarToFixed(matrix.getScaleX());
1469 dy = SkScalarToFixed(matrix.getSkewY());
1470 }
1471
1472 for (; count > 0; --count)
1473 {
1474 *dstC++ = cache[SkATan2_255(fy, fx)];
1475 fx += dx;
1476 fy += dy;
1477 }
1478 }
1479 else // perspective case
1480 {
1481 for (int stop = x + count; x < stop; x++)
1482 {
1483 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1484 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1485
1486 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1487 SkScalarToFixed(srcPt.fX));
1488 *dstC++ = cache[index];
1489 }
1490 }
1491}
1492
1493void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1494{
1495 SkMatrix::MapXYProc proc = fDstToIndexProc;
1496 const SkMatrix& matrix = fDstToIndex;
1497 const uint16_t* cache = this->getCache16();
1498 int toggle = ((x ^ y) & 1) << kCache16Bits;
1499 SkPoint srcPt;
1500
1501 if (fDstToIndexClass != kPerspective_MatrixClass)
1502 {
1503 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1504 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1505 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1506 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1507
1508 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1509 {
1510 SkFixed storage[2];
1511 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1512 &storage[0], &storage[1]);
1513 dx = storage[0];
1514 dy = storage[1];
1515 }
1516 else
1517 {
1518 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1519 dx = SkScalarToFixed(matrix.getScaleX());
1520 dy = SkScalarToFixed(matrix.getSkewY());
1521 }
1522
1523 for (; count > 0; --count)
1524 {
1525 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1526 *dstC++ = cache[toggle + index];
1527 toggle ^= (1 << kCache16Bits);
1528 fx += dx;
1529 fy += dy;
1530 }
1531 }
1532 else // perspective case
1533 {
1534 for (int stop = x + count; x < stop; x++)
1535 {
1536 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1537 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1538
1539 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1540 SkScalarToFixed(srcPt.fX));
1541 index >>= (8 - kCache16Bits);
1542 *dstC++ = cache[toggle + index];
1543 toggle ^= (1 << kCache16Bits);
1544 }
1545 }
1546}
1547
1548///////////////////////////////////////////////////////////////////////////
1549///////////////////////////////////////////////////////////////////////////
1550
1551// assumes colors is SkColor* and pos is SkScalar*
1552#define EXPAND_1_COLOR(count) \
1553 SkColor tmp[2]; \
1554 do { \
1555 if (1 == count) { \
1556 tmp[0] = tmp[1] = colors[0]; \
1557 colors = tmp; \
1558 pos = NULL; \
1559 count = 2; \
1560 } \
1561 } while (0)
1562
1563SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1564 const SkColor colors[], const SkScalar pos[], int colorCount,
1565 SkShader::TileMode mode, SkUnitMapper* mapper)
1566{
1567 if (NULL == pts || NULL == colors || colorCount < 1) {
1568 return NULL;
1569 }
1570 EXPAND_1_COLOR(colorCount);
1571
reed@android.com41bccf52009-04-03 13:33:51 +00001572 SkScalar posStorage[2];
1573 if (colorCount == 2 && pos == NULL) {
1574 posStorage[0] = SK_Scalar1/4;
1575 posStorage[1] = 3*SK_Scalar1/4;
1576 pos = posStorage;
1577 }
1578
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
1580}
1581
1582SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1583 const SkColor colors[], const SkScalar pos[], int colorCount,
1584 SkShader::TileMode mode, SkUnitMapper* mapper)
1585{
1586 if (radius <= 0 || NULL == colors || colorCount < 1) {
1587 return NULL;
1588 }
1589 EXPAND_1_COLOR(colorCount);
1590
1591 return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper));
1592}
1593
1594SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1595 const SkColor colors[],
1596 const SkScalar pos[],
1597 int count, SkUnitMapper* mapper)
1598{
1599 if (NULL == colors || count < 1) {
1600 return NULL;
1601 }
1602 EXPAND_1_COLOR(count);
1603
1604 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1605}
1606
1607static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1608 Linear_Gradient::CreateProc);
1609
1610static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1611 Radial_Gradient::CreateProc);
1612
1613static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1614 Sweep_Gradient::CreateProc);
1615