blob: 502e9ccae706446f29ad10bbaf78a2969c7d7e0a [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
reed@android.com8a1c16f2008-12-17 15:59:43 +000023///////////////////////////////////////////////////////////////////////////
24
25typedef SkFixed (*TileProc)(SkFixed);
26
reed@android.com41bccf52009-04-03 13:33:51 +000027static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000028 return SkClampMax(x, 0xFFFF);
29}
30
reed@android.com41bccf52009-04-03 13:33:51 +000031static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 return x & 0xFFFF;
33}
34
reed@android.com41bccf52009-04-03 13:33:51 +000035static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 int s = x << 15 >> 31;
37 return (x ^ s) & 0xFFFF;
38}
39
40static const TileProc gTileProcs[] = {
41 clamp_tileproc,
42 repeat_tileproc,
43 mirror_tileproc
44};
45
46//////////////////////////////////////////////////////////////////////////////
47
reed@android.com41bccf52009-04-03 13:33:51 +000048static inline int repeat_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 return x & 63;
50}
51
reed@android.com41bccf52009-04-03 13:33:51 +000052static inline int mirror_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000053#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
54 if (x & 64)
55 x = ~x;
56 return x & 63;
57#else
58 int s = x << 25 >> 31;
59 return (x ^ s) & 63;
60#endif
61}
62
reed@android.com41bccf52009-04-03 13:33:51 +000063static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 return x & 0xFF;
65}
66
reed@android.com41bccf52009-04-03 13:33:51 +000067static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000068#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000069 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000071 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 return x & 255;
73#else
74 int s = x << 23 >> 31;
75 return (x ^ s) & 0xFF;
76#endif
77}
78
79//////////////////////////////////////////////////////////////////////////////
80
81class Gradient_Shader : public SkShader {
82public:
83 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +000084 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 virtual ~Gradient_Shader();
86
87 // overrides
88 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
89 virtual uint32_t getFlags() { return fFlags; }
90
91protected:
92 Gradient_Shader(SkFlattenableReadBuffer& );
93 SkUnitMapper* fMapper;
94 SkMatrix fPtsToUnit; // set by subclass
95 SkMatrix fDstToIndex;
96 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 TileMode fTileMode;
98 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +000099 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 uint8_t fDstToIndexClass;
101 uint8_t fFlags;
102
103 struct Rec {
104 SkFixed fPos; // 0...1
105 uint32_t fScale; // (1 << 24) / range
106 };
107 Rec* fRecs;
108
109 enum {
110 kCache16Bits = 6, // seems like enough for visual accuracy
111 kCache16Count = 1 << kCache16Bits,
112 kCache32Bits = 8, // pretty much should always be 8
113 kCache32Count = 1 << kCache32Bits
114 };
115 virtual void flatten(SkFlattenableWriteBuffer& );
116 const uint16_t* getCache16();
117 const SkPMColor* getCache32();
118
reed@android.com9b46e772009-06-05 12:24:41 +0000119 // called when we kill our cached colors (to be rebuilt later on demand)
reed@android.comec3d6e52009-06-05 14:43:55 +0000120 virtual void onCacheReset() = 0;
reed@android.com9b46e772009-06-05 12:24:41 +0000121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122private:
123 enum {
124 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
125
reed@android.com1c12abe2009-07-02 15:01:02 +0000126 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 };
128 SkColor fStorage[(kStorageSize + 3) >> 2];
129 SkColor* fOrigColors;
130
131 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
132 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
133
134 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
135 SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
136 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
137
138 typedef SkShader INHERITED;
139};
140
reed@android.com41bccf52009-04-03 13:33:51 +0000141static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 SkASSERT(x >= 0 && x <= SK_Scalar1);
143
144#ifdef SK_SCALAR_IS_FLOAT
145 return (unsigned)(x * 0xFFFF);
146#else
147 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
148#endif
149}
150
reed@android.com41bccf52009-04-03 13:33:51 +0000151Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
152 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 SkASSERT(colorCount > 1);
154
155 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
156
157 fMapper = mapper;
158 mapper->safeRef();
159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
161 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
162 fTileMode = mode;
163 fTileProc = gTileProcs[mode];
reed@android.com41bccf52009-04-03 13:33:51 +0000164
165 fCache16 = fCache16Storage = NULL;
166 fCache32 = fCache32Storage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
reed@android.com41bccf52009-04-03 13:33:51 +0000168 /* Note: we let the caller skip the first and/or last position.
169 i.e. pos[0] = 0.3, pos[1] = 0.7
170 In these cases, we insert dummy entries to ensure that the final data
171 will be bracketed by [0, 1].
172 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
173
174 Thus colorCount (the caller's value, and fColorCount (our value) may
175 differ by up to 2. In the above example:
176 colorCount = 2
177 fColorCount = 4
178 */
179 fColorCount = colorCount;
180 // check if we need to add in dummy start and/or end position/colors
181 bool dummyFirst = false;
182 bool dummyLast = false;
183 if (pos) {
184 dummyFirst = pos[0] != 0;
185 dummyLast = pos[colorCount - 1] != SK_Scalar1;
186 fColorCount += dummyFirst + dummyLast;
187 }
188
189 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000190 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000191 fOrigColors = reinterpret_cast<SkColor*>(
192 sk_malloc_throw(size * fColorCount));
193 }
194 else {
195 fOrigColors = fStorage;
196 }
197
198 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 {
reed@android.com41bccf52009-04-03 13:33:51 +0000200 SkColor* origColors = fOrigColors;
201 if (dummyFirst) {
202 *origColors++ = colors[0];
203 }
204 memcpy(origColors, colors, colorCount * sizeof(SkColor));
205 if (dummyLast) {
206 origColors += colorCount;
207 *origColors = colors[colorCount - 1];
208 }
209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210
reed@android.com1c12abe2009-07-02 15:01:02 +0000211 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000212 if (fColorCount > 2) {
213 Rec* recs = fRecs;
214 recs->fPos = 0;
215 // recs->fScale = 0; // unused;
216 recs += 1;
217 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 /* We need to convert the user's array of relative positions into
219 fixed-point positions and scale factors. We need these results
220 to be strictly monotonic (no two values equal or out of order).
221 Hence this complex loop that just jams a zero for the scale
222 value if it sees a segment out of order, and it assures that
223 we start at 0 and end at 1.0
224 */
225 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000226 int startIndex = dummyFirst ? 0 : 1;
227 int count = colorCount + dummyLast;
228 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 // force the last value to be 1.0
230 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000231 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000233 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 }
reed@android.com41bccf52009-04-03 13:33:51 +0000236 // pin curr withing range
237 if (curr < 0) {
238 curr = 0;
239 } else if (curr > SK_Fixed1) {
240 curr = SK_Fixed1;
241 }
242 recs->fPos = curr;
243 if (curr > prev) {
244 recs->fScale = (1 << 24) / (curr - prev);
245 } else {
246 recs->fScale = 0; // ignore this segment
247 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 // get ready for the next value
249 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000250 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 }
reed@android.com41bccf52009-04-03 13:33:51 +0000252 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkFixed dp = SK_Fixed1 / (colorCount - 1);
254 SkFixed p = dp;
255 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000256 for (int i = 1; i < colorCount; i++) {
257 recs->fPos = p;
258 recs->fScale = scale;
259 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 p += dp;
261 }
262 }
263 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000264 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265}
266
267Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000268 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 fCacheAlpha = 256;
270
271 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
272
273 fCache16 = fCache16Storage = NULL;
274 fCache32 = fCache32Storage = NULL;
275
reed@android.com41bccf52009-04-03 13:33:51 +0000276 int colorCount = fColorCount = buffer.readU32();
277 if (colorCount > kColorStorageCount) {
278 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
279 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
280 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
285 fTileMode = (TileMode)buffer.readU8();
286 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000287 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 if (colorCount > 2) {
289 Rec* recs = fRecs;
290 recs[0].fPos = 0;
291 for (int i = 1; i < colorCount; i++) {
292 recs[i].fPos = buffer.readS32();
293 recs[i].fScale = buffer.readU32();
294 }
295 }
296 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000297 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298}
299
reed@android.com41bccf52009-04-03 13:33:51 +0000300Gradient_Shader::~Gradient_Shader() {
301 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000303 }
304 if (fCache32Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 sk_free(fCache32Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000306 }
307 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 fMapper->safeUnref();
311}
312
reed@android.com41bccf52009-04-03 13:33:51 +0000313void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 this->INHERITED::flatten(buffer);
315 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000316 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
318 buffer.write8(fTileMode);
319 if (fColorCount > 2) {
320 Rec* recs = fRecs;
321 for (int i = 1; i < fColorCount; i++) {
322 buffer.write32(recs[i].fPos);
323 buffer.write32(recs[i].fScale);
324 }
325 }
326 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
327}
328
329bool Gradient_Shader::setContext(const SkBitmap& device,
330 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000331 const SkMatrix& matrix) {
332 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000334 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335
336 const SkMatrix& inverse = this->getTotalInverse();
337
338 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
339 return false;
340 }
341
342 fDstToIndexProc = fDstToIndex.getMapXYProc();
343 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
344
345 // now convert our colors in to PMColors
346 unsigned paintAlpha = this->getPaintAlpha();
347 unsigned colorAlpha = 0xFF;
348
reed@android.com3d06a8c2009-07-07 18:19:59 +0000349 // FIXME: record colorAlpha in constructor, since this is not affected
350 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000351 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 SkColor src = fOrigColors[i];
353 unsigned sa = SkColorGetA(src);
354 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 }
356
357 fFlags = this->INHERITED::getFlags();
358 if ((colorAlpha & paintAlpha) == 0xFF) {
359 fFlags |= kOpaqueAlpha_Flag;
360 }
361 // we can do span16 as long as our individual colors are opaque,
362 // regardless of the paint's alpha
363 if (0xFF == colorAlpha) {
364 fFlags |= kHasSpan16_Flag;
365 }
366
367 // if the new alpha differs from the previous time we were called, inval our cache
368 // this will trigger the cache to be rebuilt.
369 // we don't care about the first time, since the cache ptrs will already be NULL
370 if (fCacheAlpha != paintAlpha) {
371 fCache16 = NULL; // inval the cache
372 fCache32 = NULL; // inval the cache
373 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000374 // inform our subclasses
375 this->onCacheReset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 }
377 return true;
378}
379
reed@android.com41bccf52009-04-03 13:33:51 +0000380static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 SkASSERT(a == SkToU8(a));
382 SkASSERT(b == SkToU8(b));
383 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 return a + ((b - a) * scale >> 8);
385}
386
reed@android.com41bccf52009-04-03 13:33:51 +0000387static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
388 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389#if 0
390 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
391 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
392 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
393 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
394
395 return SkPackARGB32(a, r, g, b);
396#else
397 int otherBlend = 256 - blend;
398
399#if 0
400 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
401 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
402 SkASSERT((t0 & t1) == 0);
403 return t0 | t1;
404#else
405 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
406 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
407#endif
408
409#endif
410}
411
412#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
413
reed@android.com41bccf52009-04-03 13:33:51 +0000414/** We take the original colors, not our premultiplied PMColors, since we can
415 build a 16bit table as long as the original colors are opaque, even if the
416 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417*/
reed@android.com41bccf52009-04-03 13:33:51 +0000418static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
419 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 SkASSERT(count > 1);
421 SkASSERT(SkColorGetA(c0) == 0xFF);
422 SkASSERT(SkColorGetA(c1) == 0xFF);
423
424 SkFixed r = SkColorGetR(c0);
425 SkFixed g = SkColorGetG(c0);
426 SkFixed b = SkColorGetB(c0);
427
428 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
429 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
430 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
431
432 r = SkIntToFixed(r) + 0x8000;
433 g = SkIntToFixed(g) + 0x8000;
434 b = SkIntToFixed(b) + 0x8000;
435
436 do {
437 unsigned rr = r >> 16;
438 unsigned gg = g >> 16;
439 unsigned bb = b >> 16;
440 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
441 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
442 cache += 1;
443 r += dr;
444 g += dg;
445 b += db;
446 } while (--count != 0);
447}
448
reed@android.com1c12abe2009-07-02 15:01:02 +0000449static void build_32bit_cache(SkPMColor cache[], SkColor c0, SkColor c1,
450 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 SkASSERT(count > 1);
452
reed@android.com1c12abe2009-07-02 15:01:02 +0000453 // need to apply paintAlpha to our two endpoints
454 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
455 SkFixed da;
456 {
457 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
458 da = SkIntToFixed(tmp - a) / (count - 1);
459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460
reed@android.com1c12abe2009-07-02 15:01:02 +0000461 SkFixed r = SkColorGetR(c0);
462 SkFixed g = SkColorGetG(c0);
463 SkFixed b = SkColorGetB(c0);
464 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
465 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
466 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467
468 a = SkIntToFixed(a) + 0x8000;
469 r = SkIntToFixed(r) + 0x8000;
470 g = SkIntToFixed(g) + 0x8000;
471 b = SkIntToFixed(b) + 0x8000;
472
473 do {
reed@android.com1c12abe2009-07-02 15:01:02 +0000474 *cache++ = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 a += da;
476 r += dr;
477 g += dg;
478 b += db;
479 } while (--count != 0);
480}
481
reed@android.com41bccf52009-04-03 13:33:51 +0000482static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 SkASSERT((unsigned)x <= SK_Fixed1);
484 return x - (x >> 16);
485}
486
reed@android.com41bccf52009-04-03 13:33:51 +0000487static inline U16CPU dot6to16(unsigned x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 SkASSERT(x < 64);
489 return (x << 10) | (x << 4) | (x >> 2);
490}
491
reed@android.com41bccf52009-04-03 13:33:51 +0000492const uint16_t* Gradient_Shader::getCache16() {
493 if (fCache16 == NULL) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000494 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000496 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000498 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000500 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 Rec* rec = fRecs;
502 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000503 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
505 SkASSERT(nextIndex < kCache16Count);
506
507 if (nextIndex > prevIndex)
508 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
509 prevIndex = nextIndex;
510 }
511 SkASSERT(prevIndex == kCache16Count - 1);
512 }
513
reed@android.com41bccf52009-04-03 13:33:51 +0000514 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 uint16_t* linear = fCache16; // just computed linear data
517 uint16_t* mapped = fCache16Storage; // storage for mapped data
518 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000519 for (int i = 0; i < 64; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 int index = map->mapUnit16(dot6to16(i)) >> 10;
521 mapped[i] = linear[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 mapped[i + 64] = linear[index + 64];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 }
524 sk_free(fCache16);
525 fCache16 = fCache16Storage;
526 }
527 }
528 return fCache16;
529}
530
reed@android.com41bccf52009-04-03 13:33:51 +0000531const SkPMColor* Gradient_Shader::getCache32() {
532 if (fCache32 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 if (fCache32Storage == NULL) // set the storage and our working ptr
534 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
535
536 fCache32 = fCache32Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000537 if (fColorCount == 2) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000538 build_32bit_cache(fCache32, fOrigColors[0], fOrigColors[1],
539 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000540 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 Rec* rec = fRecs;
542 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000543 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
545 SkASSERT(nextIndex < kCache32Count);
546
547 if (nextIndex > prevIndex)
reed@android.com1c12abe2009-07-02 15:01:02 +0000548 build_32bit_cache(fCache32 + prevIndex, fOrigColors[i-1],
549 fOrigColors[i],
550 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 prevIndex = nextIndex;
552 }
553 SkASSERT(prevIndex == kCache32Count - 1);
554 }
555
reed@android.com41bccf52009-04-03 13:33:51 +0000556 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
558 SkPMColor* linear = fCache32; // just computed linear data
559 SkPMColor* mapped = fCache32Storage; // storage for mapped data
560 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000561 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000563 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 sk_free(fCache32);
565 fCache32 = fCache32Storage;
566 }
567 }
568 return fCache32;
569}
570
571///////////////////////////////////////////////////////////////////////////
572
reed@android.com41bccf52009-04-03 13:33:51 +0000573static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 SkVector vec = pts[1] - pts[0];
575 SkScalar mag = vec.length();
576 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
577
578 vec.scale(inv);
579 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
580 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
581 matrix->postScale(inv, inv);
582}
583
584///////////////////////////////////////////////////////////////////////////////
585
586class Linear_Gradient : public Gradient_Shader {
587public:
588 Linear_Gradient(const SkPoint pts[2],
589 const SkColor colors[], const SkScalar pos[], int colorCount,
590 SkShader::TileMode mode, SkUnitMapper* mapper)
591 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
592 {
reed@android.com9b46e772009-06-05 12:24:41 +0000593 fCachedBitmap = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 pts_to_unit_matrix(pts, &fPtsToUnit);
595 }
reed@android.com9b46e772009-06-05 12:24:41 +0000596 virtual ~Linear_Gradient() {
597 if (fCachedBitmap) {
598 SkDELETE(fCachedBitmap);
599 }
600 }
601
reed@android.com5119bdb2009-06-12 21:27:03 +0000602 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
604 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
605 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
reed@android.com9b46e772009-06-05 12:24:41 +0000606 virtual void onCacheReset() {
607 if (fCachedBitmap) {
608 SkDELETE(fCachedBitmap);
609 fCachedBitmap = NULL;
610 }
611 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612
613 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
614 return SkNEW_ARGS(Linear_Gradient, (buffer));
615 }
616
617protected:
reed@android.comec3d6e52009-06-05 14:43:55 +0000618 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {
619 fCachedBitmap = NULL;
620 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 virtual Factory getFactory() { return CreateProc; }
622
623private:
reed@android.com9b46e772009-06-05 12:24:41 +0000624 SkBitmap* fCachedBitmap; // allocated on demand
625
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 typedef Gradient_Shader INHERITED;
627};
628
reed@android.com5119bdb2009-06-12 21:27:03 +0000629bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
630 const SkMatrix& matrix) {
631 if (!this->INHERITED::setContext(device, paint, matrix)) {
632 return false;
633 }
634
635 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
636 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000637 fFlags |= SkShader::kConstInY32_Flag;
638 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
639 // only claim this if we do have a 16bit mode (i.e. none of our
640 // colors have alpha), and if we are not dithering (which obviously
641 // is not const in Y).
642 fFlags |= SkShader::kConstInY16_Flag;
643 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000644 }
645 return true;
646}
647
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000649static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650{
651 SkASSERT(count > 0);
652 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
653}
654
655void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
656{
657 SkASSERT(count > 0);
658
659 SkPoint srcPt;
660 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
661 TileProc proc = fTileProc;
662 const SkPMColor* cache = this->getCache32();
663
reed@android.comc552a432009-06-12 20:02:50 +0000664 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
666 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
reed@android.comc552a432009-06-12 20:02:50 +0000667 // preround fx by half the amount we throw away
668 fx += 1 << 7;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669
reed@android.comc552a432009-06-12 20:02:50 +0000670 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 SkFixed dxStorage[1];
672 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
673 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000674 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
676 dx = SkScalarToFixed(fDstToIndex.getScaleX());
677 }
678
reed@android.comc552a432009-06-12 20:02:50 +0000679 if (SkFixedNearlyZero(dx)) {
680 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 unsigned fi = proc(fx);
682 SkASSERT(fi <= 0xFFFF);
683 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000684 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685#if 0
686 if (no_need_for_clamp(fx, dx, count))
687 {
688 unsigned fi;
689 while ((count -= 4) >= 0)
690 {
691 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
692 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
693 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
694 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
695 }
696 SkASSERT(count <= -1 && count >= -4);
697 count += 4;
698 while (--count >= 0)
699 {
700 fi = fx >> 8;
701 SkASSERT(fi <= 0xFF);
702 fx += dx;
703 *dstC++ = cache[fi];
704 }
705 }
706 else
707#endif
708 do {
709 unsigned fi = SkClampMax(fx >> 8, 0xFF);
710 SkASSERT(fi <= 0xFF);
711 fx += dx;
712 *dstC++ = cache[fi];
713 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000714 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 do {
716 unsigned fi = mirror_8bits(fx >> 8);
717 SkASSERT(fi <= 0xFF);
718 fx += dx;
719 *dstC++ = cache[fi];
720 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000721 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 SkASSERT(proc == repeat_tileproc);
723 do {
724 unsigned fi = repeat_8bits(fx >> 8);
725 SkASSERT(fi <= 0xFF);
726 fx += dx;
727 *dstC++ = cache[fi];
728 } while (--count != 0);
729 }
reed@android.comc552a432009-06-12 20:02:50 +0000730 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 SkScalar dstX = SkIntToScalar(x);
732 SkScalar dstY = SkIntToScalar(y);
733 do {
734 dstProc(fDstToIndex, dstX, dstY, &srcPt);
735 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
736 SkASSERT(fi <= 0xFFFF);
737 *dstC++ = cache[fi >> (16 - kCache32Bits)];
738 dstX += SK_Scalar1;
739 } while (--count != 0);
740 }
741}
742
743bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
744 TileMode xy[]) {
reed@android.com9b46e772009-06-05 12:24:41 +0000745 // we cache our "bitmap", so it's generationID will be const on subsequent
746 // calls to asABitmap
747 if (NULL == fCachedBitmap) {
748 fCachedBitmap = SkNEW(SkBitmap);
749 fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
750 fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
751 }
752
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 if (bitmap) {
reed@android.com9b46e772009-06-05 12:24:41 +0000754 *bitmap = *fCachedBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 }
756 if (matrix) {
757 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
758 matrix->preConcat(fPtsToUnit);
759 }
760 if (xy) {
761 xy[0] = fTileMode;
762 xy[1] = kClamp_TileMode;
763 }
764 return true;
765}
766
reed@android.com3c9b2a42009-08-27 19:28:37 +0000767static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
768 int count) {
769 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 *dst++ = value;
771 count -= 1;
772 SkTSwap(value, other);
773 }
774
775 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
776
reed@android.com3c9b2a42009-08-27 19:28:37 +0000777 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000779 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781
782void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
783{
784 SkASSERT(count > 0);
785
786 SkPoint srcPt;
787 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
788 TileProc proc = fTileProc;
789 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000792 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
794 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000795 // preround fx by half the amount we throw away
796 fx += 1 << 7;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000798 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 SkFixed dxStorage[1];
800 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
801 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000802 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
804 dx = SkScalarToFixed(fDstToIndex.getScaleX());
805 }
806
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000807 if (SkFixedNearlyZero(dx)) {
808 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 unsigned fi = proc(fx) >> 10;
810 SkASSERT(fi <= 63);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000812 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 do {
814 unsigned fi = SkClampMax(fx >> 10, 63);
815 SkASSERT(fi <= 63);
816 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 *dstC++ = cache[toggle + fi];
818 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000820 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 do {
822 unsigned fi = mirror_6bits(fx >> 10);
823 SkASSERT(fi <= 0x3F);
824 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 *dstC++ = cache[toggle + fi];
826 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000828 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 SkASSERT(proc == repeat_tileproc);
830 do {
831 unsigned fi = repeat_6bits(fx >> 10);
832 SkASSERT(fi <= 0x3F);
833 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 *dstC++ = cache[toggle + fi];
835 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 } while (--count != 0);
837 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000838 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 SkScalar dstX = SkIntToScalar(x);
840 SkScalar dstY = SkIntToScalar(y);
841 do {
842 dstProc(fDstToIndex, dstX, dstY, &srcPt);
843 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
844 SkASSERT(fi <= 0xFFFF);
845
846 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 *dstC++ = cache[toggle + index];
848 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849
850 dstX += SK_Scalar1;
851 } while (--count != 0);
852 }
853}
854
855///////////////////////////////////////////////////////////////////////////////
856
857#define kSQRT_TABLE_BITS 11
858#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
859
860#include "SkRadialGradient_Table.h"
861
862#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
863
864#include <stdio.h>
865
866void SkRadialGradient_BuildTable()
867{
868 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
869
870 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
871 SkASSERT(file);
872 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
873
874 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
875 {
876 if ((i & 15) == 0)
877 ::fprintf(file, "\t");
878
879 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
880
881 ::fprintf(file, "0x%02X", value);
882 if (i < kSQRT_TABLE_SIZE-1)
883 ::fprintf(file, ", ");
884 if ((i & 15) == 15)
885 ::fprintf(file, "\n");
886 }
887 ::fprintf(file, "};\n");
888 ::fclose(file);
889}
890
891#endif
892
893
894static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
895{
896 SkScalar inv = SkScalarInvert(radius);
897
898 matrix->setTranslate(-center.fX, -center.fY);
899 matrix->postScale(inv, inv);
900}
901
902class Radial_Gradient : public Gradient_Shader {
903public:
904 Radial_Gradient(const SkPoint& center, SkScalar radius,
905 const SkColor colors[], const SkScalar pos[], int colorCount,
906 SkShader::TileMode mode, SkUnitMapper* mapper)
907 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
908 {
909 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
910 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
911
912 rad_to_unit_matrix(center, radius, &fPtsToUnit);
913 }
914 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
915 {
916 SkASSERT(count > 0);
917
918 SkPoint srcPt;
919 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
920 TileProc proc = fTileProc;
921 const SkPMColor* cache = this->getCache32();
922
923 if (fDstToIndexClass != kPerspective_MatrixClass)
924 {
925 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
926 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
927 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
928
929 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
930 {
931 SkFixed storage[2];
932 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
933 dx = storage[0];
934 dy = storage[1];
935 }
936 else
937 {
938 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
939 dx = SkScalarToFixed(fDstToIndex.getScaleX());
940 dy = SkScalarToFixed(fDstToIndex.getSkewY());
941 }
942
943 if (proc == clamp_tileproc)
944 {
945 const uint8_t* sqrt_table = gSqrt8Table;
946 fx >>= 1;
947 dx >>= 1;
948 fy >>= 1;
949 dy >>= 1;
950 do {
951 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
952 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
953 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
954 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
955 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
956 fx += dx;
957 fy += dy;
958 } while (--count != 0);
959 }
960 else if (proc == mirror_tileproc)
961 {
962 do {
963 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
964 unsigned fi = mirror_tileproc(dist);
965 SkASSERT(fi <= 0xFFFF);
966 *dstC++ = cache[fi >> (16 - kCache32Bits)];
967 fx += dx;
968 fy += dy;
969 } while (--count != 0);
970 }
971 else
972 {
973 SkASSERT(proc == repeat_tileproc);
974 do {
975 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
976 unsigned fi = repeat_tileproc(dist);
977 SkASSERT(fi <= 0xFFFF);
978 *dstC++ = cache[fi >> (16 - kCache32Bits)];
979 fx += dx;
980 fy += dy;
981 } while (--count != 0);
982 }
983 }
984 else // perspective case
985 {
986 SkScalar dstX = SkIntToScalar(x);
987 SkScalar dstY = SkIntToScalar(y);
988 do {
989 dstProc(fDstToIndex, dstX, dstY, &srcPt);
990 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
991 SkASSERT(fi <= 0xFFFF);
992 *dstC++ = cache[fi >> (16 - kCache32Bits)];
993 dstX += SK_Scalar1;
994 } while (--count != 0);
995 }
996 }
reed@android.com3c9b2a42009-08-27 19:28:37 +0000997
998 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 SkASSERT(count > 0);
1000
1001 SkPoint srcPt;
1002 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1003 TileProc proc = fTileProc;
1004 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006
reed@android.com3c9b2a42009-08-27 19:28:37 +00001007 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1009 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1010 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1011
reed@android.com3c9b2a42009-08-27 19:28:37 +00001012 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 SkFixed storage[2];
1014 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1015 dx = storage[0];
1016 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001017 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1019 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1020 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1021 }
1022
reed@android.com3c9b2a42009-08-27 19:28:37 +00001023 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 const uint8_t* sqrt_table = gSqrt8Table;
1025
1026 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1027 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1028 precision, but that appears to be visually OK. If we decide this is OK for
1029 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1030 to avoid having to do these extra shifts each time.
1031 */
1032 fx >>= 1;
1033 dx >>= 1;
1034 fy >>= 1;
1035 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001036 if (dy == 0) { // might perform this check for the other modes, but the win will be a smaller % of the total
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1038 fy *= fy;
1039 do {
1040 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1041 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1042 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1043 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1045 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001047 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 do {
1049 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1050 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1051 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1052 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1053 fx += dx;
1054 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1056 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 } while (--count != 0);
1058 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001059 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 do {
1061 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1062 unsigned fi = mirror_tileproc(dist);
1063 SkASSERT(fi <= 0xFFFF);
1064 fx += dx;
1065 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1067 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001069 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 SkASSERT(proc == repeat_tileproc);
1071 do {
1072 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1073 unsigned fi = repeat_tileproc(dist);
1074 SkASSERT(fi <= 0xFFFF);
1075 fx += dx;
1076 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1078 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 } while (--count != 0);
1080 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001081 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 SkScalar dstX = SkIntToScalar(x);
1083 SkScalar dstY = SkIntToScalar(y);
1084 do {
1085 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1086 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1087 SkASSERT(fi <= 0xFFFF);
1088
1089 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 *dstC++ = cache[toggle + index];
1091 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092
1093 dstX += SK_Scalar1;
1094 } while (--count != 0);
1095 }
1096 }
1097
1098 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1099 return SkNEW_ARGS(Radial_Gradient, (buffer));
1100 }
1101
1102protected:
1103 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1104 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001105 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106
1107private:
1108 typedef Gradient_Shader INHERITED;
1109};
1110
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001111/* Two-point radial gradients are specified by two circles, each with a center
1112 point and radius. The gradient can be considered to be a series of
1113 concentric circles, with the color interpolated from the start circle
1114 (at t=0) to the end circle (at t=1).
1115
1116 For each point (x, y) in the span, we want to find the
1117 interpolated circle that intersects that point. The center
1118 of the desired circle (Cx, Cy) falls at some distance t
1119 along the line segment between the start point (Sx, Sy) and
1120 end point (Ex, Ey):
1121
1122 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1123 Cy = (1 - t) * Sy + t * Ey
1124
1125 The radius of the desired circle (r) is also a linear interpolation t
1126 between the start and end radii (Sr and Er):
1127
1128 r = (1 - t) * Sr + t * Er
1129
1130 But
1131
1132 (x - Cx)^2 + (y - Cy)^2 = r^2
1133
1134 so
1135
1136 (x - ((1 - t) * Sx + t * Ex))^2
1137 + (y - ((1 - t) * Sy + t * Ey))^2
1138 = ((1 - t) * Sr + t * Er)^2
1139
1140 Solving for t yields
1141
1142 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1143 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1144 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
1145
1146 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1147
1148 [Dx^2 + Dy^2 - Dr^2)] * t^2
1149 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1150 + [dx^2 + dy^2 - Sr^2] = 0
1151
1152 A quadratic in t. The two roots of the quadratic reflect the two
1153 possible circles on which the point may fall. Solving for t yields
1154 the gradient value to use.
1155
1156 If a<0, the start circle is entirely contained in the
1157 end circle, and one of the roots will be <0 or >1 (off the line
1158 segment). If a>0, the start circle falls at least partially
1159 outside the end circle (or vice versa), and the gradient
1160 defines a "tube" where a point may be on one circle (on the
1161 inside of the tube) or the other (outside of the tube). We choose
1162 one arbitrarily.
1163
1164 In order to keep the math to within the limits of fixed point,
1165 we divide the entire quadratic by Dr^2, and replace
1166 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
1167
1168 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1169 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1170 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
1171
1172 (x' and y' are computed by appending the subtract and scale to the
1173 fDstToIndex matrix in the constructor).
1174
1175 Since the 'A' component of the quadratic is independent of x' and y', it
1176 is precomputed in the constructor. Since the 'B' component is linear in
1177 x' and y', if x and y are linear in the span, 'B' can be computed
1178 incrementally with a simple delta (db below). If it is not (e.g.,
1179 a perspective projection), it must be computed in the loop.
1180
1181*/
1182
1183static inline SkFixed two_point_radial(SkFixed b, SkFixed fx, SkFixed fy, SkFixed sr2d2, SkFixed foura, SkFixed oneOverTwoA, bool posRoot) {
1184 SkFixed c = SkFixedSquare(fx) + SkFixedSquare(fy) - sr2d2;
1185 SkFixed discrim = SkFixedSquare(b) - SkFixedMul(foura, c);
1186 if (discrim < 0) {
1187 discrim = -discrim;
1188 }
1189 SkFixed rootDiscrim = SkFixedSqrt(discrim);
1190 if (posRoot) {
1191 return SkFixedMul(-b + rootDiscrim, oneOverTwoA);
1192 } else {
1193 return SkFixedMul(-b - rootDiscrim, oneOverTwoA);
1194 }
1195}
1196
1197class Two_Point_Radial_Gradient : public Gradient_Shader {
1198public:
1199 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1200 const SkPoint& end, SkScalar endRadius,
1201 const SkColor colors[], const SkScalar pos[],
1202 int colorCount, SkShader::TileMode mode,
1203 SkUnitMapper* mapper)
1204 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
1205 {
1206 fDiff = start - end;
1207 fDiffRadius = endRadius - startRadius;
1208 SkScalar inv = SkScalarInvert(fDiffRadius);
1209 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1210 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1211 fStartRadius = SkScalarMul(startRadius, inv);
1212 fSr2D2 = SkScalarSquare(fStartRadius);
1213 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
1214 fOneOverTwoA = SkScalarInvert(fA * 2);
1215
1216 fPtsToUnit.setTranslate(-start.fX, -start.fY);
1217 fPtsToUnit.postScale(inv, inv);
1218 }
1219 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1220 {
1221 SkASSERT(count > 0);
1222
1223 // Zero difference between radii: fill with transparent black.
1224 if (fDiffRadius == 0) {
1225 sk_bzero(dstC, count * sizeof(*dstC));
1226 return;
1227 }
1228 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1229 TileProc proc = fTileProc;
1230 const SkPMColor* cache = this->getCache32();
1231 SkFixed diffx = SkScalarToFixed(fDiff.fX);
1232 SkFixed diffy = SkScalarToFixed(fDiff.fY);
1233 SkFixed foura = SkScalarToFixed(SkScalarMul(fA, 4));
1234 SkFixed startRadius = SkScalarToFixed(fStartRadius);
1235 SkFixed sr2D2 = SkScalarToFixed(fSr2D2);
1236 SkFixed oneOverTwoA = SkScalarToFixed(fOneOverTwoA);
1237 bool posRoot = fDiffRadius < 0;
1238 if (fDstToIndexClass != kPerspective_MatrixClass)
1239 {
1240 SkPoint srcPt;
1241 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1242 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1243 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1244
1245 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1246 {
1247 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &dx, &dy);
1248 }
1249 else
1250 {
1251 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1252 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1253 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1254 }
1255 SkFixed b = (SkFixedMul(diffx, fx) +
1256 SkFixedMul(diffy, fy) - startRadius) << 1;
1257 SkFixed db = (SkFixedMul(diffx, dx) +
1258 SkFixedMul(diffy, dy)) << 1;
1259 if (proc == clamp_tileproc)
1260 {
1261 for (; count > 0; --count) {
1262 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1263 SkFixed index = SkClampMax(t, 0xFFFF);
1264 SkASSERT(index <= 0xFFFF);
1265 *dstC++ = cache[index >> (16 - kCache32Bits)];
1266 fx += dx;
1267 fy += dy;
1268 b += db;
1269 }
1270 }
1271 else if (proc == mirror_tileproc)
1272 {
1273 for (; count > 0; --count) {
1274 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1275 SkFixed index = mirror_tileproc(t);
1276 SkASSERT(index <= 0xFFFF);
1277 *dstC++ = cache[index >> (16 - kCache32Bits)];
1278 fx += dx;
1279 fy += dy;
1280 b += db;
1281 }
1282 }
1283 else
1284 {
1285 SkASSERT(proc == repeat_tileproc);
1286 for (; count > 0; --count) {
1287 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1288 SkFixed index = repeat_tileproc(t);
1289 SkASSERT(index <= 0xFFFF);
1290 *dstC++ = cache[index >> (16 - kCache32Bits)];
1291 fx += dx;
1292 fy += dy;
1293 b += db;
1294 }
1295 }
1296 }
1297 else // perspective case
1298 {
1299 for (; count > 0; --count) {
1300 SkPoint srcPt;
1301 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1302 SkFixed fx = SkScalarToFixed(srcPt.fX);
1303 SkFixed fy = SkScalarToFixed(srcPt.fY);
1304 SkFixed b = (SkFixedMul(diffx, fx) +
1305 SkFixedMul(diffy, fy) - startRadius) << 1;
1306 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1307 SkFixed index = proc(t);
1308 SkASSERT(index <= 0xFFFF);
1309 *dstC++ = cache[index >> (16 - kCache32Bits)];
1310 x += SK_Scalar1;
1311 }
1312 }
1313 }
1314
1315 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1316 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1317 }
1318
1319protected:
1320 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1321 virtual Factory getFactory() { return CreateProc; }
1322 virtual void onCacheReset() {}
1323
1324private:
1325 typedef Gradient_Shader INHERITED;
1326 SkPoint fDiff;
1327 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
1328};
1329
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330///////////////////////////////////////////////////////////////////////////////
1331
1332class Sweep_Gradient : public Gradient_Shader {
1333public:
1334 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1335 const SkScalar pos[], int count, SkUnitMapper* mapper)
1336 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1337 {
1338 fPtsToUnit.setTranslate(-cx, -cy);
1339 }
1340 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1341 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1342
1343 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1344 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1345 }
1346
1347protected:
1348 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001350 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351
1352private:
1353 typedef Gradient_Shader INHERITED;
1354};
1355
1356#ifdef COMPUTE_SWEEP_TABLE
1357#define PI 3.14159265
1358static bool gSweepTableReady;
1359static uint8_t gSweepTable[65];
1360
1361/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1362 We scale the results to [0..32]
1363*/
1364static const uint8_t* build_sweep_table()
1365{
1366 if (!gSweepTableReady)
1367 {
1368 const int N = 65;
1369 const double DENOM = N - 1;
1370
1371 for (int i = 0; i < N; i++)
1372 {
1373 double arg = i / DENOM;
1374 double v = atan(arg);
1375 int iv = (int)round(v * DENOM * 2 / PI);
1376// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1377 printf("%d, ", iv);
1378 gSweepTable[i] = iv;
1379 }
1380 gSweepTableReady = true;
1381 }
1382 return gSweepTable;
1383}
1384#else
1385static const uint8_t gSweepTable[] = {
1386 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1387 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1388 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1389 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1390 32
1391};
1392static const uint8_t* build_sweep_table() { return gSweepTable; }
1393#endif
1394
1395// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1396// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1397// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1398
1399//unsigned div_64(int numer, int denom);
1400static unsigned div_64(int numer, int denom)
1401{
1402 SkASSERT(numer <= denom);
1403 SkASSERT(numer > 0);
1404 SkASSERT(denom > 0);
1405
1406 int nbits = SkCLZ(numer);
1407 int dbits = SkCLZ(denom);
1408 int bits = 6 - nbits + dbits;
1409 SkASSERT(bits <= 6);
1410
1411 if (bits < 0) // detect underflow
1412 return 0;
1413
1414 denom <<= dbits - 1;
1415 numer <<= nbits - 1;
1416
1417 unsigned result = 0;
1418
1419 // do the first one
1420 if ((numer -= denom) >= 0)
1421 result = 1;
1422 else
1423 numer += denom;
1424
1425 // Now fall into our switch statement if there are more bits to compute
1426 if (bits > 0)
1427 {
1428 // make room for the rest of the answer bits
1429 result <<= bits;
1430 switch (bits) {
1431 case 6:
1432 if ((numer = (numer << 1) - denom) >= 0)
1433 result |= 32;
1434 else
1435 numer += denom;
1436 case 5:
1437 if ((numer = (numer << 1) - denom) >= 0)
1438 result |= 16;
1439 else
1440 numer += denom;
1441 case 4:
1442 if ((numer = (numer << 1) - denom) >= 0)
1443 result |= 8;
1444 else
1445 numer += denom;
1446 case 3:
1447 if ((numer = (numer << 1) - denom) >= 0)
1448 result |= 4;
1449 else
1450 numer += denom;
1451 case 2:
1452 if ((numer = (numer << 1) - denom) >= 0)
1453 result |= 2;
1454 else
1455 numer += denom;
1456 case 1:
1457 default: // not strictly need, but makes GCC make better ARM code
1458 if ((numer = (numer << 1) - denom) >= 0)
1459 result |= 1;
1460 else
1461 numer += denom;
1462 }
1463 }
1464 return result;
1465}
1466
1467// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1468static unsigned atan_0_90(SkFixed y, SkFixed x)
1469{
1470#ifdef SK_DEBUG
1471 {
1472 static bool gOnce;
1473 if (!gOnce)
1474 {
1475 gOnce = true;
1476 SkASSERT(div_64(55, 55) == 64);
1477 SkASSERT(div_64(128, 256) == 32);
1478 SkASSERT(div_64(2326528, 4685824) == 31);
1479 SkASSERT(div_64(753664, 5210112) == 9);
1480 SkASSERT(div_64(229376, 4882432) == 3);
1481 SkASSERT(div_64(2, 64) == 2);
1482 SkASSERT(div_64(1, 64) == 1);
1483 // test that we handle underflow correctly
1484 SkASSERT(div_64(12345, 0x54321234) == 0);
1485 }
1486 }
1487#endif
1488
1489 SkASSERT(y > 0 && x > 0);
1490 const uint8_t* table = build_sweep_table();
1491
1492 unsigned result;
1493 bool swap = (x < y);
1494 if (swap)
1495 {
1496 // first part of the atan(v) = PI/2 - atan(1/v) identity
1497 // since our div_64 and table want v <= 1, where v = y/x
1498 SkTSwap<SkFixed>(x, y);
1499 }
1500
1501 result = div_64(y, x);
1502
1503#ifdef SK_DEBUG
1504 {
1505 unsigned result2 = SkDivBits(y, x, 6);
1506 SkASSERT(result2 == result ||
1507 (result == 1 && result2 == 0));
1508 }
1509#endif
1510
1511 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1512 result = table[result];
1513
1514 if (swap)
1515 {
1516 // complete the atan(v) = PI/2 - atan(1/v) identity
1517 result = 64 - result;
1518 // pin to 63
1519 result -= result >> 6;
1520 }
1521
1522 SkASSERT(result <= 63);
1523 return result;
1524}
1525
1526// returns angle in a circle [0..2PI) -> [0..255]
1527static unsigned SkATan2_255(SkFixed y, SkFixed x)
1528{
1529 if (x == 0)
1530 {
1531 if (y == 0)
1532 return 0;
1533 return y < 0 ? 192 : 64;
1534 }
1535 if (y == 0)
1536 return x < 0 ? 128 : 0;
1537
1538 /* Find the right quadrant for x,y
1539 Since atan_0_90 only handles the first quadrant, we rotate x,y
1540 appropriately before calling it, and then add the right amount
1541 to account for the real quadrant.
1542 quadrant 0 : add 0 | x > 0 && y > 0
1543 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1544 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1545 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1546
1547 map x<0 to (1 << 6)
1548 map y<0 to (3 << 6)
1549 add = map_x ^ map_y
1550 */
1551 int xsign = x >> 31;
1552 int ysign = y >> 31;
1553 int add = ((-xsign) ^ (ysign & 3)) << 6;
1554
1555#ifdef SK_DEBUG
1556 if (0 == add)
1557 SkASSERT(x > 0 && y > 0);
1558 else if (64 == add)
1559 SkASSERT(x < 0 && y > 0);
1560 else if (128 == add)
1561 SkASSERT(x < 0 && y < 0);
1562 else if (192 == add)
1563 SkASSERT(x > 0 && y < 0);
1564 else
1565 SkASSERT(!"bad value for add");
1566#endif
1567
1568 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1569 where we need to rotate x,y by 90 or -90
1570 */
1571 x = (x ^ xsign) - xsign;
1572 y = (y ^ ysign) - ysign;
1573 if (add & 64) // quads 1 or 3 need to swap x,y
1574 SkTSwap<SkFixed>(x, y);
1575
1576 unsigned result = add + atan_0_90(y, x);
1577 SkASSERT(result < 256);
1578 return result;
1579}
1580
1581void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1582{
1583 SkMatrix::MapXYProc proc = fDstToIndexProc;
1584 const SkMatrix& matrix = fDstToIndex;
1585 const SkPMColor* cache = this->getCache32();
1586 SkPoint srcPt;
1587
1588 if (fDstToIndexClass != kPerspective_MatrixClass)
1589 {
1590 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1591 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1592 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1593 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1594
1595 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1596 {
1597 SkFixed storage[2];
1598 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1599 &storage[0], &storage[1]);
1600 dx = storage[0];
1601 dy = storage[1];
1602 }
1603 else
1604 {
1605 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1606 dx = SkScalarToFixed(matrix.getScaleX());
1607 dy = SkScalarToFixed(matrix.getSkewY());
1608 }
1609
1610 for (; count > 0; --count)
1611 {
1612 *dstC++ = cache[SkATan2_255(fy, fx)];
1613 fx += dx;
1614 fy += dy;
1615 }
1616 }
1617 else // perspective case
1618 {
1619 for (int stop = x + count; x < stop; x++)
1620 {
1621 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1622 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1623
1624 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1625 SkScalarToFixed(srcPt.fX));
1626 *dstC++ = cache[index];
1627 }
1628 }
1629}
1630
1631void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1632{
1633 SkMatrix::MapXYProc proc = fDstToIndexProc;
1634 const SkMatrix& matrix = fDstToIndex;
1635 const uint16_t* cache = this->getCache16();
1636 int toggle = ((x ^ y) & 1) << kCache16Bits;
1637 SkPoint srcPt;
1638
1639 if (fDstToIndexClass != kPerspective_MatrixClass)
1640 {
1641 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1642 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1643 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1644 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1645
1646 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1647 {
1648 SkFixed storage[2];
1649 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1650 &storage[0], &storage[1]);
1651 dx = storage[0];
1652 dy = storage[1];
1653 }
1654 else
1655 {
1656 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1657 dx = SkScalarToFixed(matrix.getScaleX());
1658 dy = SkScalarToFixed(matrix.getSkewY());
1659 }
1660
1661 for (; count > 0; --count)
1662 {
1663 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1664 *dstC++ = cache[toggle + index];
1665 toggle ^= (1 << kCache16Bits);
1666 fx += dx;
1667 fy += dy;
1668 }
1669 }
1670 else // perspective case
1671 {
1672 for (int stop = x + count; x < stop; x++)
1673 {
1674 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1675 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1676
1677 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1678 SkScalarToFixed(srcPt.fX));
1679 index >>= (8 - kCache16Bits);
1680 *dstC++ = cache[toggle + index];
1681 toggle ^= (1 << kCache16Bits);
1682 }
1683 }
1684}
1685
1686///////////////////////////////////////////////////////////////////////////
1687///////////////////////////////////////////////////////////////////////////
1688
1689// assumes colors is SkColor* and pos is SkScalar*
1690#define EXPAND_1_COLOR(count) \
1691 SkColor tmp[2]; \
1692 do { \
1693 if (1 == count) { \
1694 tmp[0] = tmp[1] = colors[0]; \
1695 colors = tmp; \
1696 pos = NULL; \
1697 count = 2; \
1698 } \
1699 } while (0)
1700
1701SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1702 const SkColor colors[], const SkScalar pos[], int colorCount,
1703 SkShader::TileMode mode, SkUnitMapper* mapper)
1704{
1705 if (NULL == pts || NULL == colors || colorCount < 1) {
1706 return NULL;
1707 }
1708 EXPAND_1_COLOR(colorCount);
1709
reed@android.comab840b82009-07-01 17:00:03 +00001710 return SkNEW_ARGS(Linear_Gradient,
1711 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712}
1713
1714SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1715 const SkColor colors[], const SkScalar pos[], int colorCount,
1716 SkShader::TileMode mode, SkUnitMapper* mapper)
1717{
1718 if (radius <= 0 || NULL == colors || colorCount < 1) {
1719 return NULL;
1720 }
1721 EXPAND_1_COLOR(colorCount);
1722
reed@android.comab840b82009-07-01 17:00:03 +00001723 return SkNEW_ARGS(Radial_Gradient,
1724 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725}
1726
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001727SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
1728 SkScalar startRadius,
1729 const SkPoint& end,
1730 SkScalar endRadius,
1731 const SkColor colors[],
1732 const SkScalar pos[],
1733 int colorCount,
1734 SkShader::TileMode mode,
1735 SkUnitMapper* mapper)
1736{
1737 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
1738 return NULL;
1739 }
1740 EXPAND_1_COLOR(colorCount);
1741
1742 return SkNEW_ARGS(Two_Point_Radial_Gradient,
1743 (start, startRadius, end, endRadius, colors, pos, colorCount, mode, mapper));
1744}
1745
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1747 const SkColor colors[],
1748 const SkScalar pos[],
1749 int count, SkUnitMapper* mapper)
1750{
1751 if (NULL == colors || count < 1) {
1752 return NULL;
1753 }
1754 EXPAND_1_COLOR(count);
1755
1756 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1757}
1758
1759static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1760 Linear_Gradient::CreateProc);
1761
1762static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1763 Radial_Gradient::CreateProc);
1764
1765static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1766 Sweep_Gradient::CreateProc);
1767