blob: 0518686a21dd8405cff03c9bb94694f6b0aa75a8 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008
9#include "SkGradientShader.h"
10#include "SkColorPriv.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000011#include "SkMallocPixelRef.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkUnitMapper.h"
13#include "SkUtils.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000014#include "SkTemplates.h"
15#include "SkBitmapCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016
reed@google.com51baf5a2011-09-21 13:38:36 +000017#if defined(SK_SCALAR_IS_FLOAT) && !defined(SK_DONT_USE_FLOAT_SQRT)
18 #define SK_USE_FLOAT_SQRT
19#endif
20
reed@google.com5eb158d2011-04-15 15:50:34 +000021static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
22 int count) {
23 if (count > 0) {
24 if (v0 == v1) {
25 sk_memset32(dst, v0, count);
26 } else {
27 int pairs = count >> 1;
28 for (int i = 0; i < pairs; i++) {
29 *dst++ = v0;
30 *dst++ = v1;
31 }
32 if (count & 1) {
33 *dst = v0;
34 }
35 }
36 }
37}
38
reed@google.com61eb0402011-04-15 12:11:12 +000039///////////////////////////////////////////////////////////////////////////////
tomhudson@google.com9ce767c2011-04-25 20:49:39 +000040// Can't use a two-argument function with side effects like this in a
41// constructor's initializer's argument list because the order of
42// evaluations in that context is undefined (and backwards on linux/gcc).
43static SkPoint unflatten_point(SkReader32& buffer) {
44 SkPoint retval;
45 retval.fX = buffer.readScalar();
46 retval.fY = buffer.readScalar();
47 return retval;
48}
49
50///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
52typedef SkFixed (*TileProc)(SkFixed);
53
reed@android.com41bccf52009-04-03 13:33:51 +000054static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 return SkClampMax(x, 0xFFFF);
56}
57
reed@android.com41bccf52009-04-03 13:33:51 +000058static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 return x & 0xFFFF;
60}
61
reed@android.com41bccf52009-04-03 13:33:51 +000062static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000063 int s = x << 15 >> 31;
64 return (x ^ s) & 0xFFFF;
65}
66
67static const TileProc gTileProcs[] = {
68 clamp_tileproc,
69 repeat_tileproc,
70 mirror_tileproc
71};
72
reed@google.com61eb0402011-04-15 12:11:12 +000073///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000074
reed@android.com200645d2009-12-14 16:41:57 +000075static inline int repeat_bits(int x, const int bits) {
76 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000077}
78
reed@android.com200645d2009-12-14 16:41:57 +000079static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000080#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000081 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000083 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000084#else
reed@android.com200645d2009-12-14 16:41:57 +000085 int s = x << (31 - bits) >> 31;
86 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000087#endif
88}
89
reed@android.com41bccf52009-04-03 13:33:51 +000090static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 return x & 0xFF;
92}
93
reed@android.com41bccf52009-04-03 13:33:51 +000094static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000095#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000096 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000098 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 return x & 255;
100#else
101 int s = x << 23 >> 31;
102 return (x ^ s) & 0xFF;
103#endif
104}
105
reed@google.com61eb0402011-04-15 12:11:12 +0000106///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107
108class Gradient_Shader : public SkShader {
109public:
110 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000111 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 virtual ~Gradient_Shader();
113
114 // overrides
reed@google.com7716afb2011-12-07 15:17:50 +0000115 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
116 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117
118protected:
119 Gradient_Shader(SkFlattenableReadBuffer& );
120 SkUnitMapper* fMapper;
121 SkMatrix fPtsToUnit; // set by subclass
122 SkMatrix fDstToIndex;
123 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 TileMode fTileMode;
125 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000126 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 uint8_t fDstToIndexClass;
128 uint8_t fFlags;
129
130 struct Rec {
131 SkFixed fPos; // 0...1
132 uint32_t fScale; // (1 << 24) / range
133 };
134 Rec* fRecs;
135
136 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000137 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000139 kCache16Mask = kCache16Count - 1,
140 kCache16Shift = 16 - kCache16Bits,
141
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 kCache32Bits = 8, // pretty much should always be 8
143 kCache32Count = 1 << kCache32Bits
144 };
145 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000146 const uint16_t* getCache16() const;
147 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148
reed@google.com7c2f27d2011-03-07 19:29:00 +0000149 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000150 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152private:
153 enum {
154 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
155
reed@android.com1c12abe2009-07-02 15:01:02 +0000156 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 };
158 SkColor fStorage[(kStorageSize + 3) >> 2];
159 SkColor* fOrigColors;
160
reed@google.com7c2f27d2011-03-07 19:29:00 +0000161 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
162 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163
reed@google.com7c2f27d2011-03-07 19:29:00 +0000164 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
165 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000166 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 +0000167
reed@android.com512a8762009-12-14 15:25:36 +0000168 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000169 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
170 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000171 void setCacheAlpha(U8CPU alpha) const;
reed@android.com512a8762009-12-14 15:25:36 +0000172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 typedef SkShader INHERITED;
174};
175
reed@android.com41bccf52009-04-03 13:33:51 +0000176static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 SkASSERT(x >= 0 && x <= SK_Scalar1);
178
179#ifdef SK_SCALAR_IS_FLOAT
180 return (unsigned)(x * 0xFFFF);
181#else
182 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
183#endif
184}
185
reed@android.com41bccf52009-04-03 13:33:51 +0000186Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
187 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 SkASSERT(colorCount > 1);
189
190 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
191
192 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000193 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
196 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
197 fTileMode = mode;
198 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000199
reed@android.com41bccf52009-04-03 13:33:51 +0000200 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000201 fCache32 = NULL;
202 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203
reed@android.com41bccf52009-04-03 13:33:51 +0000204 /* Note: we let the caller skip the first and/or last position.
205 i.e. pos[0] = 0.3, pos[1] = 0.7
206 In these cases, we insert dummy entries to ensure that the final data
207 will be bracketed by [0, 1].
208 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
209
210 Thus colorCount (the caller's value, and fColorCount (our value) may
211 differ by up to 2. In the above example:
212 colorCount = 2
213 fColorCount = 4
214 */
215 fColorCount = colorCount;
216 // check if we need to add in dummy start and/or end position/colors
217 bool dummyFirst = false;
218 bool dummyLast = false;
219 if (pos) {
220 dummyFirst = pos[0] != 0;
221 dummyLast = pos[colorCount - 1] != SK_Scalar1;
222 fColorCount += dummyFirst + dummyLast;
223 }
224
225 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000226 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000227 fOrigColors = reinterpret_cast<SkColor*>(
228 sk_malloc_throw(size * fColorCount));
229 }
230 else {
231 fOrigColors = fStorage;
232 }
233
234 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 {
reed@android.com41bccf52009-04-03 13:33:51 +0000236 SkColor* origColors = fOrigColors;
237 if (dummyFirst) {
238 *origColors++ = colors[0];
239 }
240 memcpy(origColors, colors, colorCount * sizeof(SkColor));
241 if (dummyLast) {
242 origColors += colorCount;
243 *origColors = colors[colorCount - 1];
244 }
245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246
reed@android.com1c12abe2009-07-02 15:01:02 +0000247 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000248 if (fColorCount > 2) {
249 Rec* recs = fRecs;
250 recs->fPos = 0;
251 // recs->fScale = 0; // unused;
252 recs += 1;
253 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 /* We need to convert the user's array of relative positions into
255 fixed-point positions and scale factors. We need these results
256 to be strictly monotonic (no two values equal or out of order).
257 Hence this complex loop that just jams a zero for the scale
258 value if it sees a segment out of order, and it assures that
259 we start at 0 and end at 1.0
260 */
261 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000262 int startIndex = dummyFirst ? 0 : 1;
263 int count = colorCount + dummyLast;
264 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 // force the last value to be 1.0
266 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000267 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000269 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 }
reed@android.com41bccf52009-04-03 13:33:51 +0000272 // pin curr withing range
273 if (curr < 0) {
274 curr = 0;
275 } else if (curr > SK_Fixed1) {
276 curr = SK_Fixed1;
277 }
278 recs->fPos = curr;
279 if (curr > prev) {
280 recs->fScale = (1 << 24) / (curr - prev);
281 } else {
282 recs->fScale = 0; // ignore this segment
283 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 // get ready for the next value
285 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000286 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 }
reed@android.com41bccf52009-04-03 13:33:51 +0000288 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 SkFixed dp = SK_Fixed1 / (colorCount - 1);
290 SkFixed p = dp;
291 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000292 for (int i = 1; i < colorCount; i++) {
293 recs->fPos = p;
294 recs->fScale = scale;
295 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 p += dp;
297 }
298 }
299 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000300 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301}
302
303Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000304 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 fCacheAlpha = 256;
306
307 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
308
309 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000310 fCache32 = NULL;
311 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312
reed@android.com41bccf52009-04-03 13:33:51 +0000313 int colorCount = fColorCount = buffer.readU32();
314 if (colorCount > kColorStorageCount) {
315 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
316 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
317 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000319 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321
322 fTileMode = (TileMode)buffer.readU8();
323 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000324 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 if (colorCount > 2) {
326 Rec* recs = fRecs;
327 recs[0].fPos = 0;
328 for (int i = 1; i < colorCount; i++) {
329 recs[i].fPos = buffer.readS32();
330 recs[i].fScale = buffer.readU32();
331 }
332 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000333 SkReadMatrix(&buffer, &fPtsToUnit);
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000334 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335}
336
reed@android.com41bccf52009-04-03 13:33:51 +0000337Gradient_Shader::~Gradient_Shader() {
338 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000340 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000341 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000342 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000344 }
reed@google.com82065d62011-02-07 15:30:46 +0000345 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346}
347
reed@android.com41bccf52009-04-03 13:33:51 +0000348void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 this->INHERITED::flatten(buffer);
350 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000351 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
353 buffer.write8(fTileMode);
354 if (fColorCount > 2) {
355 Rec* recs = fRecs;
356 for (int i = 1; i < fColorCount; i++) {
357 buffer.write32(recs[i].fPos);
358 buffer.write32(recs[i].fScale);
359 }
360 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000361 SkWriteMatrix(&buffer, fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362}
363
364bool Gradient_Shader::setContext(const SkBitmap& device,
365 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000366 const SkMatrix& matrix) {
367 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000369 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370
371 const SkMatrix& inverse = this->getTotalInverse();
372
373 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
374 return false;
375 }
376
377 fDstToIndexProc = fDstToIndex.getMapXYProc();
378 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
379
380 // now convert our colors in to PMColors
381 unsigned paintAlpha = this->getPaintAlpha();
382 unsigned colorAlpha = 0xFF;
383
reed@android.com3d06a8c2009-07-07 18:19:59 +0000384 // FIXME: record colorAlpha in constructor, since this is not affected
385 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000386 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 SkColor src = fOrigColors[i];
388 unsigned sa = SkColorGetA(src);
389 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 }
391
392 fFlags = this->INHERITED::getFlags();
393 if ((colorAlpha & paintAlpha) == 0xFF) {
394 fFlags |= kOpaqueAlpha_Flag;
395 }
396 // we can do span16 as long as our individual colors are opaque,
397 // regardless of the paint's alpha
398 if (0xFF == colorAlpha) {
399 fFlags |= kHasSpan16_Flag;
400 }
401
reed@google.com95eed982011-07-05 17:01:56 +0000402 this->setCacheAlpha(paintAlpha);
403 return true;
404}
405
406void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 // if the new alpha differs from the previous time we were called, inval our cache
408 // this will trigger the cache to be rebuilt.
409 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000410 if (fCacheAlpha != alpha) {
411 fCache16 = NULL; // inval the cache
412 fCache32 = NULL; // inval the cache
413 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000414 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000415 if (fCache32PixelRef) {
416 fCache32PixelRef->notifyPixelsChanged();
417 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419}
420
reed@android.com41bccf52009-04-03 13:33:51 +0000421static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422 SkASSERT(a == SkToU8(a));
423 SkASSERT(b == SkToU8(b));
424 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 return a + ((b - a) * scale >> 8);
426}
427
reed@android.com41bccf52009-04-03 13:33:51 +0000428static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
429 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430#if 0
431 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
432 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
433 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
434 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
435
436 return SkPackARGB32(a, r, g, b);
437#else
438 int otherBlend = 256 - blend;
439
440#if 0
441 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
442 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
443 SkASSERT((t0 & t1) == 0);
444 return t0 | t1;
445#else
446 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
447 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
448#endif
449
450#endif
451}
452
453#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
454
reed@android.com41bccf52009-04-03 13:33:51 +0000455/** We take the original colors, not our premultiplied PMColors, since we can
456 build a 16bit table as long as the original colors are opaque, even if the
457 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458*/
reed@android.com512a8762009-12-14 15:25:36 +0000459void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
460 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 SkASSERT(count > 1);
462 SkASSERT(SkColorGetA(c0) == 0xFF);
463 SkASSERT(SkColorGetA(c1) == 0xFF);
464
465 SkFixed r = SkColorGetR(c0);
466 SkFixed g = SkColorGetG(c0);
467 SkFixed b = SkColorGetB(c0);
468
469 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
470 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
471 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
472
473 r = SkIntToFixed(r) + 0x8000;
474 g = SkIntToFixed(g) + 0x8000;
475 b = SkIntToFixed(b) + 0x8000;
476
477 do {
478 unsigned rr = r >> 16;
479 unsigned gg = g >> 16;
480 unsigned bb = b >> 16;
481 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000482 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 cache += 1;
484 r += dr;
485 g += dg;
486 b += db;
487 } while (--count != 0);
488}
489
reed@google.com55b8e8c2011-01-13 16:22:35 +0000490/*
491 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
492 * semantics of how we 2x2 dither 32->16
493 */
494static inline U8CPU dither_fixed_to_8(SkFixed n) {
495 n >>= 8;
496 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
497}
498
499/*
500 * For dithering with premultiply, we want to ceiling the alpha component,
501 * to ensure that it is always >= any color component.
502 */
503static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
504 n >>= 8;
505 return ((n << 1) - (n | (n >> 8))) >> 8;
506}
507
508void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
509 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 SkASSERT(count > 1);
511
reed@android.com1c12abe2009-07-02 15:01:02 +0000512 // need to apply paintAlpha to our two endpoints
513 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
514 SkFixed da;
515 {
516 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
517 da = SkIntToFixed(tmp - a) / (count - 1);
518 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519
reed@android.com1c12abe2009-07-02 15:01:02 +0000520 SkFixed r = SkColorGetR(c0);
521 SkFixed g = SkColorGetG(c0);
522 SkFixed b = SkColorGetB(c0);
523 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
524 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
525 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526
527 a = SkIntToFixed(a) + 0x8000;
528 r = SkIntToFixed(r) + 0x8000;
529 g = SkIntToFixed(g) + 0x8000;
530 b = SkIntToFixed(b) + 0x8000;
531
532 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000533 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
534 cache[kCache32Count] = SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
535 dither_fixed_to_8(r),
536 dither_fixed_to_8(g),
537 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000538 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 a += da;
540 r += dr;
541 g += dg;
542 b += db;
543 } while (--count != 0);
544}
545
reed@android.com41bccf52009-04-03 13:33:51 +0000546static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 SkASSERT((unsigned)x <= SK_Fixed1);
548 return x - (x >> 16);
549}
550
reed@android.com200645d2009-12-14 16:41:57 +0000551static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000552 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000553 if (6 == bits) {
554 return (x << 10) | (x << 4) | (x >> 2);
555 }
556 if (8 == bits) {
557 return (x << 8) | x;
558 }
559 sk_throw();
560 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561}
562
reed@google.com7c2f27d2011-03-07 19:29:00 +0000563const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000564 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000565 // double the count for dither entries
566 const int entryCount = kCache16Count * 2;
567 const size_t allocSize = sizeof(uint16_t) * entryCount;
568
reed@android.com3c9b2a42009-08-27 19:28:37 +0000569 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000570 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000571 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000573 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000574 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000575 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 Rec* rec = fRecs;
577 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000578 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000579 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 SkASSERT(nextIndex < kCache16Count);
581
582 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000583 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 prevIndex = nextIndex;
585 }
586 SkASSERT(prevIndex == kCache16Count - 1);
587 }
588
reed@android.com41bccf52009-04-03 13:33:51 +0000589 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000590 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 uint16_t* linear = fCache16; // just computed linear data
592 uint16_t* mapped = fCache16Storage; // storage for mapped data
593 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000594 for (int i = 0; i < kCache16Count; i++) {
595 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000597 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 }
599 sk_free(fCache16);
600 fCache16 = fCache16Storage;
601 }
602 }
603 return fCache16;
604}
605
reed@google.com7c2f27d2011-03-07 19:29:00 +0000606const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000607 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000608 // double the count for dither entries
609 const int entryCount = kCache32Count * 2;
610 const size_t allocSize = sizeof(SkPMColor) * entryCount;
611
reed@google.comdc731fd2010-12-23 15:19:47 +0000612 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000613 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
614 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000615 }
616 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000617 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000618 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
619 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000620 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 Rec* rec = fRecs;
622 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000623 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
625 SkASSERT(nextIndex < kCache32Count);
626
627 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000628 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
629 fOrigColors[i],
630 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 prevIndex = nextIndex;
632 }
633 SkASSERT(prevIndex == kCache32Count - 1);
634 }
635
reed@android.com41bccf52009-04-03 13:33:51 +0000636 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000637 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000638 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000640 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000642 for (int i = 0; i < kCache32Count; i++) {
643 int index = map->mapUnit16((i << 8) | i) >> 8;
644 mapped[i] = linear[index];
645 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000646 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000647 fCache32PixelRef->unref();
648 fCache32PixelRef = newPR;
649 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 }
651 }
652 return fCache32;
653}
654
reed@google.comdc731fd2010-12-23 15:19:47 +0000655/*
656 * Because our caller might rebuild the same (logically the same) gradient
657 * over and over, we'd like to return exactly the same "bitmap" if possible,
658 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
659 * To do that, we maintain a private cache of built-bitmaps, based on our
660 * colors and positions. Note: we don't try to flatten the fMapper, so if one
661 * is present, we skip the cache for now.
662 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000663void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000664 // our caller assumes no external alpha, so we ensure that our cache is
665 // built with 0xFF
666 this->setCacheAlpha(0xFF);
667
reed@google.comdc731fd2010-12-23 15:19:47 +0000668 // don't have a way to put the mapper into our cache-key yet
669 if (fMapper) {
670 // force our cahce32pixelref to be built
671 (void)this->getCache32();
672 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
673 bitmap->setPixelRef(fCache32PixelRef);
674 return;
675 }
676
677 // build our key: [numColors + colors[] + {positions[]} ]
678 int count = 1 + fColorCount;
679 if (fColorCount > 2) {
680 count += fColorCount - 1; // fRecs[].fPos
681 }
682
683 SkAutoSTMalloc<16, int32_t> storage(count);
684 int32_t* buffer = storage.get();
685
686 *buffer++ = fColorCount;
687 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
688 buffer += fColorCount;
689 if (fColorCount > 2) {
690 for (int i = 1; i < fColorCount; i++) {
691 *buffer++ = fRecs[i].fPos;
692 }
693 }
694 SkASSERT(buffer - storage.get() == count);
695
696 ///////////////////////////////////
697
698 static SkMutex gMutex;
699 static SkBitmapCache* gCache;
700 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
701 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
702 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000703
reed@google.comdc731fd2010-12-23 15:19:47 +0000704 if (NULL == gCache) {
705 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
706 }
707 size_t size = count * sizeof(int32_t);
708
709 if (!gCache->find(storage.get(), size, bitmap)) {
710 // force our cahce32pixelref to be built
711 (void)this->getCache32();
712 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
713 bitmap->setPixelRef(fCache32PixelRef);
714
715 gCache->add(storage.get(), size, *bitmap);
716 }
717}
718
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000719void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
720 if (info) {
721 if (info->fColorCount >= fColorCount) {
722 if (info->fColors) {
723 memcpy(info->fColors, fOrigColors,
724 fColorCount * sizeof(SkColor));
725 }
726 if (info->fColorOffsets) {
727 if (fColorCount == 2) {
728 info->fColorOffsets[0] = 0;
729 info->fColorOffsets[1] = SK_Scalar1;
730 } else if (fColorCount > 2) {
731 for (int i = 0; i < fColorCount; i++)
732 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
733 }
734 }
735 }
736 info->fColorCount = fColorCount;
737 info->fTileMode = fTileMode;
738 }
739}
740
reed@google.com61eb0402011-04-15 12:11:12 +0000741///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742
reed@android.com41bccf52009-04-03 13:33:51 +0000743static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 SkVector vec = pts[1] - pts[0];
745 SkScalar mag = vec.length();
746 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
747
748 vec.scale(inv);
749 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
750 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
751 matrix->postScale(inv, inv);
752}
753
754///////////////////////////////////////////////////////////////////////////////
755
756class Linear_Gradient : public Gradient_Shader {
757public:
758 Linear_Gradient(const SkPoint pts[2],
759 const SkColor colors[], const SkScalar pos[], int colorCount,
760 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000761 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
762 fStart(pts[0]),
763 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 {
765 pts_to_unit_matrix(pts, &fPtsToUnit);
766 }
reed@android.com9b46e772009-06-05 12:24:41 +0000767
reed@google.com7716afb2011-12-07 15:17:50 +0000768 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
769 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
770 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
771 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
772 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
773 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774
reed@google.com55b8e8c2011-01-13 16:22:35 +0000775 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 return SkNEW_ARGS(Linear_Gradient, (buffer));
777 }
778
reed@google.com7716afb2011-12-07 15:17:50 +0000779 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000780 this->INHERITED::flatten(buffer);
781 buffer.writeScalar(fStart.fX);
782 buffer.writeScalar(fStart.fY);
783 buffer.writeScalar(fEnd.fX);
784 buffer.writeScalar(fEnd.fY);
785 }
786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000788 Linear_Gradient(SkFlattenableReadBuffer& buffer)
789 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +0000790 fStart(unflatten_point(buffer)),
791 fEnd(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000792 }
reed@google.com7716afb2011-12-07 15:17:50 +0000793 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794
795private:
796 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000797 const SkPoint fStart;
798 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799};
800
reed@android.com5119bdb2009-06-12 21:27:03 +0000801bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
802 const SkMatrix& matrix) {
803 if (!this->INHERITED::setContext(device, paint, matrix)) {
804 return false;
805 }
806
807 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
808 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000809 fFlags |= SkShader::kConstInY32_Flag;
810 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
811 // only claim this if we do have a 16bit mode (i.e. none of our
812 // colors have alpha), and if we are not dithering (which obviously
813 // is not const in Y).
814 fFlags |= SkShader::kConstInY16_Flag;
815 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000816 }
817 return true;
818}
819
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@google.com61eb0402011-04-15 12:11:12 +0000821static inline bool no_need_for_clamp(int fx, int dx, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 SkASSERT(count > 0);
823 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
824}
825
reed@google.com5eb158d2011-04-15 15:50:34 +0000826#include "SkClampRange.h"
827
828#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000829 do { \
830 unsigned fi = fx >> 8; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000831 SkASSERT(fi <= 0xFF); \
832 fx += dx; \
833 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +0000834 toggle ^= TOGGLE_MASK; \
835 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000836
837
reed@google.comdd5bd672011-09-20 15:56:13 +0000838void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 SkASSERT(count > 0);
840
841 SkPoint srcPt;
842 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
843 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000844 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com55b8e8c2011-01-13 16:22:35 +0000845 int toggle = ((x ^ y) & 1) << kCache32Bits;
846 const int TOGGLE_MASK = (1 << kCache32Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847
reed@android.comc552a432009-06-12 20:02:50 +0000848 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000849 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
850 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
852
reed@android.comc552a432009-06-12 20:02:50 +0000853 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 SkFixed dxStorage[1];
855 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
856 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000857 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
859 dx = SkScalarToFixed(fDstToIndex.getScaleX());
860 }
861
reed@android.comc552a432009-06-12 20:02:50 +0000862 if (SkFixedNearlyZero(dx)) {
863 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 unsigned fi = proc(fx);
865 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000866 // TODO: dither version
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000868 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000869 SkClampRange range;
870 range.init(fx, dx, count, 0, 0xFF);
871
872 if ((count = range.fCount0) > 0) {
873 sk_memset32_dither(dstC,
874 cache[toggle + range.fV0],
875 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
876 count);
877 dstC += count;
878 }
879 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +0000880 int unroll = count >> 3;
881 fx = range.fFx1;
882 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000883 NO_CHECK_ITER; NO_CHECK_ITER;
884 NO_CHECK_ITER; NO_CHECK_ITER;
885 NO_CHECK_ITER; NO_CHECK_ITER;
886 NO_CHECK_ITER; NO_CHECK_ITER;
887 }
888 if ((count &= 7) > 0) {
889 do {
890 NO_CHECK_ITER;
891 } while (--count != 0);
892 }
893 }
894 if ((count = range.fCount2) > 0) {
895 sk_memset32_dither(dstC,
896 cache[toggle + range.fV1],
897 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
898 count);
899 }
reed@android.comc552a432009-06-12 20:02:50 +0000900 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 do {
902 unsigned fi = mirror_8bits(fx >> 8);
903 SkASSERT(fi <= 0xFF);
904 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000905 *dstC++ = cache[toggle + fi];
906 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000908 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 SkASSERT(proc == repeat_tileproc);
910 do {
911 unsigned fi = repeat_8bits(fx >> 8);
912 SkASSERT(fi <= 0xFF);
913 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000914 *dstC++ = cache[toggle + fi];
915 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 } while (--count != 0);
917 }
reed@android.comc552a432009-06-12 20:02:50 +0000918 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 SkScalar dstX = SkIntToScalar(x);
920 SkScalar dstY = SkIntToScalar(y);
921 do {
922 dstProc(fDstToIndex, dstX, dstY, &srcPt);
923 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
924 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000925 *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))];
926 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 dstX += SK_Scalar1;
928 } while (--count != 0);
929 }
930}
931
reed@google.com55b8e8c2011-01-13 16:22:35 +0000932SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000933 SkMatrix* matrix,
934 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +0000935 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000937 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 }
939 if (matrix) {
940 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
941 matrix->preConcat(fPtsToUnit);
942 }
943 if (xy) {
944 xy[0] = fTileMode;
945 xy[1] = kClamp_TileMode;
946 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000947 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948}
949
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000950SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
951 if (info) {
952 commonAsAGradient(info);
953 info->fPoint[0] = fStart;
954 info->fPoint[1] = fEnd;
955 }
956 return kLinear_GradientType;
957}
958
reed@android.com3c9b2a42009-08-27 19:28:37 +0000959static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
960 int count) {
961 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 *dst++ = value;
963 count -= 1;
964 SkTSwap(value, other);
965 }
966
967 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000968
reed@android.com3c9b2a42009-08-27 19:28:37 +0000969 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000971 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973
reed@google.com5eb158d2011-04-15 15:50:34 +0000974#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +0000975 do { \
976 unsigned fi = fx >> kCache16Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000977 SkASSERT(fi <= kCache16Mask); \
978 fx += dx; \
979 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +0000980 toggle ^= TOGGLE_MASK; \
981 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000982
983
reed@google.comdd5bd672011-09-20 15:56:13 +0000984void Linear_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 SkASSERT(count > 0);
986
987 SkPoint srcPt;
988 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
989 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000990 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@google.com5eb158d2011-04-15 15:50:34 +0000992 const int TOGGLE_MASK = (1 << kCache32Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000994 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000995 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
996 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
998
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000999 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 SkFixed dxStorage[1];
1001 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1002 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001003 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1005 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1006 }
1007
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001008 if (SkFixedNearlyZero(dx)) {
1009 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +00001010 unsigned fi = proc(fx) >> kCache16Shift;
1011 SkASSERT(fi <= kCache16Mask);
reed@google.com5eb158d2011-04-15 15:50:34 +00001012 dither_memset16(dstC, cache[toggle + fi],
1013 cache[(toggle ^ TOGGLE_MASK) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001014 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001015 SkClampRange range;
1016 range.init(fx, dx, count, 0, kCache16Mask);
1017
1018 if ((count = range.fCount0) > 0) {
1019 dither_memset16(dstC,
1020 cache[toggle + range.fV0],
1021 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
1022 count);
1023 dstC += count;
1024 }
1025 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +00001026 int unroll = count >> 3;
1027 fx = range.fFx1;
1028 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001029 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1030 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1031 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1032 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1033 }
1034 if ((count &= 7) > 0) {
1035 do {
1036 NO_CHECK_ITER_16;
1037 } while (--count != 0);
1038 }
1039 }
1040 if ((count = range.fCount2) > 0) {
1041 dither_memset16(dstC,
1042 cache[toggle + range.fV1],
1043 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
1044 count);
1045 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001046 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 do {
reed@android.com200645d2009-12-14 16:41:57 +00001048 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001049 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001052 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001054 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 SkASSERT(proc == repeat_tileproc);
1056 do {
reed@android.com200645d2009-12-14 16:41:57 +00001057 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001058 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001061 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 } while (--count != 0);
1063 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001064 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 SkScalar dstX = SkIntToScalar(x);
1066 SkScalar dstY = SkIntToScalar(y);
1067 do {
1068 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1069 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1070 SkASSERT(fi <= 0xFFFF);
1071
reed@android.com512a8762009-12-14 15:25:36 +00001072 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 *dstC++ = cache[toggle + index];
reed@google.com5eb158d2011-04-15 15:50:34 +00001074 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075
1076 dstX += SK_Scalar1;
1077 } while (--count != 0);
1078 }
1079}
1080
1081///////////////////////////////////////////////////////////////////////////////
1082
1083#define kSQRT_TABLE_BITS 11
1084#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1085
1086#include "SkRadialGradient_Table.h"
1087
1088#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1089
1090#include <stdio.h>
1091
reed@google.com61eb0402011-04-15 12:11:12 +00001092void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1094
1095 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1096 SkASSERT(file);
1097 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1098
reed@google.com61eb0402011-04-15 12:11:12 +00001099 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1100 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001102 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103
1104 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1105
1106 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001107 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001109 }
1110 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001112 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 }
1114 ::fprintf(file, "};\n");
1115 ::fclose(file);
1116}
1117
1118#endif
1119
1120
reed@google.com61eb0402011-04-15 12:11:12 +00001121static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1122 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 SkScalar inv = SkScalarInvert(radius);
1124
1125 matrix->setTranslate(-center.fX, -center.fY);
1126 matrix->postScale(inv, inv);
1127}
1128
1129class Radial_Gradient : public Gradient_Shader {
1130public:
1131 Radial_Gradient(const SkPoint& center, SkScalar radius,
1132 const SkColor colors[], const SkScalar pos[], int colorCount,
1133 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001134 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1135 fCenter(center),
1136 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 {
1138 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1139 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1140
1141 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1142 }
reed@google.com61eb0402011-04-15 12:11:12 +00001143
reed@google.com8e6d9142011-12-07 15:30:34 +00001144 virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count) SK_OVERRIDE;
reed@google.com7716afb2011-12-07 15:17:50 +00001145 virtual void shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 SkASSERT(count > 0);
1147
1148 SkPoint srcPt;
1149 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1150 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001151 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153
reed@android.com3c9b2a42009-08-27 19:28:37 +00001154 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001155 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1156 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1158 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1159
reed@android.com3c9b2a42009-08-27 19:28:37 +00001160 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 SkFixed storage[2];
1162 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1163 dx = storage[0];
1164 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001165 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1167 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1168 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1169 }
1170
reed@android.com3c9b2a42009-08-27 19:28:37 +00001171 if (proc == clamp_tileproc) {
reed@google.comdd5bd672011-09-20 15:56:13 +00001172 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173
1174 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1175 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1176 precision, but that appears to be visually OK. If we decide this is OK for
1177 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1178 to avoid having to do these extra shifts each time.
1179 */
1180 fx >>= 1;
1181 dx >>= 1;
1182 fy >>= 1;
1183 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001184 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 +00001185 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1186 fy *= fy;
1187 do {
1188 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1189 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1190 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1191 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1193 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001195 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 do {
1197 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1198 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1199 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1200 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1201 fx += dx;
1202 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1204 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 } while (--count != 0);
1206 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001207 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 do {
1209 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1210 unsigned fi = mirror_tileproc(dist);
1211 SkASSERT(fi <= 0xFFFF);
1212 fx += dx;
1213 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1215 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001217 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218 SkASSERT(proc == repeat_tileproc);
1219 do {
1220 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1221 unsigned fi = repeat_tileproc(dist);
1222 SkASSERT(fi <= 0xFFFF);
1223 fx += dx;
1224 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1226 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 } while (--count != 0);
1228 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001229 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 SkScalar dstX = SkIntToScalar(x);
1231 SkScalar dstY = SkIntToScalar(y);
1232 do {
1233 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1234 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1235 SkASSERT(fi <= 0xFFFF);
1236
1237 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 *dstC++ = cache[toggle + index];
1239 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240
1241 dstX += SK_Scalar1;
1242 } while (--count != 0);
1243 }
1244 }
1245
reed@google.com55b8e8c2011-01-13 16:22:35 +00001246 virtual BitmapType asABitmap(SkBitmap* bitmap,
1247 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001248 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00001249 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001250 if (bitmap) {
1251 this->commonAsABitmap(bitmap);
1252 }
1253 if (matrix) {
1254 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1255 matrix->preConcat(fPtsToUnit);
1256 }
1257 if (xy) {
1258 xy[0] = fTileMode;
1259 xy[1] = kClamp_TileMode;
1260 }
1261 return kRadial_BitmapType;
1262 }
reed@google.com7716afb2011-12-07 15:17:50 +00001263 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001264 if (info) {
1265 commonAsAGradient(info);
1266 info->fPoint[0] = fCenter;
1267 info->fRadius[0] = fRadius;
1268 }
1269 return kRadial_GradientType;
1270 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001271
reed@google.com8e6d9142011-12-07 15:30:34 +00001272 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 return SkNEW_ARGS(Radial_Gradient, (buffer));
1274 }
1275
reed@google.com7716afb2011-12-07 15:17:50 +00001276 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001277 this->INHERITED::flatten(buffer);
1278 buffer.writeScalar(fCenter.fX);
1279 buffer.writeScalar(fCenter.fY);
1280 buffer.writeScalar(fRadius);
1281 }
1282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001284 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1285 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001286 fCenter(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001287 fRadius(buffer.readScalar()) {
1288 }
reed@google.com7716afb2011-12-07 15:17:50 +00001289 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290
1291private:
1292 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001293 const SkPoint fCenter;
1294 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295};
1296
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001297static inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
1298 // fast, overly-conservative test: checks unit square instead
1299 // of unit circle
1300 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
1301 (fx <= -SK_FixedHalf && dx <= 0);
1302 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
1303 (fy <= -SK_FixedHalf && dy <= 0);
1304
1305 return xClamped || yClamped;
1306}
1307
1308// Return true if (fx * fy) is always inside the unit circle
1309// SkPin32 is expensive, but so are all the SkFixedMul in this test,
1310// so it shouldn't be run if count is small.
1311static inline bool no_need_for_radial_pin(int fx, int dx,
1312 int fy, int dy, int count) {
1313 SkASSERT(count > 0);
1314 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1315 return false;
1316 }
1317 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
1318 return false;
1319 }
1320 fx += (count - 1) * dx;
1321 fy += (count - 1) * dy;
1322 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1323 return false;
1324 }
1325 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
1326}
1327
1328#define UNPINNED_RADIAL_STEP \
1329 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
1330 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)]; \
1331 fx += dx; \
1332 fy += dy;
1333
1334// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
1335static void radial_clamp(SkFixed fx, SkFixed fy, SkFixed dx, SkFixed dy,
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001336 SkPMColor* SK_RESTRICT dstC, int count,
1337 const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001338 const int kCache32Bits, const int kCache32Count) {
1339 // Floating point seems to be slower than fixed point,
1340 // even when we have float hardware.
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001341 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001342 fx >>= 1;
1343 dx >>= 1;
1344 fy >>= 1;
1345 dy >>= 1;
1346 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
1347 sk_memset32(dstC, cache[kCache32Count - 1], count);
1348 } else if ((count > 4) &&
1349 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
1350 unsigned fi;
1351 // 4x unroll appears to be no faster than 2x unroll on Linux
1352 while (count > 1) {
1353 UNPINNED_RADIAL_STEP;
1354 UNPINNED_RADIAL_STEP;
1355 count -= 2;
1356 }
1357 if (count) {
1358 UNPINNED_RADIAL_STEP;
1359 }
1360 }
1361 else {
1362 do {
1363 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1364 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1365 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1366 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1367 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1368 fx += dx;
1369 fy += dy;
1370 } while (--count != 0);
1371 }
1372}
1373
1374void Radial_Gradient::shadeSpan(int x, int y,
1375 SkPMColor* SK_RESTRICT dstC, int count) {
1376 SkASSERT(count > 0);
1377
1378 SkPoint srcPt;
1379 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1380 TileProc proc = fTileProc;
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001381 const SkPMColor* SK_RESTRICT cache = this->getCache32();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001382
1383 if (fDstToIndexClass != kPerspective_MatrixClass) {
1384 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1385 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1386 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1387 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1388#ifdef SK_USE_FLOAT_SQRT
1389 float fdx, fdy;
1390#endif
1391
1392 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1393 SkFixed storage[2];
1394 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1395 dx = storage[0];
1396 dy = storage[1];
1397#ifdef SK_USE_FLOAT_SQRT
1398 fdx = SkFixedToFloat(storage[0]);
1399 fdy = SkFixedToFloat(storage[1]);
1400#endif
1401 } else {
1402 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1403 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1404 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1405#ifdef SK_USE_FLOAT_SQRT
1406 fdx = fDstToIndex.getScaleX();
1407 fdy = fDstToIndex.getSkewY();
1408#endif
1409 }
1410
1411 if (proc == clamp_tileproc) {
1412 radial_clamp(fx, fy, dx, dy, dstC, count, cache,
1413 kCache32Bits, kCache32Count);
1414 } else if (proc == mirror_tileproc) {
1415#ifdef SK_USE_FLOAT_SQRT
1416 float ffx = srcPt.fX;
1417 float ffy = srcPt.fY;
1418 do {
1419 float fdist = sk_float_sqrt(ffx*ffx + ffy*ffy);
1420 unsigned fi = mirror_tileproc(SkFloatToFixed(fdist));
1421 SkASSERT(fi <= 0xFFFF);
1422 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1423 ffx += fdx;
1424 ffy += fdy;
1425 } while (--count != 0);
1426#else
1427 do {
1428 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1429 SkFixedSquare(fy);
1430 if (magnitudeSquared < 0) // Overflow.
1431 magnitudeSquared = SK_FixedMax;
1432 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1433 unsigned fi = mirror_tileproc(dist);
1434 SkASSERT(fi <= 0xFFFF);
1435 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1436 fx += dx;
1437 fy += dy;
1438 } while (--count != 0);
1439#endif
1440 } else {
1441 SkASSERT(proc == repeat_tileproc);
1442 do {
1443 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1444 SkFixedSquare(fy);
1445 if (magnitudeSquared < 0) // Overflow.
1446 magnitudeSquared = SK_FixedMax;
1447 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1448 unsigned fi = repeat_tileproc(dist);
1449 SkASSERT(fi <= 0xFFFF);
1450 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1451 fx += dx;
1452 fy += dy;
1453 } while (--count != 0);
1454 }
1455 } else { // perspective case
1456 SkScalar dstX = SkIntToScalar(x);
1457 SkScalar dstY = SkIntToScalar(y);
1458 do {
1459 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1460 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1461 SkASSERT(fi <= 0xFFFF);
1462 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1463 dstX += SK_Scalar1;
1464 } while (--count != 0);
1465 }
1466}
1467
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001468/* Two-point radial gradients are specified by two circles, each with a center
1469 point and radius. The gradient can be considered to be a series of
1470 concentric circles, with the color interpolated from the start circle
1471 (at t=0) to the end circle (at t=1).
1472
1473 For each point (x, y) in the span, we want to find the
1474 interpolated circle that intersects that point. The center
1475 of the desired circle (Cx, Cy) falls at some distance t
1476 along the line segment between the start point (Sx, Sy) and
1477 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001478
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001479 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1480 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001481
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001482 The radius of the desired circle (r) is also a linear interpolation t
1483 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001484
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001485 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001486
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001487 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001488
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001489 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001490
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001491 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001492
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001493 (x - ((1 - t) * Sx + t * Ex))^2
1494 + (y - ((1 - t) * Sy + t * Ey))^2
1495 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001496
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001497 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001498
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001499 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1500 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1501 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001502
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001503 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1504
1505 [Dx^2 + Dy^2 - Dr^2)] * t^2
1506 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1507 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001508
1509 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001510 possible circles on which the point may fall. Solving for t yields
1511 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001512
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001513 If a<0, the start circle is entirely contained in the
1514 end circle, and one of the roots will be <0 or >1 (off the line
1515 segment). If a>0, the start circle falls at least partially
1516 outside the end circle (or vice versa), and the gradient
1517 defines a "tube" where a point may be on one circle (on the
1518 inside of the tube) or the other (outside of the tube). We choose
1519 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001520
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001521 In order to keep the math to within the limits of fixed point,
1522 we divide the entire quadratic by Dr^2, and replace
1523 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001524
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001525 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1526 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1527 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001528
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001529 (x' and y' are computed by appending the subtract and scale to the
1530 fDstToIndex matrix in the constructor).
1531
1532 Since the 'A' component of the quadratic is independent of x' and y', it
1533 is precomputed in the constructor. Since the 'B' component is linear in
1534 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001535 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001536 a perspective projection), it must be computed in the loop.
1537
1538*/
1539
reed@google.com84e9c082011-04-13 17:44:24 +00001540static inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1541 SkScalar sr2d2, SkScalar foura,
1542 SkScalar oneOverTwoA, bool posRoot) {
1543 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001544 if (0 == foura) {
1545 return SkScalarToFixed(SkScalarDiv(-c, b));
1546 }
1547
reed@google.com84e9c082011-04-13 17:44:24 +00001548 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001549 if (discrim < 0) {
1550 discrim = -discrim;
1551 }
reed@google.com84e9c082011-04-13 17:44:24 +00001552 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1553 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001554 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001555 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001556 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001557 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001558 }
reed@google.com84e9c082011-04-13 17:44:24 +00001559 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001560}
1561
1562class Two_Point_Radial_Gradient : public Gradient_Shader {
1563public:
1564 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1565 const SkPoint& end, SkScalar endRadius,
1566 const SkColor colors[], const SkScalar pos[],
1567 int colorCount, SkShader::TileMode mode,
1568 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001569 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1570 fCenter1(start),
1571 fCenter2(end),
1572 fRadius1(startRadius),
1573 fRadius2(endRadius) {
1574 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001575 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001576
1577 virtual BitmapType asABitmap(SkBitmap* bitmap,
1578 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001579 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001580 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001581 if (bitmap) {
1582 this->commonAsABitmap(bitmap);
1583 }
1584 SkScalar diffL = 0; // just to avoid gcc warning
1585 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001586 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001587 SkScalarSquare(fDiff.fY));
1588 }
1589 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001590 if (diffL) {
1591 SkScalar invDiffL = SkScalarInvert(diffL);
1592 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1593 SkScalarMul(invDiffL, fDiff.fX));
1594 } else {
1595 matrix->reset();
1596 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001597 matrix->preConcat(fPtsToUnit);
1598 }
1599 if (xy) {
1600 xy[0] = fTileMode;
1601 xy[1] = kClamp_TileMode;
1602 }
1603 if (NULL != twoPointRadialParams) {
1604 twoPointRadialParams[0] = diffL;
1605 twoPointRadialParams[1] = fStartRadius;
1606 twoPointRadialParams[2] = fDiffRadius;
1607 }
1608 return kTwoPointRadial_BitmapType;
1609 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001610
reed@google.com8e6d9142011-12-07 15:30:34 +00001611 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001612 if (info) {
1613 commonAsAGradient(info);
1614 info->fPoint[0] = fCenter1;
1615 info->fPoint[1] = fCenter2;
1616 info->fRadius[0] = fRadius1;
1617 info->fRadius[1] = fRadius2;
1618 }
1619 return kRadial2_GradientType;
1620 }
1621
reed@google.com8e6d9142011-12-07 15:30:34 +00001622 virtual void shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) SK_OVERRIDE {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001623 SkASSERT(count > 0);
1624
1625 // Zero difference between radii: fill with transparent black.
1626 if (fDiffRadius == 0) {
1627 sk_bzero(dstC, count * sizeof(*dstC));
1628 return;
1629 }
1630 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1631 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001632 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001633
1634 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001635 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001636 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001637 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001638 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1639 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001640 SkScalar dx, fx = srcPt.fX;
1641 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001642
reed@google.com61eb0402011-04-15 12:11:12 +00001643 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001644 SkFixed fixedX, fixedY;
1645 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1646 dx = SkFixedToScalar(fixedX);
1647 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001648 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001649 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001650 dx = fDstToIndex.getScaleX();
1651 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001652 }
reed@google.com84e9c082011-04-13 17:44:24 +00001653 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1654 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1655 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1656 SkScalarMul(fDiff.fY, dy)) * 2;
reed@google.com61eb0402011-04-15 12:11:12 +00001657 if (proc == clamp_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001658 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001659 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001660 SkFixed index = SkClampMax(t, 0xFFFF);
1661 SkASSERT(index <= 0xFFFF);
1662 *dstC++ = cache[index >> (16 - kCache32Bits)];
1663 fx += dx;
1664 fy += dy;
1665 b += db;
1666 }
reed@google.com61eb0402011-04-15 12:11:12 +00001667 } else if (proc == mirror_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001668 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001669 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001670 SkFixed index = mirror_tileproc(t);
1671 SkASSERT(index <= 0xFFFF);
1672 *dstC++ = cache[index >> (16 - kCache32Bits)];
1673 fx += dx;
1674 fy += dy;
1675 b += db;
1676 }
reed@google.com61eb0402011-04-15 12:11:12 +00001677 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001678 SkASSERT(proc == repeat_tileproc);
1679 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001680 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001681 SkFixed index = repeat_tileproc(t);
1682 SkASSERT(index <= 0xFFFF);
1683 *dstC++ = cache[index >> (16 - kCache32Bits)];
1684 fx += dx;
1685 fy += dy;
1686 b += db;
1687 }
1688 }
reed@google.com61eb0402011-04-15 12:11:12 +00001689 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001690 SkScalar dstX = SkIntToScalar(x);
1691 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001692 for (; count > 0; --count) {
1693 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001694 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001695 SkScalar fx = srcPt.fX;
1696 SkScalar fy = srcPt.fY;
1697 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1698 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1699 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001700 SkFixed index = proc(t);
1701 SkASSERT(index <= 0xFFFF);
1702 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001703 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001704 }
1705 }
1706 }
1707
reed@android.com6c59a172009-09-22 20:24:05 +00001708 virtual bool setContext(const SkBitmap& device,
1709 const SkPaint& paint,
reed@google.com8e6d9142011-12-07 15:30:34 +00001710 const SkMatrix& matrix) SK_OVERRIDE {
1711 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com6c59a172009-09-22 20:24:05 +00001712 return false;
1713 }
1714
1715 // we don't have a span16 proc
1716 fFlags &= ~kHasSpan16_Flag;
1717 return true;
1718 }
1719
reed@google.com8e6d9142011-12-07 15:30:34 +00001720 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001721 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1722 }
1723
reed@google.com7716afb2011-12-07 15:17:50 +00001724 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
reed@android.combcfc7332009-11-10 16:19:39 +00001725 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001726 buffer.writeScalar(fCenter1.fX);
1727 buffer.writeScalar(fCenter1.fY);
1728 buffer.writeScalar(fCenter2.fX);
1729 buffer.writeScalar(fCenter2.fY);
1730 buffer.writeScalar(fRadius1);
1731 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001732 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001733
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001734protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001735 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001736 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001737 fCenter1(unflatten_point(buffer)),
1738 fCenter2(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001739 fRadius1(buffer.readScalar()),
1740 fRadius2(buffer.readScalar()) {
1741 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001742 };
reed@google.com7716afb2011-12-07 15:17:50 +00001743 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001744
1745private:
1746 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001747 const SkPoint fCenter1;
1748 const SkPoint fCenter2;
1749 const SkScalar fRadius1;
1750 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001751 SkPoint fDiff;
1752 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001753
1754 void init() {
1755 fDiff = fCenter1 - fCenter2;
1756 fDiffRadius = fRadius2 - fRadius1;
1757 SkScalar inv = SkScalarInvert(fDiffRadius);
1758 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1759 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1760 fStartRadius = SkScalarMul(fRadius1, inv);
1761 fSr2D2 = SkScalarSquare(fStartRadius);
1762 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00001763 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001764
1765 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1766 fPtsToUnit.postScale(inv, inv);
1767 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001768};
1769
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770///////////////////////////////////////////////////////////////////////////////
1771
1772class Sweep_Gradient : public Gradient_Shader {
1773public:
1774 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1775 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001776 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
1777 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778 {
1779 fPtsToUnit.setTranslate(-cx, -cy);
1780 }
reed@google.com7716afb2011-12-07 15:17:50 +00001781 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
1782 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001783
1784 virtual BitmapType asABitmap(SkBitmap* bitmap,
1785 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001786 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00001787 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001788 if (bitmap) {
1789 this->commonAsABitmap(bitmap);
1790 }
1791 if (matrix) {
1792 *matrix = fPtsToUnit;
1793 }
1794 if (xy) {
1795 xy[0] = fTileMode;
1796 xy[1] = kClamp_TileMode;
1797 }
1798 return kSweep_BitmapType;
1799 }
1800
reed@google.com7716afb2011-12-07 15:17:50 +00001801 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001802 if (info) {
1803 commonAsAGradient(info);
1804 info->fPoint[0] = fCenter;
1805 }
1806 return kSweep_GradientType;
1807 }
1808
reed@google.com8e6d9142011-12-07 15:30:34 +00001809 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1811 }
1812
reed@google.com7716afb2011-12-07 15:17:50 +00001813 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001814 this->INHERITED::flatten(buffer);
1815 buffer.writeScalar(fCenter.fX);
1816 buffer.writeScalar(fCenter.fY);
1817 }
1818
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001820 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
1821 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001822 fCenter(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001823 }
1824
reed@google.com7716afb2011-12-07 15:17:50 +00001825 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826
1827private:
1828 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001829 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830};
1831
1832#ifdef COMPUTE_SWEEP_TABLE
1833#define PI 3.14159265
1834static bool gSweepTableReady;
1835static uint8_t gSweepTable[65];
1836
1837/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1838 We scale the results to [0..32]
1839*/
reed@google.com61eb0402011-04-15 12:11:12 +00001840static const uint8_t* build_sweep_table() {
1841 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842 const int N = 65;
1843 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001844
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 for (int i = 0; i < N; i++)
1846 {
1847 double arg = i / DENOM;
1848 double v = atan(arg);
1849 int iv = (int)round(v * DENOM * 2 / PI);
1850// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1851 printf("%d, ", iv);
1852 gSweepTable[i] = iv;
1853 }
1854 gSweepTableReady = true;
1855 }
1856 return gSweepTable;
1857}
1858#else
1859static const uint8_t gSweepTable[] = {
1860 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1861 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1862 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1863 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1864 32
1865};
1866static const uint8_t* build_sweep_table() { return gSweepTable; }
1867#endif
1868
1869// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1870// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1871// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1872
1873//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00001874static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875 SkASSERT(numer <= denom);
1876 SkASSERT(numer > 0);
1877 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001878
reed@android.com8a1c16f2008-12-17 15:59:43 +00001879 int nbits = SkCLZ(numer);
1880 int dbits = SkCLZ(denom);
1881 int bits = 6 - nbits + dbits;
1882 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001883
reed@google.com61eb0402011-04-15 12:11:12 +00001884 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001886 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887
1888 denom <<= dbits - 1;
1889 numer <<= nbits - 1;
1890
1891 unsigned result = 0;
1892
1893 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00001894 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00001896 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001897 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00001898 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001899
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00001901 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001902 // make room for the rest of the answer bits
1903 result <<= bits;
1904 switch (bits) {
1905 case 6:
1906 if ((numer = (numer << 1) - denom) >= 0)
1907 result |= 32;
1908 else
1909 numer += denom;
1910 case 5:
1911 if ((numer = (numer << 1) - denom) >= 0)
1912 result |= 16;
1913 else
1914 numer += denom;
1915 case 4:
1916 if ((numer = (numer << 1) - denom) >= 0)
1917 result |= 8;
1918 else
1919 numer += denom;
1920 case 3:
1921 if ((numer = (numer << 1) - denom) >= 0)
1922 result |= 4;
1923 else
1924 numer += denom;
1925 case 2:
1926 if ((numer = (numer << 1) - denom) >= 0)
1927 result |= 2;
1928 else
1929 numer += denom;
1930 case 1:
1931 default: // not strictly need, but makes GCC make better ARM code
1932 if ((numer = (numer << 1) - denom) >= 0)
1933 result |= 1;
1934 else
1935 numer += denom;
1936 }
1937 }
1938 return result;
1939}
1940
1941// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00001942static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001943#ifdef SK_DEBUG
1944 {
1945 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00001946 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 gOnce = true;
1948 SkASSERT(div_64(55, 55) == 64);
1949 SkASSERT(div_64(128, 256) == 32);
1950 SkASSERT(div_64(2326528, 4685824) == 31);
1951 SkASSERT(div_64(753664, 5210112) == 9);
1952 SkASSERT(div_64(229376, 4882432) == 3);
1953 SkASSERT(div_64(2, 64) == 2);
1954 SkASSERT(div_64(1, 64) == 1);
1955 // test that we handle underflow correctly
1956 SkASSERT(div_64(12345, 0x54321234) == 0);
1957 }
1958 }
1959#endif
1960
1961 SkASSERT(y > 0 && x > 0);
1962 const uint8_t* table = build_sweep_table();
1963
1964 unsigned result;
1965 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00001966 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967 // first part of the atan(v) = PI/2 - atan(1/v) identity
1968 // since our div_64 and table want v <= 1, where v = y/x
1969 SkTSwap<SkFixed>(x, y);
1970 }
1971
1972 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001973
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974#ifdef SK_DEBUG
1975 {
1976 unsigned result2 = SkDivBits(y, x, 6);
1977 SkASSERT(result2 == result ||
1978 (result == 1 && result2 == 0));
1979 }
1980#endif
1981
1982 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1983 result = table[result];
1984
reed@google.com61eb0402011-04-15 12:11:12 +00001985 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986 // complete the atan(v) = PI/2 - atan(1/v) identity
1987 result = 64 - result;
1988 // pin to 63
1989 result -= result >> 6;
1990 }
1991
1992 SkASSERT(result <= 63);
1993 return result;
1994}
1995
1996// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com51baf5a2011-09-21 13:38:36 +00001997#ifdef SK_SCALAR_IS_FLOAT
1998static unsigned SkATan2_255(float y, float x) {
1999 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
2000 static const float g255Over2PI = 40.584510488433314f;
2001
2002 float result = sk_float_atan2(y, x);
2003 if (result < 0) {
2004 result += 2 * SK_ScalarPI;
2005 }
2006 SkASSERT(result >= 0);
2007 // since our value is always >= 0, we can cast to int, which is faster than
2008 // calling floorf()
2009 int ir = (int)(result * g255Over2PI);
2010 SkASSERT(ir >= 0 && ir <= 255);
2011 return ir;
2012}
2013#else
reed@google.com61eb0402011-04-15 12:11:12 +00002014static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2015 if (x == 0) {
2016 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002017 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002018 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019 return y < 0 ? 192 : 64;
2020 }
reed@google.com61eb0402011-04-15 12:11:12 +00002021 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002023 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002024
reed@android.com8a1c16f2008-12-17 15:59:43 +00002025 /* Find the right quadrant for x,y
2026 Since atan_0_90 only handles the first quadrant, we rotate x,y
2027 appropriately before calling it, and then add the right amount
2028 to account for the real quadrant.
2029 quadrant 0 : add 0 | x > 0 && y > 0
2030 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2031 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2032 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002033
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034 map x<0 to (1 << 6)
2035 map y<0 to (3 << 6)
2036 add = map_x ^ map_y
2037 */
2038 int xsign = x >> 31;
2039 int ysign = y >> 31;
2040 int add = ((-xsign) ^ (ysign & 3)) << 6;
2041
2042#ifdef SK_DEBUG
2043 if (0 == add)
2044 SkASSERT(x > 0 && y > 0);
2045 else if (64 == add)
2046 SkASSERT(x < 0 && y > 0);
2047 else if (128 == add)
2048 SkASSERT(x < 0 && y < 0);
2049 else if (192 == add)
2050 SkASSERT(x > 0 && y < 0);
2051 else
2052 SkASSERT(!"bad value for add");
2053#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002054
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2056 where we need to rotate x,y by 90 or -90
2057 */
2058 x = (x ^ xsign) - xsign;
2059 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002060 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002062 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063
2064 unsigned result = add + atan_0_90(y, x);
2065 SkASSERT(result < 256);
2066 return result;
2067}
reed@google.com51baf5a2011-09-21 13:38:36 +00002068#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069
reed@google.comdd5bd672011-09-20 15:56:13 +00002070void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071 SkMatrix::MapXYProc proc = fDstToIndexProc;
2072 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002073 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002074 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002075
reed@google.com61eb0402011-04-15 12:11:12 +00002076 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2078 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002079 SkScalar dx, fx = srcPt.fX;
2080 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002081
reed@google.com61eb0402011-04-15 12:11:12 +00002082 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083 SkFixed storage[2];
2084 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2085 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002086 dx = SkFixedToScalar(storage[0]);
2087 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002088 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002090 dx = matrix.getScaleX();
2091 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002093
reed@google.com61eb0402011-04-15 12:11:12 +00002094 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 *dstC++ = cache[SkATan2_255(fy, fx)];
2096 fx += dx;
2097 fy += dy;
2098 }
reed@google.com61eb0402011-04-15 12:11:12 +00002099 } else { // perspective case
2100 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com51baf5a2011-09-21 13:38:36 +00002102 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2103 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104 }
2105 }
2106}
2107
reed@google.comdd5bd672011-09-20 15:56:13 +00002108void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109 SkMatrix::MapXYProc proc = fDstToIndexProc;
2110 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002111 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 int toggle = ((x ^ y) & 1) << kCache16Bits;
2113 SkPoint srcPt;
2114
reed@google.com61eb0402011-04-15 12:11:12 +00002115 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2117 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002118 SkScalar dx, fx = srcPt.fX;
2119 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002120
reed@google.com61eb0402011-04-15 12:11:12 +00002121 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 SkFixed storage[2];
2123 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2124 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002125 dx = SkFixedToScalar(storage[0]);
2126 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002127 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002129 dx = matrix.getScaleX();
2130 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002132
reed@google.com61eb0402011-04-15 12:11:12 +00002133 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2135 *dstC++ = cache[toggle + index];
2136 toggle ^= (1 << kCache16Bits);
2137 fx += dx;
2138 fy += dy;
2139 }
reed@google.com61eb0402011-04-15 12:11:12 +00002140 } else { // perspective case
2141 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2143 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002144
reed@google.com51baf5a2011-09-21 13:38:36 +00002145 int index = SkATan2_255(srcPt.fY, srcPt.fX);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146 index >>= (8 - kCache16Bits);
2147 *dstC++ = cache[toggle + index];
2148 toggle ^= (1 << kCache16Bits);
2149 }
2150 }
2151}
2152
reed@google.com61eb0402011-04-15 12:11:12 +00002153///////////////////////////////////////////////////////////////////////////////
2154///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002155
2156// assumes colors is SkColor* and pos is SkScalar*
2157#define EXPAND_1_COLOR(count) \
2158 SkColor tmp[2]; \
2159 do { \
2160 if (1 == count) { \
2161 tmp[0] = tmp[1] = colors[0]; \
2162 colors = tmp; \
2163 pos = NULL; \
2164 count = 2; \
2165 } \
2166 } while (0)
2167
reed@google.com61eb0402011-04-15 12:11:12 +00002168SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2169 const SkColor colors[],
2170 const SkScalar pos[], int colorCount,
2171 SkShader::TileMode mode,
2172 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 if (NULL == pts || NULL == colors || colorCount < 1) {
2174 return NULL;
2175 }
2176 EXPAND_1_COLOR(colorCount);
2177
reed@android.comab840b82009-07-01 17:00:03 +00002178 return SkNEW_ARGS(Linear_Gradient,
2179 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180}
2181
reed@google.com61eb0402011-04-15 12:11:12 +00002182SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2183 const SkColor colors[],
2184 const SkScalar pos[], int colorCount,
2185 SkShader::TileMode mode,
2186 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187 if (radius <= 0 || NULL == colors || colorCount < 1) {
2188 return NULL;
2189 }
2190 EXPAND_1_COLOR(colorCount);
2191
reed@android.comab840b82009-07-01 17:00:03 +00002192 return SkNEW_ARGS(Radial_Gradient,
2193 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194}
2195
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002196SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2197 SkScalar startRadius,
2198 const SkPoint& end,
2199 SkScalar endRadius,
2200 const SkColor colors[],
2201 const SkScalar pos[],
2202 int colorCount,
2203 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002204 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002205 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2206 return NULL;
2207 }
2208 EXPAND_1_COLOR(colorCount);
2209
2210 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002211 (start, startRadius, end, endRadius, colors, pos,
2212 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002213}
2214
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2216 const SkColor colors[],
2217 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002218 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219 if (NULL == colors || count < 1) {
2220 return NULL;
2221 }
2222 EXPAND_1_COLOR(count);
2223
2224 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2225}
2226
2227static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
2228 Linear_Gradient::CreateProc);
2229
2230static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
2231 Radial_Gradient::CreateProc);
2232
2233static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
2234 Sweep_Gradient::CreateProc);
tomhudson@google.com938d6042011-04-27 13:57:03 +00002235
2236static SkFlattenable::Registrar
2237 gTwoPointRadialGradientReg("Two_Point_Radial_Gradient",
2238 Two_Point_Radial_Gradient::CreateProc);