blob: 729d39b1d0194dee96693e7e3509b87de8565358 [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"
tomhudson@google.come8c984d2012-01-09 13:45:36 +000011#include "SkClampRange.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColorPriv.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000013#include "SkMallocPixelRef.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkUnitMapper.h"
15#include "SkUtils.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000016#include "SkTemplates.h"
17#include "SkBitmapCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018
reed@google.com0e734bd2011-12-08 17:24:44 +000019#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
20 #define USE_DITHER_32BIT_GRADIENT
21#endif
22
reed@google.com5eb158d2011-04-15 15:50:34 +000023static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
24 int count) {
25 if (count > 0) {
26 if (v0 == v1) {
27 sk_memset32(dst, v0, count);
28 } else {
29 int pairs = count >> 1;
30 for (int i = 0; i < pairs; i++) {
31 *dst++ = v0;
32 *dst++ = v1;
33 }
34 if (count & 1) {
35 *dst = v0;
36 }
37 }
38 }
39}
40
reed@google.com61eb0402011-04-15 12:11:12 +000041///////////////////////////////////////////////////////////////////////////////
tomhudson@google.com9ce767c2011-04-25 20:49:39 +000042// Can't use a two-argument function with side effects like this in a
43// constructor's initializer's argument list because the order of
44// evaluations in that context is undefined (and backwards on linux/gcc).
45static SkPoint unflatten_point(SkReader32& buffer) {
46 SkPoint retval;
47 retval.fX = buffer.readScalar();
48 retval.fY = buffer.readScalar();
49 return retval;
50}
51
52///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000053
54typedef SkFixed (*TileProc)(SkFixed);
55
reed@android.com41bccf52009-04-03 13:33:51 +000056static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000057 return SkClampMax(x, 0xFFFF);
58}
59
reed@android.com41bccf52009-04-03 13:33:51 +000060static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 return x & 0xFFFF;
62}
63
reed@android.com41bccf52009-04-03 13:33:51 +000064static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 int s = x << 15 >> 31;
66 return (x ^ s) & 0xFFFF;
67}
68
69static const TileProc gTileProcs[] = {
70 clamp_tileproc,
71 repeat_tileproc,
72 mirror_tileproc
73};
74
reed@google.com61eb0402011-04-15 12:11:12 +000075///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000076
reed@android.com200645d2009-12-14 16:41:57 +000077static inline int repeat_bits(int x, const int bits) {
78 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000079}
80
reed@android.com200645d2009-12-14 16:41:57 +000081static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000083 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000085 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000086#else
reed@android.com200645d2009-12-14 16:41:57 +000087 int s = x << (31 - bits) >> 31;
88 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000089#endif
90}
91
reed@android.com41bccf52009-04-03 13:33:51 +000092static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 return x & 0xFF;
94}
95
reed@android.com41bccf52009-04-03 13:33:51 +000096static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000097#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000098 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +0000100 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 return x & 255;
102#else
103 int s = x << 23 >> 31;
104 return (x ^ s) & 0xFF;
105#endif
106}
107
reed@google.com61eb0402011-04-15 12:11:12 +0000108///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109
110class Gradient_Shader : public SkShader {
111public:
112 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000113 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 virtual ~Gradient_Shader();
115
116 // overrides
reed@google.com7716afb2011-12-07 15:17:50 +0000117 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
118 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000119 virtual bool isOpaque() const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000121 enum {
122 kCache16Bits = 8, // seems like enough for visual accuracy
123 kCache16Count = 1 << kCache16Bits,
124 kCache16Mask = kCache16Count - 1,
125 kCache16Shift = 16 - kCache16Bits,
126 kSqrt16Shift = 8 - kCache16Bits,
127
128 kCache32Bits = 8, // pretty much should always be 8
129 kCache32Count = 1 << kCache32Bits,
130 kCache32Mask = kCache32Count - 1,
131 kCache32Shift = 16 - kCache32Bits,
132 kSqrt32Shift = 8 - kCache32Bits,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000133
134 /** ToggleMasks are used in dithering to switch between the two
135 halves of the gradient cache; they should be equal to the size
136 of a half-cache.
137 */
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000138#ifdef USE_DITHER_32BIT_GRADIENT
139 kToggleMask32 = kCache32Count,
140#else
141 kToggleMask32 = 0,
142#endif
143 kToggleMask16 = kCache16Count
144 };
145
146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147protected:
148 Gradient_Shader(SkFlattenableReadBuffer& );
149 SkUnitMapper* fMapper;
150 SkMatrix fPtsToUnit; // set by subclass
151 SkMatrix fDstToIndex;
152 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 TileMode fTileMode;
154 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000155 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 uint8_t fDstToIndexClass;
157 uint8_t fFlags;
158
159 struct Rec {
160 SkFixed fPos; // 0...1
161 uint32_t fScale; // (1 << 24) / range
162 };
163 Rec* fRecs;
164
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000166 const uint16_t* getCache16() const;
167 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
reed@google.com7c2f27d2011-03-07 19:29:00 +0000169 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000170 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000171
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172private:
173 enum {
174 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
175
reed@android.com1c12abe2009-07-02 15:01:02 +0000176 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 };
178 SkColor fStorage[(kStorageSize + 3) >> 2];
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000179 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
180 bool fColorsAreOpaque;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181
reed@google.com7c2f27d2011-03-07 19:29:00 +0000182 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
183 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
reed@google.com7c2f27d2011-03-07 19:29:00 +0000185 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
186 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000187 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 +0000188
reed@android.com512a8762009-12-14 15:25:36 +0000189 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000190 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
191 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000192 void setCacheAlpha(U8CPU alpha) const;
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000193 void initCommon();
reed@android.com512a8762009-12-14 15:25:36 +0000194
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 typedef SkShader INHERITED;
196};
197
reed@android.com41bccf52009-04-03 13:33:51 +0000198static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 SkASSERT(x >= 0 && x <= SK_Scalar1);
200
201#ifdef SK_SCALAR_IS_FLOAT
202 return (unsigned)(x * 0xFFFF);
203#else
204 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
205#endif
206}
207
reed@android.com41bccf52009-04-03 13:33:51 +0000208Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
209 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 SkASSERT(colorCount > 1);
211
212 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
213
214 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000215 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
218 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
219 fTileMode = mode;
220 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000221
reed@android.com41bccf52009-04-03 13:33:51 +0000222 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000223 fCache32 = NULL;
224 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225
reed@android.com41bccf52009-04-03 13:33:51 +0000226 /* Note: we let the caller skip the first and/or last position.
227 i.e. pos[0] = 0.3, pos[1] = 0.7
228 In these cases, we insert dummy entries to ensure that the final data
229 will be bracketed by [0, 1].
230 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
231
232 Thus colorCount (the caller's value, and fColorCount (our value) may
233 differ by up to 2. In the above example:
234 colorCount = 2
235 fColorCount = 4
236 */
237 fColorCount = colorCount;
238 // check if we need to add in dummy start and/or end position/colors
239 bool dummyFirst = false;
240 bool dummyLast = false;
241 if (pos) {
242 dummyFirst = pos[0] != 0;
243 dummyLast = pos[colorCount - 1] != SK_Scalar1;
244 fColorCount += dummyFirst + dummyLast;
245 }
246
247 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000248 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000249 fOrigColors = reinterpret_cast<SkColor*>(
250 sk_malloc_throw(size * fColorCount));
251 }
252 else {
253 fOrigColors = fStorage;
254 }
255
256 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 {
reed@android.com41bccf52009-04-03 13:33:51 +0000258 SkColor* origColors = fOrigColors;
259 if (dummyFirst) {
260 *origColors++ = colors[0];
261 }
262 memcpy(origColors, colors, colorCount * sizeof(SkColor));
263 if (dummyLast) {
264 origColors += colorCount;
265 *origColors = colors[colorCount - 1];
266 }
267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268
reed@android.com1c12abe2009-07-02 15:01:02 +0000269 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000270 if (fColorCount > 2) {
271 Rec* recs = fRecs;
272 recs->fPos = 0;
273 // recs->fScale = 0; // unused;
274 recs += 1;
275 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 /* We need to convert the user's array of relative positions into
277 fixed-point positions and scale factors. We need these results
278 to be strictly monotonic (no two values equal or out of order).
279 Hence this complex loop that just jams a zero for the scale
280 value if it sees a segment out of order, and it assures that
281 we start at 0 and end at 1.0
282 */
283 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000284 int startIndex = dummyFirst ? 0 : 1;
285 int count = colorCount + dummyLast;
286 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 // force the last value to be 1.0
288 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000289 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000291 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 }
reed@android.com41bccf52009-04-03 13:33:51 +0000294 // pin curr withing range
295 if (curr < 0) {
296 curr = 0;
297 } else if (curr > SK_Fixed1) {
298 curr = SK_Fixed1;
299 }
300 recs->fPos = curr;
301 if (curr > prev) {
302 recs->fScale = (1 << 24) / (curr - prev);
303 } else {
304 recs->fScale = 0; // ignore this segment
305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 // get ready for the next value
307 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000308 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 }
reed@android.com41bccf52009-04-03 13:33:51 +0000310 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 SkFixed dp = SK_Fixed1 / (colorCount - 1);
312 SkFixed p = dp;
313 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000314 for (int i = 1; i < colorCount; i++) {
315 recs->fPos = p;
316 recs->fScale = scale;
317 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 p += dp;
319 }
320 }
321 }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000322 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323}
324
325Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000326 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 fCacheAlpha = 256;
328
329 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
330
331 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000332 fCache32 = NULL;
333 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334
reed@android.com41bccf52009-04-03 13:33:51 +0000335 int colorCount = fColorCount = buffer.readU32();
336 if (colorCount > kColorStorageCount) {
337 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
338 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
339 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000341 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343
344 fTileMode = (TileMode)buffer.readU8();
345 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000346 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 if (colorCount > 2) {
348 Rec* recs = fRecs;
349 recs[0].fPos = 0;
350 for (int i = 1; i < colorCount; i++) {
351 recs[i].fPos = buffer.readS32();
352 recs[i].fScale = buffer.readU32();
353 }
354 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000355 SkReadMatrix(&buffer, &fPtsToUnit);
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000356 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357}
358
reed@android.com41bccf52009-04-03 13:33:51 +0000359Gradient_Shader::~Gradient_Shader() {
360 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000362 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000363 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000364 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000366 }
reed@google.com82065d62011-02-07 15:30:46 +0000367 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368}
369
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000370void Gradient_Shader::initCommon() {
371 fFlags = 0;
372 unsigned colorAlpha = 0xFF;
373 for (int i = 0; i < fColorCount; i++) {
374 colorAlpha &= SkColorGetA(fOrigColors[i]);
375 }
376 fColorsAreOpaque = colorAlpha == 0xFF;
377}
378
reed@android.com41bccf52009-04-03 13:33:51 +0000379void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 this->INHERITED::flatten(buffer);
381 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000382 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
384 buffer.write8(fTileMode);
385 if (fColorCount > 2) {
386 Rec* recs = fRecs;
387 for (int i = 1; i < fColorCount; i++) {
388 buffer.write32(recs[i].fPos);
389 buffer.write32(recs[i].fScale);
390 }
391 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000392 SkWriteMatrix(&buffer, fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393}
394
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000395bool Gradient_Shader::isOpaque() const {
396 return fColorsAreOpaque;
397}
398
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399bool Gradient_Shader::setContext(const SkBitmap& device,
400 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000401 const SkMatrix& matrix) {
402 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000404 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405
406 const SkMatrix& inverse = this->getTotalInverse();
407
408 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
409 return false;
410 }
411
412 fDstToIndexProc = fDstToIndex.getMapXYProc();
413 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
414
415 // now convert our colors in to PMColors
416 unsigned paintAlpha = this->getPaintAlpha();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417
418 fFlags = this->INHERITED::getFlags();
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000419 if (fColorsAreOpaque && paintAlpha == 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 fFlags |= kOpaqueAlpha_Flag;
421 }
422 // we can do span16 as long as our individual colors are opaque,
423 // regardless of the paint's alpha
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000424 if (fColorsAreOpaque) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 fFlags |= kHasSpan16_Flag;
426 }
427
reed@google.com95eed982011-07-05 17:01:56 +0000428 this->setCacheAlpha(paintAlpha);
429 return true;
430}
431
432void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 // if the new alpha differs from the previous time we were called, inval our cache
434 // this will trigger the cache to be rebuilt.
435 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000436 if (fCacheAlpha != alpha) {
437 fCache16 = NULL; // inval the cache
438 fCache32 = NULL; // inval the cache
439 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000440 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000441 if (fCache32PixelRef) {
442 fCache32PixelRef->notifyPixelsChanged();
443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445}
446
reed@android.com41bccf52009-04-03 13:33:51 +0000447static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 SkASSERT(a == SkToU8(a));
449 SkASSERT(b == SkToU8(b));
450 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 return a + ((b - a) * scale >> 8);
452}
453
reed@android.com41bccf52009-04-03 13:33:51 +0000454static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
455 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456#if 0
457 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
458 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
459 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
460 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
461
462 return SkPackARGB32(a, r, g, b);
463#else
464 int otherBlend = 256 - blend;
465
466#if 0
467 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
468 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
469 SkASSERT((t0 & t1) == 0);
470 return t0 | t1;
471#else
472 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
473 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
474#endif
475
476#endif
477}
478
479#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
480
reed@android.com41bccf52009-04-03 13:33:51 +0000481/** We take the original colors, not our premultiplied PMColors, since we can
482 build a 16bit table as long as the original colors are opaque, even if the
483 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484*/
reed@android.com512a8762009-12-14 15:25:36 +0000485void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
486 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 SkASSERT(count > 1);
488 SkASSERT(SkColorGetA(c0) == 0xFF);
489 SkASSERT(SkColorGetA(c1) == 0xFF);
490
491 SkFixed r = SkColorGetR(c0);
492 SkFixed g = SkColorGetG(c0);
493 SkFixed b = SkColorGetB(c0);
494
495 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
496 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
497 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
498
499 r = SkIntToFixed(r) + 0x8000;
500 g = SkIntToFixed(g) + 0x8000;
501 b = SkIntToFixed(b) + 0x8000;
502
503 do {
504 unsigned rr = r >> 16;
505 unsigned gg = g >> 16;
506 unsigned bb = b >> 16;
507 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000508 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 cache += 1;
510 r += dr;
511 g += dg;
512 b += db;
513 } while (--count != 0);
514}
515
reed@google.com55b8e8c2011-01-13 16:22:35 +0000516/*
517 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
518 * semantics of how we 2x2 dither 32->16
519 */
520static inline U8CPU dither_fixed_to_8(SkFixed n) {
521 n >>= 8;
522 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
523}
524
525/*
526 * For dithering with premultiply, we want to ceiling the alpha component,
527 * to ensure that it is always >= any color component.
528 */
529static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
530 n >>= 8;
531 return ((n << 1) - (n | (n >> 8))) >> 8;
532}
533
534void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
535 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 SkASSERT(count > 1);
537
reed@android.com1c12abe2009-07-02 15:01:02 +0000538 // need to apply paintAlpha to our two endpoints
539 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
540 SkFixed da;
541 {
542 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
543 da = SkIntToFixed(tmp - a) / (count - 1);
544 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545
reed@android.com1c12abe2009-07-02 15:01:02 +0000546 SkFixed r = SkColorGetR(c0);
547 SkFixed g = SkColorGetG(c0);
548 SkFixed b = SkColorGetB(c0);
549 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
550 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
551 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552
553 a = SkIntToFixed(a) + 0x8000;
554 r = SkIntToFixed(r) + 0x8000;
555 g = SkIntToFixed(g) + 0x8000;
556 b = SkIntToFixed(b) + 0x8000;
557
558 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000559 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
560 cache[kCache32Count] = SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
561 dither_fixed_to_8(r),
562 dither_fixed_to_8(g),
563 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000564 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 a += da;
566 r += dr;
567 g += dg;
568 b += db;
569 } while (--count != 0);
570}
571
reed@android.com41bccf52009-04-03 13:33:51 +0000572static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 SkASSERT((unsigned)x <= SK_Fixed1);
574 return x - (x >> 16);
575}
576
reed@android.com200645d2009-12-14 16:41:57 +0000577static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000578 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000579 if (6 == bits) {
580 return (x << 10) | (x << 4) | (x >> 2);
581 }
582 if (8 == bits) {
583 return (x << 8) | x;
584 }
585 sk_throw();
586 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587}
588
reed@google.com7c2f27d2011-03-07 19:29:00 +0000589const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000590 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000591 // double the count for dither entries
592 const int entryCount = kCache16Count * 2;
593 const size_t allocSize = sizeof(uint16_t) * entryCount;
594
reed@android.com3c9b2a42009-08-27 19:28:37 +0000595 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000596 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000597 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000599 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000600 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000601 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 Rec* rec = fRecs;
603 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000604 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000605 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 SkASSERT(nextIndex < kCache16Count);
607
608 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000609 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 prevIndex = nextIndex;
611 }
612 SkASSERT(prevIndex == kCache16Count - 1);
613 }
614
reed@android.com41bccf52009-04-03 13:33:51 +0000615 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000616 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 uint16_t* linear = fCache16; // just computed linear data
618 uint16_t* mapped = fCache16Storage; // storage for mapped data
619 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000620 for (int i = 0; i < kCache16Count; i++) {
621 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000623 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 }
625 sk_free(fCache16);
626 fCache16 = fCache16Storage;
627 }
628 }
629 return fCache16;
630}
631
reed@google.com7c2f27d2011-03-07 19:29:00 +0000632const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000633 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000634 // double the count for dither entries
635 const int entryCount = kCache32Count * 2;
636 const size_t allocSize = sizeof(SkPMColor) * entryCount;
637
reed@google.comdc731fd2010-12-23 15:19:47 +0000638 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000639 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
640 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000641 }
642 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000643 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000644 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
645 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000646 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 Rec* rec = fRecs;
648 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000649 for (int i = 1; i < fColorCount; i++) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000650 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 SkASSERT(nextIndex < kCache32Count);
652
653 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000654 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
655 fOrigColors[i],
656 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 prevIndex = nextIndex;
658 }
659 SkASSERT(prevIndex == kCache32Count - 1);
660 }
661
reed@android.com41bccf52009-04-03 13:33:51 +0000662 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000663 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000664 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000666 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000668 for (int i = 0; i < kCache32Count; i++) {
669 int index = map->mapUnit16((i << 8) | i) >> 8;
670 mapped[i] = linear[index];
671 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000672 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000673 fCache32PixelRef->unref();
674 fCache32PixelRef = newPR;
675 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 }
677 }
678 return fCache32;
679}
680
reed@google.comdc731fd2010-12-23 15:19:47 +0000681/*
682 * Because our caller might rebuild the same (logically the same) gradient
683 * over and over, we'd like to return exactly the same "bitmap" if possible,
684 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
685 * To do that, we maintain a private cache of built-bitmaps, based on our
686 * colors and positions. Note: we don't try to flatten the fMapper, so if one
687 * is present, we skip the cache for now.
688 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000689void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000690 // our caller assumes no external alpha, so we ensure that our cache is
691 // built with 0xFF
692 this->setCacheAlpha(0xFF);
693
reed@google.comdc731fd2010-12-23 15:19:47 +0000694 // don't have a way to put the mapper into our cache-key yet
695 if (fMapper) {
696 // force our cahce32pixelref to be built
697 (void)this->getCache32();
698 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
699 bitmap->setPixelRef(fCache32PixelRef);
700 return;
701 }
702
703 // build our key: [numColors + colors[] + {positions[]} ]
704 int count = 1 + fColorCount;
705 if (fColorCount > 2) {
706 count += fColorCount - 1; // fRecs[].fPos
707 }
708
709 SkAutoSTMalloc<16, int32_t> storage(count);
710 int32_t* buffer = storage.get();
711
712 *buffer++ = fColorCount;
713 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
714 buffer += fColorCount;
715 if (fColorCount > 2) {
716 for (int i = 1; i < fColorCount; i++) {
717 *buffer++ = fRecs[i].fPos;
718 }
719 }
720 SkASSERT(buffer - storage.get() == count);
721
722 ///////////////////////////////////
723
724 static SkMutex gMutex;
725 static SkBitmapCache* gCache;
726 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
727 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
728 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000729
reed@google.comdc731fd2010-12-23 15:19:47 +0000730 if (NULL == gCache) {
731 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
732 }
733 size_t size = count * sizeof(int32_t);
734
735 if (!gCache->find(storage.get(), size, bitmap)) {
736 // force our cahce32pixelref to be built
737 (void)this->getCache32();
738 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
739 bitmap->setPixelRef(fCache32PixelRef);
740
741 gCache->add(storage.get(), size, *bitmap);
742 }
743}
744
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000745void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
746 if (info) {
747 if (info->fColorCount >= fColorCount) {
748 if (info->fColors) {
749 memcpy(info->fColors, fOrigColors,
750 fColorCount * sizeof(SkColor));
751 }
752 if (info->fColorOffsets) {
753 if (fColorCount == 2) {
754 info->fColorOffsets[0] = 0;
755 info->fColorOffsets[1] = SK_Scalar1;
756 } else if (fColorCount > 2) {
757 for (int i = 0; i < fColorCount; i++)
758 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
759 }
760 }
761 }
762 info->fColorCount = fColorCount;
763 info->fTileMode = fTileMode;
764 }
765}
766
reed@google.com61eb0402011-04-15 12:11:12 +0000767///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768
reed@android.com41bccf52009-04-03 13:33:51 +0000769static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 SkVector vec = pts[1] - pts[0];
771 SkScalar mag = vec.length();
772 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
773
774 vec.scale(inv);
775 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
776 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
777 matrix->postScale(inv, inv);
778}
779
780///////////////////////////////////////////////////////////////////////////////
781
782class Linear_Gradient : public Gradient_Shader {
783public:
784 Linear_Gradient(const SkPoint pts[2],
785 const SkColor colors[], const SkScalar pos[], int colorCount,
786 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000787 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
788 fStart(pts[0]),
789 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 {
791 pts_to_unit_matrix(pts, &fPtsToUnit);
792 }
reed@android.com9b46e772009-06-05 12:24:41 +0000793
reed@google.com7716afb2011-12-07 15:17:50 +0000794 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
795 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
796 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
797 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
798 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
799 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800
reed@google.com55b8e8c2011-01-13 16:22:35 +0000801 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 return SkNEW_ARGS(Linear_Gradient, (buffer));
803 }
804
reed@google.com7716afb2011-12-07 15:17:50 +0000805 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000806 this->INHERITED::flatten(buffer);
807 buffer.writeScalar(fStart.fX);
808 buffer.writeScalar(fStart.fY);
809 buffer.writeScalar(fEnd.fX);
810 buffer.writeScalar(fEnd.fY);
811 }
812
caryclark@google.comd26147a2011-12-15 14:16:43 +0000813 SK_DECLARE_FLATTENABLE_REGISTRAR()
814
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000816 Linear_Gradient(SkFlattenableReadBuffer& buffer)
817 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +0000818 fStart(unflatten_point(buffer)),
819 fEnd(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000820 }
reed@google.com7716afb2011-12-07 15:17:50 +0000821 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822
823private:
824 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000825 const SkPoint fStart;
826 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827};
828
reed@android.com5119bdb2009-06-12 21:27:03 +0000829bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
830 const SkMatrix& matrix) {
831 if (!this->INHERITED::setContext(device, paint, matrix)) {
832 return false;
833 }
834
835 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
836 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000837 fFlags |= SkShader::kConstInY32_Flag;
838 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
839 // only claim this if we do have a 16bit mode (i.e. none of our
840 // colors have alpha), and if we are not dithering (which obviously
841 // is not const in Y).
842 fFlags |= SkShader::kConstInY16_Flag;
843 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000844 }
845 return true;
846}
847
reed@google.com5eb158d2011-04-15 15:50:34 +0000848#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000849 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000850 unsigned fi = fx >> Gradient_Shader::kCache32Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000851 SkASSERT(fi <= 0xFF); \
852 fx += dx; \
853 *dstC++ = cache[toggle + fi]; \
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000854 toggle ^= Gradient_Shader::kToggleMask32; \
reed@google.com13659f12011-04-18 19:59:38 +0000855 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000856
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000857namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +0000858
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000859typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000860 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000861 int toggle, int count);
862
863void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
864 SkPMColor* SK_RESTRICT dstC,
865 const SkPMColor* SK_RESTRICT cache,
866 int toggle, int count) {
867 // we're a vertical gradient, so no change in a span
868 unsigned fi = proc(fx) >> Gradient_Shader::kCache32Shift;
869 sk_memset32_dither(dstC, cache[toggle + fi],
870 cache[(toggle ^ Gradient_Shader::kToggleMask32) + fi], count);
871
872}
873
874void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
875 SkPMColor* SK_RESTRICT dstC,
876 const SkPMColor* SK_RESTRICT cache,
877 int toggle, int count) {
878 SkClampRange range;
879 range.init(fx, dx, count, 0, 0xFF);
880
881 if ((count = range.fCount0) > 0) {
882 sk_memset32_dither(dstC,
883 cache[toggle + range.fV0],
884 cache[(toggle ^ Gradient_Shader::kToggleMask32) + range.fV0],
885 count);
886 dstC += count;
887 }
888 if ((count = range.fCount1) > 0) {
889 int unroll = count >> 3;
890 fx = range.fFx1;
891 for (int i = 0; i < unroll; i++) {
892 NO_CHECK_ITER; NO_CHECK_ITER;
893 NO_CHECK_ITER; NO_CHECK_ITER;
894 NO_CHECK_ITER; NO_CHECK_ITER;
895 NO_CHECK_ITER; NO_CHECK_ITER;
896 }
897 if ((count &= 7) > 0) {
898 do {
899 NO_CHECK_ITER;
900 } while (--count != 0);
901 }
902 }
903 if ((count = range.fCount2) > 0) {
904 sk_memset32_dither(dstC,
905 cache[toggle + range.fV1],
906 cache[(toggle ^ Gradient_Shader::kToggleMask32) + range.fV1],
907 count);
908 }
909}
910
911// TODO: we could merge mirror and repeat if we passed in a pointer to the
912// *_8bits proc, but that'd lose inlining, which might be significant here.
913void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
914 SkPMColor* SK_RESTRICT dstC,
915 const SkPMColor* SK_RESTRICT cache,
916 int toggle, int count) {
917 do {
918 unsigned fi = mirror_8bits(fx >> 8);
919 SkASSERT(fi <= 0xFF);
920 fx += dx;
921 *dstC++ = cache[toggle + fi];
922 toggle ^= Gradient_Shader::kToggleMask32;
923 } while (--count != 0);
924}
925
926void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
927 SkPMColor* SK_RESTRICT dstC,
928 const SkPMColor* SK_RESTRICT cache,
929 int toggle, int count) {
930 do {
931 unsigned fi = repeat_8bits(fx >> 8);
932 SkASSERT(fi <= 0xFF);
933 fx += dx;
934 *dstC++ = cache[toggle + fi];
935 toggle ^= Gradient_Shader::kToggleMask32;
936 } while (--count != 0);
937}
938}
939
940void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
941 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 SkASSERT(count > 0);
943
944 SkPoint srcPt;
945 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
946 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000947 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com0e734bd2011-12-08 17:24:44 +0000948#ifdef USE_DITHER_32BIT_GRADIENT
reed@google.com55b8e8c2011-01-13 16:22:35 +0000949 int toggle = ((x ^ y) & 1) << kCache32Bits;
reed@google.com0e734bd2011-12-08 17:24:44 +0000950#else
951 int toggle = 0;
reed@google.com0e734bd2011-12-08 17:24:44 +0000952#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953
reed@android.comc552a432009-06-12 20:02:50 +0000954 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000955 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
956 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
958
reed@android.comc552a432009-06-12 20:02:50 +0000959 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960 SkFixed dxStorage[1];
961 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
962 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000963 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
965 dx = SkScalarToFixed(fDstToIndex.getScaleX());
966 }
967
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000968 LinearShadeProc shadeProc = shadeSpan_linear_repeat;
reed@android.comc552a432009-06-12 20:02:50 +0000969 if (SkFixedNearlyZero(dx)) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000970 shadeProc = shadeSpan_linear_vertical;
reed@android.comc552a432009-06-12 20:02:50 +0000971 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000972 shadeProc = shadeSpan_linear_clamp;
reed@android.comc552a432009-06-12 20:02:50 +0000973 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000974 shadeProc = shadeSpan_linear_mirror;
reed@android.comc552a432009-06-12 20:02:50 +0000975 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000978 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comc552a432009-06-12 20:02:50 +0000979 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 SkScalar dstX = SkIntToScalar(x);
981 SkScalar dstY = SkIntToScalar(y);
982 do {
983 dstProc(fDstToIndex, dstX, dstY, &srcPt);
984 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
985 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000986 *dstC++ = cache[toggle + (fi >> kCache32Shift)];
987 toggle ^= Gradient_Shader::kToggleMask32;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 dstX += SK_Scalar1;
989 } while (--count != 0);
990 }
991}
992
reed@google.com55b8e8c2011-01-13 16:22:35 +0000993SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000994 SkMatrix* matrix,
995 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +0000996 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000998 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 }
1000 if (matrix) {
1001 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
1002 matrix->preConcat(fPtsToUnit);
1003 }
1004 if (xy) {
1005 xy[0] = fTileMode;
1006 xy[1] = kClamp_TileMode;
1007 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001008 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009}
1010
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001011SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
1012 if (info) {
1013 commonAsAGradient(info);
1014 info->fPoint[0] = fStart;
1015 info->fPoint[1] = fEnd;
1016 }
1017 return kLinear_GradientType;
1018}
1019
reed@android.com3c9b2a42009-08-27 19:28:37 +00001020static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
1021 int count) {
1022 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 *dst++ = value;
1024 count -= 1;
1025 SkTSwap(value, other);
1026 }
1027
1028 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001029
reed@android.com3c9b2a42009-08-27 19:28:37 +00001030 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001032 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034
reed@google.com5eb158d2011-04-15 15:50:34 +00001035#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +00001036 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001037 unsigned fi = fx >> Gradient_Shader::kCache16Shift; \
1038 SkASSERT(fi <= Gradient_Shader::kCache16Mask); \
reed@google.com5eb158d2011-04-15 15:50:34 +00001039 fx += dx; \
1040 *dstC++ = cache[toggle + fi]; \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001041 toggle ^= Gradient_Shader::kToggleMask16; \
reed@google.com13659f12011-04-18 19:59:38 +00001042 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +00001043
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001044namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +00001045
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001046typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001047 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001048 int toggle, int count);
1049
1050void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
1051 uint16_t* SK_RESTRICT dstC,
1052 const uint16_t* SK_RESTRICT cache,
1053 int toggle, int count) {
1054 // we're a vertical gradient, so no change in a span
1055 unsigned fi = proc(fx) >> Gradient_Shader::kCache16Shift;
1056 SkASSERT(fi <= Gradient_Shader::kCache16Mask);
1057 dither_memset16(dstC, cache[toggle + fi],
1058 cache[(toggle ^ Gradient_Shader::kToggleMask16) + fi], count);
1059
1060}
1061
1062void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
1063 uint16_t* SK_RESTRICT dstC,
1064 const uint16_t* SK_RESTRICT cache,
1065 int toggle, int count) {
1066 SkClampRange range;
1067 range.init(fx, dx, count, 0, Gradient_Shader::kCache16Mask);
1068
1069 if ((count = range.fCount0) > 0) {
1070 dither_memset16(dstC,
1071 cache[toggle + range.fV0],
1072 cache[(toggle ^ Gradient_Shader::kToggleMask16) + range.fV0],
1073 count);
1074 dstC += count;
1075 }
1076 if ((count = range.fCount1) > 0) {
1077 int unroll = count >> 3;
1078 fx = range.fFx1;
1079 for (int i = 0; i < unroll; i++) {
1080 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1081 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1082 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1083 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1084 }
1085 if ((count &= 7) > 0) {
1086 do {
1087 NO_CHECK_ITER_16;
1088 } while (--count != 0);
1089 }
1090 }
1091 if ((count = range.fCount2) > 0) {
1092 dither_memset16(dstC,
1093 cache[toggle + range.fV1],
1094 cache[(toggle ^ Gradient_Shader::kToggleMask16) + range.fV1],
1095 count);
1096 }
1097}
1098
1099void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
1100 uint16_t* SK_RESTRICT dstC,
1101 const uint16_t* SK_RESTRICT cache,
1102 int toggle, int count) {
1103 do {
1104 unsigned fi = mirror_bits(fx >> Gradient_Shader::kCache16Shift,
1105 Gradient_Shader::kCache16Bits);
1106 SkASSERT(fi <= Gradient_Shader::kCache16Mask);
1107 fx += dx;
1108 *dstC++ = cache[toggle + fi];
1109 toggle ^= Gradient_Shader::kToggleMask16;
1110 } while (--count != 0);
1111}
1112
1113void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
1114 uint16_t* SK_RESTRICT dstC,
1115 const uint16_t* SK_RESTRICT cache,
1116 int toggle, int count) {
1117 SkASSERT(proc == repeat_tileproc);
1118 do {
1119 unsigned fi = repeat_bits(fx >> Gradient_Shader::kCache16Shift,
1120 Gradient_Shader::kCache16Bits);
1121 SkASSERT(fi <= Gradient_Shader::kCache16Mask);
1122 fx += dx;
1123 *dstC++ = cache[toggle + fi];
1124 toggle ^= Gradient_Shader::kToggleMask16;
1125 } while (--count != 0);
1126}
1127}
1128
1129void Linear_Gradient::shadeSpan16(int x, int y,
1130 uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 SkASSERT(count > 0);
1132
1133 SkPoint srcPt;
1134 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1135 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001136 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001139 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001140 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1141 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1143
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001144 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 SkFixed dxStorage[1];
1146 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1147 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001148 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1150 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1151 }
1152
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001153 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001154 if (SkFixedNearlyZero(dx)) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001155 shadeProc = shadeSpan16_linear_vertical;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001156 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001157 shadeProc = shadeSpan16_linear_clamp;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001158 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001159 shadeProc = shadeSpan16_linear_mirror;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001160 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001163 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001164 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 SkScalar dstX = SkIntToScalar(x);
1166 SkScalar dstY = SkIntToScalar(y);
1167 do {
1168 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1169 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1170 SkASSERT(fi <= 0xFFFF);
1171
reed@android.com512a8762009-12-14 15:25:36 +00001172 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 *dstC++ = cache[toggle + index];
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001174 toggle ^= Gradient_Shader::kToggleMask16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175
1176 dstX += SK_Scalar1;
1177 } while (--count != 0);
1178 }
1179}
1180
1181///////////////////////////////////////////////////////////////////////////////
1182
1183#define kSQRT_TABLE_BITS 11
1184#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1185
1186#include "SkRadialGradient_Table.h"
1187
1188#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1189
1190#include <stdio.h>
1191
reed@google.com61eb0402011-04-15 12:11:12 +00001192void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1194
1195 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1196 SkASSERT(file);
1197 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1198
reed@google.com61eb0402011-04-15 12:11:12 +00001199 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1200 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001202 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203
1204 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1205
1206 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001207 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001209 }
1210 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 }
1214 ::fprintf(file, "};\n");
1215 ::fclose(file);
1216}
1217
1218#endif
1219
1220
reed@google.com61eb0402011-04-15 12:11:12 +00001221static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1222 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 SkScalar inv = SkScalarInvert(radius);
1224
1225 matrix->setTranslate(-center.fX, -center.fY);
1226 matrix->postScale(inv, inv);
1227}
1228
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001229
1230namespace {
1231
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001232typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
1233 SkScalar sfy, SkScalar sdy,
1234 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001235 int toggle, int count);
1236
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001237void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
1238 SkScalar sfy, SkScalar sdy,
1239 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001240 int toggle, int count) {
1241 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
1242
1243 /* knock these down so we can pin against +- 0x7FFF, which is an
1244 immediate load, rather than 0xFFFF which is slower. This is a
1245 compromise, since it reduces our precision, but that appears
1246 to be visually OK. If we decide this is OK for all of our cases,
1247 we could (it seems) put this scale-down into fDstToIndex,
1248 to avoid having to do these extra shifts each time.
1249 */
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001250 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1251 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1252 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1253 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001254 // might perform this check for the other modes,
1255 // but the win will be a smaller % of the total
1256 if (dy == 0) {
1257 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1258 fy *= fy;
1259 do {
1260 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1261 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1262 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1263 fx += dx;
1264 *dstC++ = cache[toggle +
1265 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
1266 toggle ^= Gradient_Shader::kToggleMask16;
1267 } while (--count != 0);
1268 } else {
1269 do {
1270 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1271 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1272 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1273 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1274 fx += dx;
1275 fy += dy;
1276 *dstC++ = cache[toggle +
1277 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
1278 toggle ^= Gradient_Shader::kToggleMask16;
1279 } while (--count != 0);
1280 }
1281}
1282
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001283void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
1284 SkScalar sfy, SkScalar sdy,
1285 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001286 int toggle, int count) {
1287 do {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001288#ifdef SK_SCALAR_IS_FLOAT
1289 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1290 SkFixed dist = SkFloatToFixed(fdist);
1291#else
1292 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1293 SkFixedSquare(sfy);
1294 if (magnitudeSquared < 0) // Overflow.
1295 magnitudeSquared = SK_FixedMax;
1296 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1297#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001298 unsigned fi = mirror_tileproc(dist);
1299 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001300 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
1301 toggle ^= Gradient_Shader::kToggleMask16;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001302 sfx += sdx;
1303 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001304 } while (--count != 0);
1305}
1306
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001307void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
1308 SkScalar sfy, SkScalar sdy,
1309 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001310 int toggle, int count) {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001311 SkFixed fx = SkScalarToFixed(sfx);
1312 SkFixed dx = SkScalarToFixed(sdx);
1313 SkFixed fy = SkScalarToFixed(sfy);
1314 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001315 do {
1316 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1317 unsigned fi = repeat_tileproc(dist);
1318 SkASSERT(fi <= 0xFFFF);
1319 fx += dx;
1320 fy += dy;
1321 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
1322 toggle ^= Gradient_Shader::kToggleMask16;
1323 } while (--count != 0);
1324}
1325
1326}
1327
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328class Radial_Gradient : public Gradient_Shader {
1329public:
1330 Radial_Gradient(const SkPoint& center, SkScalar radius,
1331 const SkColor colors[], const SkScalar pos[], int colorCount,
1332 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001333 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1334 fCenter(center),
1335 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 {
1337 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1338 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1339
1340 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1341 }
reed@google.com61eb0402011-04-15 12:11:12 +00001342
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001343 virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
1344 SK_OVERRIDE;
1345 virtual void shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
1346 int count) SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 SkASSERT(count > 0);
1348
1349 SkPoint srcPt;
1350 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1351 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001352 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354
reed@android.com3c9b2a42009-08-27 19:28:37 +00001355 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001356 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1357 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001358
1359 SkScalar sdx = fDstToIndex.getScaleX();
1360 SkScalar sdy = fDstToIndex.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361
reed@android.com3c9b2a42009-08-27 19:28:37 +00001362 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 SkFixed storage[2];
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001364 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1365 &storage[0], &storage[1]);
1366 sdx = SkFixedToScalar(storage[0]);
1367 sdy = SkFixedToScalar(storage[1]);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001368 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 }
1371
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001372 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001373 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001374 shadeProc = shadeSpan16_radial_clamp;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001375 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001376 shadeProc = shadeSpan16_radial_mirror;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001377 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 }
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001380 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
1381 cache, toggle, count);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001382 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 SkScalar dstX = SkIntToScalar(x);
1384 SkScalar dstY = SkIntToScalar(y);
1385 do {
1386 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1387 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1388 SkASSERT(fi <= 0xFFFF);
1389
1390 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391 *dstC++ = cache[toggle + index];
1392 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393
1394 dstX += SK_Scalar1;
1395 } while (--count != 0);
1396 }
1397 }
1398
reed@google.com55b8e8c2011-01-13 16:22:35 +00001399 virtual BitmapType asABitmap(SkBitmap* bitmap,
1400 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001401 TileMode* xy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001402 SkScalar* twoPointRadialParams)
1403 const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001404 if (bitmap) {
1405 this->commonAsABitmap(bitmap);
1406 }
1407 if (matrix) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001408 matrix->setScale(SkIntToScalar(kCache32Count),
1409 SkIntToScalar(kCache32Count));
reed@google.comdc731fd2010-12-23 15:19:47 +00001410 matrix->preConcat(fPtsToUnit);
1411 }
1412 if (xy) {
1413 xy[0] = fTileMode;
1414 xy[1] = kClamp_TileMode;
1415 }
1416 return kRadial_BitmapType;
1417 }
reed@google.com7716afb2011-12-07 15:17:50 +00001418 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001419 if (info) {
1420 commonAsAGradient(info);
1421 info->fPoint[0] = fCenter;
1422 info->fRadius[0] = fRadius;
1423 }
1424 return kRadial_GradientType;
1425 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001426
reed@google.com8e6d9142011-12-07 15:30:34 +00001427 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 return SkNEW_ARGS(Radial_Gradient, (buffer));
1429 }
1430
reed@google.com7716afb2011-12-07 15:17:50 +00001431 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001432 this->INHERITED::flatten(buffer);
1433 buffer.writeScalar(fCenter.fX);
1434 buffer.writeScalar(fCenter.fY);
1435 buffer.writeScalar(fRadius);
1436 }
1437
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001439 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1440 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001441 fCenter(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001442 fRadius(buffer.readScalar()) {
1443 }
reed@google.com7716afb2011-12-07 15:17:50 +00001444 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445
1446private:
1447 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001448 const SkPoint fCenter;
1449 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450};
1451
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001452namespace {
1453
1454inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001455 // fast, overly-conservative test: checks unit square instead
1456 // of unit circle
1457 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
1458 (fx <= -SK_FixedHalf && dx <= 0);
1459 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
1460 (fy <= -SK_FixedHalf && dy <= 0);
1461
1462 return xClamped || yClamped;
1463}
1464
1465// Return true if (fx * fy) is always inside the unit circle
1466// SkPin32 is expensive, but so are all the SkFixedMul in this test,
1467// so it shouldn't be run if count is small.
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001468inline bool no_need_for_radial_pin(int fx, int dx,
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001469 int fy, int dy, int count) {
1470 SkASSERT(count > 0);
1471 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1472 return false;
1473 }
1474 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
1475 return false;
1476 }
1477 fx += (count - 1) * dx;
1478 fy += (count - 1) * dy;
1479 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1480 return false;
1481 }
1482 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
1483}
1484
1485#define UNPINNED_RADIAL_STEP \
1486 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001487 *dstC++ = cache[sqrt_table[fi] >> Gradient_Shader::kSqrt32Shift]; \
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001488 fx += dx; \
1489 fy += dy;
1490
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001491typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
1492 SkScalar sfy, SkScalar sdy,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001493 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001494 int count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001495
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001496// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001497void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
1498 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001499 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001500 int count) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001501 // Floating point seems to be slower than fixed point,
1502 // even when we have float hardware.
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001503 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001504 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1505 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1506 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1507 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001508 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001509 sk_memset32(dstC, cache[Gradient_Shader::kCache32Count - 1], count);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001510 } else if ((count > 4) &&
1511 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
1512 unsigned fi;
1513 // 4x unroll appears to be no faster than 2x unroll on Linux
1514 while (count > 1) {
1515 UNPINNED_RADIAL_STEP;
1516 UNPINNED_RADIAL_STEP;
1517 count -= 2;
1518 }
1519 if (count) {
1520 UNPINNED_RADIAL_STEP;
1521 }
1522 }
1523 else {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001524 // Specializing for dy == 0 gains us 25% on Skia benchmarks
1525 if (dy == 0) {
1526 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1527 yy *= yy;
1528 do {
1529 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1530 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
1531 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1532 *dstC++ = cache[sqrt_table[fi] >>
1533 Gradient_Shader::kSqrt32Shift];
1534 fx += dx;
1535 } while (--count != 0);
1536 } else {
1537 do {
1538 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1539 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1540 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1541 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1542 *dstC++ = cache[sqrt_table[fi] >>
1543 Gradient_Shader::kSqrt32Shift];
1544 fx += dx;
1545 fy += dy;
1546 } while (--count != 0);
1547 }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001548 }
1549}
1550
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001551// Unrolling this loop doesn't seem to help (when float); we're stalling to
1552// get the results of the sqrt (?), and don't have enough extra registers to
1553// have many in flight.
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001554void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
1555 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001556 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001557 int count) {
1558 do {
tomhudson@google.comf925ba42012-01-11 21:54:26 +00001559#ifdef SK_SCALAR_IS_FLOAT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001560 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1561 SkFixed dist = SkFloatToFixed(fdist);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001562#else
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001563 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1564 SkFixedSquare(sfy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001565 if (magnitudeSquared < 0) // Overflow.
1566 magnitudeSquared = SK_FixedMax;
1567 SkFixed dist = SkFixedSqrt(magnitudeSquared);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001568#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001569 unsigned fi = mirror_tileproc(dist);
1570 SkASSERT(fi <= 0xFFFF);
1571 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001572 sfx += sdx;
1573 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001574 } while (--count != 0);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001575}
1576
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001577void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
1578 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001579 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001580 int count) {
1581 SkFixed fx = SkScalarToFixed(sfx);
1582 SkFixed dx = SkScalarToFixed(sdx);
1583 SkFixed fy = SkScalarToFixed(sfy);
1584 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001585 do {
1586 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1587 SkFixedSquare(fy);
1588 if (magnitudeSquared < 0) // Overflow.
1589 magnitudeSquared = SK_FixedMax;
1590 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1591 unsigned fi = repeat_tileproc(dist);
1592 SkASSERT(fi <= 0xFFFF);
1593 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
1594 fx += dx;
1595 fy += dy;
1596 } while (--count != 0);
1597}
1598}
1599
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001600void Radial_Gradient::shadeSpan(int x, int y,
1601 SkPMColor* SK_RESTRICT dstC, int count) {
1602 SkASSERT(count > 0);
1603
1604 SkPoint srcPt;
1605 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1606 TileProc proc = fTileProc;
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001607 const SkPMColor* SK_RESTRICT cache = this->getCache32();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001608
1609 if (fDstToIndexClass != kPerspective_MatrixClass) {
1610 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1611 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001612 SkScalar sdx = fDstToIndex.getScaleX();
1613 SkScalar sdy = fDstToIndex.getSkewY();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001614
1615 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1616 SkFixed storage[2];
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001617 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1618 &storage[0], &storage[1]);
1619 sdx = SkFixedToScalar(storage[0]);
1620 sdy = SkFixedToScalar(storage[1]);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001621 } else {
1622 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001623 }
1624
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001625 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001626 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001627 shadeProc = shadeSpan_radial_clamp;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001628 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001629 shadeProc = shadeSpan_radial_mirror;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001630 } else {
1631 SkASSERT(proc == repeat_tileproc);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001632 }
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001633 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001634 } else { // perspective case
1635 SkScalar dstX = SkIntToScalar(x);
1636 SkScalar dstY = SkIntToScalar(y);
1637 do {
1638 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1639 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1640 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001641 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001642 dstX += SK_Scalar1;
1643 } while (--count != 0);
1644 }
1645}
1646
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001647/* Two-point radial gradients are specified by two circles, each with a center
1648 point and radius. The gradient can be considered to be a series of
1649 concentric circles, with the color interpolated from the start circle
1650 (at t=0) to the end circle (at t=1).
1651
1652 For each point (x, y) in the span, we want to find the
1653 interpolated circle that intersects that point. The center
1654 of the desired circle (Cx, Cy) falls at some distance t
1655 along the line segment between the start point (Sx, Sy) and
1656 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001657
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001658 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1659 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001660
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001661 The radius of the desired circle (r) is also a linear interpolation t
1662 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001663
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001664 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001665
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001666 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001667
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001668 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001669
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001670 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001671
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001672 (x - ((1 - t) * Sx + t * Ex))^2
1673 + (y - ((1 - t) * Sy + t * Ey))^2
1674 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001675
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001676 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001677
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001678 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1679 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1680 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001681
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001682 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1683
1684 [Dx^2 + Dy^2 - Dr^2)] * t^2
1685 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1686 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001687
1688 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001689 possible circles on which the point may fall. Solving for t yields
1690 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001691
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001692 If a<0, the start circle is entirely contained in the
1693 end circle, and one of the roots will be <0 or >1 (off the line
1694 segment). If a>0, the start circle falls at least partially
1695 outside the end circle (or vice versa), and the gradient
1696 defines a "tube" where a point may be on one circle (on the
1697 inside of the tube) or the other (outside of the tube). We choose
1698 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001699
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001700 In order to keep the math to within the limits of fixed point,
1701 we divide the entire quadratic by Dr^2, and replace
1702 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001703
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001704 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1705 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1706 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001707
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001708 (x' and y' are computed by appending the subtract and scale to the
1709 fDstToIndex matrix in the constructor).
1710
1711 Since the 'A' component of the quadratic is independent of x' and y', it
1712 is precomputed in the constructor. Since the 'B' component is linear in
1713 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001714 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001715 a perspective projection), it must be computed in the loop.
1716
1717*/
1718
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001719namespace {
1720
1721inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1722 SkScalar sr2d2, SkScalar foura,
1723 SkScalar oneOverTwoA, bool posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001724 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001725 if (0 == foura) {
1726 return SkScalarToFixed(SkScalarDiv(-c, b));
1727 }
1728
reed@google.com84e9c082011-04-13 17:44:24 +00001729 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001730 if (discrim < 0) {
1731 discrim = -discrim;
1732 }
reed@google.com84e9c082011-04-13 17:44:24 +00001733 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1734 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001735 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001736 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001737 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001738 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001739 }
reed@google.com84e9c082011-04-13 17:44:24 +00001740 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001741}
1742
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001743typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
1744 SkScalar fy, SkScalar dy,
1745 SkScalar b, SkScalar db,
1746 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001747 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001748 int count);
1749
1750void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
1751 SkScalar fy, SkScalar dy,
1752 SkScalar b, SkScalar db,
1753 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001754 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001755 int count) {
1756 for (; count > 0; --count) {
1757 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1758 fOneOverTwoA, posRoot);
1759 SkFixed index = SkClampMax(t, 0xFFFF);
1760 SkASSERT(index <= 0xFFFF);
1761 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1762 fx += dx;
1763 fy += dy;
1764 b += db;
1765 }
1766}
1767void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
1768 SkScalar fy, SkScalar dy,
1769 SkScalar b, SkScalar db,
1770 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001771 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001772 int count) {
1773 for (; count > 0; --count) {
1774 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1775 fOneOverTwoA, posRoot);
1776 SkFixed index = mirror_tileproc(t);
1777 SkASSERT(index <= 0xFFFF);
1778 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1779 fx += dx;
1780 fy += dy;
1781 b += db;
1782 }
1783}
1784
1785void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
1786 SkScalar fy, SkScalar dy,
1787 SkScalar b, SkScalar db,
1788 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001789 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001790 int count) {
1791 for (; count > 0; --count) {
1792 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1793 fOneOverTwoA, posRoot);
1794 SkFixed index = repeat_tileproc(t);
1795 SkASSERT(index <= 0xFFFF);
1796 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1797 fx += dx;
1798 fy += dy;
1799 b += db;
1800 }
1801}
1802
1803
1804
1805}
1806
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001807class Two_Point_Radial_Gradient : public Gradient_Shader {
1808public:
1809 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1810 const SkPoint& end, SkScalar endRadius,
1811 const SkColor colors[], const SkScalar pos[],
1812 int colorCount, SkShader::TileMode mode,
1813 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001814 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1815 fCenter1(start),
1816 fCenter2(end),
1817 fRadius1(startRadius),
1818 fRadius2(endRadius) {
1819 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001820 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001821
1822 virtual BitmapType asABitmap(SkBitmap* bitmap,
1823 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001824 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001825 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001826 if (bitmap) {
1827 this->commonAsABitmap(bitmap);
1828 }
1829 SkScalar diffL = 0; // just to avoid gcc warning
1830 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001831 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001832 SkScalarSquare(fDiff.fY));
1833 }
1834 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001835 if (diffL) {
1836 SkScalar invDiffL = SkScalarInvert(diffL);
1837 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1838 SkScalarMul(invDiffL, fDiff.fX));
1839 } else {
1840 matrix->reset();
1841 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001842 matrix->preConcat(fPtsToUnit);
1843 }
1844 if (xy) {
1845 xy[0] = fTileMode;
1846 xy[1] = kClamp_TileMode;
1847 }
1848 if (NULL != twoPointRadialParams) {
1849 twoPointRadialParams[0] = diffL;
1850 twoPointRadialParams[1] = fStartRadius;
1851 twoPointRadialParams[2] = fDiffRadius;
1852 }
1853 return kTwoPointRadial_BitmapType;
1854 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001855
reed@google.com8e6d9142011-12-07 15:30:34 +00001856 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001857 if (info) {
1858 commonAsAGradient(info);
1859 info->fPoint[0] = fCenter1;
1860 info->fPoint[1] = fCenter2;
1861 info->fRadius[0] = fRadius1;
1862 info->fRadius[1] = fRadius2;
1863 }
1864 return kRadial2_GradientType;
1865 }
1866
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001867 virtual void shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
1868 int count) SK_OVERRIDE {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001869 SkASSERT(count > 0);
1870
1871 // Zero difference between radii: fill with transparent black.
1872 if (fDiffRadius == 0) {
1873 sk_bzero(dstC, count * sizeof(*dstC));
1874 return;
1875 }
1876 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1877 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001878 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001879
1880 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001881 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001882 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001883 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001884 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1885 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001886 SkScalar dx, fx = srcPt.fX;
1887 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001888
reed@google.com61eb0402011-04-15 12:11:12 +00001889 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001890 SkFixed fixedX, fixedY;
1891 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1892 dx = SkFixedToScalar(fixedX);
1893 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001894 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001895 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001896 dx = fDstToIndex.getScaleX();
1897 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001898 }
reed@google.com84e9c082011-04-13 17:44:24 +00001899 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1900 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1901 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1902 SkScalarMul(fDiff.fY, dy)) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001903
1904 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
reed@google.com61eb0402011-04-15 12:11:12 +00001905 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001906 shadeProc = shadeSpan_twopoint_clamp;
reed@google.com61eb0402011-04-15 12:11:12 +00001907 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001908 shadeProc = shadeSpan_twopoint_mirror;
reed@google.com61eb0402011-04-15 12:11:12 +00001909 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001910 SkASSERT(proc == repeat_tileproc);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001911 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001912 (*shadeProc)(fx, dx, fy, dy, b, db,
1913 fSr2D2, foura, fOneOverTwoA, posRoot,
1914 dstC, cache, count);
reed@google.com61eb0402011-04-15 12:11:12 +00001915 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001916 SkScalar dstX = SkIntToScalar(x);
1917 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001918 for (; count > 0; --count) {
1919 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001920 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001921 SkScalar fx = srcPt.fX;
1922 SkScalar fy = srcPt.fY;
1923 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1924 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001925 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1926 fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001927 SkFixed index = proc(t);
1928 SkASSERT(index <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001929 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
reed@android.com6c59a172009-09-22 20:24:05 +00001930 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001931 }
1932 }
1933 }
1934
reed@android.com6c59a172009-09-22 20:24:05 +00001935 virtual bool setContext(const SkBitmap& device,
1936 const SkPaint& paint,
reed@google.com8e6d9142011-12-07 15:30:34 +00001937 const SkMatrix& matrix) SK_OVERRIDE {
1938 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com6c59a172009-09-22 20:24:05 +00001939 return false;
1940 }
1941
1942 // we don't have a span16 proc
1943 fFlags &= ~kHasSpan16_Flag;
1944 return true;
1945 }
1946
reed@google.com8e6d9142011-12-07 15:30:34 +00001947 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001948 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1949 }
1950
reed@google.com7716afb2011-12-07 15:17:50 +00001951 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
reed@android.combcfc7332009-11-10 16:19:39 +00001952 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001953 buffer.writeScalar(fCenter1.fX);
1954 buffer.writeScalar(fCenter1.fY);
1955 buffer.writeScalar(fCenter2.fX);
1956 buffer.writeScalar(fCenter2.fY);
1957 buffer.writeScalar(fRadius1);
1958 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001959 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001960
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001961protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001962 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001963 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001964 fCenter1(unflatten_point(buffer)),
1965 fCenter2(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001966 fRadius1(buffer.readScalar()),
1967 fRadius2(buffer.readScalar()) {
1968 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001969 };
reed@google.com7716afb2011-12-07 15:17:50 +00001970 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001971
1972private:
1973 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001974 const SkPoint fCenter1;
1975 const SkPoint fCenter2;
1976 const SkScalar fRadius1;
1977 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001978 SkPoint fDiff;
1979 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001980
1981 void init() {
1982 fDiff = fCenter1 - fCenter2;
1983 fDiffRadius = fRadius2 - fRadius1;
1984 SkScalar inv = SkScalarInvert(fDiffRadius);
1985 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1986 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1987 fStartRadius = SkScalarMul(fRadius1, inv);
1988 fSr2D2 = SkScalarSquare(fStartRadius);
1989 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00001990 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001991
1992 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1993 fPtsToUnit.postScale(inv, inv);
1994 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001995};
1996
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997///////////////////////////////////////////////////////////////////////////////
1998
1999class Sweep_Gradient : public Gradient_Shader {
2000public:
2001 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
2002 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002003 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
2004 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005 {
2006 fPtsToUnit.setTranslate(-cx, -cy);
2007 }
reed@google.com7716afb2011-12-07 15:17:50 +00002008 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
2009 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002010
2011 virtual BitmapType asABitmap(SkBitmap* bitmap,
2012 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00002013 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00002014 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00002015 if (bitmap) {
2016 this->commonAsABitmap(bitmap);
2017 }
2018 if (matrix) {
2019 *matrix = fPtsToUnit;
2020 }
2021 if (xy) {
2022 xy[0] = fTileMode;
2023 xy[1] = kClamp_TileMode;
2024 }
2025 return kSweep_BitmapType;
2026 }
2027
reed@google.com7716afb2011-12-07 15:17:50 +00002028 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002029 if (info) {
2030 commonAsAGradient(info);
2031 info->fPoint[0] = fCenter;
2032 }
2033 return kSweep_GradientType;
2034 }
2035
reed@google.com8e6d9142011-12-07 15:30:34 +00002036 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037 return SkNEW_ARGS(Sweep_Gradient, (buffer));
2038 }
2039
reed@google.com7716afb2011-12-07 15:17:50 +00002040 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002041 this->INHERITED::flatten(buffer);
2042 buffer.writeScalar(fCenter.fX);
2043 buffer.writeScalar(fCenter.fY);
2044 }
2045
reed@android.com8a1c16f2008-12-17 15:59:43 +00002046protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002047 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
2048 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00002049 fCenter(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002050 }
2051
reed@google.com7716afb2011-12-07 15:17:50 +00002052 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002053
2054private:
2055 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002056 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002057};
2058
2059#ifdef COMPUTE_SWEEP_TABLE
2060#define PI 3.14159265
2061static bool gSweepTableReady;
2062static uint8_t gSweepTable[65];
2063
2064/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
2065 We scale the results to [0..32]
2066*/
reed@google.com61eb0402011-04-15 12:11:12 +00002067static const uint8_t* build_sweep_table() {
2068 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069 const int N = 65;
2070 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002071
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 for (int i = 0; i < N; i++)
2073 {
2074 double arg = i / DENOM;
2075 double v = atan(arg);
2076 int iv = (int)round(v * DENOM * 2 / PI);
2077// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
2078 printf("%d, ", iv);
2079 gSweepTable[i] = iv;
2080 }
2081 gSweepTableReady = true;
2082 }
2083 return gSweepTable;
2084}
2085#else
2086static const uint8_t gSweepTable[] = {
2087 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
2088 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
2089 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
2090 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
2091 32
2092};
2093static const uint8_t* build_sweep_table() { return gSweepTable; }
2094#endif
2095
2096// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
2097// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
2098// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
2099
2100//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00002101static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102 SkASSERT(numer <= denom);
2103 SkASSERT(numer > 0);
2104 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002105
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106 int nbits = SkCLZ(numer);
2107 int dbits = SkCLZ(denom);
2108 int bits = 6 - nbits + dbits;
2109 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002110
reed@google.com61eb0402011-04-15 12:11:12 +00002111 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002113 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114
2115 denom <<= dbits - 1;
2116 numer <<= nbits - 1;
2117
2118 unsigned result = 0;
2119
2120 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00002121 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00002123 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00002125 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002126
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00002128 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002129 // make room for the rest of the answer bits
2130 result <<= bits;
2131 switch (bits) {
2132 case 6:
2133 if ((numer = (numer << 1) - denom) >= 0)
2134 result |= 32;
2135 else
2136 numer += denom;
2137 case 5:
2138 if ((numer = (numer << 1) - denom) >= 0)
2139 result |= 16;
2140 else
2141 numer += denom;
2142 case 4:
2143 if ((numer = (numer << 1) - denom) >= 0)
2144 result |= 8;
2145 else
2146 numer += denom;
2147 case 3:
2148 if ((numer = (numer << 1) - denom) >= 0)
2149 result |= 4;
2150 else
2151 numer += denom;
2152 case 2:
2153 if ((numer = (numer << 1) - denom) >= 0)
2154 result |= 2;
2155 else
2156 numer += denom;
2157 case 1:
2158 default: // not strictly need, but makes GCC make better ARM code
2159 if ((numer = (numer << 1) - denom) >= 0)
2160 result |= 1;
2161 else
2162 numer += denom;
2163 }
2164 }
2165 return result;
2166}
2167
2168// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00002169static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170#ifdef SK_DEBUG
2171 {
2172 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00002173 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 gOnce = true;
2175 SkASSERT(div_64(55, 55) == 64);
2176 SkASSERT(div_64(128, 256) == 32);
2177 SkASSERT(div_64(2326528, 4685824) == 31);
2178 SkASSERT(div_64(753664, 5210112) == 9);
2179 SkASSERT(div_64(229376, 4882432) == 3);
2180 SkASSERT(div_64(2, 64) == 2);
2181 SkASSERT(div_64(1, 64) == 1);
2182 // test that we handle underflow correctly
2183 SkASSERT(div_64(12345, 0x54321234) == 0);
2184 }
2185 }
2186#endif
2187
2188 SkASSERT(y > 0 && x > 0);
2189 const uint8_t* table = build_sweep_table();
2190
2191 unsigned result;
2192 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00002193 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 // first part of the atan(v) = PI/2 - atan(1/v) identity
2195 // since our div_64 and table want v <= 1, where v = y/x
2196 SkTSwap<SkFixed>(x, y);
2197 }
2198
2199 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002200
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201#ifdef SK_DEBUG
2202 {
2203 unsigned result2 = SkDivBits(y, x, 6);
2204 SkASSERT(result2 == result ||
2205 (result == 1 && result2 == 0));
2206 }
2207#endif
2208
2209 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2210 result = table[result];
2211
reed@google.com61eb0402011-04-15 12:11:12 +00002212 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213 // complete the atan(v) = PI/2 - atan(1/v) identity
2214 result = 64 - result;
2215 // pin to 63
2216 result -= result >> 6;
2217 }
2218
2219 SkASSERT(result <= 63);
2220 return result;
2221}
2222
2223// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com51baf5a2011-09-21 13:38:36 +00002224#ifdef SK_SCALAR_IS_FLOAT
2225static unsigned SkATan2_255(float y, float x) {
2226 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
2227 static const float g255Over2PI = 40.584510488433314f;
2228
2229 float result = sk_float_atan2(y, x);
2230 if (result < 0) {
2231 result += 2 * SK_ScalarPI;
2232 }
2233 SkASSERT(result >= 0);
2234 // since our value is always >= 0, we can cast to int, which is faster than
2235 // calling floorf()
2236 int ir = (int)(result * g255Over2PI);
2237 SkASSERT(ir >= 0 && ir <= 255);
2238 return ir;
2239}
2240#else
reed@google.com61eb0402011-04-15 12:11:12 +00002241static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2242 if (x == 0) {
2243 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246 return y < 0 ? 192 : 64;
2247 }
reed@google.com61eb0402011-04-15 12:11:12 +00002248 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002250 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002251
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 /* Find the right quadrant for x,y
2253 Since atan_0_90 only handles the first quadrant, we rotate x,y
2254 appropriately before calling it, and then add the right amount
2255 to account for the real quadrant.
2256 quadrant 0 : add 0 | x > 0 && y > 0
2257 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2258 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2259 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002260
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 map x<0 to (1 << 6)
2262 map y<0 to (3 << 6)
2263 add = map_x ^ map_y
2264 */
2265 int xsign = x >> 31;
2266 int ysign = y >> 31;
2267 int add = ((-xsign) ^ (ysign & 3)) << 6;
2268
2269#ifdef SK_DEBUG
2270 if (0 == add)
2271 SkASSERT(x > 0 && y > 0);
2272 else if (64 == add)
2273 SkASSERT(x < 0 && y > 0);
2274 else if (128 == add)
2275 SkASSERT(x < 0 && y < 0);
2276 else if (192 == add)
2277 SkASSERT(x > 0 && y < 0);
2278 else
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002279 SkDEBUGFAIL("bad value for add");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002281
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2283 where we need to rotate x,y by 90 or -90
2284 */
2285 x = (x ^ xsign) - xsign;
2286 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002287 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002289 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290
2291 unsigned result = add + atan_0_90(y, x);
2292 SkASSERT(result < 256);
2293 return result;
2294}
reed@google.com51baf5a2011-09-21 13:38:36 +00002295#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002296
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002297void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
2298 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002299 SkMatrix::MapXYProc proc = fDstToIndexProc;
2300 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002301 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002303
reed@google.com61eb0402011-04-15 12:11:12 +00002304 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2306 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002307 SkScalar dx, fx = srcPt.fX;
2308 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002309
reed@google.com61eb0402011-04-15 12:11:12 +00002310 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 SkFixed storage[2];
2312 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2313 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002314 dx = SkFixedToScalar(storage[0]);
2315 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002316 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002317 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002318 dx = matrix.getScaleX();
2319 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002321
reed@google.com61eb0402011-04-15 12:11:12 +00002322 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002323 *dstC++ = cache[SkATan2_255(fy, fx)];
2324 fx += dx;
2325 fy += dy;
2326 }
reed@google.com61eb0402011-04-15 12:11:12 +00002327 } else { // perspective case
2328 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com51baf5a2011-09-21 13:38:36 +00002330 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2331 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002332 }
2333 }
2334}
2335
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002336void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
2337 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338 SkMatrix::MapXYProc proc = fDstToIndexProc;
2339 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002340 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002341 int toggle = ((x ^ y) & 1) << kCache16Bits;
2342 SkPoint srcPt;
2343
reed@google.com61eb0402011-04-15 12:11:12 +00002344 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2346 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002347 SkScalar dx, fx = srcPt.fX;
2348 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002349
reed@google.com61eb0402011-04-15 12:11:12 +00002350 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 SkFixed storage[2];
2352 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2353 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002354 dx = SkFixedToScalar(storage[0]);
2355 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002356 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002358 dx = matrix.getScaleX();
2359 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002361
reed@google.com61eb0402011-04-15 12:11:12 +00002362 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002363 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2364 *dstC++ = cache[toggle + index];
2365 toggle ^= (1 << kCache16Bits);
2366 fx += dx;
2367 fy += dy;
2368 }
reed@google.com61eb0402011-04-15 12:11:12 +00002369 } else { // perspective case
2370 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2372 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002373
reed@google.com51baf5a2011-09-21 13:38:36 +00002374 int index = SkATan2_255(srcPt.fY, srcPt.fX);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002375 index >>= (8 - kCache16Bits);
2376 *dstC++ = cache[toggle + index];
2377 toggle ^= (1 << kCache16Bits);
2378 }
2379 }
2380}
2381
reed@google.com61eb0402011-04-15 12:11:12 +00002382///////////////////////////////////////////////////////////////////////////////
2383///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384
2385// assumes colors is SkColor* and pos is SkScalar*
2386#define EXPAND_1_COLOR(count) \
2387 SkColor tmp[2]; \
2388 do { \
2389 if (1 == count) { \
2390 tmp[0] = tmp[1] = colors[0]; \
2391 colors = tmp; \
2392 pos = NULL; \
2393 count = 2; \
2394 } \
2395 } while (0)
2396
reed@google.com61eb0402011-04-15 12:11:12 +00002397SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2398 const SkColor colors[],
2399 const SkScalar pos[], int colorCount,
2400 SkShader::TileMode mode,
2401 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402 if (NULL == pts || NULL == colors || colorCount < 1) {
2403 return NULL;
2404 }
2405 EXPAND_1_COLOR(colorCount);
2406
reed@android.comab840b82009-07-01 17:00:03 +00002407 return SkNEW_ARGS(Linear_Gradient,
2408 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409}
2410
reed@google.com61eb0402011-04-15 12:11:12 +00002411SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2412 const SkColor colors[],
2413 const SkScalar pos[], int colorCount,
2414 SkShader::TileMode mode,
2415 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 if (radius <= 0 || NULL == colors || colorCount < 1) {
2417 return NULL;
2418 }
2419 EXPAND_1_COLOR(colorCount);
2420
reed@android.comab840b82009-07-01 17:00:03 +00002421 return SkNEW_ARGS(Radial_Gradient,
2422 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423}
2424
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002425SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2426 SkScalar startRadius,
2427 const SkPoint& end,
2428 SkScalar endRadius,
2429 const SkColor colors[],
2430 const SkScalar pos[],
2431 int colorCount,
2432 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002433 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002434 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2435 return NULL;
2436 }
2437 EXPAND_1_COLOR(colorCount);
2438
2439 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002440 (start, startRadius, end, endRadius, colors, pos,
2441 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002442}
2443
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2445 const SkColor colors[],
2446 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002447 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448 if (NULL == colors || count < 1) {
2449 return NULL;
2450 }
2451 EXPAND_1_COLOR(count);
2452
2453 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2454}
2455
caryclark@google.comd26147a2011-12-15 14:16:43 +00002456SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
2457 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Linear_Gradient)
2458 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Radial_Gradient)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459
caryclark@google.comd26147a2011-12-15 14:16:43 +00002460 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sweep_Gradient)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002461
caryclark@google.comd26147a2011-12-15 14:16:43 +00002462 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Radial_Gradient)
2463SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END