blob: cabce20fc2493244f327066112378061350c1ebb [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"
reed@google.comdc731fd2010-12-23 15:19:47 +000020#include "SkMallocPixelRef.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkUnitMapper.h"
22#include "SkUtils.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000023#include "SkTemplates.h"
24#include "SkBitmapCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025
reed@android.com8a1c16f2008-12-17 15:59:43 +000026///////////////////////////////////////////////////////////////////////////
27
28typedef SkFixed (*TileProc)(SkFixed);
29
reed@android.com41bccf52009-04-03 13:33:51 +000030static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 return SkClampMax(x, 0xFFFF);
32}
33
reed@android.com41bccf52009-04-03 13:33:51 +000034static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000035 return x & 0xFFFF;
36}
37
reed@android.com41bccf52009-04-03 13:33:51 +000038static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 int s = x << 15 >> 31;
40 return (x ^ s) & 0xFFFF;
41}
42
43static const TileProc gTileProcs[] = {
44 clamp_tileproc,
45 repeat_tileproc,
46 mirror_tileproc
47};
48
49//////////////////////////////////////////////////////////////////////////////
50
reed@android.com200645d2009-12-14 16:41:57 +000051static inline int repeat_bits(int x, const int bits) {
52 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000053}
54
reed@android.com200645d2009-12-14 16:41:57 +000055static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000056#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000057 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000059 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000060#else
reed@android.com200645d2009-12-14 16:41:57 +000061 int s = x << (31 - bits) >> 31;
62 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000063#endif
64}
65
reed@android.com41bccf52009-04-03 13:33:51 +000066static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 return x & 0xFF;
68}
69
reed@android.com41bccf52009-04-03 13:33:51 +000070static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000071#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000072 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000074 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 return x & 255;
76#else
77 int s = x << 23 >> 31;
78 return (x ^ s) & 0xFF;
79#endif
80}
81
82//////////////////////////////////////////////////////////////////////////////
83
84class Gradient_Shader : public SkShader {
85public:
86 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +000087 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 virtual ~Gradient_Shader();
89
90 // overrides
91 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
92 virtual uint32_t getFlags() { return fFlags; }
93
94protected:
95 Gradient_Shader(SkFlattenableReadBuffer& );
96 SkUnitMapper* fMapper;
97 SkMatrix fPtsToUnit; // set by subclass
98 SkMatrix fDstToIndex;
99 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 TileMode fTileMode;
101 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000102 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 uint8_t fDstToIndexClass;
104 uint8_t fFlags;
105
106 struct Rec {
107 SkFixed fPos; // 0...1
108 uint32_t fScale; // (1 << 24) / range
109 };
110 Rec* fRecs;
111
112 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000113 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000115 kCache16Mask = kCache16Count - 1,
116 kCache16Shift = 16 - kCache16Bits,
117
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 kCache32Bits = 8, // pretty much should always be 8
119 kCache32Count = 1 << kCache32Bits
120 };
121 virtual void flatten(SkFlattenableWriteBuffer& );
122 const uint16_t* getCache16();
123 const SkPMColor* getCache32();
124
reed@google.comdc731fd2010-12-23 15:19:47 +0000125 SkMallocPixelRef* fCache32PixelRef;
126
127 void commonAsABitmap(SkBitmap*);
reed@android.com9b46e772009-06-05 12:24:41 +0000128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129private:
130 enum {
131 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
132
reed@android.com1c12abe2009-07-02 15:01:02 +0000133 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 };
135 SkColor fStorage[(kStorageSize + 3) >> 2];
136 SkColor* fOrigColors;
137
138 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
139 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
140
141 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
143
reed@android.com512a8762009-12-14 15:25:36 +0000144 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
145
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 typedef SkShader INHERITED;
147};
148
reed@android.com41bccf52009-04-03 13:33:51 +0000149static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 SkASSERT(x >= 0 && x <= SK_Scalar1);
151
152#ifdef SK_SCALAR_IS_FLOAT
153 return (unsigned)(x * 0xFFFF);
154#else
155 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
156#endif
157}
158
reed@android.com41bccf52009-04-03 13:33:51 +0000159Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
160 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 SkASSERT(colorCount > 1);
162
163 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
164
165 fMapper = mapper;
166 mapper->safeRef();
167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
169 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
170 fTileMode = mode;
171 fTileProc = gTileProcs[mode];
reed@android.com41bccf52009-04-03 13:33:51 +0000172
173 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000174 fCache32 = NULL;
175 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
reed@android.com41bccf52009-04-03 13:33:51 +0000177 /* Note: we let the caller skip the first and/or last position.
178 i.e. pos[0] = 0.3, pos[1] = 0.7
179 In these cases, we insert dummy entries to ensure that the final data
180 will be bracketed by [0, 1].
181 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
182
183 Thus colorCount (the caller's value, and fColorCount (our value) may
184 differ by up to 2. In the above example:
185 colorCount = 2
186 fColorCount = 4
187 */
188 fColorCount = colorCount;
189 // check if we need to add in dummy start and/or end position/colors
190 bool dummyFirst = false;
191 bool dummyLast = false;
192 if (pos) {
193 dummyFirst = pos[0] != 0;
194 dummyLast = pos[colorCount - 1] != SK_Scalar1;
195 fColorCount += dummyFirst + dummyLast;
196 }
197
198 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000199 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000200 fOrigColors = reinterpret_cast<SkColor*>(
201 sk_malloc_throw(size * fColorCount));
202 }
203 else {
204 fOrigColors = fStorage;
205 }
206
207 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 {
reed@android.com41bccf52009-04-03 13:33:51 +0000209 SkColor* origColors = fOrigColors;
210 if (dummyFirst) {
211 *origColors++ = colors[0];
212 }
213 memcpy(origColors, colors, colorCount * sizeof(SkColor));
214 if (dummyLast) {
215 origColors += colorCount;
216 *origColors = colors[colorCount - 1];
217 }
218 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219
reed@android.com1c12abe2009-07-02 15:01:02 +0000220 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000221 if (fColorCount > 2) {
222 Rec* recs = fRecs;
223 recs->fPos = 0;
224 // recs->fScale = 0; // unused;
225 recs += 1;
226 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 /* We need to convert the user's array of relative positions into
228 fixed-point positions and scale factors. We need these results
229 to be strictly monotonic (no two values equal or out of order).
230 Hence this complex loop that just jams a zero for the scale
231 value if it sees a segment out of order, and it assures that
232 we start at 0 and end at 1.0
233 */
234 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000235 int startIndex = dummyFirst ? 0 : 1;
236 int count = colorCount + dummyLast;
237 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 // force the last value to be 1.0
239 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000240 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000242 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
reed@android.com41bccf52009-04-03 13:33:51 +0000245 // pin curr withing range
246 if (curr < 0) {
247 curr = 0;
248 } else if (curr > SK_Fixed1) {
249 curr = SK_Fixed1;
250 }
251 recs->fPos = curr;
252 if (curr > prev) {
253 recs->fScale = (1 << 24) / (curr - prev);
254 } else {
255 recs->fScale = 0; // ignore this segment
256 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 // get ready for the next value
258 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000259 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 }
reed@android.com41bccf52009-04-03 13:33:51 +0000261 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 SkFixed dp = SK_Fixed1 / (colorCount - 1);
263 SkFixed p = dp;
264 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000265 for (int i = 1; i < colorCount; i++) {
266 recs->fPos = p;
267 recs->fScale = scale;
268 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 p += dp;
270 }
271 }
272 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000273 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274}
275
276Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000277 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 fCacheAlpha = 256;
279
280 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
281
282 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000283 fCache32 = NULL;
284 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285
reed@android.com41bccf52009-04-03 13:33:51 +0000286 int colorCount = fColorCount = buffer.readU32();
287 if (colorCount > kColorStorageCount) {
288 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
289 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
290 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294
295 fTileMode = (TileMode)buffer.readU8();
296 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000297 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 if (colorCount > 2) {
299 Rec* recs = fRecs;
300 recs[0].fPos = 0;
301 for (int i = 1; i < colorCount; i++) {
302 recs[i].fPos = buffer.readS32();
303 recs[i].fScale = buffer.readU32();
304 }
305 }
306 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000307 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308}
309
reed@android.com41bccf52009-04-03 13:33:51 +0000310Gradient_Shader::~Gradient_Shader() {
311 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000313 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000314 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000315 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000317 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 fMapper->safeUnref();
319}
320
reed@android.com41bccf52009-04-03 13:33:51 +0000321void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 this->INHERITED::flatten(buffer);
323 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000324 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
326 buffer.write8(fTileMode);
327 if (fColorCount > 2) {
328 Rec* recs = fRecs;
329 for (int i = 1; i < fColorCount; i++) {
330 buffer.write32(recs[i].fPos);
331 buffer.write32(recs[i].fScale);
332 }
333 }
334 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
335}
336
337bool Gradient_Shader::setContext(const SkBitmap& device,
338 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000339 const SkMatrix& matrix) {
340 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000342 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343
344 const SkMatrix& inverse = this->getTotalInverse();
345
346 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
347 return false;
348 }
349
350 fDstToIndexProc = fDstToIndex.getMapXYProc();
351 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
352
353 // now convert our colors in to PMColors
354 unsigned paintAlpha = this->getPaintAlpha();
355 unsigned colorAlpha = 0xFF;
356
reed@android.com3d06a8c2009-07-07 18:19:59 +0000357 // FIXME: record colorAlpha in constructor, since this is not affected
358 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000359 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 SkColor src = fOrigColors[i];
361 unsigned sa = SkColorGetA(src);
362 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 }
364
365 fFlags = this->INHERITED::getFlags();
366 if ((colorAlpha & paintAlpha) == 0xFF) {
367 fFlags |= kOpaqueAlpha_Flag;
368 }
369 // we can do span16 as long as our individual colors are opaque,
370 // regardless of the paint's alpha
371 if (0xFF == colorAlpha) {
372 fFlags |= kHasSpan16_Flag;
373 }
374
375 // if the new alpha differs from the previous time we were called, inval our cache
376 // this will trigger the cache to be rebuilt.
377 // we don't care about the first time, since the cache ptrs will already be NULL
378 if (fCacheAlpha != paintAlpha) {
379 fCache16 = NULL; // inval the cache
380 fCache32 = NULL; // inval the cache
381 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000382 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000383 if (fCache32PixelRef) {
384 fCache32PixelRef->notifyPixelsChanged();
385 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386 }
387 return true;
388}
389
reed@android.com41bccf52009-04-03 13:33:51 +0000390static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 SkASSERT(a == SkToU8(a));
392 SkASSERT(b == SkToU8(b));
393 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 return a + ((b - a) * scale >> 8);
395}
396
reed@android.com41bccf52009-04-03 13:33:51 +0000397static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
398 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399#if 0
400 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
401 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
402 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
403 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
404
405 return SkPackARGB32(a, r, g, b);
406#else
407 int otherBlend = 256 - blend;
408
409#if 0
410 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
411 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
412 SkASSERT((t0 & t1) == 0);
413 return t0 | t1;
414#else
415 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
416 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
417#endif
418
419#endif
420}
421
422#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
423
reed@android.com41bccf52009-04-03 13:33:51 +0000424/** We take the original colors, not our premultiplied PMColors, since we can
425 build a 16bit table as long as the original colors are opaque, even if the
426 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427*/
reed@android.com512a8762009-12-14 15:25:36 +0000428void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
429 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 SkASSERT(count > 1);
431 SkASSERT(SkColorGetA(c0) == 0xFF);
432 SkASSERT(SkColorGetA(c1) == 0xFF);
433
434 SkFixed r = SkColorGetR(c0);
435 SkFixed g = SkColorGetG(c0);
436 SkFixed b = SkColorGetB(c0);
437
438 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
439 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
440 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
441
442 r = SkIntToFixed(r) + 0x8000;
443 g = SkIntToFixed(g) + 0x8000;
444 b = SkIntToFixed(b) + 0x8000;
445
446 do {
447 unsigned rr = r >> 16;
448 unsigned gg = g >> 16;
449 unsigned bb = b >> 16;
450 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000451 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 cache += 1;
453 r += dr;
454 g += dg;
455 b += db;
456 } while (--count != 0);
457}
458
reed@android.com1c12abe2009-07-02 15:01:02 +0000459static void build_32bit_cache(SkPMColor cache[], SkColor c0, SkColor c1,
460 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 SkASSERT(count > 1);
462
reed@android.com1c12abe2009-07-02 15:01:02 +0000463 // need to apply paintAlpha to our two endpoints
464 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
465 SkFixed da;
466 {
467 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
468 da = SkIntToFixed(tmp - a) / (count - 1);
469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470
reed@android.com1c12abe2009-07-02 15:01:02 +0000471 SkFixed r = SkColorGetR(c0);
472 SkFixed g = SkColorGetG(c0);
473 SkFixed b = SkColorGetB(c0);
474 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
475 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
476 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477
478 a = SkIntToFixed(a) + 0x8000;
479 r = SkIntToFixed(r) + 0x8000;
480 g = SkIntToFixed(g) + 0x8000;
481 b = SkIntToFixed(b) + 0x8000;
482
483 do {
reed@android.com1c12abe2009-07-02 15:01:02 +0000484 *cache++ = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 a += da;
486 r += dr;
487 g += dg;
488 b += db;
489 } while (--count != 0);
490}
491
reed@android.com41bccf52009-04-03 13:33:51 +0000492static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 SkASSERT((unsigned)x <= SK_Fixed1);
494 return x - (x >> 16);
495}
496
reed@android.com200645d2009-12-14 16:41:57 +0000497static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000498 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000499 if (6 == bits) {
500 return (x << 10) | (x << 4) | (x >> 2);
501 }
502 if (8 == bits) {
503 return (x << 8) | x;
504 }
505 sk_throw();
506 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507}
508
reed@android.com41bccf52009-04-03 13:33:51 +0000509const uint16_t* Gradient_Shader::getCache16() {
510 if (fCache16 == NULL) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000511 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000513 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000515 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000516 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000517 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 Rec* rec = fRecs;
519 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000520 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000521 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 SkASSERT(nextIndex < kCache16Count);
523
524 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000525 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 prevIndex = nextIndex;
527 }
528 SkASSERT(prevIndex == kCache16Count - 1);
529 }
530
reed@android.com41bccf52009-04-03 13:33:51 +0000531 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 uint16_t* linear = fCache16; // just computed linear data
534 uint16_t* mapped = fCache16Storage; // storage for mapped data
535 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000536 for (int i = 0; i < kCache16Count; i++) {
537 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000539 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 }
541 sk_free(fCache16);
542 fCache16 = fCache16Storage;
543 }
544 }
545 return fCache16;
546}
547
reed@android.com41bccf52009-04-03 13:33:51 +0000548const SkPMColor* Gradient_Shader::getCache32() {
549 if (fCache32 == NULL) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000550 if (NULL == fCache32PixelRef) {
551 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef, (NULL,
552 sizeof(SkPMColor) * kCache32Count,
553 NULL));
554 }
555 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000556 if (fColorCount == 2) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000557 build_32bit_cache(fCache32, fOrigColors[0], fOrigColors[1],
558 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000559 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 Rec* rec = fRecs;
561 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000562 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
564 SkASSERT(nextIndex < kCache32Count);
565
566 if (nextIndex > prevIndex)
reed@android.com1c12abe2009-07-02 15:01:02 +0000567 build_32bit_cache(fCache32 + prevIndex, fOrigColors[i-1],
568 fOrigColors[i],
569 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 prevIndex = nextIndex;
571 }
572 SkASSERT(prevIndex == kCache32Count - 1);
573 }
574
reed@android.com41bccf52009-04-03 13:33:51 +0000575 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000576 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
577 (NULL,
578 sizeof(SkPMColor) * kCache32Count,
579 NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000581 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000583 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000585 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000586 fCache32PixelRef->unref();
587 fCache32PixelRef = newPR;
588 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 }
590 }
591 return fCache32;
592}
593
reed@google.comdc731fd2010-12-23 15:19:47 +0000594/*
595 * Because our caller might rebuild the same (logically the same) gradient
596 * over and over, we'd like to return exactly the same "bitmap" if possible,
597 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
598 * To do that, we maintain a private cache of built-bitmaps, based on our
599 * colors and positions. Note: we don't try to flatten the fMapper, so if one
600 * is present, we skip the cache for now.
601 */
602void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) {
603 // don't have a way to put the mapper into our cache-key yet
604 if (fMapper) {
605 // force our cahce32pixelref to be built
606 (void)this->getCache32();
607 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
608 bitmap->setPixelRef(fCache32PixelRef);
609 return;
610 }
611
612 // build our key: [numColors + colors[] + {positions[]} ]
613 int count = 1 + fColorCount;
614 if (fColorCount > 2) {
615 count += fColorCount - 1; // fRecs[].fPos
616 }
617
618 SkAutoSTMalloc<16, int32_t> storage(count);
619 int32_t* buffer = storage.get();
620
621 *buffer++ = fColorCount;
622 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
623 buffer += fColorCount;
624 if (fColorCount > 2) {
625 for (int i = 1; i < fColorCount; i++) {
626 *buffer++ = fRecs[i].fPos;
627 }
628 }
629 SkASSERT(buffer - storage.get() == count);
630
631 ///////////////////////////////////
632
633 static SkMutex gMutex;
634 static SkBitmapCache* gCache;
635 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
636 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
637 SkAutoMutexAcquire ama(gMutex);
638
639 if (NULL == gCache) {
640 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
641 }
642 size_t size = count * sizeof(int32_t);
643
644 if (!gCache->find(storage.get(), size, bitmap)) {
645 // force our cahce32pixelref to be built
646 (void)this->getCache32();
647 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
648 bitmap->setPixelRef(fCache32PixelRef);
649
650 gCache->add(storage.get(), size, *bitmap);
651 }
652}
653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654///////////////////////////////////////////////////////////////////////////
655
reed@android.com41bccf52009-04-03 13:33:51 +0000656static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 SkVector vec = pts[1] - pts[0];
658 SkScalar mag = vec.length();
659 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
660
661 vec.scale(inv);
662 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
663 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
664 matrix->postScale(inv, inv);
665}
666
667///////////////////////////////////////////////////////////////////////////////
668
669class Linear_Gradient : public Gradient_Shader {
670public:
671 Linear_Gradient(const SkPoint pts[2],
672 const SkColor colors[], const SkScalar pos[], int colorCount,
673 SkShader::TileMode mode, SkUnitMapper* mapper)
674 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
675 {
676 pts_to_unit_matrix(pts, &fPtsToUnit);
677 }
reed@android.com9b46e772009-06-05 12:24:41 +0000678
reed@android.com5119bdb2009-06-12 21:27:03 +0000679 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
681 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.comdc731fd2010-12-23 15:19:47 +0000682 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*,
683 TileMode*, SkScalar* twoPointRadialParams);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684
685 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
686 return SkNEW_ARGS(Linear_Gradient, (buffer));
687 }
688
689protected:
reed@google.comdc731fd2010-12-23 15:19:47 +0000690 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691 virtual Factory getFactory() { return CreateProc; }
692
693private:
694 typedef Gradient_Shader INHERITED;
695};
696
reed@android.com5119bdb2009-06-12 21:27:03 +0000697bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
698 const SkMatrix& matrix) {
699 if (!this->INHERITED::setContext(device, paint, matrix)) {
700 return false;
701 }
702
703 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
704 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000705 fFlags |= SkShader::kConstInY32_Flag;
706 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
707 // only claim this if we do have a 16bit mode (i.e. none of our
708 // colors have alpha), and if we are not dithering (which obviously
709 // is not const in Y).
710 fFlags |= SkShader::kConstInY16_Flag;
711 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000712 }
713 return true;
714}
715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000717static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718{
719 SkASSERT(count > 0);
720 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
721}
722
723void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
724{
725 SkASSERT(count > 0);
726
727 SkPoint srcPt;
728 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
729 TileProc proc = fTileProc;
730 const SkPMColor* cache = this->getCache32();
731
reed@android.comc552a432009-06-12 20:02:50 +0000732 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000733 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
734 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
736
reed@android.comc552a432009-06-12 20:02:50 +0000737 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 SkFixed dxStorage[1];
739 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
740 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000741 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
743 dx = SkScalarToFixed(fDstToIndex.getScaleX());
744 }
745
reed@android.comc552a432009-06-12 20:02:50 +0000746 if (SkFixedNearlyZero(dx)) {
747 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 unsigned fi = proc(fx);
749 SkASSERT(fi <= 0xFFFF);
750 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000751 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752#if 0
753 if (no_need_for_clamp(fx, dx, count))
754 {
755 unsigned fi;
756 while ((count -= 4) >= 0)
757 {
758 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
759 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
760 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
761 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
762 }
763 SkASSERT(count <= -1 && count >= -4);
764 count += 4;
765 while (--count >= 0)
766 {
767 fi = fx >> 8;
768 SkASSERT(fi <= 0xFF);
769 fx += dx;
770 *dstC++ = cache[fi];
771 }
772 }
773 else
774#endif
775 do {
776 unsigned fi = SkClampMax(fx >> 8, 0xFF);
777 SkASSERT(fi <= 0xFF);
778 fx += dx;
779 *dstC++ = cache[fi];
780 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000781 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 do {
783 unsigned fi = mirror_8bits(fx >> 8);
784 SkASSERT(fi <= 0xFF);
785 fx += dx;
786 *dstC++ = cache[fi];
787 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000788 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 SkASSERT(proc == repeat_tileproc);
790 do {
791 unsigned fi = repeat_8bits(fx >> 8);
792 SkASSERT(fi <= 0xFF);
793 fx += dx;
794 *dstC++ = cache[fi];
795 } while (--count != 0);
796 }
reed@android.comc552a432009-06-12 20:02:50 +0000797 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 SkScalar dstX = SkIntToScalar(x);
799 SkScalar dstY = SkIntToScalar(y);
800 do {
801 dstProc(fDstToIndex, dstX, dstY, &srcPt);
802 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
803 SkASSERT(fi <= 0xFFFF);
804 *dstC++ = cache[fi >> (16 - kCache32Bits)];
805 dstX += SK_Scalar1;
806 } while (--count != 0);
807 }
808}
809
reed@google.comdc731fd2010-12-23 15:19:47 +0000810SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
811 SkMatrix* matrix,
812 TileMode xy[],
813 SkScalar* twoPointRadialParams) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000815 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 }
817 if (matrix) {
818 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
819 matrix->preConcat(fPtsToUnit);
820 }
821 if (xy) {
822 xy[0] = fTileMode;
823 xy[1] = kClamp_TileMode;
824 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000825 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826}
827
reed@android.com3c9b2a42009-08-27 19:28:37 +0000828static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
829 int count) {
830 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 *dst++ = value;
832 count -= 1;
833 SkTSwap(value, other);
834 }
835
836 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
837
reed@android.com3c9b2a42009-08-27 19:28:37 +0000838 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000840 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842
843void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
844{
845 SkASSERT(count > 0);
846
847 SkPoint srcPt;
848 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
849 TileProc proc = fTileProc;
850 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000853 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000854 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
855 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
857
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000858 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 SkFixed dxStorage[1];
860 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
861 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000862 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
864 dx = SkScalarToFixed(fDstToIndex.getScaleX());
865 }
866
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000867 if (SkFixedNearlyZero(dx)) {
868 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +0000869 unsigned fi = proc(fx) >> kCache16Shift;
870 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000872 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 do {
reed@android.com512a8762009-12-14 15:25:36 +0000874 unsigned fi = SkClampMax(fx >> kCache16Shift, kCache16Mask);
875 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 *dstC++ = cache[toggle + fi];
878 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000880 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 do {
reed@android.com200645d2009-12-14 16:41:57 +0000882 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +0000883 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 *dstC++ = cache[toggle + fi];
886 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000888 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 SkASSERT(proc == repeat_tileproc);
890 do {
reed@android.com200645d2009-12-14 16:41:57 +0000891 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +0000892 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 *dstC++ = cache[toggle + fi];
895 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 } while (--count != 0);
897 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000898 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 SkScalar dstX = SkIntToScalar(x);
900 SkScalar dstY = SkIntToScalar(y);
901 do {
902 dstProc(fDstToIndex, dstX, dstY, &srcPt);
903 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
904 SkASSERT(fi <= 0xFFFF);
905
reed@android.com512a8762009-12-14 15:25:36 +0000906 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 *dstC++ = cache[toggle + index];
908 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909
910 dstX += SK_Scalar1;
911 } while (--count != 0);
912 }
913}
914
915///////////////////////////////////////////////////////////////////////////////
916
917#define kSQRT_TABLE_BITS 11
918#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
919
920#include "SkRadialGradient_Table.h"
921
922#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
923
924#include <stdio.h>
925
926void SkRadialGradient_BuildTable()
927{
928 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
929
930 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
931 SkASSERT(file);
932 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
933
934 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
935 {
936 if ((i & 15) == 0)
937 ::fprintf(file, "\t");
938
939 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
940
941 ::fprintf(file, "0x%02X", value);
942 if (i < kSQRT_TABLE_SIZE-1)
943 ::fprintf(file, ", ");
944 if ((i & 15) == 15)
945 ::fprintf(file, "\n");
946 }
947 ::fprintf(file, "};\n");
948 ::fclose(file);
949}
950
951#endif
952
953
954static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
955{
956 SkScalar inv = SkScalarInvert(radius);
957
958 matrix->setTranslate(-center.fX, -center.fY);
959 matrix->postScale(inv, inv);
960}
961
962class Radial_Gradient : public Gradient_Shader {
963public:
964 Radial_Gradient(const SkPoint& center, SkScalar radius,
965 const SkColor colors[], const SkScalar pos[], int colorCount,
966 SkShader::TileMode mode, SkUnitMapper* mapper)
967 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
968 {
969 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
970 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
971
972 rad_to_unit_matrix(center, radius, &fPtsToUnit);
973 }
974 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
975 {
976 SkASSERT(count > 0);
977
978 SkPoint srcPt;
979 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
980 TileProc proc = fTileProc;
981 const SkPMColor* cache = this->getCache32();
982
983 if (fDstToIndexClass != kPerspective_MatrixClass)
984 {
reed@google.comdc731fd2010-12-23 15:19:47 +0000985 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
986 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
988 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
989
990 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
991 {
992 SkFixed storage[2];
993 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
994 dx = storage[0];
995 dy = storage[1];
996 }
997 else
998 {
999 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1000 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1001 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1002 }
1003
1004 if (proc == clamp_tileproc)
1005 {
1006 const uint8_t* sqrt_table = gSqrt8Table;
1007 fx >>= 1;
1008 dx >>= 1;
1009 fy >>= 1;
1010 dy >>= 1;
1011 do {
1012 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1013 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1014 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1015 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1016 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1017 fx += dx;
1018 fy += dy;
1019 } while (--count != 0);
1020 }
1021 else if (proc == mirror_tileproc)
1022 {
1023 do {
1024 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1025 unsigned fi = mirror_tileproc(dist);
1026 SkASSERT(fi <= 0xFFFF);
1027 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1028 fx += dx;
1029 fy += dy;
1030 } while (--count != 0);
1031 }
1032 else
1033 {
1034 SkASSERT(proc == repeat_tileproc);
1035 do {
1036 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1037 unsigned fi = repeat_tileproc(dist);
1038 SkASSERT(fi <= 0xFFFF);
1039 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1040 fx += dx;
1041 fy += dy;
1042 } while (--count != 0);
1043 }
1044 }
1045 else // perspective case
1046 {
1047 SkScalar dstX = SkIntToScalar(x);
1048 SkScalar dstY = SkIntToScalar(y);
1049 do {
1050 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1051 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1052 SkASSERT(fi <= 0xFFFF);
1053 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1054 dstX += SK_Scalar1;
1055 } while (--count != 0);
1056 }
1057 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001058
1059 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 SkASSERT(count > 0);
1061
1062 SkPoint srcPt;
1063 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1064 TileProc proc = fTileProc;
1065 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067
reed@android.com3c9b2a42009-08-27 19:28:37 +00001068 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001069 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1070 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1072 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1073
reed@android.com3c9b2a42009-08-27 19:28:37 +00001074 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 SkFixed storage[2];
1076 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1077 dx = storage[0];
1078 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001079 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1081 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1082 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1083 }
1084
reed@android.com3c9b2a42009-08-27 19:28:37 +00001085 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 const uint8_t* sqrt_table = gSqrt8Table;
1087
1088 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1089 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1090 precision, but that appears to be visually OK. If we decide this is OK for
1091 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1092 to avoid having to do these extra shifts each time.
1093 */
1094 fx >>= 1;
1095 dx >>= 1;
1096 fy >>= 1;
1097 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001098 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 +00001099 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1100 fy *= fy;
1101 do {
1102 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1103 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1104 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1105 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1107 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001109 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 do {
1111 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1112 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1113 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1114 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1115 fx += dx;
1116 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1118 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 } while (--count != 0);
1120 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001121 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 do {
1123 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1124 unsigned fi = mirror_tileproc(dist);
1125 SkASSERT(fi <= 0xFFFF);
1126 fx += dx;
1127 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1129 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001131 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 SkASSERT(proc == repeat_tileproc);
1133 do {
1134 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1135 unsigned fi = repeat_tileproc(dist);
1136 SkASSERT(fi <= 0xFFFF);
1137 fx += dx;
1138 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1140 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 } while (--count != 0);
1142 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001143 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 SkScalar dstX = SkIntToScalar(x);
1145 SkScalar dstY = SkIntToScalar(y);
1146 do {
1147 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1148 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1149 SkASSERT(fi <= 0xFFFF);
1150
1151 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 *dstC++ = cache[toggle + index];
1153 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154
1155 dstX += SK_Scalar1;
1156 } while (--count != 0);
1157 }
1158 }
1159
reed@google.comdc731fd2010-12-23 15:19:47 +00001160 virtual BitmapType asABitmap(SkBitmap* bitmap,
1161 SkMatrix* matrix,
1162 TileMode* xy,
1163 SkScalar* twoPointRadialParams) {
1164 if (bitmap) {
1165 this->commonAsABitmap(bitmap);
1166 }
1167 if (matrix) {
1168 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1169 matrix->preConcat(fPtsToUnit);
1170 }
1171 if (xy) {
1172 xy[0] = fTileMode;
1173 xy[1] = kClamp_TileMode;
1174 }
1175 return kRadial_BitmapType;
1176 }
1177
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1179 return SkNEW_ARGS(Radial_Gradient, (buffer));
1180 }
1181
1182protected:
1183 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1184 virtual Factory getFactory() { return CreateProc; }
1185
1186private:
1187 typedef Gradient_Shader INHERITED;
1188};
1189
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001190/* Two-point radial gradients are specified by two circles, each with a center
1191 point and radius. The gradient can be considered to be a series of
1192 concentric circles, with the color interpolated from the start circle
1193 (at t=0) to the end circle (at t=1).
1194
1195 For each point (x, y) in the span, we want to find the
1196 interpolated circle that intersects that point. The center
1197 of the desired circle (Cx, Cy) falls at some distance t
1198 along the line segment between the start point (Sx, Sy) and
1199 end point (Ex, Ey):
1200
1201 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1202 Cy = (1 - t) * Sy + t * Ey
1203
1204 The radius of the desired circle (r) is also a linear interpolation t
1205 between the start and end radii (Sr and Er):
1206
1207 r = (1 - t) * Sr + t * Er
1208
1209 But
1210
1211 (x - Cx)^2 + (y - Cy)^2 = r^2
1212
1213 so
1214
1215 (x - ((1 - t) * Sx + t * Ex))^2
1216 + (y - ((1 - t) * Sy + t * Ey))^2
1217 = ((1 - t) * Sr + t * Er)^2
1218
1219 Solving for t yields
1220
1221 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1222 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1223 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
1224
1225 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1226
1227 [Dx^2 + Dy^2 - Dr^2)] * t^2
1228 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1229 + [dx^2 + dy^2 - Sr^2] = 0
1230
1231 A quadratic in t. The two roots of the quadratic reflect the two
1232 possible circles on which the point may fall. Solving for t yields
1233 the gradient value to use.
1234
1235 If a<0, the start circle is entirely contained in the
1236 end circle, and one of the roots will be <0 or >1 (off the line
1237 segment). If a>0, the start circle falls at least partially
1238 outside the end circle (or vice versa), and the gradient
1239 defines a "tube" where a point may be on one circle (on the
1240 inside of the tube) or the other (outside of the tube). We choose
1241 one arbitrarily.
1242
1243 In order to keep the math to within the limits of fixed point,
1244 we divide the entire quadratic by Dr^2, and replace
1245 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
1246
1247 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1248 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1249 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
1250
1251 (x' and y' are computed by appending the subtract and scale to the
1252 fDstToIndex matrix in the constructor).
1253
1254 Since the 'A' component of the quadratic is independent of x' and y', it
1255 is precomputed in the constructor. Since the 'B' component is linear in
1256 x' and y', if x and y are linear in the span, 'B' can be computed
1257 incrementally with a simple delta (db below). If it is not (e.g.,
1258 a perspective projection), it must be computed in the loop.
1259
1260*/
1261
1262static inline SkFixed two_point_radial(SkFixed b, SkFixed fx, SkFixed fy, SkFixed sr2d2, SkFixed foura, SkFixed oneOverTwoA, bool posRoot) {
1263 SkFixed c = SkFixedSquare(fx) + SkFixedSquare(fy) - sr2d2;
1264 SkFixed discrim = SkFixedSquare(b) - SkFixedMul(foura, c);
1265 if (discrim < 0) {
1266 discrim = -discrim;
1267 }
1268 SkFixed rootDiscrim = SkFixedSqrt(discrim);
1269 if (posRoot) {
1270 return SkFixedMul(-b + rootDiscrim, oneOverTwoA);
1271 } else {
1272 return SkFixedMul(-b - rootDiscrim, oneOverTwoA);
1273 }
1274}
1275
1276class Two_Point_Radial_Gradient : public Gradient_Shader {
1277public:
1278 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1279 const SkPoint& end, SkScalar endRadius,
1280 const SkColor colors[], const SkScalar pos[],
1281 int colorCount, SkShader::TileMode mode,
1282 SkUnitMapper* mapper)
1283 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
1284 {
1285 fDiff = start - end;
1286 fDiffRadius = endRadius - startRadius;
1287 SkScalar inv = SkScalarInvert(fDiffRadius);
1288 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1289 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1290 fStartRadius = SkScalarMul(startRadius, inv);
1291 fSr2D2 = SkScalarSquare(fStartRadius);
1292 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
1293 fOneOverTwoA = SkScalarInvert(fA * 2);
1294
1295 fPtsToUnit.setTranslate(-start.fX, -start.fY);
1296 fPtsToUnit.postScale(inv, inv);
1297 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001298
1299 virtual BitmapType asABitmap(SkBitmap* bitmap,
1300 SkMatrix* matrix,
1301 TileMode* xy,
1302 SkScalar* twoPointRadialParams) {
1303 if (bitmap) {
1304 this->commonAsABitmap(bitmap);
1305 }
1306 SkScalar diffL = 0; // just to avoid gcc warning
1307 if (matrix || twoPointRadialParams) {
1308 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
1309 SkScalarSquare(fDiff.fY));
1310 }
1311 if (matrix) {
1312 SkScalar invDiffL = SkScalarInvert(diffL);
1313 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1314 SkScalarMul(invDiffL, fDiff.fX));
1315 matrix->preConcat(fPtsToUnit);
1316 }
1317 if (xy) {
1318 xy[0] = fTileMode;
1319 xy[1] = kClamp_TileMode;
1320 }
1321 if (NULL != twoPointRadialParams) {
1322 twoPointRadialParams[0] = diffL;
1323 twoPointRadialParams[1] = fStartRadius;
1324 twoPointRadialParams[2] = fDiffRadius;
1325 }
1326 return kTwoPointRadial_BitmapType;
1327 }
1328
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001329 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1330 {
1331 SkASSERT(count > 0);
1332
1333 // Zero difference between radii: fill with transparent black.
1334 if (fDiffRadius == 0) {
1335 sk_bzero(dstC, count * sizeof(*dstC));
1336 return;
1337 }
1338 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1339 TileProc proc = fTileProc;
1340 const SkPMColor* cache = this->getCache32();
1341 SkFixed diffx = SkScalarToFixed(fDiff.fX);
1342 SkFixed diffy = SkScalarToFixed(fDiff.fY);
1343 SkFixed foura = SkScalarToFixed(SkScalarMul(fA, 4));
1344 SkFixed startRadius = SkScalarToFixed(fStartRadius);
1345 SkFixed sr2D2 = SkScalarToFixed(fSr2D2);
1346 SkFixed oneOverTwoA = SkScalarToFixed(fOneOverTwoA);
1347 bool posRoot = fDiffRadius < 0;
1348 if (fDstToIndexClass != kPerspective_MatrixClass)
1349 {
1350 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001351 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1352 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001353 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1354 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1355
1356 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1357 {
1358 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &dx, &dy);
1359 }
1360 else
1361 {
1362 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1363 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1364 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1365 }
1366 SkFixed b = (SkFixedMul(diffx, fx) +
1367 SkFixedMul(diffy, fy) - startRadius) << 1;
1368 SkFixed db = (SkFixedMul(diffx, dx) +
1369 SkFixedMul(diffy, dy)) << 1;
1370 if (proc == clamp_tileproc)
1371 {
1372 for (; count > 0; --count) {
1373 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1374 SkFixed index = SkClampMax(t, 0xFFFF);
1375 SkASSERT(index <= 0xFFFF);
1376 *dstC++ = cache[index >> (16 - kCache32Bits)];
1377 fx += dx;
1378 fy += dy;
1379 b += db;
1380 }
1381 }
1382 else if (proc == mirror_tileproc)
1383 {
1384 for (; count > 0; --count) {
1385 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1386 SkFixed index = mirror_tileproc(t);
1387 SkASSERT(index <= 0xFFFF);
1388 *dstC++ = cache[index >> (16 - kCache32Bits)];
1389 fx += dx;
1390 fy += dy;
1391 b += db;
1392 }
1393 }
1394 else
1395 {
1396 SkASSERT(proc == repeat_tileproc);
1397 for (; count > 0; --count) {
1398 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1399 SkFixed index = repeat_tileproc(t);
1400 SkASSERT(index <= 0xFFFF);
1401 *dstC++ = cache[index >> (16 - kCache32Bits)];
1402 fx += dx;
1403 fy += dy;
1404 b += db;
1405 }
1406 }
1407 }
1408 else // perspective case
1409 {
reed@android.com6c59a172009-09-22 20:24:05 +00001410 SkScalar dstX = SkIntToScalar(x);
1411 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001412 for (; count > 0; --count) {
1413 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001414 dstProc(fDstToIndex, dstX, dstY, &srcPt);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001415 SkFixed fx = SkScalarToFixed(srcPt.fX);
1416 SkFixed fy = SkScalarToFixed(srcPt.fY);
1417 SkFixed b = (SkFixedMul(diffx, fx) +
1418 SkFixedMul(diffy, fy) - startRadius) << 1;
1419 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1420 SkFixed index = proc(t);
1421 SkASSERT(index <= 0xFFFF);
1422 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001423 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001424 }
1425 }
1426 }
1427
reed@android.com6c59a172009-09-22 20:24:05 +00001428 virtual bool setContext(const SkBitmap& device,
1429 const SkPaint& paint,
1430 const SkMatrix& matrix) {
1431 if (!this->INHERITED::setContext(device, paint, matrix)) {
1432 return false;
1433 }
1434
1435 // we don't have a span16 proc
1436 fFlags &= ~kHasSpan16_Flag;
1437 return true;
1438 }
1439
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001440 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1441 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1442 }
1443
reed@android.combcfc7332009-11-10 16:19:39 +00001444 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1445 this->INHERITED::flatten(buffer);
1446 buffer.writeScalar(fDiff.fX);
1447 buffer.writeScalar(fDiff.fY);
1448 buffer.writeScalar(fStartRadius);
1449 buffer.writeScalar(fDiffRadius);
1450 buffer.writeScalar(fSr2D2);
1451 buffer.writeScalar(fA);
1452 buffer.writeScalar(fOneOverTwoA);
1453 }
1454
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001455protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001456 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
1457 : Gradient_Shader(buffer) {
1458 fDiff.fX = buffer.readScalar();
1459 fDiff.fY = buffer.readScalar();
1460 fStartRadius = buffer.readScalar();
1461 fDiffRadius = buffer.readScalar();
1462 fSr2D2 = buffer.readScalar();
1463 fA = buffer.readScalar();
1464 fOneOverTwoA = buffer.readScalar();
1465 };
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001466 virtual Factory getFactory() { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001467
1468private:
1469 typedef Gradient_Shader INHERITED;
1470 SkPoint fDiff;
1471 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
1472};
1473
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474///////////////////////////////////////////////////////////////////////////////
1475
1476class Sweep_Gradient : public Gradient_Shader {
1477public:
1478 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1479 const SkScalar pos[], int count, SkUnitMapper* mapper)
1480 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1481 {
1482 fPtsToUnit.setTranslate(-cx, -cy);
1483 }
1484 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1485 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1486
reed@google.comdc731fd2010-12-23 15:19:47 +00001487 virtual BitmapType asABitmap(SkBitmap* bitmap,
1488 SkMatrix* matrix,
1489 TileMode* xy,
1490 SkScalar* twoPointRadialParams) {
1491 if (bitmap) {
1492 this->commonAsABitmap(bitmap);
1493 }
1494 if (matrix) {
1495 *matrix = fPtsToUnit;
1496 }
1497 if (xy) {
1498 xy[0] = fTileMode;
1499 xy[1] = kClamp_TileMode;
1500 }
1501 return kSweep_BitmapType;
1502 }
1503
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1505 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1506 }
1507
1508protected:
1509 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 virtual Factory getFactory() { return CreateProc; }
1511
1512private:
1513 typedef Gradient_Shader INHERITED;
1514};
1515
1516#ifdef COMPUTE_SWEEP_TABLE
1517#define PI 3.14159265
1518static bool gSweepTableReady;
1519static uint8_t gSweepTable[65];
1520
1521/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1522 We scale the results to [0..32]
1523*/
1524static const uint8_t* build_sweep_table()
1525{
1526 if (!gSweepTableReady)
1527 {
1528 const int N = 65;
1529 const double DENOM = N - 1;
1530
1531 for (int i = 0; i < N; i++)
1532 {
1533 double arg = i / DENOM;
1534 double v = atan(arg);
1535 int iv = (int)round(v * DENOM * 2 / PI);
1536// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1537 printf("%d, ", iv);
1538 gSweepTable[i] = iv;
1539 }
1540 gSweepTableReady = true;
1541 }
1542 return gSweepTable;
1543}
1544#else
1545static const uint8_t gSweepTable[] = {
1546 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1547 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1548 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1549 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1550 32
1551};
1552static const uint8_t* build_sweep_table() { return gSweepTable; }
1553#endif
1554
1555// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1556// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1557// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1558
1559//unsigned div_64(int numer, int denom);
1560static unsigned div_64(int numer, int denom)
1561{
1562 SkASSERT(numer <= denom);
1563 SkASSERT(numer > 0);
1564 SkASSERT(denom > 0);
1565
1566 int nbits = SkCLZ(numer);
1567 int dbits = SkCLZ(denom);
1568 int bits = 6 - nbits + dbits;
1569 SkASSERT(bits <= 6);
1570
1571 if (bits < 0) // detect underflow
1572 return 0;
1573
1574 denom <<= dbits - 1;
1575 numer <<= nbits - 1;
1576
1577 unsigned result = 0;
1578
1579 // do the first one
1580 if ((numer -= denom) >= 0)
1581 result = 1;
1582 else
1583 numer += denom;
1584
1585 // Now fall into our switch statement if there are more bits to compute
1586 if (bits > 0)
1587 {
1588 // make room for the rest of the answer bits
1589 result <<= bits;
1590 switch (bits) {
1591 case 6:
1592 if ((numer = (numer << 1) - denom) >= 0)
1593 result |= 32;
1594 else
1595 numer += denom;
1596 case 5:
1597 if ((numer = (numer << 1) - denom) >= 0)
1598 result |= 16;
1599 else
1600 numer += denom;
1601 case 4:
1602 if ((numer = (numer << 1) - denom) >= 0)
1603 result |= 8;
1604 else
1605 numer += denom;
1606 case 3:
1607 if ((numer = (numer << 1) - denom) >= 0)
1608 result |= 4;
1609 else
1610 numer += denom;
1611 case 2:
1612 if ((numer = (numer << 1) - denom) >= 0)
1613 result |= 2;
1614 else
1615 numer += denom;
1616 case 1:
1617 default: // not strictly need, but makes GCC make better ARM code
1618 if ((numer = (numer << 1) - denom) >= 0)
1619 result |= 1;
1620 else
1621 numer += denom;
1622 }
1623 }
1624 return result;
1625}
1626
1627// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1628static unsigned atan_0_90(SkFixed y, SkFixed x)
1629{
1630#ifdef SK_DEBUG
1631 {
1632 static bool gOnce;
1633 if (!gOnce)
1634 {
1635 gOnce = true;
1636 SkASSERT(div_64(55, 55) == 64);
1637 SkASSERT(div_64(128, 256) == 32);
1638 SkASSERT(div_64(2326528, 4685824) == 31);
1639 SkASSERT(div_64(753664, 5210112) == 9);
1640 SkASSERT(div_64(229376, 4882432) == 3);
1641 SkASSERT(div_64(2, 64) == 2);
1642 SkASSERT(div_64(1, 64) == 1);
1643 // test that we handle underflow correctly
1644 SkASSERT(div_64(12345, 0x54321234) == 0);
1645 }
1646 }
1647#endif
1648
1649 SkASSERT(y > 0 && x > 0);
1650 const uint8_t* table = build_sweep_table();
1651
1652 unsigned result;
1653 bool swap = (x < y);
1654 if (swap)
1655 {
1656 // first part of the atan(v) = PI/2 - atan(1/v) identity
1657 // since our div_64 and table want v <= 1, where v = y/x
1658 SkTSwap<SkFixed>(x, y);
1659 }
1660
1661 result = div_64(y, x);
1662
1663#ifdef SK_DEBUG
1664 {
1665 unsigned result2 = SkDivBits(y, x, 6);
1666 SkASSERT(result2 == result ||
1667 (result == 1 && result2 == 0));
1668 }
1669#endif
1670
1671 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1672 result = table[result];
1673
1674 if (swap)
1675 {
1676 // complete the atan(v) = PI/2 - atan(1/v) identity
1677 result = 64 - result;
1678 // pin to 63
1679 result -= result >> 6;
1680 }
1681
1682 SkASSERT(result <= 63);
1683 return result;
1684}
1685
1686// returns angle in a circle [0..2PI) -> [0..255]
1687static unsigned SkATan2_255(SkFixed y, SkFixed x)
1688{
1689 if (x == 0)
1690 {
1691 if (y == 0)
1692 return 0;
1693 return y < 0 ? 192 : 64;
1694 }
1695 if (y == 0)
1696 return x < 0 ? 128 : 0;
1697
1698 /* Find the right quadrant for x,y
1699 Since atan_0_90 only handles the first quadrant, we rotate x,y
1700 appropriately before calling it, and then add the right amount
1701 to account for the real quadrant.
1702 quadrant 0 : add 0 | x > 0 && y > 0
1703 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1704 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1705 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1706
1707 map x<0 to (1 << 6)
1708 map y<0 to (3 << 6)
1709 add = map_x ^ map_y
1710 */
1711 int xsign = x >> 31;
1712 int ysign = y >> 31;
1713 int add = ((-xsign) ^ (ysign & 3)) << 6;
1714
1715#ifdef SK_DEBUG
1716 if (0 == add)
1717 SkASSERT(x > 0 && y > 0);
1718 else if (64 == add)
1719 SkASSERT(x < 0 && y > 0);
1720 else if (128 == add)
1721 SkASSERT(x < 0 && y < 0);
1722 else if (192 == add)
1723 SkASSERT(x > 0 && y < 0);
1724 else
1725 SkASSERT(!"bad value for add");
1726#endif
1727
1728 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1729 where we need to rotate x,y by 90 or -90
1730 */
1731 x = (x ^ xsign) - xsign;
1732 y = (y ^ ysign) - ysign;
1733 if (add & 64) // quads 1 or 3 need to swap x,y
1734 SkTSwap<SkFixed>(x, y);
1735
1736 unsigned result = add + atan_0_90(y, x);
1737 SkASSERT(result < 256);
1738 return result;
1739}
1740
1741void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1742{
1743 SkMatrix::MapXYProc proc = fDstToIndexProc;
1744 const SkMatrix& matrix = fDstToIndex;
1745 const SkPMColor* cache = this->getCache32();
1746 SkPoint srcPt;
1747
1748 if (fDstToIndexClass != kPerspective_MatrixClass)
1749 {
1750 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1751 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1752 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1753 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1754
1755 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1756 {
1757 SkFixed storage[2];
1758 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1759 &storage[0], &storage[1]);
1760 dx = storage[0];
1761 dy = storage[1];
1762 }
1763 else
1764 {
1765 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1766 dx = SkScalarToFixed(matrix.getScaleX());
1767 dy = SkScalarToFixed(matrix.getSkewY());
1768 }
1769
1770 for (; count > 0; --count)
1771 {
1772 *dstC++ = cache[SkATan2_255(fy, fx)];
1773 fx += dx;
1774 fy += dy;
1775 }
1776 }
1777 else // perspective case
1778 {
1779 for (int stop = x + count; x < stop; x++)
1780 {
1781 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1782 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1783
1784 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1785 SkScalarToFixed(srcPt.fX));
1786 *dstC++ = cache[index];
1787 }
1788 }
1789}
1790
1791void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1792{
1793 SkMatrix::MapXYProc proc = fDstToIndexProc;
1794 const SkMatrix& matrix = fDstToIndex;
1795 const uint16_t* cache = this->getCache16();
1796 int toggle = ((x ^ y) & 1) << kCache16Bits;
1797 SkPoint srcPt;
1798
1799 if (fDstToIndexClass != kPerspective_MatrixClass)
1800 {
1801 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1802 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1803 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1804 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1805
1806 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1807 {
1808 SkFixed storage[2];
1809 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1810 &storage[0], &storage[1]);
1811 dx = storage[0];
1812 dy = storage[1];
1813 }
1814 else
1815 {
1816 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1817 dx = SkScalarToFixed(matrix.getScaleX());
1818 dy = SkScalarToFixed(matrix.getSkewY());
1819 }
1820
1821 for (; count > 0; --count)
1822 {
1823 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1824 *dstC++ = cache[toggle + index];
1825 toggle ^= (1 << kCache16Bits);
1826 fx += dx;
1827 fy += dy;
1828 }
1829 }
1830 else // perspective case
1831 {
1832 for (int stop = x + count; x < stop; x++)
1833 {
1834 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1835 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1836
1837 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1838 SkScalarToFixed(srcPt.fX));
1839 index >>= (8 - kCache16Bits);
1840 *dstC++ = cache[toggle + index];
1841 toggle ^= (1 << kCache16Bits);
1842 }
1843 }
1844}
1845
1846///////////////////////////////////////////////////////////////////////////
1847///////////////////////////////////////////////////////////////////////////
1848
1849// assumes colors is SkColor* and pos is SkScalar*
1850#define EXPAND_1_COLOR(count) \
1851 SkColor tmp[2]; \
1852 do { \
1853 if (1 == count) { \
1854 tmp[0] = tmp[1] = colors[0]; \
1855 colors = tmp; \
1856 pos = NULL; \
1857 count = 2; \
1858 } \
1859 } while (0)
1860
1861SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1862 const SkColor colors[], const SkScalar pos[], int colorCount,
1863 SkShader::TileMode mode, SkUnitMapper* mapper)
1864{
1865 if (NULL == pts || NULL == colors || colorCount < 1) {
1866 return NULL;
1867 }
1868 EXPAND_1_COLOR(colorCount);
1869
reed@android.comab840b82009-07-01 17:00:03 +00001870 return SkNEW_ARGS(Linear_Gradient,
1871 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872}
1873
1874SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1875 const SkColor colors[], const SkScalar pos[], int colorCount,
1876 SkShader::TileMode mode, SkUnitMapper* mapper)
1877{
1878 if (radius <= 0 || NULL == colors || colorCount < 1) {
1879 return NULL;
1880 }
1881 EXPAND_1_COLOR(colorCount);
1882
reed@android.comab840b82009-07-01 17:00:03 +00001883 return SkNEW_ARGS(Radial_Gradient,
1884 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885}
1886
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001887SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
1888 SkScalar startRadius,
1889 const SkPoint& end,
1890 SkScalar endRadius,
1891 const SkColor colors[],
1892 const SkScalar pos[],
1893 int colorCount,
1894 SkShader::TileMode mode,
1895 SkUnitMapper* mapper)
1896{
1897 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
1898 return NULL;
1899 }
1900 EXPAND_1_COLOR(colorCount);
1901
1902 return SkNEW_ARGS(Two_Point_Radial_Gradient,
1903 (start, startRadius, end, endRadius, colors, pos, colorCount, mode, mapper));
1904}
1905
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1907 const SkColor colors[],
1908 const SkScalar pos[],
1909 int count, SkUnitMapper* mapper)
1910{
1911 if (NULL == colors || count < 1) {
1912 return NULL;
1913 }
1914 EXPAND_1_COLOR(count);
1915
1916 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1917}
1918
1919static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1920 Linear_Gradient::CreateProc);
1921
1922static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1923 Radial_Gradient::CreateProc);
1924
1925static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1926 Sweep_Gradient::CreateProc);
1927