blob: 36177b1b327b3dcbea2bfab3edb0d679a01eaaab [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkGradientShader.h"
11#include "SkColorPriv.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000012#include "SkMallocPixelRef.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkUnitMapper.h"
14#include "SkUtils.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000015#include "SkTemplates.h"
16#include "SkBitmapCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
reed@google.com9c7443d2011-01-17 18:46:37 +000018#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
19 #define USE_DITHER_32BIT_GRADIENT
20#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +000021
reed@google.com5eb158d2011-04-15 15:50:34 +000022static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
23 int count) {
24 if (count > 0) {
25 if (v0 == v1) {
26 sk_memset32(dst, v0, count);
27 } else {
28 int pairs = count >> 1;
29 for (int i = 0; i < pairs; i++) {
30 *dst++ = v0;
31 *dst++ = v1;
32 }
33 if (count & 1) {
34 *dst = v0;
35 }
36 }
37 }
38}
39
reed@google.com61eb0402011-04-15 12:11:12 +000040///////////////////////////////////////////////////////////////////////////////
tomhudson@google.com9ce767c2011-04-25 20:49:39 +000041// Can't use a two-argument function with side effects like this in a
42// constructor's initializer's argument list because the order of
43// evaluations in that context is undefined (and backwards on linux/gcc).
44static SkPoint unflatten_point(SkReader32& buffer) {
45 SkPoint retval;
46 retval.fX = buffer.readScalar();
47 retval.fY = buffer.readScalar();
48 return retval;
49}
50
51///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000052
53typedef SkFixed (*TileProc)(SkFixed);
54
reed@android.com41bccf52009-04-03 13:33:51 +000055static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 return SkClampMax(x, 0xFFFF);
57}
58
reed@android.com41bccf52009-04-03 13:33:51 +000059static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 return x & 0xFFFF;
61}
62
reed@android.com41bccf52009-04-03 13:33:51 +000063static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 int s = x << 15 >> 31;
65 return (x ^ s) & 0xFFFF;
66}
67
68static const TileProc gTileProcs[] = {
69 clamp_tileproc,
70 repeat_tileproc,
71 mirror_tileproc
72};
73
reed@google.com61eb0402011-04-15 12:11:12 +000074///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000075
reed@android.com200645d2009-12-14 16:41:57 +000076static inline int repeat_bits(int x, const int bits) {
77 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000078}
79
reed@android.com200645d2009-12-14 16:41:57 +000080static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000082 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000084 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085#else
reed@android.com200645d2009-12-14 16:41:57 +000086 int s = x << (31 - bits) >> 31;
87 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000088#endif
89}
90
reed@android.com41bccf52009-04-03 13:33:51 +000091static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 return x & 0xFF;
93}
94
reed@android.com41bccf52009-04-03 13:33:51 +000095static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000096#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000097 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000099 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 return x & 255;
101#else
102 int s = x << 23 >> 31;
103 return (x ^ s) & 0xFF;
104#endif
105}
106
reed@google.com61eb0402011-04-15 12:11:12 +0000107///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
109class Gradient_Shader : public SkShader {
110public:
111 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000112 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 virtual ~Gradient_Shader();
114
115 // overrides
116 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
117 virtual uint32_t getFlags() { return fFlags; }
118
119protected:
120 Gradient_Shader(SkFlattenableReadBuffer& );
121 SkUnitMapper* fMapper;
122 SkMatrix fPtsToUnit; // set by subclass
123 SkMatrix fDstToIndex;
124 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 TileMode fTileMode;
126 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000127 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 uint8_t fDstToIndexClass;
129 uint8_t fFlags;
130
131 struct Rec {
132 SkFixed fPos; // 0...1
133 uint32_t fScale; // (1 << 24) / range
134 };
135 Rec* fRecs;
136
137 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000138 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000140 kCache16Mask = kCache16Count - 1,
141 kCache16Shift = 16 - kCache16Bits,
142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 kCache32Bits = 8, // pretty much should always be 8
144 kCache32Count = 1 << kCache32Bits
145 };
146 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000147 const uint16_t* getCache16() const;
148 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149
reed@google.com7c2f27d2011-03-07 19:29:00 +0000150 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000151 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000152
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153private:
154 enum {
155 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
156
reed@android.com1c12abe2009-07-02 15:01:02 +0000157 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 };
159 SkColor fStorage[(kStorageSize + 3) >> 2];
160 SkColor* fOrigColors;
161
reed@google.com7c2f27d2011-03-07 19:29:00 +0000162 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
163 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
reed@google.com7c2f27d2011-03-07 19:29:00 +0000165 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
166 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000167 mutable unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
reed@android.com512a8762009-12-14 15:25:36 +0000169 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000170 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
171 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000172 void setCacheAlpha(U8CPU alpha) const;
reed@android.com512a8762009-12-14 15:25:36 +0000173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 typedef SkShader INHERITED;
175};
176
reed@android.com41bccf52009-04-03 13:33:51 +0000177static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 SkASSERT(x >= 0 && x <= SK_Scalar1);
179
180#ifdef SK_SCALAR_IS_FLOAT
181 return (unsigned)(x * 0xFFFF);
182#else
183 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
184#endif
185}
186
reed@android.com41bccf52009-04-03 13:33:51 +0000187Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
188 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkASSERT(colorCount > 1);
190
191 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
192
193 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000194 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
197 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
198 fTileMode = mode;
199 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000200
reed@android.com41bccf52009-04-03 13:33:51 +0000201 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000202 fCache32 = NULL;
203 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
reed@android.com41bccf52009-04-03 13:33:51 +0000205 /* Note: we let the caller skip the first and/or last position.
206 i.e. pos[0] = 0.3, pos[1] = 0.7
207 In these cases, we insert dummy entries to ensure that the final data
208 will be bracketed by [0, 1].
209 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
210
211 Thus colorCount (the caller's value, and fColorCount (our value) may
212 differ by up to 2. In the above example:
213 colorCount = 2
214 fColorCount = 4
215 */
216 fColorCount = colorCount;
217 // check if we need to add in dummy start and/or end position/colors
218 bool dummyFirst = false;
219 bool dummyLast = false;
220 if (pos) {
221 dummyFirst = pos[0] != 0;
222 dummyLast = pos[colorCount - 1] != SK_Scalar1;
223 fColorCount += dummyFirst + dummyLast;
224 }
225
226 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000227 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000228 fOrigColors = reinterpret_cast<SkColor*>(
229 sk_malloc_throw(size * fColorCount));
230 }
231 else {
232 fOrigColors = fStorage;
233 }
234
235 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 {
reed@android.com41bccf52009-04-03 13:33:51 +0000237 SkColor* origColors = fOrigColors;
238 if (dummyFirst) {
239 *origColors++ = colors[0];
240 }
241 memcpy(origColors, colors, colorCount * sizeof(SkColor));
242 if (dummyLast) {
243 origColors += colorCount;
244 *origColors = colors[colorCount - 1];
245 }
246 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
reed@android.com1c12abe2009-07-02 15:01:02 +0000248 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000249 if (fColorCount > 2) {
250 Rec* recs = fRecs;
251 recs->fPos = 0;
252 // recs->fScale = 0; // unused;
253 recs += 1;
254 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 /* We need to convert the user's array of relative positions into
256 fixed-point positions and scale factors. We need these results
257 to be strictly monotonic (no two values equal or out of order).
258 Hence this complex loop that just jams a zero for the scale
259 value if it sees a segment out of order, and it assures that
260 we start at 0 and end at 1.0
261 */
262 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000263 int startIndex = dummyFirst ? 0 : 1;
264 int count = colorCount + dummyLast;
265 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 // force the last value to be 1.0
267 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000268 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000270 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 }
reed@android.com41bccf52009-04-03 13:33:51 +0000273 // pin curr withing range
274 if (curr < 0) {
275 curr = 0;
276 } else if (curr > SK_Fixed1) {
277 curr = SK_Fixed1;
278 }
279 recs->fPos = curr;
280 if (curr > prev) {
281 recs->fScale = (1 << 24) / (curr - prev);
282 } else {
283 recs->fScale = 0; // ignore this segment
284 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 // get ready for the next value
286 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000287 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 }
reed@android.com41bccf52009-04-03 13:33:51 +0000289 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 SkFixed dp = SK_Fixed1 / (colorCount - 1);
291 SkFixed p = dp;
292 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000293 for (int i = 1; i < colorCount; i++) {
294 recs->fPos = p;
295 recs->fScale = scale;
296 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 p += dp;
298 }
299 }
300 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000301 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302}
303
304Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000305 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 fCacheAlpha = 256;
307
308 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
309
310 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000311 fCache32 = NULL;
312 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313
reed@android.com41bccf52009-04-03 13:33:51 +0000314 int colorCount = fColorCount = buffer.readU32();
315 if (colorCount > kColorStorageCount) {
316 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
317 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
318 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322
323 fTileMode = (TileMode)buffer.readU8();
324 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000325 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 if (colorCount > 2) {
327 Rec* recs = fRecs;
328 recs[0].fPos = 0;
329 for (int i = 1; i < colorCount; i++) {
330 recs[i].fPos = buffer.readS32();
331 recs[i].fScale = buffer.readU32();
332 }
333 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000334 SkReadMatrix(&buffer, &fPtsToUnit);
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000335 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336}
337
reed@android.com41bccf52009-04-03 13:33:51 +0000338Gradient_Shader::~Gradient_Shader() {
339 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000341 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000342 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000343 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000345 }
reed@google.com82065d62011-02-07 15:30:46 +0000346 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347}
348
reed@android.com41bccf52009-04-03 13:33:51 +0000349void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 this->INHERITED::flatten(buffer);
351 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000352 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
354 buffer.write8(fTileMode);
355 if (fColorCount > 2) {
356 Rec* recs = fRecs;
357 for (int i = 1; i < fColorCount; i++) {
358 buffer.write32(recs[i].fPos);
359 buffer.write32(recs[i].fScale);
360 }
361 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000362 SkWriteMatrix(&buffer, fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363}
364
365bool Gradient_Shader::setContext(const SkBitmap& device,
366 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000367 const SkMatrix& matrix) {
368 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000370 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371
372 const SkMatrix& inverse = this->getTotalInverse();
373
374 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
375 return false;
376 }
377
378 fDstToIndexProc = fDstToIndex.getMapXYProc();
379 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
380
381 // now convert our colors in to PMColors
382 unsigned paintAlpha = this->getPaintAlpha();
383 unsigned colorAlpha = 0xFF;
384
reed@android.com3d06a8c2009-07-07 18:19:59 +0000385 // FIXME: record colorAlpha in constructor, since this is not affected
386 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000387 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 SkColor src = fOrigColors[i];
389 unsigned sa = SkColorGetA(src);
390 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 }
392
393 fFlags = this->INHERITED::getFlags();
394 if ((colorAlpha & paintAlpha) == 0xFF) {
395 fFlags |= kOpaqueAlpha_Flag;
396 }
397 // we can do span16 as long as our individual colors are opaque,
398 // regardless of the paint's alpha
399 if (0xFF == colorAlpha) {
400 fFlags |= kHasSpan16_Flag;
401 }
402
reed@google.com95eed982011-07-05 17:01:56 +0000403 this->setCacheAlpha(paintAlpha);
404 return true;
405}
406
407void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 // if the new alpha differs from the previous time we were called, inval our cache
409 // this will trigger the cache to be rebuilt.
410 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000411 if (fCacheAlpha != alpha) {
412 fCache16 = NULL; // inval the cache
413 fCache32 = NULL; // inval the cache
414 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000415 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000416 if (fCache32PixelRef) {
417 fCache32PixelRef->notifyPixelsChanged();
418 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420}
421
reed@android.com41bccf52009-04-03 13:33:51 +0000422static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 SkASSERT(a == SkToU8(a));
424 SkASSERT(b == SkToU8(b));
425 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 return a + ((b - a) * scale >> 8);
427}
428
reed@android.com41bccf52009-04-03 13:33:51 +0000429static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
430 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431#if 0
432 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
433 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
434 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
435 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
436
437 return SkPackARGB32(a, r, g, b);
438#else
439 int otherBlend = 256 - blend;
440
441#if 0
442 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
443 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
444 SkASSERT((t0 & t1) == 0);
445 return t0 | t1;
446#else
447 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
448 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
449#endif
450
451#endif
452}
453
454#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
455
reed@android.com41bccf52009-04-03 13:33:51 +0000456/** We take the original colors, not our premultiplied PMColors, since we can
457 build a 16bit table as long as the original colors are opaque, even if the
458 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459*/
reed@android.com512a8762009-12-14 15:25:36 +0000460void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
461 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkASSERT(count > 1);
463 SkASSERT(SkColorGetA(c0) == 0xFF);
464 SkASSERT(SkColorGetA(c1) == 0xFF);
465
466 SkFixed r = SkColorGetR(c0);
467 SkFixed g = SkColorGetG(c0);
468 SkFixed b = SkColorGetB(c0);
469
470 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
471 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
472 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
473
474 r = SkIntToFixed(r) + 0x8000;
475 g = SkIntToFixed(g) + 0x8000;
476 b = SkIntToFixed(b) + 0x8000;
477
478 do {
479 unsigned rr = r >> 16;
480 unsigned gg = g >> 16;
481 unsigned bb = b >> 16;
482 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000483 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 cache += 1;
485 r += dr;
486 g += dg;
487 b += db;
488 } while (--count != 0);
489}
490
reed@google.com55b8e8c2011-01-13 16:22:35 +0000491/*
492 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
493 * semantics of how we 2x2 dither 32->16
494 */
495static inline U8CPU dither_fixed_to_8(SkFixed n) {
496 n >>= 8;
497 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
498}
499
500/*
501 * For dithering with premultiply, we want to ceiling the alpha component,
502 * to ensure that it is always >= any color component.
503 */
504static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
505 n >>= 8;
506 return ((n << 1) - (n | (n >> 8))) >> 8;
507}
508
509void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
510 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 SkASSERT(count > 1);
512
reed@android.com1c12abe2009-07-02 15:01:02 +0000513 // need to apply paintAlpha to our two endpoints
514 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
515 SkFixed da;
516 {
517 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
518 da = SkIntToFixed(tmp - a) / (count - 1);
519 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520
reed@android.com1c12abe2009-07-02 15:01:02 +0000521 SkFixed r = SkColorGetR(c0);
522 SkFixed g = SkColorGetG(c0);
523 SkFixed b = SkColorGetB(c0);
524 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
525 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
526 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527
528 a = SkIntToFixed(a) + 0x8000;
529 r = SkIntToFixed(r) + 0x8000;
530 g = SkIntToFixed(g) + 0x8000;
531 b = SkIntToFixed(b) + 0x8000;
532
533 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000534 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
535 cache[kCache32Count] = SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
536 dither_fixed_to_8(r),
537 dither_fixed_to_8(g),
538 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000539 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 a += da;
541 r += dr;
542 g += dg;
543 b += db;
544 } while (--count != 0);
545}
546
reed@android.com41bccf52009-04-03 13:33:51 +0000547static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 SkASSERT((unsigned)x <= SK_Fixed1);
549 return x - (x >> 16);
550}
551
reed@android.com200645d2009-12-14 16:41:57 +0000552static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000553 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000554 if (6 == bits) {
555 return (x << 10) | (x << 4) | (x >> 2);
556 }
557 if (8 == bits) {
558 return (x << 8) | x;
559 }
560 sk_throw();
561 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562}
563
reed@google.com7c2f27d2011-03-07 19:29:00 +0000564const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000565 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000566 // double the count for dither entries
567 const int entryCount = kCache16Count * 2;
568 const size_t allocSize = sizeof(uint16_t) * entryCount;
569
reed@android.com3c9b2a42009-08-27 19:28:37 +0000570 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000571 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000572 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000574 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000575 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000576 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 Rec* rec = fRecs;
578 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000579 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000580 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 SkASSERT(nextIndex < kCache16Count);
582
583 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000584 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 prevIndex = nextIndex;
586 }
587 SkASSERT(prevIndex == kCache16Count - 1);
588 }
589
reed@android.com41bccf52009-04-03 13:33:51 +0000590 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000591 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 uint16_t* linear = fCache16; // just computed linear data
593 uint16_t* mapped = fCache16Storage; // storage for mapped data
594 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000595 for (int i = 0; i < kCache16Count; i++) {
596 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000598 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 }
600 sk_free(fCache16);
601 fCache16 = fCache16Storage;
602 }
603 }
604 return fCache16;
605}
606
reed@google.com7c2f27d2011-03-07 19:29:00 +0000607const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000608 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000609 // double the count for dither entries
610 const int entryCount = kCache32Count * 2;
611 const size_t allocSize = sizeof(SkPMColor) * entryCount;
612
reed@google.comdc731fd2010-12-23 15:19:47 +0000613 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000614 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
615 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000616 }
617 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000618 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000619 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
620 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000621 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 Rec* rec = fRecs;
623 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000624 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
626 SkASSERT(nextIndex < kCache32Count);
627
628 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000629 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
630 fOrigColors[i],
631 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 prevIndex = nextIndex;
633 }
634 SkASSERT(prevIndex == kCache32Count - 1);
635 }
636
reed@android.com41bccf52009-04-03 13:33:51 +0000637 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000638 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000639 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000641 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000643 for (int i = 0; i < kCache32Count; i++) {
644 int index = map->mapUnit16((i << 8) | i) >> 8;
645 mapped[i] = linear[index];
646 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000647 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000648 fCache32PixelRef->unref();
649 fCache32PixelRef = newPR;
650 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 }
652 }
653 return fCache32;
654}
655
reed@google.comdc731fd2010-12-23 15:19:47 +0000656/*
657 * Because our caller might rebuild the same (logically the same) gradient
658 * over and over, we'd like to return exactly the same "bitmap" if possible,
659 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
660 * To do that, we maintain a private cache of built-bitmaps, based on our
661 * colors and positions. Note: we don't try to flatten the fMapper, so if one
662 * is present, we skip the cache for now.
663 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000664void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000665 // our caller assumes no external alpha, so we ensure that our cache is
666 // built with 0xFF
667 this->setCacheAlpha(0xFF);
668
reed@google.comdc731fd2010-12-23 15:19:47 +0000669 // don't have a way to put the mapper into our cache-key yet
670 if (fMapper) {
671 // force our cahce32pixelref to be built
672 (void)this->getCache32();
673 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
674 bitmap->setPixelRef(fCache32PixelRef);
675 return;
676 }
677
678 // build our key: [numColors + colors[] + {positions[]} ]
679 int count = 1 + fColorCount;
680 if (fColorCount > 2) {
681 count += fColorCount - 1; // fRecs[].fPos
682 }
683
684 SkAutoSTMalloc<16, int32_t> storage(count);
685 int32_t* buffer = storage.get();
686
687 *buffer++ = fColorCount;
688 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
689 buffer += fColorCount;
690 if (fColorCount > 2) {
691 for (int i = 1; i < fColorCount; i++) {
692 *buffer++ = fRecs[i].fPos;
693 }
694 }
695 SkASSERT(buffer - storage.get() == count);
696
697 ///////////////////////////////////
698
699 static SkMutex gMutex;
700 static SkBitmapCache* gCache;
701 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
702 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
703 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000704
reed@google.comdc731fd2010-12-23 15:19:47 +0000705 if (NULL == gCache) {
706 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
707 }
708 size_t size = count * sizeof(int32_t);
709
710 if (!gCache->find(storage.get(), size, bitmap)) {
711 // force our cahce32pixelref to be built
712 (void)this->getCache32();
713 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
714 bitmap->setPixelRef(fCache32PixelRef);
715
716 gCache->add(storage.get(), size, *bitmap);
717 }
718}
719
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000720void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
721 if (info) {
722 if (info->fColorCount >= fColorCount) {
723 if (info->fColors) {
724 memcpy(info->fColors, fOrigColors,
725 fColorCount * sizeof(SkColor));
726 }
727 if (info->fColorOffsets) {
728 if (fColorCount == 2) {
729 info->fColorOffsets[0] = 0;
730 info->fColorOffsets[1] = SK_Scalar1;
731 } else if (fColorCount > 2) {
732 for (int i = 0; i < fColorCount; i++)
733 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
734 }
735 }
736 }
737 info->fColorCount = fColorCount;
738 info->fTileMode = fTileMode;
739 }
740}
741
reed@google.com61eb0402011-04-15 12:11:12 +0000742///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743
reed@android.com41bccf52009-04-03 13:33:51 +0000744static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 SkVector vec = pts[1] - pts[0];
746 SkScalar mag = vec.length();
747 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
748
749 vec.scale(inv);
750 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
751 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
752 matrix->postScale(inv, inv);
753}
754
755///////////////////////////////////////////////////////////////////////////////
756
757class Linear_Gradient : public Gradient_Shader {
758public:
759 Linear_Gradient(const SkPoint pts[2],
760 const SkColor colors[], const SkScalar pos[], int colorCount,
761 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000762 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
763 fStart(pts[0]),
764 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 {
766 pts_to_unit_matrix(pts, &fPtsToUnit);
767 }
reed@android.com9b46e772009-06-05 12:24:41 +0000768
reed@android.com5119bdb2009-06-12 21:27:03 +0000769 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
771 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000772 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*,
reed@google.com7c2f27d2011-03-07 19:29:00 +0000773 TileMode*, SkScalar* twoPointRadialParams) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000774 virtual GradientType asAGradient(GradientInfo* info) const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775
reed@google.com55b8e8c2011-01-13 16:22:35 +0000776 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 return SkNEW_ARGS(Linear_Gradient, (buffer));
778 }
779
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000780 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
781 this->INHERITED::flatten(buffer);
782 buffer.writeScalar(fStart.fX);
783 buffer.writeScalar(fStart.fY);
784 buffer.writeScalar(fEnd.fX);
785 buffer.writeScalar(fEnd.fY);
786 }
787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000789 Linear_Gradient(SkFlattenableReadBuffer& buffer)
790 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +0000791 fStart(unflatten_point(buffer)),
792 fEnd(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000793 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 virtual Factory getFactory() { return CreateProc; }
795
796private:
797 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000798 const SkPoint fStart;
799 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800};
801
reed@android.com5119bdb2009-06-12 21:27:03 +0000802bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
803 const SkMatrix& matrix) {
804 if (!this->INHERITED::setContext(device, paint, matrix)) {
805 return false;
806 }
807
808 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
809 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000810 fFlags |= SkShader::kConstInY32_Flag;
811 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
812 // only claim this if we do have a 16bit mode (i.e. none of our
813 // colors have alpha), and if we are not dithering (which obviously
814 // is not const in Y).
815 fFlags |= SkShader::kConstInY16_Flag;
816 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000817 }
818 return true;
819}
820
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@google.com61eb0402011-04-15 12:11:12 +0000822static inline bool no_need_for_clamp(int fx, int dx, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 SkASSERT(count > 0);
824 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
825}
826
reed@google.com5eb158d2011-04-15 15:50:34 +0000827#include "SkClampRange.h"
828
829#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000830 do { \
831 unsigned fi = fx >> 8; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000832 SkASSERT(fi <= 0xFF); \
833 fx += dx; \
834 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +0000835 toggle ^= TOGGLE_MASK; \
836 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000837
838
reed@google.comdd5bd672011-09-20 15:56:13 +0000839void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 SkASSERT(count > 0);
841
842 SkPoint srcPt;
843 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
844 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000845 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com55b8e8c2011-01-13 16:22:35 +0000846#ifdef USE_DITHER_32BIT_GRADIENT
847 int toggle = ((x ^ y) & 1) << kCache32Bits;
848 const int TOGGLE_MASK = (1 << kCache32Bits);
849#else
850 int toggle = 0;
851 const int TOGGLE_MASK = 0;
852#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853
reed@android.comc552a432009-06-12 20:02:50 +0000854 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000855 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
856 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
858
reed@android.comc552a432009-06-12 20:02:50 +0000859 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 SkFixed dxStorage[1];
861 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
862 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000863 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
865 dx = SkScalarToFixed(fDstToIndex.getScaleX());
866 }
867
reed@android.comc552a432009-06-12 20:02:50 +0000868 if (SkFixedNearlyZero(dx)) {
869 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 unsigned fi = proc(fx);
871 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000872 // TODO: dither version
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000874 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000875 SkClampRange range;
876 range.init(fx, dx, count, 0, 0xFF);
877
878 if ((count = range.fCount0) > 0) {
879 sk_memset32_dither(dstC,
880 cache[toggle + range.fV0],
881 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
882 count);
883 dstC += count;
884 }
885 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +0000886 int unroll = count >> 3;
887 fx = range.fFx1;
888 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000889 NO_CHECK_ITER; NO_CHECK_ITER;
890 NO_CHECK_ITER; NO_CHECK_ITER;
891 NO_CHECK_ITER; NO_CHECK_ITER;
892 NO_CHECK_ITER; NO_CHECK_ITER;
893 }
894 if ((count &= 7) > 0) {
895 do {
896 NO_CHECK_ITER;
897 } while (--count != 0);
898 }
899 }
900 if ((count = range.fCount2) > 0) {
901 sk_memset32_dither(dstC,
902 cache[toggle + range.fV1],
903 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
904 count);
905 }
reed@android.comc552a432009-06-12 20:02:50 +0000906 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 do {
908 unsigned fi = mirror_8bits(fx >> 8);
909 SkASSERT(fi <= 0xFF);
910 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000911 *dstC++ = cache[toggle + fi];
912 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000914 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 SkASSERT(proc == repeat_tileproc);
916 do {
917 unsigned fi = repeat_8bits(fx >> 8);
918 SkASSERT(fi <= 0xFF);
919 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000920 *dstC++ = cache[toggle + fi];
921 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 } while (--count != 0);
923 }
reed@android.comc552a432009-06-12 20:02:50 +0000924 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 SkScalar dstX = SkIntToScalar(x);
926 SkScalar dstY = SkIntToScalar(y);
927 do {
928 dstProc(fDstToIndex, dstX, dstY, &srcPt);
929 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
930 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000931 *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))];
932 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 dstX += SK_Scalar1;
934 } while (--count != 0);
935 }
936}
937
reed@google.com55b8e8c2011-01-13 16:22:35 +0000938SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000939 SkMatrix* matrix,
940 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +0000941 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000943 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 }
945 if (matrix) {
946 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
947 matrix->preConcat(fPtsToUnit);
948 }
949 if (xy) {
950 xy[0] = fTileMode;
951 xy[1] = kClamp_TileMode;
952 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000953 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954}
955
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000956SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
957 if (info) {
958 commonAsAGradient(info);
959 info->fPoint[0] = fStart;
960 info->fPoint[1] = fEnd;
961 }
962 return kLinear_GradientType;
963}
964
reed@android.com3c9b2a42009-08-27 19:28:37 +0000965static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
966 int count) {
967 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 *dst++ = value;
969 count -= 1;
970 SkTSwap(value, other);
971 }
972
973 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000974
reed@android.com3c9b2a42009-08-27 19:28:37 +0000975 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000977 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979
reed@google.com5eb158d2011-04-15 15:50:34 +0000980#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +0000981 do { \
982 unsigned fi = fx >> kCache16Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000983 SkASSERT(fi <= kCache16Mask); \
984 fx += dx; \
985 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +0000986 toggle ^= TOGGLE_MASK; \
987 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000988
989
reed@google.comdd5bd672011-09-20 15:56:13 +0000990void Linear_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 SkASSERT(count > 0);
992
993 SkPoint srcPt;
994 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
995 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000996 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@google.com5eb158d2011-04-15 15:50:34 +0000998 const int TOGGLE_MASK = (1 << kCache32Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001000 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001001 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1002 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1004
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001005 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 SkFixed dxStorage[1];
1007 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1008 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001009 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1011 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1012 }
1013
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001014 if (SkFixedNearlyZero(dx)) {
1015 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +00001016 unsigned fi = proc(fx) >> kCache16Shift;
1017 SkASSERT(fi <= kCache16Mask);
reed@google.com5eb158d2011-04-15 15:50:34 +00001018 dither_memset16(dstC, cache[toggle + fi],
1019 cache[(toggle ^ TOGGLE_MASK) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001020 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001021 SkClampRange range;
1022 range.init(fx, dx, count, 0, kCache16Mask);
1023
1024 if ((count = range.fCount0) > 0) {
1025 dither_memset16(dstC,
1026 cache[toggle + range.fV0],
1027 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
1028 count);
1029 dstC += count;
1030 }
1031 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +00001032 int unroll = count >> 3;
1033 fx = range.fFx1;
1034 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001035 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1036 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1037 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1038 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1039 }
1040 if ((count &= 7) > 0) {
1041 do {
1042 NO_CHECK_ITER_16;
1043 } while (--count != 0);
1044 }
1045 }
1046 if ((count = range.fCount2) > 0) {
1047 dither_memset16(dstC,
1048 cache[toggle + range.fV1],
1049 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
1050 count);
1051 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001052 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 do {
reed@android.com200645d2009-12-14 16:41:57 +00001054 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001055 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001058 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001060 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 SkASSERT(proc == repeat_tileproc);
1062 do {
reed@android.com200645d2009-12-14 16:41:57 +00001063 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001064 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001067 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 } while (--count != 0);
1069 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001070 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 SkScalar dstX = SkIntToScalar(x);
1072 SkScalar dstY = SkIntToScalar(y);
1073 do {
1074 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1075 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1076 SkASSERT(fi <= 0xFFFF);
1077
reed@android.com512a8762009-12-14 15:25:36 +00001078 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 *dstC++ = cache[toggle + index];
reed@google.com5eb158d2011-04-15 15:50:34 +00001080 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081
1082 dstX += SK_Scalar1;
1083 } while (--count != 0);
1084 }
1085}
1086
1087///////////////////////////////////////////////////////////////////////////////
1088
1089#define kSQRT_TABLE_BITS 11
1090#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1091
1092#include "SkRadialGradient_Table.h"
1093
1094#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1095
1096#include <stdio.h>
1097
reed@google.com61eb0402011-04-15 12:11:12 +00001098void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1100
1101 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1102 SkASSERT(file);
1103 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1104
reed@google.com61eb0402011-04-15 12:11:12 +00001105 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1106 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001108 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109
1110 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1111
1112 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001113 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001115 }
1116 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001118 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 }
1120 ::fprintf(file, "};\n");
1121 ::fclose(file);
1122}
1123
1124#endif
1125
1126
reed@google.com61eb0402011-04-15 12:11:12 +00001127static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1128 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 SkScalar inv = SkScalarInvert(radius);
1130
1131 matrix->setTranslate(-center.fX, -center.fY);
1132 matrix->postScale(inv, inv);
1133}
1134
1135class Radial_Gradient : public Gradient_Shader {
1136public:
1137 Radial_Gradient(const SkPoint& center, SkScalar radius,
1138 const SkColor colors[], const SkScalar pos[], int colorCount,
1139 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001140 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1141 fCenter(center),
1142 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 {
1144 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1145 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1146
1147 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1148 }
reed@google.com61eb0402011-04-15 12:11:12 +00001149
reed@google.comdd5bd672011-09-20 15:56:13 +00001150 virtual void shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 SkASSERT(count > 0);
1152
1153 SkPoint srcPt;
1154 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1155 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001156 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157
reed@google.com61eb0402011-04-15 12:11:12 +00001158 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001159 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1160 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1162 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1163
reed@google.com61eb0402011-04-15 12:11:12 +00001164 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 SkFixed storage[2];
1166 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1167 dx = storage[0];
1168 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00001169 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1171 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1172 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1173 }
1174
reed@google.com61eb0402011-04-15 12:11:12 +00001175 if (proc == clamp_tileproc) {
reed@google.comdd5bd672011-09-20 15:56:13 +00001176 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 fx >>= 1;
1178 dx >>= 1;
1179 fy >>= 1;
1180 dy >>= 1;
1181 do {
1182 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1183 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1184 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1185 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1186 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1187 fx += dx;
1188 fy += dy;
1189 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001190 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001192 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1193 if (magnitudeSquared < 0) // Overflow.
1194 magnitudeSquared = SK_FixedMax;
1195 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 unsigned fi = mirror_tileproc(dist);
1197 SkASSERT(fi <= 0xFFFF);
1198 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1199 fx += dx;
1200 fy += dy;
1201 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001202 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 SkASSERT(proc == repeat_tileproc);
1204 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001205 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1206 if (magnitudeSquared < 0) // Overflow.
1207 magnitudeSquared = SK_FixedMax;
1208 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 unsigned fi = repeat_tileproc(dist);
1210 SkASSERT(fi <= 0xFFFF);
1211 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1212 fx += dx;
1213 fy += dy;
1214 } while (--count != 0);
1215 }
reed@google.com61eb0402011-04-15 12:11:12 +00001216 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 SkScalar dstX = SkIntToScalar(x);
1218 SkScalar dstY = SkIntToScalar(y);
1219 do {
1220 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1221 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1222 SkASSERT(fi <= 0xFFFF);
1223 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1224 dstX += SK_Scalar1;
1225 } while (--count != 0);
1226 }
1227 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001228
reed@google.comdd5bd672011-09-20 15:56:13 +00001229 virtual void shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 SkASSERT(count > 0);
1231
1232 SkPoint srcPt;
1233 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1234 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001235 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237
reed@android.com3c9b2a42009-08-27 19:28:37 +00001238 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001239 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1240 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1242 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1243
reed@android.com3c9b2a42009-08-27 19:28:37 +00001244 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 SkFixed storage[2];
1246 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1247 dx = storage[0];
1248 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001249 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1251 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1252 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1253 }
1254
reed@android.com3c9b2a42009-08-27 19:28:37 +00001255 if (proc == clamp_tileproc) {
reed@google.comdd5bd672011-09-20 15:56:13 +00001256 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257
1258 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1259 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1260 precision, but that appears to be visually OK. If we decide this is OK for
1261 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1262 to avoid having to do these extra shifts each time.
1263 */
1264 fx >>= 1;
1265 dx >>= 1;
1266 fy >>= 1;
1267 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001268 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 +00001269 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1270 fy *= fy;
1271 do {
1272 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1273 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1274 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1275 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1277 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001279 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 do {
1281 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1282 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1283 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1284 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1285 fx += dx;
1286 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1288 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 } while (--count != 0);
1290 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001291 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 do {
1293 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1294 unsigned fi = mirror_tileproc(dist);
1295 SkASSERT(fi <= 0xFFFF);
1296 fx += dx;
1297 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1299 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001301 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 SkASSERT(proc == repeat_tileproc);
1303 do {
1304 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1305 unsigned fi = repeat_tileproc(dist);
1306 SkASSERT(fi <= 0xFFFF);
1307 fx += dx;
1308 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1310 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 } while (--count != 0);
1312 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001313 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 SkScalar dstX = SkIntToScalar(x);
1315 SkScalar dstY = SkIntToScalar(y);
1316 do {
1317 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1318 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1319 SkASSERT(fi <= 0xFFFF);
1320
1321 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 *dstC++ = cache[toggle + index];
1323 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324
1325 dstX += SK_Scalar1;
1326 } while (--count != 0);
1327 }
1328 }
1329
reed@google.com55b8e8c2011-01-13 16:22:35 +00001330 virtual BitmapType asABitmap(SkBitmap* bitmap,
1331 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001332 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001333 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001334 if (bitmap) {
1335 this->commonAsABitmap(bitmap);
1336 }
1337 if (matrix) {
1338 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1339 matrix->preConcat(fPtsToUnit);
1340 }
1341 if (xy) {
1342 xy[0] = fTileMode;
1343 xy[1] = kClamp_TileMode;
1344 }
1345 return kRadial_BitmapType;
1346 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001347 virtual GradientType asAGradient(GradientInfo* info) const {
1348 if (info) {
1349 commonAsAGradient(info);
1350 info->fPoint[0] = fCenter;
1351 info->fRadius[0] = fRadius;
1352 }
1353 return kRadial_GradientType;
1354 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001355
1356 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 return SkNEW_ARGS(Radial_Gradient, (buffer));
1358 }
1359
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001360 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1361 this->INHERITED::flatten(buffer);
1362 buffer.writeScalar(fCenter.fX);
1363 buffer.writeScalar(fCenter.fY);
1364 buffer.writeScalar(fRadius);
1365 }
1366
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001368 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1369 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001370 fCenter(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001371 fRadius(buffer.readScalar()) {
1372 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 virtual Factory getFactory() { return CreateProc; }
1374
1375private:
1376 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001377 const SkPoint fCenter;
1378 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379};
1380
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001381/* Two-point radial gradients are specified by two circles, each with a center
1382 point and radius. The gradient can be considered to be a series of
1383 concentric circles, with the color interpolated from the start circle
1384 (at t=0) to the end circle (at t=1).
1385
1386 For each point (x, y) in the span, we want to find the
1387 interpolated circle that intersects that point. The center
1388 of the desired circle (Cx, Cy) falls at some distance t
1389 along the line segment between the start point (Sx, Sy) and
1390 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001391
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001392 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1393 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001394
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001395 The radius of the desired circle (r) is also a linear interpolation t
1396 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001397
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001398 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001399
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001400 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001401
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001402 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001403
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001404 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001405
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001406 (x - ((1 - t) * Sx + t * Ex))^2
1407 + (y - ((1 - t) * Sy + t * Ey))^2
1408 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001409
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001410 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001411
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001412 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1413 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1414 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001415
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001416 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1417
1418 [Dx^2 + Dy^2 - Dr^2)] * t^2
1419 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1420 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001421
1422 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001423 possible circles on which the point may fall. Solving for t yields
1424 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001425
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001426 If a<0, the start circle is entirely contained in the
1427 end circle, and one of the roots will be <0 or >1 (off the line
1428 segment). If a>0, the start circle falls at least partially
1429 outside the end circle (or vice versa), and the gradient
1430 defines a "tube" where a point may be on one circle (on the
1431 inside of the tube) or the other (outside of the tube). We choose
1432 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001433
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001434 In order to keep the math to within the limits of fixed point,
1435 we divide the entire quadratic by Dr^2, and replace
1436 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001437
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001438 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1439 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1440 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001441
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001442 (x' and y' are computed by appending the subtract and scale to the
1443 fDstToIndex matrix in the constructor).
1444
1445 Since the 'A' component of the quadratic is independent of x' and y', it
1446 is precomputed in the constructor. Since the 'B' component is linear in
1447 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001448 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001449 a perspective projection), it must be computed in the loop.
1450
1451*/
1452
reed@google.com84e9c082011-04-13 17:44:24 +00001453static inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1454 SkScalar sr2d2, SkScalar foura,
1455 SkScalar oneOverTwoA, bool posRoot) {
1456 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001457 if (0 == foura) {
1458 return SkScalarToFixed(SkScalarDiv(-c, b));
1459 }
1460
reed@google.com84e9c082011-04-13 17:44:24 +00001461 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001462 if (discrim < 0) {
1463 discrim = -discrim;
1464 }
reed@google.com84e9c082011-04-13 17:44:24 +00001465 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1466 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001467 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001468 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001469 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001470 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001471 }
reed@google.com84e9c082011-04-13 17:44:24 +00001472 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001473}
1474
1475class Two_Point_Radial_Gradient : public Gradient_Shader {
1476public:
1477 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1478 const SkPoint& end, SkScalar endRadius,
1479 const SkColor colors[], const SkScalar pos[],
1480 int colorCount, SkShader::TileMode mode,
1481 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001482 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1483 fCenter1(start),
1484 fCenter2(end),
1485 fRadius1(startRadius),
1486 fRadius2(endRadius) {
1487 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001488 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001489
1490 virtual BitmapType asABitmap(SkBitmap* bitmap,
1491 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001492 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001493 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001494 if (bitmap) {
1495 this->commonAsABitmap(bitmap);
1496 }
1497 SkScalar diffL = 0; // just to avoid gcc warning
1498 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001499 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001500 SkScalarSquare(fDiff.fY));
1501 }
1502 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001503 if (diffL) {
1504 SkScalar invDiffL = SkScalarInvert(diffL);
1505 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1506 SkScalarMul(invDiffL, fDiff.fX));
1507 } else {
1508 matrix->reset();
1509 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001510 matrix->preConcat(fPtsToUnit);
1511 }
1512 if (xy) {
1513 xy[0] = fTileMode;
1514 xy[1] = kClamp_TileMode;
1515 }
1516 if (NULL != twoPointRadialParams) {
1517 twoPointRadialParams[0] = diffL;
1518 twoPointRadialParams[1] = fStartRadius;
1519 twoPointRadialParams[2] = fDiffRadius;
1520 }
1521 return kTwoPointRadial_BitmapType;
1522 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001523
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001524 virtual GradientType asAGradient(GradientInfo* info) const {
1525 if (info) {
1526 commonAsAGradient(info);
1527 info->fPoint[0] = fCenter1;
1528 info->fPoint[1] = fCenter2;
1529 info->fRadius[0] = fRadius1;
1530 info->fRadius[1] = fRadius2;
1531 }
1532 return kRadial2_GradientType;
1533 }
1534
reed@google.comdd5bd672011-09-20 15:56:13 +00001535 virtual void shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001536 SkASSERT(count > 0);
1537
1538 // Zero difference between radii: fill with transparent black.
1539 if (fDiffRadius == 0) {
1540 sk_bzero(dstC, count * sizeof(*dstC));
1541 return;
1542 }
1543 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1544 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001545 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001546
1547 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001548 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001549 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001550 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001551 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1552 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001553 SkScalar dx, fx = srcPt.fX;
1554 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001555
reed@google.com61eb0402011-04-15 12:11:12 +00001556 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001557 SkFixed fixedX, fixedY;
1558 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1559 dx = SkFixedToScalar(fixedX);
1560 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001561 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001562 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001563 dx = fDstToIndex.getScaleX();
1564 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001565 }
reed@google.com84e9c082011-04-13 17:44:24 +00001566 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1567 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1568 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1569 SkScalarMul(fDiff.fY, dy)) * 2;
reed@google.com61eb0402011-04-15 12:11:12 +00001570 if (proc == clamp_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001571 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001572 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001573 SkFixed index = SkClampMax(t, 0xFFFF);
1574 SkASSERT(index <= 0xFFFF);
1575 *dstC++ = cache[index >> (16 - kCache32Bits)];
1576 fx += dx;
1577 fy += dy;
1578 b += db;
1579 }
reed@google.com61eb0402011-04-15 12:11:12 +00001580 } else if (proc == mirror_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001581 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001582 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001583 SkFixed index = mirror_tileproc(t);
1584 SkASSERT(index <= 0xFFFF);
1585 *dstC++ = cache[index >> (16 - kCache32Bits)];
1586 fx += dx;
1587 fy += dy;
1588 b += db;
1589 }
reed@google.com61eb0402011-04-15 12:11:12 +00001590 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001591 SkASSERT(proc == repeat_tileproc);
1592 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001593 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001594 SkFixed index = repeat_tileproc(t);
1595 SkASSERT(index <= 0xFFFF);
1596 *dstC++ = cache[index >> (16 - kCache32Bits)];
1597 fx += dx;
1598 fy += dy;
1599 b += db;
1600 }
1601 }
reed@google.com61eb0402011-04-15 12:11:12 +00001602 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001603 SkScalar dstX = SkIntToScalar(x);
1604 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001605 for (; count > 0; --count) {
1606 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001607 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001608 SkScalar fx = srcPt.fX;
1609 SkScalar fy = srcPt.fY;
1610 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1611 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1612 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001613 SkFixed index = proc(t);
1614 SkASSERT(index <= 0xFFFF);
1615 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001616 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001617 }
1618 }
1619 }
1620
reed@android.com6c59a172009-09-22 20:24:05 +00001621 virtual bool setContext(const SkBitmap& device,
1622 const SkPaint& paint,
1623 const SkMatrix& matrix) {
1624 if (!this->INHERITED::setContext(device, paint, matrix)) {
1625 return false;
1626 }
1627
1628 // we don't have a span16 proc
1629 fFlags &= ~kHasSpan16_Flag;
1630 return true;
1631 }
1632
reed@google.com55b8e8c2011-01-13 16:22:35 +00001633 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001634 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1635 }
1636
reed@android.combcfc7332009-11-10 16:19:39 +00001637 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1638 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001639 buffer.writeScalar(fCenter1.fX);
1640 buffer.writeScalar(fCenter1.fY);
1641 buffer.writeScalar(fCenter2.fX);
1642 buffer.writeScalar(fCenter2.fY);
1643 buffer.writeScalar(fRadius1);
1644 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001645 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001646
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001647protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001648 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001649 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001650 fCenter1(unflatten_point(buffer)),
1651 fCenter2(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001652 fRadius1(buffer.readScalar()),
1653 fRadius2(buffer.readScalar()) {
1654 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001655 };
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001656 virtual Factory getFactory() { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001657
1658private:
1659 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001660 const SkPoint fCenter1;
1661 const SkPoint fCenter2;
1662 const SkScalar fRadius1;
1663 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001664 SkPoint fDiff;
1665 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001666
1667 void init() {
1668 fDiff = fCenter1 - fCenter2;
1669 fDiffRadius = fRadius2 - fRadius1;
1670 SkScalar inv = SkScalarInvert(fDiffRadius);
1671 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1672 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1673 fStartRadius = SkScalarMul(fRadius1, inv);
1674 fSr2D2 = SkScalarSquare(fStartRadius);
1675 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00001676 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001677
1678 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1679 fPtsToUnit.postScale(inv, inv);
1680 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001681};
1682
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683///////////////////////////////////////////////////////////////////////////////
1684
1685class Sweep_Gradient : public Gradient_Shader {
1686public:
1687 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1688 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001689 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
1690 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691 {
1692 fPtsToUnit.setTranslate(-cx, -cy);
1693 }
1694 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1695 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001696
1697 virtual BitmapType asABitmap(SkBitmap* bitmap,
1698 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001699 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001700 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001701 if (bitmap) {
1702 this->commonAsABitmap(bitmap);
1703 }
1704 if (matrix) {
1705 *matrix = fPtsToUnit;
1706 }
1707 if (xy) {
1708 xy[0] = fTileMode;
1709 xy[1] = kClamp_TileMode;
1710 }
1711 return kSweep_BitmapType;
1712 }
1713
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001714 virtual GradientType asAGradient(GradientInfo* info) const {
1715 if (info) {
1716 commonAsAGradient(info);
1717 info->fPoint[0] = fCenter;
1718 }
1719 return kSweep_GradientType;
1720 }
1721
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1723 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1724 }
1725
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001726 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1727 this->INHERITED::flatten(buffer);
1728 buffer.writeScalar(fCenter.fX);
1729 buffer.writeScalar(fCenter.fY);
1730 }
1731
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001733 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
1734 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001735 fCenter(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001736 }
1737
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738 virtual Factory getFactory() { return CreateProc; }
1739
1740private:
1741 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001742 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743};
1744
1745#ifdef COMPUTE_SWEEP_TABLE
1746#define PI 3.14159265
1747static bool gSweepTableReady;
1748static uint8_t gSweepTable[65];
1749
1750/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1751 We scale the results to [0..32]
1752*/
reed@google.com61eb0402011-04-15 12:11:12 +00001753static const uint8_t* build_sweep_table() {
1754 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755 const int N = 65;
1756 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001757
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 for (int i = 0; i < N; i++)
1759 {
1760 double arg = i / DENOM;
1761 double v = atan(arg);
1762 int iv = (int)round(v * DENOM * 2 / PI);
1763// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1764 printf("%d, ", iv);
1765 gSweepTable[i] = iv;
1766 }
1767 gSweepTableReady = true;
1768 }
1769 return gSweepTable;
1770}
1771#else
1772static const uint8_t gSweepTable[] = {
1773 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1774 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1775 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1776 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1777 32
1778};
1779static const uint8_t* build_sweep_table() { return gSweepTable; }
1780#endif
1781
1782// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1783// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1784// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1785
1786//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00001787static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788 SkASSERT(numer <= denom);
1789 SkASSERT(numer > 0);
1790 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001791
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 int nbits = SkCLZ(numer);
1793 int dbits = SkCLZ(denom);
1794 int bits = 6 - nbits + dbits;
1795 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001796
reed@google.com61eb0402011-04-15 12:11:12 +00001797 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001799 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800
1801 denom <<= dbits - 1;
1802 numer <<= nbits - 1;
1803
1804 unsigned result = 0;
1805
1806 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00001807 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00001809 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00001811 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001812
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00001814 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 // make room for the rest of the answer bits
1816 result <<= bits;
1817 switch (bits) {
1818 case 6:
1819 if ((numer = (numer << 1) - denom) >= 0)
1820 result |= 32;
1821 else
1822 numer += denom;
1823 case 5:
1824 if ((numer = (numer << 1) - denom) >= 0)
1825 result |= 16;
1826 else
1827 numer += denom;
1828 case 4:
1829 if ((numer = (numer << 1) - denom) >= 0)
1830 result |= 8;
1831 else
1832 numer += denom;
1833 case 3:
1834 if ((numer = (numer << 1) - denom) >= 0)
1835 result |= 4;
1836 else
1837 numer += denom;
1838 case 2:
1839 if ((numer = (numer << 1) - denom) >= 0)
1840 result |= 2;
1841 else
1842 numer += denom;
1843 case 1:
1844 default: // not strictly need, but makes GCC make better ARM code
1845 if ((numer = (numer << 1) - denom) >= 0)
1846 result |= 1;
1847 else
1848 numer += denom;
1849 }
1850 }
1851 return result;
1852}
1853
1854// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00001855static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856#ifdef SK_DEBUG
1857 {
1858 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00001859 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001860 gOnce = true;
1861 SkASSERT(div_64(55, 55) == 64);
1862 SkASSERT(div_64(128, 256) == 32);
1863 SkASSERT(div_64(2326528, 4685824) == 31);
1864 SkASSERT(div_64(753664, 5210112) == 9);
1865 SkASSERT(div_64(229376, 4882432) == 3);
1866 SkASSERT(div_64(2, 64) == 2);
1867 SkASSERT(div_64(1, 64) == 1);
1868 // test that we handle underflow correctly
1869 SkASSERT(div_64(12345, 0x54321234) == 0);
1870 }
1871 }
1872#endif
1873
1874 SkASSERT(y > 0 && x > 0);
1875 const uint8_t* table = build_sweep_table();
1876
1877 unsigned result;
1878 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00001879 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880 // first part of the atan(v) = PI/2 - atan(1/v) identity
1881 // since our div_64 and table want v <= 1, where v = y/x
1882 SkTSwap<SkFixed>(x, y);
1883 }
1884
1885 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001886
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887#ifdef SK_DEBUG
1888 {
1889 unsigned result2 = SkDivBits(y, x, 6);
1890 SkASSERT(result2 == result ||
1891 (result == 1 && result2 == 0));
1892 }
1893#endif
1894
1895 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1896 result = table[result];
1897
reed@google.com61eb0402011-04-15 12:11:12 +00001898 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899 // complete the atan(v) = PI/2 - atan(1/v) identity
1900 result = 64 - result;
1901 // pin to 63
1902 result -= result >> 6;
1903 }
1904
1905 SkASSERT(result <= 63);
1906 return result;
1907}
1908
1909// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com61eb0402011-04-15 12:11:12 +00001910static unsigned SkATan2_255(SkFixed y, SkFixed x) {
1911 if (x == 0) {
1912 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001914 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 return y < 0 ? 192 : 64;
1916 }
reed@google.com61eb0402011-04-15 12:11:12 +00001917 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001918 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001919 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001920
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921 /* Find the right quadrant for x,y
1922 Since atan_0_90 only handles the first quadrant, we rotate x,y
1923 appropriately before calling it, and then add the right amount
1924 to account for the real quadrant.
1925 quadrant 0 : add 0 | x > 0 && y > 0
1926 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1927 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1928 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001929
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930 map x<0 to (1 << 6)
1931 map y<0 to (3 << 6)
1932 add = map_x ^ map_y
1933 */
1934 int xsign = x >> 31;
1935 int ysign = y >> 31;
1936 int add = ((-xsign) ^ (ysign & 3)) << 6;
1937
1938#ifdef SK_DEBUG
1939 if (0 == add)
1940 SkASSERT(x > 0 && y > 0);
1941 else if (64 == add)
1942 SkASSERT(x < 0 && y > 0);
1943 else if (128 == add)
1944 SkASSERT(x < 0 && y < 0);
1945 else if (192 == add)
1946 SkASSERT(x > 0 && y < 0);
1947 else
1948 SkASSERT(!"bad value for add");
1949#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00001950
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1952 where we need to rotate x,y by 90 or -90
1953 */
1954 x = (x ^ xsign) - xsign;
1955 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00001956 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00001958 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959
1960 unsigned result = add + atan_0_90(y, x);
1961 SkASSERT(result < 256);
1962 return result;
1963}
1964
reed@google.comdd5bd672011-09-20 15:56:13 +00001965void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 SkMatrix::MapXYProc proc = fDstToIndexProc;
1967 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00001968 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001970
reed@google.com61eb0402011-04-15 12:11:12 +00001971 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1973 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1974 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1975 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001976
reed@google.com61eb0402011-04-15 12:11:12 +00001977 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 SkFixed storage[2];
1979 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1980 &storage[0], &storage[1]);
1981 dx = storage[0];
1982 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00001983 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001984 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1985 dx = SkScalarToFixed(matrix.getScaleX());
1986 dy = SkScalarToFixed(matrix.getSkewY());
1987 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001988
reed@google.com61eb0402011-04-15 12:11:12 +00001989 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990 *dstC++ = cache[SkATan2_255(fy, fx)];
1991 fx += dx;
1992 fy += dy;
1993 }
reed@google.com61eb0402011-04-15 12:11:12 +00001994 } else { // perspective case
1995 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1997 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001998
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
2000 SkScalarToFixed(srcPt.fX));
2001 *dstC++ = cache[index];
2002 }
2003 }
2004}
2005
reed@google.comdd5bd672011-09-20 15:56:13 +00002006void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007 SkMatrix::MapXYProc proc = fDstToIndexProc;
2008 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002009 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002010 int toggle = ((x ^ y) & 1) << kCache16Bits;
2011 SkPoint srcPt;
2012
reed@google.com61eb0402011-04-15 12:11:12 +00002013 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2015 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2016 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
2017 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002018
reed@google.com61eb0402011-04-15 12:11:12 +00002019 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002020 SkFixed storage[2];
2021 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2022 &storage[0], &storage[1]);
2023 dx = storage[0];
2024 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00002025 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
2027 dx = SkScalarToFixed(matrix.getScaleX());
2028 dy = SkScalarToFixed(matrix.getSkewY());
2029 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002030
reed@google.com61eb0402011-04-15 12:11:12 +00002031 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2033 *dstC++ = cache[toggle + index];
2034 toggle ^= (1 << kCache16Bits);
2035 fx += dx;
2036 fy += dy;
2037 }
reed@google.com61eb0402011-04-15 12:11:12 +00002038 } else { // perspective case
2039 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2041 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002042
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
2044 SkScalarToFixed(srcPt.fX));
2045 index >>= (8 - kCache16Bits);
2046 *dstC++ = cache[toggle + index];
2047 toggle ^= (1 << kCache16Bits);
2048 }
2049 }
2050}
2051
reed@google.com61eb0402011-04-15 12:11:12 +00002052///////////////////////////////////////////////////////////////////////////////
2053///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054
2055// assumes colors is SkColor* and pos is SkScalar*
2056#define EXPAND_1_COLOR(count) \
2057 SkColor tmp[2]; \
2058 do { \
2059 if (1 == count) { \
2060 tmp[0] = tmp[1] = colors[0]; \
2061 colors = tmp; \
2062 pos = NULL; \
2063 count = 2; \
2064 } \
2065 } while (0)
2066
reed@google.com61eb0402011-04-15 12:11:12 +00002067SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2068 const SkColor colors[],
2069 const SkScalar pos[], int colorCount,
2070 SkShader::TileMode mode,
2071 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 if (NULL == pts || NULL == colors || colorCount < 1) {
2073 return NULL;
2074 }
2075 EXPAND_1_COLOR(colorCount);
2076
reed@android.comab840b82009-07-01 17:00:03 +00002077 return SkNEW_ARGS(Linear_Gradient,
2078 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002079}
2080
reed@google.com61eb0402011-04-15 12:11:12 +00002081SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2082 const SkColor colors[],
2083 const SkScalar pos[], int colorCount,
2084 SkShader::TileMode mode,
2085 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 if (radius <= 0 || NULL == colors || colorCount < 1) {
2087 return NULL;
2088 }
2089 EXPAND_1_COLOR(colorCount);
2090
reed@android.comab840b82009-07-01 17:00:03 +00002091 return SkNEW_ARGS(Radial_Gradient,
2092 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002093}
2094
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002095SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2096 SkScalar startRadius,
2097 const SkPoint& end,
2098 SkScalar endRadius,
2099 const SkColor colors[],
2100 const SkScalar pos[],
2101 int colorCount,
2102 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002103 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002104 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2105 return NULL;
2106 }
2107 EXPAND_1_COLOR(colorCount);
2108
2109 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002110 (start, startRadius, end, endRadius, colors, pos,
2111 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002112}
2113
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2115 const SkColor colors[],
2116 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002117 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118 if (NULL == colors || count < 1) {
2119 return NULL;
2120 }
2121 EXPAND_1_COLOR(count);
2122
2123 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2124}
2125
2126static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
2127 Linear_Gradient::CreateProc);
2128
2129static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
2130 Radial_Gradient::CreateProc);
2131
2132static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
2133 Sweep_Gradient::CreateProc);
tomhudson@google.com938d6042011-04-27 13:57:03 +00002134
2135static SkFlattenable::Registrar
2136 gTwoPointRadialGradientReg("Two_Point_Radial_Gradient",
2137 Two_Point_Radial_Gradient::CreateProc);