blob: 780ad18977b1633a565bca8802f5b1147cf036d0 [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 {
reed@android.com6c59a172009-09-22 20:24:05 +00001299 SkScalar dstX = SkIntToScalar(x);
1300 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001301 for (; count > 0; --count) {
1302 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001303 dstProc(fDstToIndex, dstX, dstY, &srcPt);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001304 SkFixed fx = SkScalarToFixed(srcPt.fX);
1305 SkFixed fy = SkScalarToFixed(srcPt.fY);
1306 SkFixed b = (SkFixedMul(diffx, fx) +
1307 SkFixedMul(diffy, fy) - startRadius) << 1;
1308 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1309 SkFixed index = proc(t);
1310 SkASSERT(index <= 0xFFFF);
1311 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001312 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001313 }
1314 }
1315 }
1316
reed@android.com6c59a172009-09-22 20:24:05 +00001317 virtual bool setContext(const SkBitmap& device,
1318 const SkPaint& paint,
1319 const SkMatrix& matrix) {
1320 if (!this->INHERITED::setContext(device, paint, matrix)) {
1321 return false;
1322 }
1323
1324 // we don't have a span16 proc
1325 fFlags &= ~kHasSpan16_Flag;
1326 return true;
1327 }
1328
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001329 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1330 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1331 }
1332
reed@android.combcfc7332009-11-10 16:19:39 +00001333 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1334 this->INHERITED::flatten(buffer);
1335 buffer.writeScalar(fDiff.fX);
1336 buffer.writeScalar(fDiff.fY);
1337 buffer.writeScalar(fStartRadius);
1338 buffer.writeScalar(fDiffRadius);
1339 buffer.writeScalar(fSr2D2);
1340 buffer.writeScalar(fA);
1341 buffer.writeScalar(fOneOverTwoA);
1342 }
1343
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001344protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001345 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
1346 : Gradient_Shader(buffer) {
1347 fDiff.fX = buffer.readScalar();
1348 fDiff.fY = buffer.readScalar();
1349 fStartRadius = buffer.readScalar();
1350 fDiffRadius = buffer.readScalar();
1351 fSr2D2 = buffer.readScalar();
1352 fA = buffer.readScalar();
1353 fOneOverTwoA = buffer.readScalar();
1354 };
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001355 virtual Factory getFactory() { return CreateProc; }
1356 virtual void onCacheReset() {}
1357
1358private:
1359 typedef Gradient_Shader INHERITED;
1360 SkPoint fDiff;
1361 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
1362};
1363
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364///////////////////////////////////////////////////////////////////////////////
1365
1366class Sweep_Gradient : public Gradient_Shader {
1367public:
1368 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1369 const SkScalar pos[], int count, SkUnitMapper* mapper)
1370 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1371 {
1372 fPtsToUnit.setTranslate(-cx, -cy);
1373 }
1374 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1375 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1376
1377 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1378 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1379 }
1380
1381protected:
1382 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001384 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385
1386private:
1387 typedef Gradient_Shader INHERITED;
1388};
1389
1390#ifdef COMPUTE_SWEEP_TABLE
1391#define PI 3.14159265
1392static bool gSweepTableReady;
1393static uint8_t gSweepTable[65];
1394
1395/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1396 We scale the results to [0..32]
1397*/
1398static const uint8_t* build_sweep_table()
1399{
1400 if (!gSweepTableReady)
1401 {
1402 const int N = 65;
1403 const double DENOM = N - 1;
1404
1405 for (int i = 0; i < N; i++)
1406 {
1407 double arg = i / DENOM;
1408 double v = atan(arg);
1409 int iv = (int)round(v * DENOM * 2 / PI);
1410// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1411 printf("%d, ", iv);
1412 gSweepTable[i] = iv;
1413 }
1414 gSweepTableReady = true;
1415 }
1416 return gSweepTable;
1417}
1418#else
1419static const uint8_t gSweepTable[] = {
1420 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1421 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1422 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1423 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1424 32
1425};
1426static const uint8_t* build_sweep_table() { return gSweepTable; }
1427#endif
1428
1429// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1430// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1431// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1432
1433//unsigned div_64(int numer, int denom);
1434static unsigned div_64(int numer, int denom)
1435{
1436 SkASSERT(numer <= denom);
1437 SkASSERT(numer > 0);
1438 SkASSERT(denom > 0);
1439
1440 int nbits = SkCLZ(numer);
1441 int dbits = SkCLZ(denom);
1442 int bits = 6 - nbits + dbits;
1443 SkASSERT(bits <= 6);
1444
1445 if (bits < 0) // detect underflow
1446 return 0;
1447
1448 denom <<= dbits - 1;
1449 numer <<= nbits - 1;
1450
1451 unsigned result = 0;
1452
1453 // do the first one
1454 if ((numer -= denom) >= 0)
1455 result = 1;
1456 else
1457 numer += denom;
1458
1459 // Now fall into our switch statement if there are more bits to compute
1460 if (bits > 0)
1461 {
1462 // make room for the rest of the answer bits
1463 result <<= bits;
1464 switch (bits) {
1465 case 6:
1466 if ((numer = (numer << 1) - denom) >= 0)
1467 result |= 32;
1468 else
1469 numer += denom;
1470 case 5:
1471 if ((numer = (numer << 1) - denom) >= 0)
1472 result |= 16;
1473 else
1474 numer += denom;
1475 case 4:
1476 if ((numer = (numer << 1) - denom) >= 0)
1477 result |= 8;
1478 else
1479 numer += denom;
1480 case 3:
1481 if ((numer = (numer << 1) - denom) >= 0)
1482 result |= 4;
1483 else
1484 numer += denom;
1485 case 2:
1486 if ((numer = (numer << 1) - denom) >= 0)
1487 result |= 2;
1488 else
1489 numer += denom;
1490 case 1:
1491 default: // not strictly need, but makes GCC make better ARM code
1492 if ((numer = (numer << 1) - denom) >= 0)
1493 result |= 1;
1494 else
1495 numer += denom;
1496 }
1497 }
1498 return result;
1499}
1500
1501// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1502static unsigned atan_0_90(SkFixed y, SkFixed x)
1503{
1504#ifdef SK_DEBUG
1505 {
1506 static bool gOnce;
1507 if (!gOnce)
1508 {
1509 gOnce = true;
1510 SkASSERT(div_64(55, 55) == 64);
1511 SkASSERT(div_64(128, 256) == 32);
1512 SkASSERT(div_64(2326528, 4685824) == 31);
1513 SkASSERT(div_64(753664, 5210112) == 9);
1514 SkASSERT(div_64(229376, 4882432) == 3);
1515 SkASSERT(div_64(2, 64) == 2);
1516 SkASSERT(div_64(1, 64) == 1);
1517 // test that we handle underflow correctly
1518 SkASSERT(div_64(12345, 0x54321234) == 0);
1519 }
1520 }
1521#endif
1522
1523 SkASSERT(y > 0 && x > 0);
1524 const uint8_t* table = build_sweep_table();
1525
1526 unsigned result;
1527 bool swap = (x < y);
1528 if (swap)
1529 {
1530 // first part of the atan(v) = PI/2 - atan(1/v) identity
1531 // since our div_64 and table want v <= 1, where v = y/x
1532 SkTSwap<SkFixed>(x, y);
1533 }
1534
1535 result = div_64(y, x);
1536
1537#ifdef SK_DEBUG
1538 {
1539 unsigned result2 = SkDivBits(y, x, 6);
1540 SkASSERT(result2 == result ||
1541 (result == 1 && result2 == 0));
1542 }
1543#endif
1544
1545 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1546 result = table[result];
1547
1548 if (swap)
1549 {
1550 // complete the atan(v) = PI/2 - atan(1/v) identity
1551 result = 64 - result;
1552 // pin to 63
1553 result -= result >> 6;
1554 }
1555
1556 SkASSERT(result <= 63);
1557 return result;
1558}
1559
1560// returns angle in a circle [0..2PI) -> [0..255]
1561static unsigned SkATan2_255(SkFixed y, SkFixed x)
1562{
1563 if (x == 0)
1564 {
1565 if (y == 0)
1566 return 0;
1567 return y < 0 ? 192 : 64;
1568 }
1569 if (y == 0)
1570 return x < 0 ? 128 : 0;
1571
1572 /* Find the right quadrant for x,y
1573 Since atan_0_90 only handles the first quadrant, we rotate x,y
1574 appropriately before calling it, and then add the right amount
1575 to account for the real quadrant.
1576 quadrant 0 : add 0 | x > 0 && y > 0
1577 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1578 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1579 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1580
1581 map x<0 to (1 << 6)
1582 map y<0 to (3 << 6)
1583 add = map_x ^ map_y
1584 */
1585 int xsign = x >> 31;
1586 int ysign = y >> 31;
1587 int add = ((-xsign) ^ (ysign & 3)) << 6;
1588
1589#ifdef SK_DEBUG
1590 if (0 == add)
1591 SkASSERT(x > 0 && y > 0);
1592 else if (64 == add)
1593 SkASSERT(x < 0 && y > 0);
1594 else if (128 == add)
1595 SkASSERT(x < 0 && y < 0);
1596 else if (192 == add)
1597 SkASSERT(x > 0 && y < 0);
1598 else
1599 SkASSERT(!"bad value for add");
1600#endif
1601
1602 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1603 where we need to rotate x,y by 90 or -90
1604 */
1605 x = (x ^ xsign) - xsign;
1606 y = (y ^ ysign) - ysign;
1607 if (add & 64) // quads 1 or 3 need to swap x,y
1608 SkTSwap<SkFixed>(x, y);
1609
1610 unsigned result = add + atan_0_90(y, x);
1611 SkASSERT(result < 256);
1612 return result;
1613}
1614
1615void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1616{
1617 SkMatrix::MapXYProc proc = fDstToIndexProc;
1618 const SkMatrix& matrix = fDstToIndex;
1619 const SkPMColor* cache = this->getCache32();
1620 SkPoint srcPt;
1621
1622 if (fDstToIndexClass != kPerspective_MatrixClass)
1623 {
1624 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1625 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1626 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1627 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1628
1629 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1630 {
1631 SkFixed storage[2];
1632 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1633 &storage[0], &storage[1]);
1634 dx = storage[0];
1635 dy = storage[1];
1636 }
1637 else
1638 {
1639 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1640 dx = SkScalarToFixed(matrix.getScaleX());
1641 dy = SkScalarToFixed(matrix.getSkewY());
1642 }
1643
1644 for (; count > 0; --count)
1645 {
1646 *dstC++ = cache[SkATan2_255(fy, fx)];
1647 fx += dx;
1648 fy += dy;
1649 }
1650 }
1651 else // perspective case
1652 {
1653 for (int stop = x + count; x < stop; x++)
1654 {
1655 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1656 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1657
1658 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1659 SkScalarToFixed(srcPt.fX));
1660 *dstC++ = cache[index];
1661 }
1662 }
1663}
1664
1665void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1666{
1667 SkMatrix::MapXYProc proc = fDstToIndexProc;
1668 const SkMatrix& matrix = fDstToIndex;
1669 const uint16_t* cache = this->getCache16();
1670 int toggle = ((x ^ y) & 1) << kCache16Bits;
1671 SkPoint srcPt;
1672
1673 if (fDstToIndexClass != kPerspective_MatrixClass)
1674 {
1675 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1676 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1677 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1678 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1679
1680 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1681 {
1682 SkFixed storage[2];
1683 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1684 &storage[0], &storage[1]);
1685 dx = storage[0];
1686 dy = storage[1];
1687 }
1688 else
1689 {
1690 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1691 dx = SkScalarToFixed(matrix.getScaleX());
1692 dy = SkScalarToFixed(matrix.getSkewY());
1693 }
1694
1695 for (; count > 0; --count)
1696 {
1697 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1698 *dstC++ = cache[toggle + index];
1699 toggle ^= (1 << kCache16Bits);
1700 fx += dx;
1701 fy += dy;
1702 }
1703 }
1704 else // perspective case
1705 {
1706 for (int stop = x + count; x < stop; x++)
1707 {
1708 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1709 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1710
1711 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1712 SkScalarToFixed(srcPt.fX));
1713 index >>= (8 - kCache16Bits);
1714 *dstC++ = cache[toggle + index];
1715 toggle ^= (1 << kCache16Bits);
1716 }
1717 }
1718}
1719
1720///////////////////////////////////////////////////////////////////////////
1721///////////////////////////////////////////////////////////////////////////
1722
1723// assumes colors is SkColor* and pos is SkScalar*
1724#define EXPAND_1_COLOR(count) \
1725 SkColor tmp[2]; \
1726 do { \
1727 if (1 == count) { \
1728 tmp[0] = tmp[1] = colors[0]; \
1729 colors = tmp; \
1730 pos = NULL; \
1731 count = 2; \
1732 } \
1733 } while (0)
1734
1735SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1736 const SkColor colors[], const SkScalar pos[], int colorCount,
1737 SkShader::TileMode mode, SkUnitMapper* mapper)
1738{
1739 if (NULL == pts || NULL == colors || colorCount < 1) {
1740 return NULL;
1741 }
1742 EXPAND_1_COLOR(colorCount);
1743
reed@android.comab840b82009-07-01 17:00:03 +00001744 return SkNEW_ARGS(Linear_Gradient,
1745 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746}
1747
1748SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1749 const SkColor colors[], const SkScalar pos[], int colorCount,
1750 SkShader::TileMode mode, SkUnitMapper* mapper)
1751{
1752 if (radius <= 0 || NULL == colors || colorCount < 1) {
1753 return NULL;
1754 }
1755 EXPAND_1_COLOR(colorCount);
1756
reed@android.comab840b82009-07-01 17:00:03 +00001757 return SkNEW_ARGS(Radial_Gradient,
1758 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759}
1760
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001761SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
1762 SkScalar startRadius,
1763 const SkPoint& end,
1764 SkScalar endRadius,
1765 const SkColor colors[],
1766 const SkScalar pos[],
1767 int colorCount,
1768 SkShader::TileMode mode,
1769 SkUnitMapper* mapper)
1770{
1771 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
1772 return NULL;
1773 }
1774 EXPAND_1_COLOR(colorCount);
1775
1776 return SkNEW_ARGS(Two_Point_Radial_Gradient,
1777 (start, startRadius, end, endRadius, colors, pos, colorCount, mode, mapper));
1778}
1779
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1781 const SkColor colors[],
1782 const SkScalar pos[],
1783 int count, SkUnitMapper* mapper)
1784{
1785 if (NULL == colors || count < 1) {
1786 return NULL;
1787 }
1788 EXPAND_1_COLOR(count);
1789
1790 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1791}
1792
1793static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1794 Linear_Gradient::CreateProc);
1795
1796static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1797 Radial_Gradient::CreateProc);
1798
1799static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1800 Sweep_Gradient::CreateProc);
1801