blob: b212dfe753626207d481e06f992bbd236c11fbb6 [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**
reed@google.com55b8e8c2011-01-13 16:22:35 +00005** 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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.com55b8e8c2011-01-13 16:22:35 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.com55b8e8c2011-01-13 16:22:35 +000011** 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
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** 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@google.com9c7443d2011-01-17 18:46:37 +000026#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
27 #define USE_DITHER_32BIT_GRADIENT
28#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +000029
reed@google.com61eb0402011-04-15 12:11:12 +000030///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000031
32typedef SkFixed (*TileProc)(SkFixed);
33
reed@android.com41bccf52009-04-03 13:33:51 +000034static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000035 return SkClampMax(x, 0xFFFF);
36}
37
reed@android.com41bccf52009-04-03 13:33:51 +000038static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 return x & 0xFFFF;
40}
41
reed@android.com41bccf52009-04-03 13:33:51 +000042static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000043 int s = x << 15 >> 31;
44 return (x ^ s) & 0xFFFF;
45}
46
47static const TileProc gTileProcs[] = {
48 clamp_tileproc,
49 repeat_tileproc,
50 mirror_tileproc
51};
52
reed@google.com61eb0402011-04-15 12:11:12 +000053///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000054
reed@android.com200645d2009-12-14 16:41:57 +000055static inline int repeat_bits(int x, const int bits) {
56 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000057}
58
reed@android.com200645d2009-12-14 16:41:57 +000059static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000061 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000063 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000064#else
reed@android.com200645d2009-12-14 16:41:57 +000065 int s = x << (31 - bits) >> 31;
66 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000067#endif
68}
69
reed@android.com41bccf52009-04-03 13:33:51 +000070static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 return x & 0xFF;
72}
73
reed@android.com41bccf52009-04-03 13:33:51 +000074static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000075#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000076 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000078 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 return x & 255;
80#else
81 int s = x << 23 >> 31;
82 return (x ^ s) & 0xFF;
83#endif
84}
85
reed@google.com61eb0402011-04-15 12:11:12 +000086///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000087
88class Gradient_Shader : public SkShader {
89public:
90 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +000091 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 virtual ~Gradient_Shader();
93
94 // overrides
95 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
96 virtual uint32_t getFlags() { return fFlags; }
97
98protected:
99 Gradient_Shader(SkFlattenableReadBuffer& );
100 SkUnitMapper* fMapper;
101 SkMatrix fPtsToUnit; // set by subclass
102 SkMatrix fDstToIndex;
103 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 TileMode fTileMode;
105 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000106 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 uint8_t fDstToIndexClass;
108 uint8_t fFlags;
109
110 struct Rec {
111 SkFixed fPos; // 0...1
112 uint32_t fScale; // (1 << 24) / range
113 };
114 Rec* fRecs;
115
116 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000117 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000119 kCache16Mask = kCache16Count - 1,
120 kCache16Shift = 16 - kCache16Bits,
121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 kCache32Bits = 8, // pretty much should always be 8
123 kCache32Count = 1 << kCache32Bits
124 };
125 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000126 const uint16_t* getCache16() const;
127 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128
reed@google.com7c2f27d2011-03-07 19:29:00 +0000129 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000130 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000131
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132private:
133 enum {
134 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
135
reed@android.com1c12abe2009-07-02 15:01:02 +0000136 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 };
138 SkColor fStorage[(kStorageSize + 3) >> 2];
139 SkColor* fOrigColors;
140
reed@google.com7c2f27d2011-03-07 19:29:00 +0000141 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
142 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143
reed@google.com7c2f27d2011-03-07 19:29:00 +0000144 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
145 mutable SkMallocPixelRef* fCache32PixelRef;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
147
reed@android.com512a8762009-12-14 15:25:36 +0000148 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000149 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
150 U8CPU alpha);
reed@android.com512a8762009-12-14 15:25:36 +0000151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 typedef SkShader INHERITED;
153};
154
reed@android.com41bccf52009-04-03 13:33:51 +0000155static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 SkASSERT(x >= 0 && x <= SK_Scalar1);
157
158#ifdef SK_SCALAR_IS_FLOAT
159 return (unsigned)(x * 0xFFFF);
160#else
161 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
162#endif
163}
164
reed@android.com41bccf52009-04-03 13:33:51 +0000165Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
166 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 SkASSERT(colorCount > 1);
168
169 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
170
171 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000172 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
175 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
176 fTileMode = mode;
177 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000178
reed@android.com41bccf52009-04-03 13:33:51 +0000179 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000180 fCache32 = NULL;
181 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182
reed@android.com41bccf52009-04-03 13:33:51 +0000183 /* Note: we let the caller skip the first and/or last position.
184 i.e. pos[0] = 0.3, pos[1] = 0.7
185 In these cases, we insert dummy entries to ensure that the final data
186 will be bracketed by [0, 1].
187 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
188
189 Thus colorCount (the caller's value, and fColorCount (our value) may
190 differ by up to 2. In the above example:
191 colorCount = 2
192 fColorCount = 4
193 */
194 fColorCount = colorCount;
195 // check if we need to add in dummy start and/or end position/colors
196 bool dummyFirst = false;
197 bool dummyLast = false;
198 if (pos) {
199 dummyFirst = pos[0] != 0;
200 dummyLast = pos[colorCount - 1] != SK_Scalar1;
201 fColorCount += dummyFirst + dummyLast;
202 }
203
204 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000205 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000206 fOrigColors = reinterpret_cast<SkColor*>(
207 sk_malloc_throw(size * fColorCount));
208 }
209 else {
210 fOrigColors = fStorage;
211 }
212
213 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 {
reed@android.com41bccf52009-04-03 13:33:51 +0000215 SkColor* origColors = fOrigColors;
216 if (dummyFirst) {
217 *origColors++ = colors[0];
218 }
219 memcpy(origColors, colors, colorCount * sizeof(SkColor));
220 if (dummyLast) {
221 origColors += colorCount;
222 *origColors = colors[colorCount - 1];
223 }
224 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225
reed@android.com1c12abe2009-07-02 15:01:02 +0000226 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000227 if (fColorCount > 2) {
228 Rec* recs = fRecs;
229 recs->fPos = 0;
230 // recs->fScale = 0; // unused;
231 recs += 1;
232 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 /* We need to convert the user's array of relative positions into
234 fixed-point positions and scale factors. We need these results
235 to be strictly monotonic (no two values equal or out of order).
236 Hence this complex loop that just jams a zero for the scale
237 value if it sees a segment out of order, and it assures that
238 we start at 0 and end at 1.0
239 */
240 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000241 int startIndex = dummyFirst ? 0 : 1;
242 int count = colorCount + dummyLast;
243 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 // force the last value to be 1.0
245 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000246 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000248 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 }
reed@android.com41bccf52009-04-03 13:33:51 +0000251 // pin curr withing range
252 if (curr < 0) {
253 curr = 0;
254 } else if (curr > SK_Fixed1) {
255 curr = SK_Fixed1;
256 }
257 recs->fPos = curr;
258 if (curr > prev) {
259 recs->fScale = (1 << 24) / (curr - prev);
260 } else {
261 recs->fScale = 0; // ignore this segment
262 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 // get ready for the next value
264 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000265 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 }
reed@android.com41bccf52009-04-03 13:33:51 +0000267 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 SkFixed dp = SK_Fixed1 / (colorCount - 1);
269 SkFixed p = dp;
270 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000271 for (int i = 1; i < colorCount; i++) {
272 recs->fPos = p;
273 recs->fScale = scale;
274 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 p += dp;
276 }
277 }
278 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000279 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280}
281
282Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000283 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fCacheAlpha = 256;
285
286 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
287
288 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000289 fCache32 = NULL;
290 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
reed@android.com41bccf52009-04-03 13:33:51 +0000292 int colorCount = fColorCount = buffer.readU32();
293 if (colorCount > kColorStorageCount) {
294 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
295 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
296 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300
301 fTileMode = (TileMode)buffer.readU8();
302 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000303 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 if (colorCount > 2) {
305 Rec* recs = fRecs;
306 recs[0].fPos = 0;
307 for (int i = 1; i < colorCount; i++) {
308 recs[i].fPos = buffer.readS32();
309 recs[i].fScale = buffer.readU32();
310 }
311 }
312 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000313 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314}
315
reed@android.com41bccf52009-04-03 13:33:51 +0000316Gradient_Shader::~Gradient_Shader() {
317 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000319 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000320 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000321 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000323 }
reed@google.com82065d62011-02-07 15:30:46 +0000324 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325}
326
reed@android.com41bccf52009-04-03 13:33:51 +0000327void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 this->INHERITED::flatten(buffer);
329 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000330 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
332 buffer.write8(fTileMode);
333 if (fColorCount > 2) {
334 Rec* recs = fRecs;
335 for (int i = 1; i < fColorCount; i++) {
336 buffer.write32(recs[i].fPos);
337 buffer.write32(recs[i].fScale);
338 }
339 }
340 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
341}
342
343bool Gradient_Shader::setContext(const SkBitmap& device,
344 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000345 const SkMatrix& matrix) {
346 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349
350 const SkMatrix& inverse = this->getTotalInverse();
351
352 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
353 return false;
354 }
355
356 fDstToIndexProc = fDstToIndex.getMapXYProc();
357 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
358
359 // now convert our colors in to PMColors
360 unsigned paintAlpha = this->getPaintAlpha();
361 unsigned colorAlpha = 0xFF;
362
reed@android.com3d06a8c2009-07-07 18:19:59 +0000363 // FIXME: record colorAlpha in constructor, since this is not affected
364 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000365 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 SkColor src = fOrigColors[i];
367 unsigned sa = SkColorGetA(src);
368 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 }
370
371 fFlags = this->INHERITED::getFlags();
372 if ((colorAlpha & paintAlpha) == 0xFF) {
373 fFlags |= kOpaqueAlpha_Flag;
374 }
375 // we can do span16 as long as our individual colors are opaque,
376 // regardless of the paint's alpha
377 if (0xFF == colorAlpha) {
378 fFlags |= kHasSpan16_Flag;
379 }
380
381 // if the new alpha differs from the previous time we were called, inval our cache
382 // this will trigger the cache to be rebuilt.
383 // we don't care about the first time, since the cache ptrs will already be NULL
384 if (fCacheAlpha != paintAlpha) {
385 fCache16 = NULL; // inval the cache
386 fCache32 = NULL; // inval the cache
387 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000388 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000389 if (fCache32PixelRef) {
390 fCache32PixelRef->notifyPixelsChanged();
391 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 }
393 return true;
394}
395
reed@android.com41bccf52009-04-03 13:33:51 +0000396static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 SkASSERT(a == SkToU8(a));
398 SkASSERT(b == SkToU8(b));
399 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 return a + ((b - a) * scale >> 8);
401}
402
reed@android.com41bccf52009-04-03 13:33:51 +0000403static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
404 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405#if 0
406 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
407 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
408 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
409 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
410
411 return SkPackARGB32(a, r, g, b);
412#else
413 int otherBlend = 256 - blend;
414
415#if 0
416 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
417 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
418 SkASSERT((t0 & t1) == 0);
419 return t0 | t1;
420#else
421 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
422 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
423#endif
424
425#endif
426}
427
428#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
429
reed@android.com41bccf52009-04-03 13:33:51 +0000430/** We take the original colors, not our premultiplied PMColors, since we can
431 build a 16bit table as long as the original colors are opaque, even if the
432 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433*/
reed@android.com512a8762009-12-14 15:25:36 +0000434void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
435 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 SkASSERT(count > 1);
437 SkASSERT(SkColorGetA(c0) == 0xFF);
438 SkASSERT(SkColorGetA(c1) == 0xFF);
439
440 SkFixed r = SkColorGetR(c0);
441 SkFixed g = SkColorGetG(c0);
442 SkFixed b = SkColorGetB(c0);
443
444 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
445 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
446 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
447
448 r = SkIntToFixed(r) + 0x8000;
449 g = SkIntToFixed(g) + 0x8000;
450 b = SkIntToFixed(b) + 0x8000;
451
452 do {
453 unsigned rr = r >> 16;
454 unsigned gg = g >> 16;
455 unsigned bb = b >> 16;
456 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000457 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 cache += 1;
459 r += dr;
460 g += dg;
461 b += db;
462 } while (--count != 0);
463}
464
reed@google.com55b8e8c2011-01-13 16:22:35 +0000465/*
466 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
467 * semantics of how we 2x2 dither 32->16
468 */
469static inline U8CPU dither_fixed_to_8(SkFixed n) {
470 n >>= 8;
471 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
472}
473
474/*
475 * For dithering with premultiply, we want to ceiling the alpha component,
476 * to ensure that it is always >= any color component.
477 */
478static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
479 n >>= 8;
480 return ((n << 1) - (n | (n >> 8))) >> 8;
481}
482
483void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
484 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 SkASSERT(count > 1);
486
reed@android.com1c12abe2009-07-02 15:01:02 +0000487 // need to apply paintAlpha to our two endpoints
488 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
489 SkFixed da;
490 {
491 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
492 da = SkIntToFixed(tmp - a) / (count - 1);
493 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494
reed@android.com1c12abe2009-07-02 15:01:02 +0000495 SkFixed r = SkColorGetR(c0);
496 SkFixed g = SkColorGetG(c0);
497 SkFixed b = SkColorGetB(c0);
498 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
499 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
500 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501
502 a = SkIntToFixed(a) + 0x8000;
503 r = SkIntToFixed(r) + 0x8000;
504 g = SkIntToFixed(g) + 0x8000;
505 b = SkIntToFixed(b) + 0x8000;
506
507 do {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000508 cache[0] = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
509 cache[kCache32Count] = SkPreMultiplyARGB(dither_ceil_fixed_to_8(a),
510 dither_fixed_to_8(r),
511 dither_fixed_to_8(g),
512 dither_fixed_to_8(b));
513 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 a += da;
515 r += dr;
516 g += dg;
517 b += db;
518 } while (--count != 0);
519}
520
reed@android.com41bccf52009-04-03 13:33:51 +0000521static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 SkASSERT((unsigned)x <= SK_Fixed1);
523 return x - (x >> 16);
524}
525
reed@android.com200645d2009-12-14 16:41:57 +0000526static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000527 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000528 if (6 == bits) {
529 return (x << 10) | (x << 4) | (x >> 2);
530 }
531 if (8 == bits) {
532 return (x << 8) | x;
533 }
534 sk_throw();
535 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536}
537
reed@google.com7c2f27d2011-03-07 19:29:00 +0000538const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000539 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000540 // double the count for dither entries
541 const int entryCount = kCache16Count * 2;
542 const size_t allocSize = sizeof(uint16_t) * entryCount;
543
reed@android.com3c9b2a42009-08-27 19:28:37 +0000544 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000545 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000546 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000548 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000549 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000550 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 Rec* rec = fRecs;
552 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000553 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000554 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 SkASSERT(nextIndex < kCache16Count);
556
557 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000558 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 prevIndex = nextIndex;
560 }
561 SkASSERT(prevIndex == kCache16Count - 1);
562 }
563
reed@android.com41bccf52009-04-03 13:33:51 +0000564 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000565 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 uint16_t* linear = fCache16; // just computed linear data
567 uint16_t* mapped = fCache16Storage; // storage for mapped data
568 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000569 for (int i = 0; i < kCache16Count; i++) {
570 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000572 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 }
574 sk_free(fCache16);
575 fCache16 = fCache16Storage;
576 }
577 }
578 return fCache16;
579}
580
reed@google.com7c2f27d2011-03-07 19:29:00 +0000581const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000582 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000583 // double the count for dither entries
584 const int entryCount = kCache32Count * 2;
585 const size_t allocSize = sizeof(SkPMColor) * entryCount;
586
reed@google.comdc731fd2010-12-23 15:19:47 +0000587 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000588 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
589 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000590 }
591 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000592 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000593 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
594 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000595 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 Rec* rec = fRecs;
597 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000598 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
600 SkASSERT(nextIndex < kCache32Count);
601
602 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000603 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
604 fOrigColors[i],
605 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 prevIndex = nextIndex;
607 }
608 SkASSERT(prevIndex == kCache32Count - 1);
609 }
610
reed@android.com41bccf52009-04-03 13:33:51 +0000611 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000612 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000613 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000615 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000617 for (int i = 0; i < kCache32Count; i++) {
618 int index = map->mapUnit16((i << 8) | i) >> 8;
619 mapped[i] = linear[index];
620 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000621 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000622 fCache32PixelRef->unref();
623 fCache32PixelRef = newPR;
624 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 }
626 }
627 return fCache32;
628}
629
reed@google.comdc731fd2010-12-23 15:19:47 +0000630/*
631 * Because our caller might rebuild the same (logically the same) gradient
632 * over and over, we'd like to return exactly the same "bitmap" if possible,
633 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
634 * To do that, we maintain a private cache of built-bitmaps, based on our
635 * colors and positions. Note: we don't try to flatten the fMapper, so if one
636 * is present, we skip the cache for now.
637 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000638void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.comdc731fd2010-12-23 15:19:47 +0000639 // don't have a way to put the mapper into our cache-key yet
640 if (fMapper) {
641 // force our cahce32pixelref to be built
642 (void)this->getCache32();
643 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
644 bitmap->setPixelRef(fCache32PixelRef);
645 return;
646 }
647
648 // build our key: [numColors + colors[] + {positions[]} ]
649 int count = 1 + fColorCount;
650 if (fColorCount > 2) {
651 count += fColorCount - 1; // fRecs[].fPos
652 }
653
654 SkAutoSTMalloc<16, int32_t> storage(count);
655 int32_t* buffer = storage.get();
656
657 *buffer++ = fColorCount;
658 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
659 buffer += fColorCount;
660 if (fColorCount > 2) {
661 for (int i = 1; i < fColorCount; i++) {
662 *buffer++ = fRecs[i].fPos;
663 }
664 }
665 SkASSERT(buffer - storage.get() == count);
666
667 ///////////////////////////////////
668
669 static SkMutex gMutex;
670 static SkBitmapCache* gCache;
671 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
672 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
673 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000674
reed@google.comdc731fd2010-12-23 15:19:47 +0000675 if (NULL == gCache) {
676 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
677 }
678 size_t size = count * sizeof(int32_t);
679
680 if (!gCache->find(storage.get(), size, bitmap)) {
681 // force our cahce32pixelref to be built
682 (void)this->getCache32();
683 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
684 bitmap->setPixelRef(fCache32PixelRef);
685
686 gCache->add(storage.get(), size, *bitmap);
687 }
688}
689
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000690void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
691 if (info) {
692 if (info->fColorCount >= fColorCount) {
693 if (info->fColors) {
694 memcpy(info->fColors, fOrigColors,
695 fColorCount * sizeof(SkColor));
696 }
697 if (info->fColorOffsets) {
698 if (fColorCount == 2) {
699 info->fColorOffsets[0] = 0;
700 info->fColorOffsets[1] = SK_Scalar1;
701 } else if (fColorCount > 2) {
702 for (int i = 0; i < fColorCount; i++)
703 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
704 }
705 }
706 }
707 info->fColorCount = fColorCount;
708 info->fTileMode = fTileMode;
709 }
710}
711
reed@google.com61eb0402011-04-15 12:11:12 +0000712///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713
reed@android.com41bccf52009-04-03 13:33:51 +0000714static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 SkVector vec = pts[1] - pts[0];
716 SkScalar mag = vec.length();
717 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
718
719 vec.scale(inv);
720 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
721 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
722 matrix->postScale(inv, inv);
723}
724
725///////////////////////////////////////////////////////////////////////////////
726
727class Linear_Gradient : public Gradient_Shader {
728public:
729 Linear_Gradient(const SkPoint pts[2],
730 const SkColor colors[], const SkScalar pos[], int colorCount,
731 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000732 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
733 fStart(pts[0]),
734 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 {
736 pts_to_unit_matrix(pts, &fPtsToUnit);
737 }
reed@android.com9b46e772009-06-05 12:24:41 +0000738
reed@android.com5119bdb2009-06-12 21:27:03 +0000739 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
741 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000742 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*,
reed@google.com7c2f27d2011-03-07 19:29:00 +0000743 TileMode*, SkScalar* twoPointRadialParams) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000744 virtual GradientType asAGradient(GradientInfo* info) const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745
reed@google.com55b8e8c2011-01-13 16:22:35 +0000746 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747 return SkNEW_ARGS(Linear_Gradient, (buffer));
748 }
749
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000750 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
751 this->INHERITED::flatten(buffer);
752 buffer.writeScalar(fStart.fX);
753 buffer.writeScalar(fStart.fY);
754 buffer.writeScalar(fEnd.fX);
755 buffer.writeScalar(fEnd.fY);
756 }
757
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000759 Linear_Gradient(SkFlattenableReadBuffer& buffer)
760 : Gradient_Shader(buffer),
761 fStart(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
762 fEnd(SkPoint::Make(buffer.readScalar(), buffer.readScalar())) {
763 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 virtual Factory getFactory() { return CreateProc; }
765
766private:
767 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000768 const SkPoint fStart;
769 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770};
771
reed@android.com5119bdb2009-06-12 21:27:03 +0000772bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
773 const SkMatrix& matrix) {
774 if (!this->INHERITED::setContext(device, paint, matrix)) {
775 return false;
776 }
777
778 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
779 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000780 fFlags |= SkShader::kConstInY32_Flag;
781 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
782 // only claim this if we do have a 16bit mode (i.e. none of our
783 // colors have alpha), and if we are not dithering (which obviously
784 // is not const in Y).
785 fFlags |= SkShader::kConstInY16_Flag;
786 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000787 }
788 return true;
789}
790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@google.com61eb0402011-04-15 12:11:12 +0000792static inline bool no_need_for_clamp(int fx, int dx, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 SkASSERT(count > 0);
794 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
795}
796
reed@google.com61eb0402011-04-15 12:11:12 +0000797void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 SkASSERT(count > 0);
799
800 SkPoint srcPt;
801 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
802 TileProc proc = fTileProc;
803 const SkPMColor* cache = this->getCache32();
reed@google.com55b8e8c2011-01-13 16:22:35 +0000804#ifdef USE_DITHER_32BIT_GRADIENT
805 int toggle = ((x ^ y) & 1) << kCache32Bits;
806 const int TOGGLE_MASK = (1 << kCache32Bits);
807#else
808 int toggle = 0;
809 const int TOGGLE_MASK = 0;
810#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811
reed@android.comc552a432009-06-12 20:02:50 +0000812 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000813 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
814 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
816
reed@android.comc552a432009-06-12 20:02:50 +0000817 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 SkFixed dxStorage[1];
819 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
820 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000821 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
823 dx = SkScalarToFixed(fDstToIndex.getScaleX());
824 }
825
reed@android.comc552a432009-06-12 20:02:50 +0000826 if (SkFixedNearlyZero(dx)) {
827 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 unsigned fi = proc(fx);
829 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000830 // TODO: dither version
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000832 } else if (proc == clamp_tileproc) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000833 do {
834 unsigned fi = SkClampMax(fx >> 8, 0xFF);
835 SkASSERT(fi <= 0xFF);
836 fx += dx;
837 *dstC++ = cache[toggle + fi];
838 toggle ^= TOGGLE_MASK;
839 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000840 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 do {
842 unsigned fi = mirror_8bits(fx >> 8);
843 SkASSERT(fi <= 0xFF);
844 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000845 *dstC++ = cache[toggle + fi];
846 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000848 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 SkASSERT(proc == repeat_tileproc);
850 do {
851 unsigned fi = repeat_8bits(fx >> 8);
852 SkASSERT(fi <= 0xFF);
853 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000854 *dstC++ = cache[toggle + fi];
855 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 } while (--count != 0);
857 }
reed@android.comc552a432009-06-12 20:02:50 +0000858 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 SkScalar dstX = SkIntToScalar(x);
860 SkScalar dstY = SkIntToScalar(y);
861 do {
862 dstProc(fDstToIndex, dstX, dstY, &srcPt);
863 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
864 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000865 *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))];
866 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 dstX += SK_Scalar1;
868 } while (--count != 0);
869 }
870}
871
reed@google.com55b8e8c2011-01-13 16:22:35 +0000872SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000873 SkMatrix* matrix,
874 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +0000875 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000877 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 }
879 if (matrix) {
880 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
881 matrix->preConcat(fPtsToUnit);
882 }
883 if (xy) {
884 xy[0] = fTileMode;
885 xy[1] = kClamp_TileMode;
886 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000887 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888}
889
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000890SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
891 if (info) {
892 commonAsAGradient(info);
893 info->fPoint[0] = fStart;
894 info->fPoint[1] = fEnd;
895 }
896 return kLinear_GradientType;
897}
898
reed@android.com3c9b2a42009-08-27 19:28:37 +0000899static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
900 int count) {
901 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 *dst++ = value;
903 count -= 1;
904 SkTSwap(value, other);
905 }
906
907 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000908
reed@android.com3c9b2a42009-08-27 19:28:37 +0000909 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000911 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913
reed@google.com61eb0402011-04-15 12:11:12 +0000914void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 SkASSERT(count > 0);
916
917 SkPoint srcPt;
918 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
919 TileProc proc = fTileProc;
920 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000923 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000924 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
925 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
927
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000928 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 SkFixed dxStorage[1];
930 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
931 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000932 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
934 dx = SkScalarToFixed(fDstToIndex.getScaleX());
935 }
936
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000937 if (SkFixedNearlyZero(dx)) {
938 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +0000939 unsigned fi = proc(fx) >> kCache16Shift;
940 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000942 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 do {
reed@android.com512a8762009-12-14 15:25:36 +0000944 unsigned fi = SkClampMax(fx >> kCache16Shift, kCache16Mask);
945 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 *dstC++ = cache[toggle + fi];
948 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000950 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 do {
reed@android.com200645d2009-12-14 16:41:57 +0000952 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +0000953 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 *dstC++ = cache[toggle + fi];
956 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000958 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 SkASSERT(proc == repeat_tileproc);
960 do {
reed@android.com200645d2009-12-14 16:41:57 +0000961 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +0000962 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 *dstC++ = cache[toggle + fi];
965 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 } while (--count != 0);
967 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000968 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969 SkScalar dstX = SkIntToScalar(x);
970 SkScalar dstY = SkIntToScalar(y);
971 do {
972 dstProc(fDstToIndex, dstX, dstY, &srcPt);
973 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
974 SkASSERT(fi <= 0xFFFF);
975
reed@android.com512a8762009-12-14 15:25:36 +0000976 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 *dstC++ = cache[toggle + index];
978 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979
980 dstX += SK_Scalar1;
981 } while (--count != 0);
982 }
983}
984
985///////////////////////////////////////////////////////////////////////////////
986
987#define kSQRT_TABLE_BITS 11
988#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
989
990#include "SkRadialGradient_Table.h"
991
992#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
993
994#include <stdio.h>
995
reed@google.com61eb0402011-04-15 12:11:12 +0000996void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
998
999 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1000 SkASSERT(file);
1001 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1002
reed@google.com61eb0402011-04-15 12:11:12 +00001003 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1004 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001006 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007
1008 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1009
1010 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001011 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001013 }
1014 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001016 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 }
1018 ::fprintf(file, "};\n");
1019 ::fclose(file);
1020}
1021
1022#endif
1023
1024
reed@google.com61eb0402011-04-15 12:11:12 +00001025static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1026 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 SkScalar inv = SkScalarInvert(radius);
1028
1029 matrix->setTranslate(-center.fX, -center.fY);
1030 matrix->postScale(inv, inv);
1031}
1032
1033class Radial_Gradient : public Gradient_Shader {
1034public:
1035 Radial_Gradient(const SkPoint& center, SkScalar radius,
1036 const SkColor colors[], const SkScalar pos[], int colorCount,
1037 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001038 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1039 fCenter(center),
1040 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 {
1042 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1043 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1044
1045 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1046 }
reed@google.com61eb0402011-04-15 12:11:12 +00001047
1048 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 SkASSERT(count > 0);
1050
1051 SkPoint srcPt;
1052 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1053 TileProc proc = fTileProc;
1054 const SkPMColor* cache = this->getCache32();
1055
reed@google.com61eb0402011-04-15 12:11:12 +00001056 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001057 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1058 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1060 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1061
reed@google.com61eb0402011-04-15 12:11:12 +00001062 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 SkFixed storage[2];
1064 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1065 dx = storage[0];
1066 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00001067 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1069 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1070 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1071 }
1072
reed@google.com61eb0402011-04-15 12:11:12 +00001073 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 const uint8_t* sqrt_table = gSqrt8Table;
1075 fx >>= 1;
1076 dx >>= 1;
1077 fy >>= 1;
1078 dy >>= 1;
1079 do {
1080 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1081 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1082 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1083 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1084 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1085 fx += dx;
1086 fy += dy;
1087 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001088 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001090 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1091 if (magnitudeSquared < 0) // Overflow.
1092 magnitudeSquared = SK_FixedMax;
1093 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 unsigned fi = mirror_tileproc(dist);
1095 SkASSERT(fi <= 0xFFFF);
1096 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1097 fx += dx;
1098 fy += dy;
1099 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001100 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 SkASSERT(proc == repeat_tileproc);
1102 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001103 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1104 if (magnitudeSquared < 0) // Overflow.
1105 magnitudeSquared = SK_FixedMax;
1106 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 unsigned fi = repeat_tileproc(dist);
1108 SkASSERT(fi <= 0xFFFF);
1109 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1110 fx += dx;
1111 fy += dy;
1112 } while (--count != 0);
1113 }
reed@google.com61eb0402011-04-15 12:11:12 +00001114 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 SkScalar dstX = SkIntToScalar(x);
1116 SkScalar dstY = SkIntToScalar(y);
1117 do {
1118 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1119 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1120 SkASSERT(fi <= 0xFFFF);
1121 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1122 dstX += SK_Scalar1;
1123 } while (--count != 0);
1124 }
1125 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001126
1127 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 SkASSERT(count > 0);
1129
1130 SkPoint srcPt;
1131 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1132 TileProc proc = fTileProc;
1133 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135
reed@android.com3c9b2a42009-08-27 19:28:37 +00001136 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001137 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1138 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1140 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1141
reed@android.com3c9b2a42009-08-27 19:28:37 +00001142 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 SkFixed storage[2];
1144 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1145 dx = storage[0];
1146 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001147 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1149 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1150 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1151 }
1152
reed@android.com3c9b2a42009-08-27 19:28:37 +00001153 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 const uint8_t* sqrt_table = gSqrt8Table;
1155
1156 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1157 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1158 precision, but that appears to be visually OK. If we decide this is OK for
1159 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1160 to avoid having to do these extra shifts each time.
1161 */
1162 fx >>= 1;
1163 dx >>= 1;
1164 fy >>= 1;
1165 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001166 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 +00001167 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1168 fy *= fy;
1169 do {
1170 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1171 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1172 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1173 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1175 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001177 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 do {
1179 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1180 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1181 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1182 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1183 fx += dx;
1184 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1186 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 } while (--count != 0);
1188 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001189 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 do {
1191 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1192 unsigned fi = mirror_tileproc(dist);
1193 SkASSERT(fi <= 0xFFFF);
1194 fx += dx;
1195 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1197 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001199 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 SkASSERT(proc == repeat_tileproc);
1201 do {
1202 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1203 unsigned fi = repeat_tileproc(dist);
1204 SkASSERT(fi <= 0xFFFF);
1205 fx += dx;
1206 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1208 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 } while (--count != 0);
1210 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001211 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 SkScalar dstX = SkIntToScalar(x);
1213 SkScalar dstY = SkIntToScalar(y);
1214 do {
1215 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1216 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1217 SkASSERT(fi <= 0xFFFF);
1218
1219 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 *dstC++ = cache[toggle + index];
1221 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222
1223 dstX += SK_Scalar1;
1224 } while (--count != 0);
1225 }
1226 }
1227
reed@google.com55b8e8c2011-01-13 16:22:35 +00001228 virtual BitmapType asABitmap(SkBitmap* bitmap,
1229 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001230 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001231 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001232 if (bitmap) {
1233 this->commonAsABitmap(bitmap);
1234 }
1235 if (matrix) {
1236 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1237 matrix->preConcat(fPtsToUnit);
1238 }
1239 if (xy) {
1240 xy[0] = fTileMode;
1241 xy[1] = kClamp_TileMode;
1242 }
1243 return kRadial_BitmapType;
1244 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001245 virtual GradientType asAGradient(GradientInfo* info) const {
1246 if (info) {
1247 commonAsAGradient(info);
1248 info->fPoint[0] = fCenter;
1249 info->fRadius[0] = fRadius;
1250 }
1251 return kRadial_GradientType;
1252 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001253
1254 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 return SkNEW_ARGS(Radial_Gradient, (buffer));
1256 }
1257
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001258 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1259 this->INHERITED::flatten(buffer);
1260 buffer.writeScalar(fCenter.fX);
1261 buffer.writeScalar(fCenter.fY);
1262 buffer.writeScalar(fRadius);
1263 }
1264
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001266 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1267 : Gradient_Shader(buffer),
1268 fCenter(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1269 fRadius(buffer.readScalar()) {
1270 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 virtual Factory getFactory() { return CreateProc; }
1272
1273private:
1274 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001275 const SkPoint fCenter;
1276 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277};
1278
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001279/* Two-point radial gradients are specified by two circles, each with a center
1280 point and radius. The gradient can be considered to be a series of
1281 concentric circles, with the color interpolated from the start circle
1282 (at t=0) to the end circle (at t=1).
1283
1284 For each point (x, y) in the span, we want to find the
1285 interpolated circle that intersects that point. The center
1286 of the desired circle (Cx, Cy) falls at some distance t
1287 along the line segment between the start point (Sx, Sy) and
1288 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001289
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001290 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1291 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001292
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001293 The radius of the desired circle (r) is also a linear interpolation t
1294 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001295
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001296 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001297
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001298 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001299
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001300 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001301
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001302 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001303
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001304 (x - ((1 - t) * Sx + t * Ex))^2
1305 + (y - ((1 - t) * Sy + t * Ey))^2
1306 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001307
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001308 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001309
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001310 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1311 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1312 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001313
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001314 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1315
1316 [Dx^2 + Dy^2 - Dr^2)] * t^2
1317 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1318 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001319
1320 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001321 possible circles on which the point may fall. Solving for t yields
1322 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001323
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001324 If a<0, the start circle is entirely contained in the
1325 end circle, and one of the roots will be <0 or >1 (off the line
1326 segment). If a>0, the start circle falls at least partially
1327 outside the end circle (or vice versa), and the gradient
1328 defines a "tube" where a point may be on one circle (on the
1329 inside of the tube) or the other (outside of the tube). We choose
1330 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001331
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001332 In order to keep the math to within the limits of fixed point,
1333 we divide the entire quadratic by Dr^2, and replace
1334 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001335
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001336 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1337 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1338 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001339
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001340 (x' and y' are computed by appending the subtract and scale to the
1341 fDstToIndex matrix in the constructor).
1342
1343 Since the 'A' component of the quadratic is independent of x' and y', it
1344 is precomputed in the constructor. Since the 'B' component is linear in
1345 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001346 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001347 a perspective projection), it must be computed in the loop.
1348
1349*/
1350
reed@google.com84e9c082011-04-13 17:44:24 +00001351static inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1352 SkScalar sr2d2, SkScalar foura,
1353 SkScalar oneOverTwoA, bool posRoot) {
1354 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
1355 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001356 if (discrim < 0) {
1357 discrim = -discrim;
1358 }
reed@google.com84e9c082011-04-13 17:44:24 +00001359 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1360 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001361 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001362 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001363 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001364 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001365 }
reed@google.com84e9c082011-04-13 17:44:24 +00001366 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001367}
1368
1369class Two_Point_Radial_Gradient : public Gradient_Shader {
1370public:
1371 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1372 const SkPoint& end, SkScalar endRadius,
1373 const SkColor colors[], const SkScalar pos[],
1374 int colorCount, SkShader::TileMode mode,
1375 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001376 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1377 fCenter1(start),
1378 fCenter2(end),
1379 fRadius1(startRadius),
1380 fRadius2(endRadius) {
1381 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001382 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001383
1384 virtual BitmapType asABitmap(SkBitmap* bitmap,
1385 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001386 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001387 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001388 if (bitmap) {
1389 this->commonAsABitmap(bitmap);
1390 }
1391 SkScalar diffL = 0; // just to avoid gcc warning
1392 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001393 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001394 SkScalarSquare(fDiff.fY));
1395 }
1396 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001397 if (diffL) {
1398 SkScalar invDiffL = SkScalarInvert(diffL);
1399 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1400 SkScalarMul(invDiffL, fDiff.fX));
1401 } else {
1402 matrix->reset();
1403 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001404 matrix->preConcat(fPtsToUnit);
1405 }
1406 if (xy) {
1407 xy[0] = fTileMode;
1408 xy[1] = kClamp_TileMode;
1409 }
1410 if (NULL != twoPointRadialParams) {
1411 twoPointRadialParams[0] = diffL;
1412 twoPointRadialParams[1] = fStartRadius;
1413 twoPointRadialParams[2] = fDiffRadius;
1414 }
1415 return kTwoPointRadial_BitmapType;
1416 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001417
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001418 virtual GradientType asAGradient(GradientInfo* info) const {
1419 if (info) {
1420 commonAsAGradient(info);
1421 info->fPoint[0] = fCenter1;
1422 info->fPoint[1] = fCenter2;
1423 info->fRadius[0] = fRadius1;
1424 info->fRadius[1] = fRadius2;
1425 }
1426 return kRadial2_GradientType;
1427 }
1428
reed@google.com61eb0402011-04-15 12:11:12 +00001429 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001430 SkASSERT(count > 0);
1431
1432 // Zero difference between radii: fill with transparent black.
1433 if (fDiffRadius == 0) {
1434 sk_bzero(dstC, count * sizeof(*dstC));
1435 return;
1436 }
1437 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1438 TileProc proc = fTileProc;
1439 const SkPMColor* cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001440
1441 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001442 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001443 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001444 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001445 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1446 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001447 SkScalar dx, fx = srcPt.fX;
1448 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001449
reed@google.com61eb0402011-04-15 12:11:12 +00001450 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001451 SkFixed fixedX, fixedY;
1452 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1453 dx = SkFixedToScalar(fixedX);
1454 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001455 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001456 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001457 dx = fDstToIndex.getScaleX();
1458 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001459 }
reed@google.com84e9c082011-04-13 17:44:24 +00001460 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1461 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1462 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1463 SkScalarMul(fDiff.fY, dy)) * 2;
reed@google.com61eb0402011-04-15 12:11:12 +00001464 if (proc == clamp_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001465 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001466 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001467 SkFixed index = SkClampMax(t, 0xFFFF);
1468 SkASSERT(index <= 0xFFFF);
1469 *dstC++ = cache[index >> (16 - kCache32Bits)];
1470 fx += dx;
1471 fy += dy;
1472 b += db;
1473 }
reed@google.com61eb0402011-04-15 12:11:12 +00001474 } else if (proc == mirror_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001475 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001476 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001477 SkFixed index = mirror_tileproc(t);
1478 SkASSERT(index <= 0xFFFF);
1479 *dstC++ = cache[index >> (16 - kCache32Bits)];
1480 fx += dx;
1481 fy += dy;
1482 b += db;
1483 }
reed@google.com61eb0402011-04-15 12:11:12 +00001484 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001485 SkASSERT(proc == repeat_tileproc);
1486 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001487 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001488 SkFixed index = repeat_tileproc(t);
1489 SkASSERT(index <= 0xFFFF);
1490 *dstC++ = cache[index >> (16 - kCache32Bits)];
1491 fx += dx;
1492 fy += dy;
1493 b += db;
1494 }
1495 }
reed@google.com61eb0402011-04-15 12:11:12 +00001496 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001497 SkScalar dstX = SkIntToScalar(x);
1498 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001499 for (; count > 0; --count) {
1500 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001501 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001502 SkScalar fx = srcPt.fX;
1503 SkScalar fy = srcPt.fY;
1504 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1505 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1506 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001507 SkFixed index = proc(t);
1508 SkASSERT(index <= 0xFFFF);
1509 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001510 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001511 }
1512 }
1513 }
1514
reed@android.com6c59a172009-09-22 20:24:05 +00001515 virtual bool setContext(const SkBitmap& device,
1516 const SkPaint& paint,
1517 const SkMatrix& matrix) {
1518 if (!this->INHERITED::setContext(device, paint, matrix)) {
1519 return false;
1520 }
1521
1522 // we don't have a span16 proc
1523 fFlags &= ~kHasSpan16_Flag;
1524 return true;
1525 }
1526
reed@google.com55b8e8c2011-01-13 16:22:35 +00001527 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001528 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1529 }
1530
reed@android.combcfc7332009-11-10 16:19:39 +00001531 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1532 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001533 buffer.writeScalar(fCenter1.fX);
1534 buffer.writeScalar(fCenter1.fY);
1535 buffer.writeScalar(fCenter2.fX);
1536 buffer.writeScalar(fCenter2.fY);
1537 buffer.writeScalar(fRadius1);
1538 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001539 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001540
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001541protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001542 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001543 : Gradient_Shader(buffer),
1544 fCenter1(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1545 fCenter2(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1546 fRadius1(buffer.readScalar()),
1547 fRadius2(buffer.readScalar()) {
1548 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001549 };
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001550 virtual Factory getFactory() { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001551
1552private:
1553 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001554 const SkPoint fCenter1;
1555 const SkPoint fCenter2;
1556 const SkScalar fRadius1;
1557 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001558 SkPoint fDiff;
1559 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001560
1561 void init() {
1562 fDiff = fCenter1 - fCenter2;
1563 fDiffRadius = fRadius2 - fRadius1;
1564 SkScalar inv = SkScalarInvert(fDiffRadius);
1565 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1566 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1567 fStartRadius = SkScalarMul(fRadius1, inv);
1568 fSr2D2 = SkScalarSquare(fStartRadius);
1569 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
1570 fOneOverTwoA = SkScalarInvert(fA * 2);
1571
1572 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1573 fPtsToUnit.postScale(inv, inv);
1574 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001575};
1576
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577///////////////////////////////////////////////////////////////////////////////
1578
1579class Sweep_Gradient : public Gradient_Shader {
1580public:
1581 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1582 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001583 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
1584 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 {
1586 fPtsToUnit.setTranslate(-cx, -cy);
1587 }
1588 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1589 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001590
1591 virtual BitmapType asABitmap(SkBitmap* bitmap,
1592 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001593 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001594 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001595 if (bitmap) {
1596 this->commonAsABitmap(bitmap);
1597 }
1598 if (matrix) {
1599 *matrix = fPtsToUnit;
1600 }
1601 if (xy) {
1602 xy[0] = fTileMode;
1603 xy[1] = kClamp_TileMode;
1604 }
1605 return kSweep_BitmapType;
1606 }
1607
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001608 virtual GradientType asAGradient(GradientInfo* info) const {
1609 if (info) {
1610 commonAsAGradient(info);
1611 info->fPoint[0] = fCenter;
1612 }
1613 return kSweep_GradientType;
1614 }
1615
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1617 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1618 }
1619
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001620 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1621 this->INHERITED::flatten(buffer);
1622 buffer.writeScalar(fCenter.fX);
1623 buffer.writeScalar(fCenter.fY);
1624 }
1625
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001627 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
1628 : Gradient_Shader(buffer),
1629 fCenter(SkPoint::Make(buffer.readScalar(), buffer.readScalar())) {
1630 }
1631
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 virtual Factory getFactory() { return CreateProc; }
1633
1634private:
1635 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001636 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637};
1638
1639#ifdef COMPUTE_SWEEP_TABLE
1640#define PI 3.14159265
1641static bool gSweepTableReady;
1642static uint8_t gSweepTable[65];
1643
1644/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1645 We scale the results to [0..32]
1646*/
reed@google.com61eb0402011-04-15 12:11:12 +00001647static const uint8_t* build_sweep_table() {
1648 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649 const int N = 65;
1650 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001651
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 for (int i = 0; i < N; i++)
1653 {
1654 double arg = i / DENOM;
1655 double v = atan(arg);
1656 int iv = (int)round(v * DENOM * 2 / PI);
1657// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1658 printf("%d, ", iv);
1659 gSweepTable[i] = iv;
1660 }
1661 gSweepTableReady = true;
1662 }
1663 return gSweepTable;
1664}
1665#else
1666static const uint8_t gSweepTable[] = {
1667 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1668 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1669 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1670 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1671 32
1672};
1673static const uint8_t* build_sweep_table() { return gSweepTable; }
1674#endif
1675
1676// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1677// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1678// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1679
1680//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00001681static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682 SkASSERT(numer <= denom);
1683 SkASSERT(numer > 0);
1684 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001685
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 int nbits = SkCLZ(numer);
1687 int dbits = SkCLZ(denom);
1688 int bits = 6 - nbits + dbits;
1689 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001690
reed@google.com61eb0402011-04-15 12:11:12 +00001691 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001693 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694
1695 denom <<= dbits - 1;
1696 numer <<= nbits - 1;
1697
1698 unsigned result = 0;
1699
1700 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00001701 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00001703 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00001705 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001706
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00001708 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709 // make room for the rest of the answer bits
1710 result <<= bits;
1711 switch (bits) {
1712 case 6:
1713 if ((numer = (numer << 1) - denom) >= 0)
1714 result |= 32;
1715 else
1716 numer += denom;
1717 case 5:
1718 if ((numer = (numer << 1) - denom) >= 0)
1719 result |= 16;
1720 else
1721 numer += denom;
1722 case 4:
1723 if ((numer = (numer << 1) - denom) >= 0)
1724 result |= 8;
1725 else
1726 numer += denom;
1727 case 3:
1728 if ((numer = (numer << 1) - denom) >= 0)
1729 result |= 4;
1730 else
1731 numer += denom;
1732 case 2:
1733 if ((numer = (numer << 1) - denom) >= 0)
1734 result |= 2;
1735 else
1736 numer += denom;
1737 case 1:
1738 default: // not strictly need, but makes GCC make better ARM code
1739 if ((numer = (numer << 1) - denom) >= 0)
1740 result |= 1;
1741 else
1742 numer += denom;
1743 }
1744 }
1745 return result;
1746}
1747
1748// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00001749static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750#ifdef SK_DEBUG
1751 {
1752 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00001753 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 gOnce = true;
1755 SkASSERT(div_64(55, 55) == 64);
1756 SkASSERT(div_64(128, 256) == 32);
1757 SkASSERT(div_64(2326528, 4685824) == 31);
1758 SkASSERT(div_64(753664, 5210112) == 9);
1759 SkASSERT(div_64(229376, 4882432) == 3);
1760 SkASSERT(div_64(2, 64) == 2);
1761 SkASSERT(div_64(1, 64) == 1);
1762 // test that we handle underflow correctly
1763 SkASSERT(div_64(12345, 0x54321234) == 0);
1764 }
1765 }
1766#endif
1767
1768 SkASSERT(y > 0 && x > 0);
1769 const uint8_t* table = build_sweep_table();
1770
1771 unsigned result;
1772 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00001773 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 // first part of the atan(v) = PI/2 - atan(1/v) identity
1775 // since our div_64 and table want v <= 1, where v = y/x
1776 SkTSwap<SkFixed>(x, y);
1777 }
1778
1779 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001780
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781#ifdef SK_DEBUG
1782 {
1783 unsigned result2 = SkDivBits(y, x, 6);
1784 SkASSERT(result2 == result ||
1785 (result == 1 && result2 == 0));
1786 }
1787#endif
1788
1789 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1790 result = table[result];
1791
reed@google.com61eb0402011-04-15 12:11:12 +00001792 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793 // complete the atan(v) = PI/2 - atan(1/v) identity
1794 result = 64 - result;
1795 // pin to 63
1796 result -= result >> 6;
1797 }
1798
1799 SkASSERT(result <= 63);
1800 return result;
1801}
1802
1803// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com61eb0402011-04-15 12:11:12 +00001804static unsigned SkATan2_255(SkFixed y, SkFixed x) {
1805 if (x == 0) {
1806 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001808 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809 return y < 0 ? 192 : 64;
1810 }
reed@google.com61eb0402011-04-15 12:11:12 +00001811 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001813 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001814
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 /* Find the right quadrant for x,y
1816 Since atan_0_90 only handles the first quadrant, we rotate x,y
1817 appropriately before calling it, and then add the right amount
1818 to account for the real quadrant.
1819 quadrant 0 : add 0 | x > 0 && y > 0
1820 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1821 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1822 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001823
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824 map x<0 to (1 << 6)
1825 map y<0 to (3 << 6)
1826 add = map_x ^ map_y
1827 */
1828 int xsign = x >> 31;
1829 int ysign = y >> 31;
1830 int add = ((-xsign) ^ (ysign & 3)) << 6;
1831
1832#ifdef SK_DEBUG
1833 if (0 == add)
1834 SkASSERT(x > 0 && y > 0);
1835 else if (64 == add)
1836 SkASSERT(x < 0 && y > 0);
1837 else if (128 == add)
1838 SkASSERT(x < 0 && y < 0);
1839 else if (192 == add)
1840 SkASSERT(x > 0 && y < 0);
1841 else
1842 SkASSERT(!"bad value for add");
1843#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00001844
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1846 where we need to rotate x,y by 90 or -90
1847 */
1848 x = (x ^ xsign) - xsign;
1849 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00001850 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00001852 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853
1854 unsigned result = add + atan_0_90(y, x);
1855 SkASSERT(result < 256);
1856 return result;
1857}
1858
reed@google.com61eb0402011-04-15 12:11:12 +00001859void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001860 SkMatrix::MapXYProc proc = fDstToIndexProc;
1861 const SkMatrix& matrix = fDstToIndex;
1862 const SkPMColor* cache = this->getCache32();
1863 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001864
reed@google.com61eb0402011-04-15 12:11:12 +00001865 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1867 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1868 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1869 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001870
reed@google.com61eb0402011-04-15 12:11:12 +00001871 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872 SkFixed storage[2];
1873 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1874 &storage[0], &storage[1]);
1875 dx = storage[0];
1876 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00001877 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1879 dx = SkScalarToFixed(matrix.getScaleX());
1880 dy = SkScalarToFixed(matrix.getSkewY());
1881 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001882
reed@google.com61eb0402011-04-15 12:11:12 +00001883 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001884 *dstC++ = cache[SkATan2_255(fy, fx)];
1885 fx += dx;
1886 fy += dy;
1887 }
reed@google.com61eb0402011-04-15 12:11:12 +00001888 } else { // perspective case
1889 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1891 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001892
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1894 SkScalarToFixed(srcPt.fX));
1895 *dstC++ = cache[index];
1896 }
1897 }
1898}
1899
reed@google.com61eb0402011-04-15 12:11:12 +00001900void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901 SkMatrix::MapXYProc proc = fDstToIndexProc;
1902 const SkMatrix& matrix = fDstToIndex;
1903 const uint16_t* cache = this->getCache16();
1904 int toggle = ((x ^ y) & 1) << kCache16Bits;
1905 SkPoint srcPt;
1906
reed@google.com61eb0402011-04-15 12:11:12 +00001907 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1909 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1910 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1911 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001912
reed@google.com61eb0402011-04-15 12:11:12 +00001913 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914 SkFixed storage[2];
1915 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1916 &storage[0], &storage[1]);
1917 dx = storage[0];
1918 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00001919 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1921 dx = SkScalarToFixed(matrix.getScaleX());
1922 dy = SkScalarToFixed(matrix.getSkewY());
1923 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001924
reed@google.com61eb0402011-04-15 12:11:12 +00001925 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1927 *dstC++ = cache[toggle + index];
1928 toggle ^= (1 << kCache16Bits);
1929 fx += dx;
1930 fy += dy;
1931 }
reed@google.com61eb0402011-04-15 12:11:12 +00001932 } else { // perspective case
1933 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1935 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001936
reed@android.com8a1c16f2008-12-17 15:59:43 +00001937 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1938 SkScalarToFixed(srcPt.fX));
1939 index >>= (8 - kCache16Bits);
1940 *dstC++ = cache[toggle + index];
1941 toggle ^= (1 << kCache16Bits);
1942 }
1943 }
1944}
1945
reed@google.com61eb0402011-04-15 12:11:12 +00001946///////////////////////////////////////////////////////////////////////////////
1947///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948
1949// assumes colors is SkColor* and pos is SkScalar*
1950#define EXPAND_1_COLOR(count) \
1951 SkColor tmp[2]; \
1952 do { \
1953 if (1 == count) { \
1954 tmp[0] = tmp[1] = colors[0]; \
1955 colors = tmp; \
1956 pos = NULL; \
1957 count = 2; \
1958 } \
1959 } while (0)
1960
reed@google.com61eb0402011-04-15 12:11:12 +00001961SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
1962 const SkColor colors[],
1963 const SkScalar pos[], int colorCount,
1964 SkShader::TileMode mode,
1965 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 if (NULL == pts || NULL == colors || colorCount < 1) {
1967 return NULL;
1968 }
1969 EXPAND_1_COLOR(colorCount);
1970
reed@android.comab840b82009-07-01 17:00:03 +00001971 return SkNEW_ARGS(Linear_Gradient,
1972 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973}
1974
reed@google.com61eb0402011-04-15 12:11:12 +00001975SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
1976 const SkColor colors[],
1977 const SkScalar pos[], int colorCount,
1978 SkShader::TileMode mode,
1979 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980 if (radius <= 0 || NULL == colors || colorCount < 1) {
1981 return NULL;
1982 }
1983 EXPAND_1_COLOR(colorCount);
1984
reed@android.comab840b82009-07-01 17:00:03 +00001985 return SkNEW_ARGS(Radial_Gradient,
1986 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987}
1988
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001989SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
1990 SkScalar startRadius,
1991 const SkPoint& end,
1992 SkScalar endRadius,
1993 const SkColor colors[],
1994 const SkScalar pos[],
1995 int colorCount,
1996 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00001997 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001998 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
1999 return NULL;
2000 }
2001 EXPAND_1_COLOR(colorCount);
2002
2003 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002004 (start, startRadius, end, endRadius, colors, pos,
2005 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002006}
2007
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2009 const SkColor colors[],
2010 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002011 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 if (NULL == colors || count < 1) {
2013 return NULL;
2014 }
2015 EXPAND_1_COLOR(count);
2016
2017 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2018}
2019
2020static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
2021 Linear_Gradient::CreateProc);
2022
2023static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
2024 Radial_Gradient::CreateProc);
2025
2026static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
2027 Sweep_Gradient::CreateProc);
2028