blob: 459bd53d83e97726effe92dab7aae28309b3dbca [file] [log] [blame]
reed@google.com0e734bd2011-12-08 17:24:44 +00001
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkGradientShader.h"
11#include "SkColorPriv.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000012#include "SkMallocPixelRef.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkUnitMapper.h"
14#include "SkUtils.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000015#include "SkTemplates.h"
16#include "SkBitmapCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
reed@google.com51baf5a2011-09-21 13:38:36 +000018#if defined(SK_SCALAR_IS_FLOAT) && !defined(SK_DONT_USE_FLOAT_SQRT)
19 #define SK_USE_FLOAT_SQRT
20#endif
21
reed@google.com0e734bd2011-12-08 17:24:44 +000022#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
23 #define USE_DITHER_32BIT_GRADIENT
24#endif
25
reed@google.com5eb158d2011-04-15 15:50:34 +000026static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
27 int count) {
28 if (count > 0) {
29 if (v0 == v1) {
30 sk_memset32(dst, v0, count);
31 } else {
32 int pairs = count >> 1;
33 for (int i = 0; i < pairs; i++) {
34 *dst++ = v0;
35 *dst++ = v1;
36 }
37 if (count & 1) {
38 *dst = v0;
39 }
40 }
41 }
42}
43
reed@google.com61eb0402011-04-15 12:11:12 +000044///////////////////////////////////////////////////////////////////////////////
tomhudson@google.com9ce767c2011-04-25 20:49:39 +000045// Can't use a two-argument function with side effects like this in a
46// constructor's initializer's argument list because the order of
47// evaluations in that context is undefined (and backwards on linux/gcc).
48static SkPoint unflatten_point(SkReader32& buffer) {
49 SkPoint retval;
50 retval.fX = buffer.readScalar();
51 retval.fY = buffer.readScalar();
52 return retval;
53}
54
55///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000056
57typedef SkFixed (*TileProc)(SkFixed);
58
reed@android.com41bccf52009-04-03 13:33:51 +000059static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 return SkClampMax(x, 0xFFFF);
61}
62
reed@android.com41bccf52009-04-03 13:33:51 +000063static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 return x & 0xFFFF;
65}
66
reed@android.com41bccf52009-04-03 13:33:51 +000067static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 int s = x << 15 >> 31;
69 return (x ^ s) & 0xFFFF;
70}
71
72static const TileProc gTileProcs[] = {
73 clamp_tileproc,
74 repeat_tileproc,
75 mirror_tileproc
76};
77
reed@google.com61eb0402011-04-15 12:11:12 +000078///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000079
reed@android.com200645d2009-12-14 16:41:57 +000080static inline int repeat_bits(int x, const int bits) {
81 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000082}
83
reed@android.com200645d2009-12-14 16:41:57 +000084static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000085#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000086 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000088 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000089#else
reed@android.com200645d2009-12-14 16:41:57 +000090 int s = x << (31 - bits) >> 31;
91 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000092#endif
93}
94
reed@android.com41bccf52009-04-03 13:33:51 +000095static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 return x & 0xFF;
97}
98
reed@android.com41bccf52009-04-03 13:33:51 +000099static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +0000101 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +0000103 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 return x & 255;
105#else
106 int s = x << 23 >> 31;
107 return (x ^ s) & 0xFF;
108#endif
109}
110
reed@google.com61eb0402011-04-15 12:11:12 +0000111///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112
113class Gradient_Shader : public SkShader {
114public:
115 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000116 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 virtual ~Gradient_Shader();
118
119 // overrides
reed@google.com7716afb2011-12-07 15:17:50 +0000120 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
121 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000122 virtual bool isOpaque() const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
124protected:
125 Gradient_Shader(SkFlattenableReadBuffer& );
126 SkUnitMapper* fMapper;
127 SkMatrix fPtsToUnit; // set by subclass
128 SkMatrix fDstToIndex;
129 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130 TileMode fTileMode;
131 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000132 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 uint8_t fDstToIndexClass;
134 uint8_t fFlags;
135
136 struct Rec {
137 SkFixed fPos; // 0...1
138 uint32_t fScale; // (1 << 24) / range
139 };
140 Rec* fRecs;
141
142 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000143 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000145 kCache16Mask = kCache16Count - 1,
146 kCache16Shift = 16 - kCache16Bits,
147
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 kCache32Bits = 8, // pretty much should always be 8
149 kCache32Count = 1 << kCache32Bits
150 };
151 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000152 const uint16_t* getCache16() const;
153 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154
reed@google.com7c2f27d2011-03-07 19:29:00 +0000155 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000156 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158private:
159 enum {
160 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
161
reed@android.com1c12abe2009-07-02 15:01:02 +0000162 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 };
164 SkColor fStorage[(kStorageSize + 3) >> 2];
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000165 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
166 bool fColorsAreOpaque;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
reed@google.com7c2f27d2011-03-07 19:29:00 +0000168 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
169 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170
reed@google.com7c2f27d2011-03-07 19:29:00 +0000171 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
172 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000173 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 +0000174
reed@android.com512a8762009-12-14 15:25:36 +0000175 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000176 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
177 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000178 void setCacheAlpha(U8CPU alpha) const;
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000179 void initCommon();
reed@android.com512a8762009-12-14 15:25:36 +0000180
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 typedef SkShader INHERITED;
182};
183
reed@android.com41bccf52009-04-03 13:33:51 +0000184static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 SkASSERT(x >= 0 && x <= SK_Scalar1);
186
187#ifdef SK_SCALAR_IS_FLOAT
188 return (unsigned)(x * 0xFFFF);
189#else
190 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
191#endif
192}
193
reed@android.com41bccf52009-04-03 13:33:51 +0000194Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
195 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 SkASSERT(colorCount > 1);
197
198 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
199
200 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000201 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
204 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
205 fTileMode = mode;
206 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000207
reed@android.com41bccf52009-04-03 13:33:51 +0000208 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000209 fCache32 = NULL;
210 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211
reed@android.com41bccf52009-04-03 13:33:51 +0000212 /* Note: we let the caller skip the first and/or last position.
213 i.e. pos[0] = 0.3, pos[1] = 0.7
214 In these cases, we insert dummy entries to ensure that the final data
215 will be bracketed by [0, 1].
216 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
217
218 Thus colorCount (the caller's value, and fColorCount (our value) may
219 differ by up to 2. In the above example:
220 colorCount = 2
221 fColorCount = 4
222 */
223 fColorCount = colorCount;
224 // check if we need to add in dummy start and/or end position/colors
225 bool dummyFirst = false;
226 bool dummyLast = false;
227 if (pos) {
228 dummyFirst = pos[0] != 0;
229 dummyLast = pos[colorCount - 1] != SK_Scalar1;
230 fColorCount += dummyFirst + dummyLast;
231 }
232
233 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000234 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000235 fOrigColors = reinterpret_cast<SkColor*>(
236 sk_malloc_throw(size * fColorCount));
237 }
238 else {
239 fOrigColors = fStorage;
240 }
241
242 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 {
reed@android.com41bccf52009-04-03 13:33:51 +0000244 SkColor* origColors = fOrigColors;
245 if (dummyFirst) {
246 *origColors++ = colors[0];
247 }
248 memcpy(origColors, colors, colorCount * sizeof(SkColor));
249 if (dummyLast) {
250 origColors += colorCount;
251 *origColors = colors[colorCount - 1];
252 }
253 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254
reed@android.com1c12abe2009-07-02 15:01:02 +0000255 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000256 if (fColorCount > 2) {
257 Rec* recs = fRecs;
258 recs->fPos = 0;
259 // recs->fScale = 0; // unused;
260 recs += 1;
261 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 /* We need to convert the user's array of relative positions into
263 fixed-point positions and scale factors. We need these results
264 to be strictly monotonic (no two values equal or out of order).
265 Hence this complex loop that just jams a zero for the scale
266 value if it sees a segment out of order, and it assures that
267 we start at 0 and end at 1.0
268 */
269 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000270 int startIndex = dummyFirst ? 0 : 1;
271 int count = colorCount + dummyLast;
272 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 // force the last value to be 1.0
274 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000275 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000277 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 }
reed@android.com41bccf52009-04-03 13:33:51 +0000280 // pin curr withing range
281 if (curr < 0) {
282 curr = 0;
283 } else if (curr > SK_Fixed1) {
284 curr = SK_Fixed1;
285 }
286 recs->fPos = curr;
287 if (curr > prev) {
288 recs->fScale = (1 << 24) / (curr - prev);
289 } else {
290 recs->fScale = 0; // ignore this segment
291 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 // get ready for the next value
293 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000294 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 }
reed@android.com41bccf52009-04-03 13:33:51 +0000296 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 SkFixed dp = SK_Fixed1 / (colorCount - 1);
298 SkFixed p = dp;
299 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000300 for (int i = 1; i < colorCount; i++) {
301 recs->fPos = p;
302 recs->fScale = scale;
303 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 p += dp;
305 }
306 }
307 }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000308 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309}
310
311Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000312 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 fCacheAlpha = 256;
314
315 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
316
317 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000318 fCache32 = NULL;
319 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320
reed@android.com41bccf52009-04-03 13:33:51 +0000321 int colorCount = fColorCount = buffer.readU32();
322 if (colorCount > kColorStorageCount) {
323 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
324 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
325 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000327 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329
330 fTileMode = (TileMode)buffer.readU8();
331 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000332 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 if (colorCount > 2) {
334 Rec* recs = fRecs;
335 recs[0].fPos = 0;
336 for (int i = 1; i < colorCount; i++) {
337 recs[i].fPos = buffer.readS32();
338 recs[i].fScale = buffer.readU32();
339 }
340 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000341 SkReadMatrix(&buffer, &fPtsToUnit);
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000342 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343}
344
reed@android.com41bccf52009-04-03 13:33:51 +0000345Gradient_Shader::~Gradient_Shader() {
346 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000348 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000349 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000350 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000352 }
reed@google.com82065d62011-02-07 15:30:46 +0000353 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354}
355
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000356void Gradient_Shader::initCommon() {
357 fFlags = 0;
358 unsigned colorAlpha = 0xFF;
359 for (int i = 0; i < fColorCount; i++) {
360 colorAlpha &= SkColorGetA(fOrigColors[i]);
361 }
362 fColorsAreOpaque = colorAlpha == 0xFF;
363}
364
reed@android.com41bccf52009-04-03 13:33:51 +0000365void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 this->INHERITED::flatten(buffer);
367 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000368 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
370 buffer.write8(fTileMode);
371 if (fColorCount > 2) {
372 Rec* recs = fRecs;
373 for (int i = 1; i < fColorCount; i++) {
374 buffer.write32(recs[i].fPos);
375 buffer.write32(recs[i].fScale);
376 }
377 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000378 SkWriteMatrix(&buffer, fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379}
380
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000381bool Gradient_Shader::isOpaque() const {
382 return fColorsAreOpaque;
383}
384
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385bool Gradient_Shader::setContext(const SkBitmap& device,
386 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000387 const SkMatrix& matrix) {
388 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000390 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391
392 const SkMatrix& inverse = this->getTotalInverse();
393
394 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
395 return false;
396 }
397
398 fDstToIndexProc = fDstToIndex.getMapXYProc();
399 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
400
401 // now convert our colors in to PMColors
402 unsigned paintAlpha = this->getPaintAlpha();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403
404 fFlags = this->INHERITED::getFlags();
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000405 if (fColorsAreOpaque && paintAlpha == 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 fFlags |= kOpaqueAlpha_Flag;
407 }
408 // we can do span16 as long as our individual colors are opaque,
409 // regardless of the paint's alpha
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000410 if (fColorsAreOpaque) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 fFlags |= kHasSpan16_Flag;
412 }
413
reed@google.com95eed982011-07-05 17:01:56 +0000414 this->setCacheAlpha(paintAlpha);
415 return true;
416}
417
418void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 // if the new alpha differs from the previous time we were called, inval our cache
420 // this will trigger the cache to be rebuilt.
421 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000422 if (fCacheAlpha != alpha) {
423 fCache16 = NULL; // inval the cache
424 fCache32 = NULL; // inval the cache
425 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000426 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000427 if (fCache32PixelRef) {
428 fCache32PixelRef->notifyPixelsChanged();
429 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431}
432
reed@android.com41bccf52009-04-03 13:33:51 +0000433static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 SkASSERT(a == SkToU8(a));
435 SkASSERT(b == SkToU8(b));
436 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 return a + ((b - a) * scale >> 8);
438}
439
reed@android.com41bccf52009-04-03 13:33:51 +0000440static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
441 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442#if 0
443 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
444 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
445 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
446 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
447
448 return SkPackARGB32(a, r, g, b);
449#else
450 int otherBlend = 256 - blend;
451
452#if 0
453 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
454 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
455 SkASSERT((t0 & t1) == 0);
456 return t0 | t1;
457#else
458 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
459 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
460#endif
461
462#endif
463}
464
465#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
466
reed@android.com41bccf52009-04-03 13:33:51 +0000467/** We take the original colors, not our premultiplied PMColors, since we can
468 build a 16bit table as long as the original colors are opaque, even if the
469 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470*/
reed@android.com512a8762009-12-14 15:25:36 +0000471void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
472 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 SkASSERT(count > 1);
474 SkASSERT(SkColorGetA(c0) == 0xFF);
475 SkASSERT(SkColorGetA(c1) == 0xFF);
476
477 SkFixed r = SkColorGetR(c0);
478 SkFixed g = SkColorGetG(c0);
479 SkFixed b = SkColorGetB(c0);
480
481 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
482 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
483 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
484
485 r = SkIntToFixed(r) + 0x8000;
486 g = SkIntToFixed(g) + 0x8000;
487 b = SkIntToFixed(b) + 0x8000;
488
489 do {
490 unsigned rr = r >> 16;
491 unsigned gg = g >> 16;
492 unsigned bb = b >> 16;
493 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000494 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 cache += 1;
496 r += dr;
497 g += dg;
498 b += db;
499 } while (--count != 0);
500}
501
reed@google.com55b8e8c2011-01-13 16:22:35 +0000502/*
503 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
504 * semantics of how we 2x2 dither 32->16
505 */
506static inline U8CPU dither_fixed_to_8(SkFixed n) {
507 n >>= 8;
508 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
509}
510
511/*
512 * For dithering with premultiply, we want to ceiling the alpha component,
513 * to ensure that it is always >= any color component.
514 */
515static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
516 n >>= 8;
517 return ((n << 1) - (n | (n >> 8))) >> 8;
518}
519
520void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
521 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 SkASSERT(count > 1);
523
reed@android.com1c12abe2009-07-02 15:01:02 +0000524 // need to apply paintAlpha to our two endpoints
525 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
526 SkFixed da;
527 {
528 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
529 da = SkIntToFixed(tmp - a) / (count - 1);
530 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531
reed@android.com1c12abe2009-07-02 15:01:02 +0000532 SkFixed r = SkColorGetR(c0);
533 SkFixed g = SkColorGetG(c0);
534 SkFixed b = SkColorGetB(c0);
535 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
536 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
537 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538
539 a = SkIntToFixed(a) + 0x8000;
540 r = SkIntToFixed(r) + 0x8000;
541 g = SkIntToFixed(g) + 0x8000;
542 b = SkIntToFixed(b) + 0x8000;
543
544 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000545 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
546 cache[kCache32Count] = SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
547 dither_fixed_to_8(r),
548 dither_fixed_to_8(g),
549 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000550 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 a += da;
552 r += dr;
553 g += dg;
554 b += db;
555 } while (--count != 0);
556}
557
reed@android.com41bccf52009-04-03 13:33:51 +0000558static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 SkASSERT((unsigned)x <= SK_Fixed1);
560 return x - (x >> 16);
561}
562
reed@android.com200645d2009-12-14 16:41:57 +0000563static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000564 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000565 if (6 == bits) {
566 return (x << 10) | (x << 4) | (x >> 2);
567 }
568 if (8 == bits) {
569 return (x << 8) | x;
570 }
571 sk_throw();
572 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573}
574
reed@google.com7c2f27d2011-03-07 19:29:00 +0000575const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000576 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000577 // double the count for dither entries
578 const int entryCount = kCache16Count * 2;
579 const size_t allocSize = sizeof(uint16_t) * entryCount;
580
reed@android.com3c9b2a42009-08-27 19:28:37 +0000581 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000582 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000585 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000586 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000587 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 Rec* rec = fRecs;
589 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000590 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000591 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 SkASSERT(nextIndex < kCache16Count);
593
594 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000595 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 prevIndex = nextIndex;
597 }
598 SkASSERT(prevIndex == kCache16Count - 1);
599 }
600
reed@android.com41bccf52009-04-03 13:33:51 +0000601 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000602 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 uint16_t* linear = fCache16; // just computed linear data
604 uint16_t* mapped = fCache16Storage; // storage for mapped data
605 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000606 for (int i = 0; i < kCache16Count; i++) {
607 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000609 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 }
611 sk_free(fCache16);
612 fCache16 = fCache16Storage;
613 }
614 }
615 return fCache16;
616}
617
reed@google.com7c2f27d2011-03-07 19:29:00 +0000618const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000619 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000620 // double the count for dither entries
621 const int entryCount = kCache32Count * 2;
622 const size_t allocSize = sizeof(SkPMColor) * entryCount;
623
reed@google.comdc731fd2010-12-23 15:19:47 +0000624 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000625 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
626 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000627 }
628 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000629 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000630 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
631 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000632 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 Rec* rec = fRecs;
634 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000635 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
637 SkASSERT(nextIndex < kCache32Count);
638
639 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000640 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
641 fOrigColors[i],
642 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 prevIndex = nextIndex;
644 }
645 SkASSERT(prevIndex == kCache32Count - 1);
646 }
647
reed@android.com41bccf52009-04-03 13:33:51 +0000648 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000649 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000650 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000652 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000654 for (int i = 0; i < kCache32Count; i++) {
655 int index = map->mapUnit16((i << 8) | i) >> 8;
656 mapped[i] = linear[index];
657 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000658 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000659 fCache32PixelRef->unref();
660 fCache32PixelRef = newPR;
661 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 }
663 }
664 return fCache32;
665}
666
reed@google.comdc731fd2010-12-23 15:19:47 +0000667/*
668 * Because our caller might rebuild the same (logically the same) gradient
669 * over and over, we'd like to return exactly the same "bitmap" if possible,
670 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
671 * To do that, we maintain a private cache of built-bitmaps, based on our
672 * colors and positions. Note: we don't try to flatten the fMapper, so if one
673 * is present, we skip the cache for now.
674 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000675void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000676 // our caller assumes no external alpha, so we ensure that our cache is
677 // built with 0xFF
678 this->setCacheAlpha(0xFF);
679
reed@google.comdc731fd2010-12-23 15:19:47 +0000680 // don't have a way to put the mapper into our cache-key yet
681 if (fMapper) {
682 // force our cahce32pixelref to be built
683 (void)this->getCache32();
684 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
685 bitmap->setPixelRef(fCache32PixelRef);
686 return;
687 }
688
689 // build our key: [numColors + colors[] + {positions[]} ]
690 int count = 1 + fColorCount;
691 if (fColorCount > 2) {
692 count += fColorCount - 1; // fRecs[].fPos
693 }
694
695 SkAutoSTMalloc<16, int32_t> storage(count);
696 int32_t* buffer = storage.get();
697
698 *buffer++ = fColorCount;
699 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
700 buffer += fColorCount;
701 if (fColorCount > 2) {
702 for (int i = 1; i < fColorCount; i++) {
703 *buffer++ = fRecs[i].fPos;
704 }
705 }
706 SkASSERT(buffer - storage.get() == count);
707
708 ///////////////////////////////////
709
710 static SkMutex gMutex;
711 static SkBitmapCache* gCache;
712 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
713 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
714 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000715
reed@google.comdc731fd2010-12-23 15:19:47 +0000716 if (NULL == gCache) {
717 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
718 }
719 size_t size = count * sizeof(int32_t);
720
721 if (!gCache->find(storage.get(), size, bitmap)) {
722 // force our cahce32pixelref to be built
723 (void)this->getCache32();
724 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
725 bitmap->setPixelRef(fCache32PixelRef);
726
727 gCache->add(storage.get(), size, *bitmap);
728 }
729}
730
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000731void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
732 if (info) {
733 if (info->fColorCount >= fColorCount) {
734 if (info->fColors) {
735 memcpy(info->fColors, fOrigColors,
736 fColorCount * sizeof(SkColor));
737 }
738 if (info->fColorOffsets) {
739 if (fColorCount == 2) {
740 info->fColorOffsets[0] = 0;
741 info->fColorOffsets[1] = SK_Scalar1;
742 } else if (fColorCount > 2) {
743 for (int i = 0; i < fColorCount; i++)
744 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
745 }
746 }
747 }
748 info->fColorCount = fColorCount;
749 info->fTileMode = fTileMode;
750 }
751}
752
reed@google.com61eb0402011-04-15 12:11:12 +0000753///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754
reed@android.com41bccf52009-04-03 13:33:51 +0000755static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 SkVector vec = pts[1] - pts[0];
757 SkScalar mag = vec.length();
758 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
759
760 vec.scale(inv);
761 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
762 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
763 matrix->postScale(inv, inv);
764}
765
766///////////////////////////////////////////////////////////////////////////////
767
768class Linear_Gradient : public Gradient_Shader {
769public:
770 Linear_Gradient(const SkPoint pts[2],
771 const SkColor colors[], const SkScalar pos[], int colorCount,
772 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000773 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
774 fStart(pts[0]),
775 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 {
777 pts_to_unit_matrix(pts, &fPtsToUnit);
778 }
reed@android.com9b46e772009-06-05 12:24:41 +0000779
reed@google.com7716afb2011-12-07 15:17:50 +0000780 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
781 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
782 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
783 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
784 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
785 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786
reed@google.com55b8e8c2011-01-13 16:22:35 +0000787 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 return SkNEW_ARGS(Linear_Gradient, (buffer));
789 }
790
reed@google.com7716afb2011-12-07 15:17:50 +0000791 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000792 this->INHERITED::flatten(buffer);
793 buffer.writeScalar(fStart.fX);
794 buffer.writeScalar(fStart.fY);
795 buffer.writeScalar(fEnd.fX);
796 buffer.writeScalar(fEnd.fY);
797 }
798
caryclark@google.comd26147a2011-12-15 14:16:43 +0000799 SK_DECLARE_FLATTENABLE_REGISTRAR()
800
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000802 Linear_Gradient(SkFlattenableReadBuffer& buffer)
803 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +0000804 fStart(unflatten_point(buffer)),
805 fEnd(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000806 }
reed@google.com7716afb2011-12-07 15:17:50 +0000807 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808
809private:
810 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000811 const SkPoint fStart;
812 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813};
814
reed@android.com5119bdb2009-06-12 21:27:03 +0000815bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
816 const SkMatrix& matrix) {
817 if (!this->INHERITED::setContext(device, paint, matrix)) {
818 return false;
819 }
820
821 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
822 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000823 fFlags |= SkShader::kConstInY32_Flag;
824 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
825 // only claim this if we do have a 16bit mode (i.e. none of our
826 // colors have alpha), and if we are not dithering (which obviously
827 // is not const in Y).
828 fFlags |= SkShader::kConstInY16_Flag;
829 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000830 }
831 return true;
832}
833
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@google.com61eb0402011-04-15 12:11:12 +0000835static inline bool no_need_for_clamp(int fx, int dx, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 SkASSERT(count > 0);
837 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
838}
839
reed@google.com5eb158d2011-04-15 15:50:34 +0000840#include "SkClampRange.h"
841
842#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000843 do { \
844 unsigned fi = fx >> 8; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000845 SkASSERT(fi <= 0xFF); \
846 fx += dx; \
847 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +0000848 toggle ^= TOGGLE_MASK; \
849 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000850
851
reed@google.comdd5bd672011-09-20 15:56:13 +0000852void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 SkASSERT(count > 0);
854
855 SkPoint srcPt;
856 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
857 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000858 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com0e734bd2011-12-08 17:24:44 +0000859#ifdef USE_DITHER_32BIT_GRADIENT
reed@google.com55b8e8c2011-01-13 16:22:35 +0000860 int toggle = ((x ^ y) & 1) << kCache32Bits;
861 const int TOGGLE_MASK = (1 << kCache32Bits);
reed@google.com0e734bd2011-12-08 17:24:44 +0000862#else
863 int toggle = 0;
864 const int TOGGLE_MASK = 0;
865#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866
reed@android.comc552a432009-06-12 20:02:50 +0000867 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000868 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
869 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
871
reed@android.comc552a432009-06-12 20:02:50 +0000872 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 SkFixed dxStorage[1];
874 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
875 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000876 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
878 dx = SkScalarToFixed(fDstToIndex.getScaleX());
879 }
880
reed@android.comc552a432009-06-12 20:02:50 +0000881 if (SkFixedNearlyZero(dx)) {
882 // we're a vertical gradient, so no change in a span
reed@google.com998343a2012-01-04 17:26:27 +0000883 unsigned fi = proc(fx) >> (16 - kCache32Bits);
884 sk_memset32_dither(dstC, cache[toggle + fi],
885 cache[(toggle ^ TOGGLE_MASK) + fi], count);
reed@android.comc552a432009-06-12 20:02:50 +0000886 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000887 SkClampRange range;
888 range.init(fx, dx, count, 0, 0xFF);
889
890 if ((count = range.fCount0) > 0) {
891 sk_memset32_dither(dstC,
892 cache[toggle + range.fV0],
893 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
894 count);
895 dstC += count;
896 }
897 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +0000898 int unroll = count >> 3;
899 fx = range.fFx1;
900 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000901 NO_CHECK_ITER; NO_CHECK_ITER;
902 NO_CHECK_ITER; NO_CHECK_ITER;
903 NO_CHECK_ITER; NO_CHECK_ITER;
904 NO_CHECK_ITER; NO_CHECK_ITER;
905 }
906 if ((count &= 7) > 0) {
907 do {
908 NO_CHECK_ITER;
909 } while (--count != 0);
910 }
911 }
912 if ((count = range.fCount2) > 0) {
913 sk_memset32_dither(dstC,
914 cache[toggle + range.fV1],
915 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
916 count);
917 }
reed@android.comc552a432009-06-12 20:02:50 +0000918 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 do {
920 unsigned fi = mirror_8bits(fx >> 8);
921 SkASSERT(fi <= 0xFF);
922 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000923 *dstC++ = cache[toggle + fi];
924 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000926 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 SkASSERT(proc == repeat_tileproc);
928 do {
929 unsigned fi = repeat_8bits(fx >> 8);
930 SkASSERT(fi <= 0xFF);
931 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000932 *dstC++ = cache[toggle + fi];
933 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 } while (--count != 0);
935 }
reed@android.comc552a432009-06-12 20:02:50 +0000936 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 SkScalar dstX = SkIntToScalar(x);
938 SkScalar dstY = SkIntToScalar(y);
939 do {
940 dstProc(fDstToIndex, dstX, dstY, &srcPt);
941 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
942 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000943 *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))];
944 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945 dstX += SK_Scalar1;
946 } while (--count != 0);
947 }
948}
949
reed@google.com55b8e8c2011-01-13 16:22:35 +0000950SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000951 SkMatrix* matrix,
952 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +0000953 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000955 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 }
957 if (matrix) {
958 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
959 matrix->preConcat(fPtsToUnit);
960 }
961 if (xy) {
962 xy[0] = fTileMode;
963 xy[1] = kClamp_TileMode;
964 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000965 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966}
967
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000968SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
969 if (info) {
970 commonAsAGradient(info);
971 info->fPoint[0] = fStart;
972 info->fPoint[1] = fEnd;
973 }
974 return kLinear_GradientType;
975}
976
reed@android.com3c9b2a42009-08-27 19:28:37 +0000977static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
978 int count) {
979 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 *dst++ = value;
981 count -= 1;
982 SkTSwap(value, other);
983 }
984
985 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000986
reed@android.com3c9b2a42009-08-27 19:28:37 +0000987 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000989 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991
reed@google.com5eb158d2011-04-15 15:50:34 +0000992#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +0000993 do { \
994 unsigned fi = fx >> kCache16Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000995 SkASSERT(fi <= kCache16Mask); \
996 fx += dx; \
997 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +0000998 toggle ^= TOGGLE_MASK; \
999 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +00001000
1001
reed@google.comdd5bd672011-09-20 15:56:13 +00001002void Linear_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 SkASSERT(count > 0);
1004
1005 SkPoint srcPt;
1006 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1007 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001008 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@google.com5eb158d2011-04-15 15:50:34 +00001010 const int TOGGLE_MASK = (1 << kCache32Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001012 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001013 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1014 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1016
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001017 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 SkFixed dxStorage[1];
1019 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1020 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001021 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1023 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1024 }
1025
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001026 if (SkFixedNearlyZero(dx)) {
1027 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +00001028 unsigned fi = proc(fx) >> kCache16Shift;
1029 SkASSERT(fi <= kCache16Mask);
reed@google.com5eb158d2011-04-15 15:50:34 +00001030 dither_memset16(dstC, cache[toggle + fi],
1031 cache[(toggle ^ TOGGLE_MASK) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001032 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001033 SkClampRange range;
1034 range.init(fx, dx, count, 0, kCache16Mask);
1035
1036 if ((count = range.fCount0) > 0) {
1037 dither_memset16(dstC,
1038 cache[toggle + range.fV0],
1039 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
1040 count);
1041 dstC += count;
1042 }
1043 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +00001044 int unroll = count >> 3;
1045 fx = range.fFx1;
1046 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001047 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1048 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1049 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1050 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1051 }
1052 if ((count &= 7) > 0) {
1053 do {
1054 NO_CHECK_ITER_16;
1055 } while (--count != 0);
1056 }
1057 }
1058 if ((count = range.fCount2) > 0) {
1059 dither_memset16(dstC,
1060 cache[toggle + range.fV1],
1061 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
1062 count);
1063 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001064 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 do {
reed@android.com200645d2009-12-14 16:41:57 +00001066 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001067 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001070 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001072 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 SkASSERT(proc == repeat_tileproc);
1074 do {
reed@android.com200645d2009-12-14 16:41:57 +00001075 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001076 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001079 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 } while (--count != 0);
1081 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001082 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 SkScalar dstX = SkIntToScalar(x);
1084 SkScalar dstY = SkIntToScalar(y);
1085 do {
1086 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1087 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1088 SkASSERT(fi <= 0xFFFF);
1089
reed@android.com512a8762009-12-14 15:25:36 +00001090 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 *dstC++ = cache[toggle + index];
reed@google.com5eb158d2011-04-15 15:50:34 +00001092 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093
1094 dstX += SK_Scalar1;
1095 } while (--count != 0);
1096 }
1097}
1098
1099///////////////////////////////////////////////////////////////////////////////
1100
1101#define kSQRT_TABLE_BITS 11
1102#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1103
1104#include "SkRadialGradient_Table.h"
1105
1106#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1107
1108#include <stdio.h>
1109
reed@google.com61eb0402011-04-15 12:11:12 +00001110void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1112
1113 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1114 SkASSERT(file);
1115 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1116
reed@google.com61eb0402011-04-15 12:11:12 +00001117 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1118 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001120 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121
1122 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1123
1124 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001125 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001127 }
1128 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001130 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 }
1132 ::fprintf(file, "};\n");
1133 ::fclose(file);
1134}
1135
1136#endif
1137
1138
reed@google.com61eb0402011-04-15 12:11:12 +00001139static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1140 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 SkScalar inv = SkScalarInvert(radius);
1142
1143 matrix->setTranslate(-center.fX, -center.fY);
1144 matrix->postScale(inv, inv);
1145}
1146
1147class Radial_Gradient : public Gradient_Shader {
1148public:
1149 Radial_Gradient(const SkPoint& center, SkScalar radius,
1150 const SkColor colors[], const SkScalar pos[], int colorCount,
1151 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001152 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1153 fCenter(center),
1154 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 {
1156 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1157 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1158
1159 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1160 }
reed@google.com61eb0402011-04-15 12:11:12 +00001161
reed@google.com8e6d9142011-12-07 15:30:34 +00001162 virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count) SK_OVERRIDE;
reed@google.com7716afb2011-12-07 15:17:50 +00001163 virtual void shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164 SkASSERT(count > 0);
1165
1166 SkPoint srcPt;
1167 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1168 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001169 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
reed@android.com3c9b2a42009-08-27 19:28:37 +00001172 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001173 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1174 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1176 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1177
reed@android.com3c9b2a42009-08-27 19:28:37 +00001178 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 SkFixed storage[2];
1180 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1181 dx = storage[0];
1182 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001183 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1185 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1186 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1187 }
1188
reed@android.com3c9b2a42009-08-27 19:28:37 +00001189 if (proc == clamp_tileproc) {
reed@google.comdd5bd672011-09-20 15:56:13 +00001190 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191
1192 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1193 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1194 precision, but that appears to be visually OK. If we decide this is OK for
1195 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1196 to avoid having to do these extra shifts each time.
1197 */
1198 fx >>= 1;
1199 dx >>= 1;
1200 fy >>= 1;
1201 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001202 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 +00001203 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1204 fy *= fy;
1205 do {
1206 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1207 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1208 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1209 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1211 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001213 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 do {
1215 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1216 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1217 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1218 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1219 fx += dx;
1220 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1222 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 } while (--count != 0);
1224 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001225 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 do {
1227 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1228 unsigned fi = mirror_tileproc(dist);
1229 SkASSERT(fi <= 0xFFFF);
1230 fx += dx;
1231 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1233 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001235 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 SkASSERT(proc == repeat_tileproc);
1237 do {
1238 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1239 unsigned fi = repeat_tileproc(dist);
1240 SkASSERT(fi <= 0xFFFF);
1241 fx += dx;
1242 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1244 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 } while (--count != 0);
1246 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001247 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 SkScalar dstX = SkIntToScalar(x);
1249 SkScalar dstY = SkIntToScalar(y);
1250 do {
1251 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1252 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1253 SkASSERT(fi <= 0xFFFF);
1254
1255 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 *dstC++ = cache[toggle + index];
1257 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258
1259 dstX += SK_Scalar1;
1260 } while (--count != 0);
1261 }
1262 }
1263
reed@google.com55b8e8c2011-01-13 16:22:35 +00001264 virtual BitmapType asABitmap(SkBitmap* bitmap,
1265 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001266 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00001267 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001268 if (bitmap) {
1269 this->commonAsABitmap(bitmap);
1270 }
1271 if (matrix) {
1272 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1273 matrix->preConcat(fPtsToUnit);
1274 }
1275 if (xy) {
1276 xy[0] = fTileMode;
1277 xy[1] = kClamp_TileMode;
1278 }
1279 return kRadial_BitmapType;
1280 }
reed@google.com7716afb2011-12-07 15:17:50 +00001281 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001282 if (info) {
1283 commonAsAGradient(info);
1284 info->fPoint[0] = fCenter;
1285 info->fRadius[0] = fRadius;
1286 }
1287 return kRadial_GradientType;
1288 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001289
reed@google.com8e6d9142011-12-07 15:30:34 +00001290 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 return SkNEW_ARGS(Radial_Gradient, (buffer));
1292 }
1293
reed@google.com7716afb2011-12-07 15:17:50 +00001294 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001295 this->INHERITED::flatten(buffer);
1296 buffer.writeScalar(fCenter.fX);
1297 buffer.writeScalar(fCenter.fY);
1298 buffer.writeScalar(fRadius);
1299 }
1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001302 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1303 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001304 fCenter(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001305 fRadius(buffer.readScalar()) {
1306 }
reed@google.com7716afb2011-12-07 15:17:50 +00001307 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308
1309private:
1310 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001311 const SkPoint fCenter;
1312 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313};
1314
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001315static inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
1316 // fast, overly-conservative test: checks unit square instead
1317 // of unit circle
1318 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
1319 (fx <= -SK_FixedHalf && dx <= 0);
1320 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
1321 (fy <= -SK_FixedHalf && dy <= 0);
1322
1323 return xClamped || yClamped;
1324}
1325
1326// Return true if (fx * fy) is always inside the unit circle
1327// SkPin32 is expensive, but so are all the SkFixedMul in this test,
1328// so it shouldn't be run if count is small.
1329static inline bool no_need_for_radial_pin(int fx, int dx,
1330 int fy, int dy, int count) {
1331 SkASSERT(count > 0);
1332 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1333 return false;
1334 }
1335 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
1336 return false;
1337 }
1338 fx += (count - 1) * dx;
1339 fy += (count - 1) * dy;
1340 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1341 return false;
1342 }
1343 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
1344}
1345
1346#define UNPINNED_RADIAL_STEP \
1347 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
1348 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)]; \
1349 fx += dx; \
1350 fy += dy;
1351
1352// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
1353static void radial_clamp(SkFixed fx, SkFixed fy, SkFixed dx, SkFixed dy,
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001354 SkPMColor* SK_RESTRICT dstC, int count,
1355 const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001356 const int kCache32Bits, const int kCache32Count) {
1357 // Floating point seems to be slower than fixed point,
1358 // even when we have float hardware.
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001359 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001360 fx >>= 1;
1361 dx >>= 1;
1362 fy >>= 1;
1363 dy >>= 1;
1364 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
1365 sk_memset32(dstC, cache[kCache32Count - 1], count);
1366 } else if ((count > 4) &&
1367 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
1368 unsigned fi;
1369 // 4x unroll appears to be no faster than 2x unroll on Linux
1370 while (count > 1) {
1371 UNPINNED_RADIAL_STEP;
1372 UNPINNED_RADIAL_STEP;
1373 count -= 2;
1374 }
1375 if (count) {
1376 UNPINNED_RADIAL_STEP;
1377 }
1378 }
1379 else {
1380 do {
1381 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1382 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1383 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1384 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1385 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1386 fx += dx;
1387 fy += dy;
1388 } while (--count != 0);
1389 }
1390}
1391
1392void Radial_Gradient::shadeSpan(int x, int y,
1393 SkPMColor* SK_RESTRICT dstC, int count) {
1394 SkASSERT(count > 0);
1395
1396 SkPoint srcPt;
1397 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1398 TileProc proc = fTileProc;
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001399 const SkPMColor* SK_RESTRICT cache = this->getCache32();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001400
1401 if (fDstToIndexClass != kPerspective_MatrixClass) {
1402 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1403 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1404 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1405 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1406#ifdef SK_USE_FLOAT_SQRT
1407 float fdx, fdy;
1408#endif
1409
1410 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1411 SkFixed storage[2];
1412 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1413 dx = storage[0];
1414 dy = storage[1];
1415#ifdef SK_USE_FLOAT_SQRT
1416 fdx = SkFixedToFloat(storage[0]);
1417 fdy = SkFixedToFloat(storage[1]);
1418#endif
1419 } else {
1420 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1421 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1422 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1423#ifdef SK_USE_FLOAT_SQRT
1424 fdx = fDstToIndex.getScaleX();
1425 fdy = fDstToIndex.getSkewY();
1426#endif
1427 }
1428
1429 if (proc == clamp_tileproc) {
1430 radial_clamp(fx, fy, dx, dy, dstC, count, cache,
1431 kCache32Bits, kCache32Count);
1432 } else if (proc == mirror_tileproc) {
1433#ifdef SK_USE_FLOAT_SQRT
1434 float ffx = srcPt.fX;
1435 float ffy = srcPt.fY;
1436 do {
1437 float fdist = sk_float_sqrt(ffx*ffx + ffy*ffy);
1438 unsigned fi = mirror_tileproc(SkFloatToFixed(fdist));
1439 SkASSERT(fi <= 0xFFFF);
1440 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1441 ffx += fdx;
1442 ffy += fdy;
1443 } while (--count != 0);
1444#else
1445 do {
1446 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1447 SkFixedSquare(fy);
1448 if (magnitudeSquared < 0) // Overflow.
1449 magnitudeSquared = SK_FixedMax;
1450 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1451 unsigned fi = mirror_tileproc(dist);
1452 SkASSERT(fi <= 0xFFFF);
1453 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1454 fx += dx;
1455 fy += dy;
1456 } while (--count != 0);
1457#endif
1458 } else {
1459 SkASSERT(proc == repeat_tileproc);
1460 do {
1461 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1462 SkFixedSquare(fy);
1463 if (magnitudeSquared < 0) // Overflow.
1464 magnitudeSquared = SK_FixedMax;
1465 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1466 unsigned fi = repeat_tileproc(dist);
1467 SkASSERT(fi <= 0xFFFF);
1468 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1469 fx += dx;
1470 fy += dy;
1471 } while (--count != 0);
1472 }
1473 } else { // perspective case
1474 SkScalar dstX = SkIntToScalar(x);
1475 SkScalar dstY = SkIntToScalar(y);
1476 do {
1477 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1478 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1479 SkASSERT(fi <= 0xFFFF);
1480 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1481 dstX += SK_Scalar1;
1482 } while (--count != 0);
1483 }
1484}
1485
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001486/* Two-point radial gradients are specified by two circles, each with a center
1487 point and radius. The gradient can be considered to be a series of
1488 concentric circles, with the color interpolated from the start circle
1489 (at t=0) to the end circle (at t=1).
1490
1491 For each point (x, y) in the span, we want to find the
1492 interpolated circle that intersects that point. The center
1493 of the desired circle (Cx, Cy) falls at some distance t
1494 along the line segment between the start point (Sx, Sy) and
1495 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001496
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001497 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1498 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001499
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001500 The radius of the desired circle (r) is also a linear interpolation t
1501 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001502
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001503 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001504
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001505 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001506
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001507 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001508
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001509 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001510
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001511 (x - ((1 - t) * Sx + t * Ex))^2
1512 + (y - ((1 - t) * Sy + t * Ey))^2
1513 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001514
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001515 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001516
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001517 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1518 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1519 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001520
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001521 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1522
1523 [Dx^2 + Dy^2 - Dr^2)] * t^2
1524 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1525 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001526
1527 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001528 possible circles on which the point may fall. Solving for t yields
1529 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001530
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001531 If a<0, the start circle is entirely contained in the
1532 end circle, and one of the roots will be <0 or >1 (off the line
1533 segment). If a>0, the start circle falls at least partially
1534 outside the end circle (or vice versa), and the gradient
1535 defines a "tube" where a point may be on one circle (on the
1536 inside of the tube) or the other (outside of the tube). We choose
1537 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001538
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001539 In order to keep the math to within the limits of fixed point,
1540 we divide the entire quadratic by Dr^2, and replace
1541 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001542
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001543 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1544 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1545 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001546
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001547 (x' and y' are computed by appending the subtract and scale to the
1548 fDstToIndex matrix in the constructor).
1549
1550 Since the 'A' component of the quadratic is independent of x' and y', it
1551 is precomputed in the constructor. Since the 'B' component is linear in
1552 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001553 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001554 a perspective projection), it must be computed in the loop.
1555
1556*/
1557
reed@google.com84e9c082011-04-13 17:44:24 +00001558static inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1559 SkScalar sr2d2, SkScalar foura,
1560 SkScalar oneOverTwoA, bool posRoot) {
1561 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001562 if (0 == foura) {
1563 return SkScalarToFixed(SkScalarDiv(-c, b));
1564 }
1565
reed@google.com84e9c082011-04-13 17:44:24 +00001566 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001567 if (discrim < 0) {
1568 discrim = -discrim;
1569 }
reed@google.com84e9c082011-04-13 17:44:24 +00001570 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1571 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001572 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001573 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001574 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001575 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001576 }
reed@google.com84e9c082011-04-13 17:44:24 +00001577 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001578}
1579
1580class Two_Point_Radial_Gradient : public Gradient_Shader {
1581public:
1582 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1583 const SkPoint& end, SkScalar endRadius,
1584 const SkColor colors[], const SkScalar pos[],
1585 int colorCount, SkShader::TileMode mode,
1586 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001587 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1588 fCenter1(start),
1589 fCenter2(end),
1590 fRadius1(startRadius),
1591 fRadius2(endRadius) {
1592 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001593 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001594
1595 virtual BitmapType asABitmap(SkBitmap* bitmap,
1596 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001597 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001598 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001599 if (bitmap) {
1600 this->commonAsABitmap(bitmap);
1601 }
1602 SkScalar diffL = 0; // just to avoid gcc warning
1603 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001604 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001605 SkScalarSquare(fDiff.fY));
1606 }
1607 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001608 if (diffL) {
1609 SkScalar invDiffL = SkScalarInvert(diffL);
1610 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1611 SkScalarMul(invDiffL, fDiff.fX));
1612 } else {
1613 matrix->reset();
1614 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001615 matrix->preConcat(fPtsToUnit);
1616 }
1617 if (xy) {
1618 xy[0] = fTileMode;
1619 xy[1] = kClamp_TileMode;
1620 }
1621 if (NULL != twoPointRadialParams) {
1622 twoPointRadialParams[0] = diffL;
1623 twoPointRadialParams[1] = fStartRadius;
1624 twoPointRadialParams[2] = fDiffRadius;
1625 }
1626 return kTwoPointRadial_BitmapType;
1627 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001628
reed@google.com8e6d9142011-12-07 15:30:34 +00001629 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001630 if (info) {
1631 commonAsAGradient(info);
1632 info->fPoint[0] = fCenter1;
1633 info->fPoint[1] = fCenter2;
1634 info->fRadius[0] = fRadius1;
1635 info->fRadius[1] = fRadius2;
1636 }
1637 return kRadial2_GradientType;
1638 }
1639
reed@google.com8e6d9142011-12-07 15:30:34 +00001640 virtual void shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) SK_OVERRIDE {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001641 SkASSERT(count > 0);
1642
1643 // Zero difference between radii: fill with transparent black.
1644 if (fDiffRadius == 0) {
1645 sk_bzero(dstC, count * sizeof(*dstC));
1646 return;
1647 }
1648 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1649 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001650 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001651
1652 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001653 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001654 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001655 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001656 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1657 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001658 SkScalar dx, fx = srcPt.fX;
1659 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001660
reed@google.com61eb0402011-04-15 12:11:12 +00001661 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001662 SkFixed fixedX, fixedY;
1663 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1664 dx = SkFixedToScalar(fixedX);
1665 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001666 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001667 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001668 dx = fDstToIndex.getScaleX();
1669 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001670 }
reed@google.com84e9c082011-04-13 17:44:24 +00001671 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1672 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1673 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1674 SkScalarMul(fDiff.fY, dy)) * 2;
reed@google.com61eb0402011-04-15 12:11:12 +00001675 if (proc == clamp_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001676 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001677 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001678 SkFixed index = SkClampMax(t, 0xFFFF);
1679 SkASSERT(index <= 0xFFFF);
1680 *dstC++ = cache[index >> (16 - kCache32Bits)];
1681 fx += dx;
1682 fy += dy;
1683 b += db;
1684 }
reed@google.com61eb0402011-04-15 12:11:12 +00001685 } else if (proc == mirror_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001686 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001687 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001688 SkFixed index = mirror_tileproc(t);
1689 SkASSERT(index <= 0xFFFF);
1690 *dstC++ = cache[index >> (16 - kCache32Bits)];
1691 fx += dx;
1692 fy += dy;
1693 b += db;
1694 }
reed@google.com61eb0402011-04-15 12:11:12 +00001695 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001696 SkASSERT(proc == repeat_tileproc);
1697 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001698 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001699 SkFixed index = repeat_tileproc(t);
1700 SkASSERT(index <= 0xFFFF);
1701 *dstC++ = cache[index >> (16 - kCache32Bits)];
1702 fx += dx;
1703 fy += dy;
1704 b += db;
1705 }
1706 }
reed@google.com61eb0402011-04-15 12:11:12 +00001707 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001708 SkScalar dstX = SkIntToScalar(x);
1709 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001710 for (; count > 0; --count) {
1711 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001712 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001713 SkScalar fx = srcPt.fX;
1714 SkScalar fy = srcPt.fY;
1715 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1716 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1717 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001718 SkFixed index = proc(t);
1719 SkASSERT(index <= 0xFFFF);
1720 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001721 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001722 }
1723 }
1724 }
1725
reed@android.com6c59a172009-09-22 20:24:05 +00001726 virtual bool setContext(const SkBitmap& device,
1727 const SkPaint& paint,
reed@google.com8e6d9142011-12-07 15:30:34 +00001728 const SkMatrix& matrix) SK_OVERRIDE {
1729 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com6c59a172009-09-22 20:24:05 +00001730 return false;
1731 }
1732
1733 // we don't have a span16 proc
1734 fFlags &= ~kHasSpan16_Flag;
1735 return true;
1736 }
1737
reed@google.com8e6d9142011-12-07 15:30:34 +00001738 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001739 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1740 }
1741
reed@google.com7716afb2011-12-07 15:17:50 +00001742 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
reed@android.combcfc7332009-11-10 16:19:39 +00001743 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001744 buffer.writeScalar(fCenter1.fX);
1745 buffer.writeScalar(fCenter1.fY);
1746 buffer.writeScalar(fCenter2.fX);
1747 buffer.writeScalar(fCenter2.fY);
1748 buffer.writeScalar(fRadius1);
1749 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001750 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001751
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001752protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001753 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001754 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001755 fCenter1(unflatten_point(buffer)),
1756 fCenter2(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001757 fRadius1(buffer.readScalar()),
1758 fRadius2(buffer.readScalar()) {
1759 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001760 };
reed@google.com7716afb2011-12-07 15:17:50 +00001761 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001762
1763private:
1764 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001765 const SkPoint fCenter1;
1766 const SkPoint fCenter2;
1767 const SkScalar fRadius1;
1768 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001769 SkPoint fDiff;
1770 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001771
1772 void init() {
1773 fDiff = fCenter1 - fCenter2;
1774 fDiffRadius = fRadius2 - fRadius1;
1775 SkScalar inv = SkScalarInvert(fDiffRadius);
1776 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1777 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1778 fStartRadius = SkScalarMul(fRadius1, inv);
1779 fSr2D2 = SkScalarSquare(fStartRadius);
1780 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00001781 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001782
1783 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1784 fPtsToUnit.postScale(inv, inv);
1785 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001786};
1787
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788///////////////////////////////////////////////////////////////////////////////
1789
1790class Sweep_Gradient : public Gradient_Shader {
1791public:
1792 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1793 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001794 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
1795 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796 {
1797 fPtsToUnit.setTranslate(-cx, -cy);
1798 }
reed@google.com7716afb2011-12-07 15:17:50 +00001799 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
1800 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001801
1802 virtual BitmapType asABitmap(SkBitmap* bitmap,
1803 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001804 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00001805 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001806 if (bitmap) {
1807 this->commonAsABitmap(bitmap);
1808 }
1809 if (matrix) {
1810 *matrix = fPtsToUnit;
1811 }
1812 if (xy) {
1813 xy[0] = fTileMode;
1814 xy[1] = kClamp_TileMode;
1815 }
1816 return kSweep_BitmapType;
1817 }
1818
reed@google.com7716afb2011-12-07 15:17:50 +00001819 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001820 if (info) {
1821 commonAsAGradient(info);
1822 info->fPoint[0] = fCenter;
1823 }
1824 return kSweep_GradientType;
1825 }
1826
reed@google.com8e6d9142011-12-07 15:30:34 +00001827 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1829 }
1830
reed@google.com7716afb2011-12-07 15:17:50 +00001831 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001832 this->INHERITED::flatten(buffer);
1833 buffer.writeScalar(fCenter.fX);
1834 buffer.writeScalar(fCenter.fY);
1835 }
1836
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001838 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
1839 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001840 fCenter(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001841 }
1842
reed@google.com7716afb2011-12-07 15:17:50 +00001843 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844
1845private:
1846 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001847 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848};
1849
1850#ifdef COMPUTE_SWEEP_TABLE
1851#define PI 3.14159265
1852static bool gSweepTableReady;
1853static uint8_t gSweepTable[65];
1854
1855/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1856 We scale the results to [0..32]
1857*/
reed@google.com61eb0402011-04-15 12:11:12 +00001858static const uint8_t* build_sweep_table() {
1859 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001860 const int N = 65;
1861 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001862
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863 for (int i = 0; i < N; i++)
1864 {
1865 double arg = i / DENOM;
1866 double v = atan(arg);
1867 int iv = (int)round(v * DENOM * 2 / PI);
1868// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1869 printf("%d, ", iv);
1870 gSweepTable[i] = iv;
1871 }
1872 gSweepTableReady = true;
1873 }
1874 return gSweepTable;
1875}
1876#else
1877static const uint8_t gSweepTable[] = {
1878 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1879 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1880 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1881 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1882 32
1883};
1884static const uint8_t* build_sweep_table() { return gSweepTable; }
1885#endif
1886
1887// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1888// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1889// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1890
1891//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00001892static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 SkASSERT(numer <= denom);
1894 SkASSERT(numer > 0);
1895 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001896
reed@android.com8a1c16f2008-12-17 15:59:43 +00001897 int nbits = SkCLZ(numer);
1898 int dbits = SkCLZ(denom);
1899 int bits = 6 - nbits + dbits;
1900 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001901
reed@google.com61eb0402011-04-15 12:11:12 +00001902 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00001903 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001904 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905
1906 denom <<= dbits - 1;
1907 numer <<= nbits - 1;
1908
1909 unsigned result = 0;
1910
1911 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00001912 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00001914 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00001916 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001917
reed@android.com8a1c16f2008-12-17 15:59:43 +00001918 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00001919 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920 // make room for the rest of the answer bits
1921 result <<= bits;
1922 switch (bits) {
1923 case 6:
1924 if ((numer = (numer << 1) - denom) >= 0)
1925 result |= 32;
1926 else
1927 numer += denom;
1928 case 5:
1929 if ((numer = (numer << 1) - denom) >= 0)
1930 result |= 16;
1931 else
1932 numer += denom;
1933 case 4:
1934 if ((numer = (numer << 1) - denom) >= 0)
1935 result |= 8;
1936 else
1937 numer += denom;
1938 case 3:
1939 if ((numer = (numer << 1) - denom) >= 0)
1940 result |= 4;
1941 else
1942 numer += denom;
1943 case 2:
1944 if ((numer = (numer << 1) - denom) >= 0)
1945 result |= 2;
1946 else
1947 numer += denom;
1948 case 1:
1949 default: // not strictly need, but makes GCC make better ARM code
1950 if ((numer = (numer << 1) - denom) >= 0)
1951 result |= 1;
1952 else
1953 numer += denom;
1954 }
1955 }
1956 return result;
1957}
1958
1959// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00001960static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001961#ifdef SK_DEBUG
1962 {
1963 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00001964 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965 gOnce = true;
1966 SkASSERT(div_64(55, 55) == 64);
1967 SkASSERT(div_64(128, 256) == 32);
1968 SkASSERT(div_64(2326528, 4685824) == 31);
1969 SkASSERT(div_64(753664, 5210112) == 9);
1970 SkASSERT(div_64(229376, 4882432) == 3);
1971 SkASSERT(div_64(2, 64) == 2);
1972 SkASSERT(div_64(1, 64) == 1);
1973 // test that we handle underflow correctly
1974 SkASSERT(div_64(12345, 0x54321234) == 0);
1975 }
1976 }
1977#endif
1978
1979 SkASSERT(y > 0 && x > 0);
1980 const uint8_t* table = build_sweep_table();
1981
1982 unsigned result;
1983 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00001984 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985 // first part of the atan(v) = PI/2 - atan(1/v) identity
1986 // since our div_64 and table want v <= 1, where v = y/x
1987 SkTSwap<SkFixed>(x, y);
1988 }
1989
1990 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001991
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992#ifdef SK_DEBUG
1993 {
1994 unsigned result2 = SkDivBits(y, x, 6);
1995 SkASSERT(result2 == result ||
1996 (result == 1 && result2 == 0));
1997 }
1998#endif
1999
2000 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2001 result = table[result];
2002
reed@google.com61eb0402011-04-15 12:11:12 +00002003 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004 // complete the atan(v) = PI/2 - atan(1/v) identity
2005 result = 64 - result;
2006 // pin to 63
2007 result -= result >> 6;
2008 }
2009
2010 SkASSERT(result <= 63);
2011 return result;
2012}
2013
2014// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com51baf5a2011-09-21 13:38:36 +00002015#ifdef SK_SCALAR_IS_FLOAT
2016static unsigned SkATan2_255(float y, float x) {
2017 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
2018 static const float g255Over2PI = 40.584510488433314f;
2019
2020 float result = sk_float_atan2(y, x);
2021 if (result < 0) {
2022 result += 2 * SK_ScalarPI;
2023 }
2024 SkASSERT(result >= 0);
2025 // since our value is always >= 0, we can cast to int, which is faster than
2026 // calling floorf()
2027 int ir = (int)(result * g255Over2PI);
2028 SkASSERT(ir >= 0 && ir <= 255);
2029 return ir;
2030}
2031#else
reed@google.com61eb0402011-04-15 12:11:12 +00002032static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2033 if (x == 0) {
2034 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002036 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037 return y < 0 ? 192 : 64;
2038 }
reed@google.com61eb0402011-04-15 12:11:12 +00002039 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002041 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002042
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043 /* Find the right quadrant for x,y
2044 Since atan_0_90 only handles the first quadrant, we rotate x,y
2045 appropriately before calling it, and then add the right amount
2046 to account for the real quadrant.
2047 quadrant 0 : add 0 | x > 0 && y > 0
2048 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2049 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2050 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002051
reed@android.com8a1c16f2008-12-17 15:59:43 +00002052 map x<0 to (1 << 6)
2053 map y<0 to (3 << 6)
2054 add = map_x ^ map_y
2055 */
2056 int xsign = x >> 31;
2057 int ysign = y >> 31;
2058 int add = ((-xsign) ^ (ysign & 3)) << 6;
2059
2060#ifdef SK_DEBUG
2061 if (0 == add)
2062 SkASSERT(x > 0 && y > 0);
2063 else if (64 == add)
2064 SkASSERT(x < 0 && y > 0);
2065 else if (128 == add)
2066 SkASSERT(x < 0 && y < 0);
2067 else if (192 == add)
2068 SkASSERT(x > 0 && y < 0);
2069 else
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002070 SkDEBUGFAIL("bad value for add");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002072
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2074 where we need to rotate x,y by 90 or -90
2075 */
2076 x = (x ^ xsign) - xsign;
2077 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002078 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002079 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002080 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081
2082 unsigned result = add + atan_0_90(y, x);
2083 SkASSERT(result < 256);
2084 return result;
2085}
reed@google.com51baf5a2011-09-21 13:38:36 +00002086#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002087
reed@google.comdd5bd672011-09-20 15:56:13 +00002088void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 SkMatrix::MapXYProc proc = fDstToIndexProc;
2090 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002091 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002093
reed@google.com61eb0402011-04-15 12:11:12 +00002094 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2096 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002097 SkScalar dx, fx = srcPt.fX;
2098 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002099
reed@google.com61eb0402011-04-15 12:11:12 +00002100 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 SkFixed storage[2];
2102 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2103 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002104 dx = SkFixedToScalar(storage[0]);
2105 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002106 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002108 dx = matrix.getScaleX();
2109 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002110 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002111
reed@google.com61eb0402011-04-15 12:11:12 +00002112 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002113 *dstC++ = cache[SkATan2_255(fy, fx)];
2114 fx += dx;
2115 fy += dy;
2116 }
reed@google.com61eb0402011-04-15 12:11:12 +00002117 } else { // perspective case
2118 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com51baf5a2011-09-21 13:38:36 +00002120 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2121 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 }
2123 }
2124}
2125
reed@google.comdd5bd672011-09-20 15:56:13 +00002126void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127 SkMatrix::MapXYProc proc = fDstToIndexProc;
2128 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002129 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130 int toggle = ((x ^ y) & 1) << kCache16Bits;
2131 SkPoint srcPt;
2132
reed@google.com61eb0402011-04-15 12:11:12 +00002133 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2135 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002136 SkScalar dx, fx = srcPt.fX;
2137 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002138
reed@google.com61eb0402011-04-15 12:11:12 +00002139 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140 SkFixed storage[2];
2141 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2142 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002143 dx = SkFixedToScalar(storage[0]);
2144 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002145 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002147 dx = matrix.getScaleX();
2148 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002150
reed@google.com61eb0402011-04-15 12:11:12 +00002151 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2153 *dstC++ = cache[toggle + index];
2154 toggle ^= (1 << kCache16Bits);
2155 fx += dx;
2156 fy += dy;
2157 }
reed@google.com61eb0402011-04-15 12:11:12 +00002158 } else { // perspective case
2159 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2161 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002162
reed@google.com51baf5a2011-09-21 13:38:36 +00002163 int index = SkATan2_255(srcPt.fY, srcPt.fX);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164 index >>= (8 - kCache16Bits);
2165 *dstC++ = cache[toggle + index];
2166 toggle ^= (1 << kCache16Bits);
2167 }
2168 }
2169}
2170
reed@google.com61eb0402011-04-15 12:11:12 +00002171///////////////////////////////////////////////////////////////////////////////
2172///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173
2174// assumes colors is SkColor* and pos is SkScalar*
2175#define EXPAND_1_COLOR(count) \
2176 SkColor tmp[2]; \
2177 do { \
2178 if (1 == count) { \
2179 tmp[0] = tmp[1] = colors[0]; \
2180 colors = tmp; \
2181 pos = NULL; \
2182 count = 2; \
2183 } \
2184 } while (0)
2185
reed@google.com61eb0402011-04-15 12:11:12 +00002186SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2187 const SkColor colors[],
2188 const SkScalar pos[], int colorCount,
2189 SkShader::TileMode mode,
2190 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 if (NULL == pts || NULL == colors || colorCount < 1) {
2192 return NULL;
2193 }
2194 EXPAND_1_COLOR(colorCount);
2195
reed@android.comab840b82009-07-01 17:00:03 +00002196 return SkNEW_ARGS(Linear_Gradient,
2197 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002198}
2199
reed@google.com61eb0402011-04-15 12:11:12 +00002200SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2201 const SkColor colors[],
2202 const SkScalar pos[], int colorCount,
2203 SkShader::TileMode mode,
2204 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 if (radius <= 0 || NULL == colors || colorCount < 1) {
2206 return NULL;
2207 }
2208 EXPAND_1_COLOR(colorCount);
2209
reed@android.comab840b82009-07-01 17:00:03 +00002210 return SkNEW_ARGS(Radial_Gradient,
2211 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002212}
2213
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002214SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2215 SkScalar startRadius,
2216 const SkPoint& end,
2217 SkScalar endRadius,
2218 const SkColor colors[],
2219 const SkScalar pos[],
2220 int colorCount,
2221 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002222 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002223 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2224 return NULL;
2225 }
2226 EXPAND_1_COLOR(colorCount);
2227
2228 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002229 (start, startRadius, end, endRadius, colors, pos,
2230 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002231}
2232
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2234 const SkColor colors[],
2235 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002236 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237 if (NULL == colors || count < 1) {
2238 return NULL;
2239 }
2240 EXPAND_1_COLOR(count);
2241
2242 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2243}
2244
caryclark@google.comd26147a2011-12-15 14:16:43 +00002245SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
2246 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Linear_Gradient)
2247 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Radial_Gradient)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248
caryclark@google.comd26147a2011-12-15 14:16:43 +00002249 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sweep_Gradient)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250
caryclark@google.comd26147a2011-12-15 14:16:43 +00002251 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Radial_Gradient)
2252SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END