blob: 8c6bcfb906648eddcbdf93097b5de42384632b10 [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 {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000122 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
123 /// it, use a larger cache.
124 kCache16Bits = 8,
125 kGradient16Length = (1 << kCache16Bits),
126 /// Each cache gets 1 extra entry at the end so we don't have to
127 /// test for end-of-cache in lerps. This is also the value used
128 /// to stride *writes* into the dither cache; it must not be zero.
129 /// Total space for a cache is 2x kCache16Count entries: one
130 /// regular cache, one for dithering.
131 kCache16Count = kGradient16Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000132 kCache16Shift = 16 - kCache16Bits,
133 kSqrt16Shift = 8 - kCache16Bits,
134
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000135 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
136 /// it, use a larger cache.
137 kCache32Bits = 8,
138 kGradient32Length = (1 << kCache32Bits),
139 /// Each cache gets 1 extra entry at the end so we don't have to
140 /// test for end-of-cache in lerps. This is also the value used
141 /// to stride *writes* into the dither cache; it must not be zero.
142 /// Total space for a cache is 2x kCache32Count entries: one
143 /// regular cache, one for dithering.
144 kCache32Count = kGradient32Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000145 kCache32Shift = 16 - kCache32Bits,
146 kSqrt32Shift = 8 - kCache32Bits,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000147
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000148 /// This value is used to *read* the dither cache; it may be 0
149 /// if dithering is disabled.
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000150#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000151 kDitherStride32 = kCache32Count,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000152#else
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000153 kDitherStride32 = 0,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000154#endif
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000155 kDitherStride16 = kCache16Count,
156 kLerpRemainderMask32 = (1 << (16 - kCache32Bits)) - 1
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000157 };
158
159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160protected:
161 Gradient_Shader(SkFlattenableReadBuffer& );
162 SkUnitMapper* fMapper;
163 SkMatrix fPtsToUnit; // set by subclass
164 SkMatrix fDstToIndex;
165 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 TileMode fTileMode;
167 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000168 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 uint8_t fDstToIndexClass;
170 uint8_t fFlags;
171
172 struct Rec {
173 SkFixed fPos; // 0...1
174 uint32_t fScale; // (1 << 24) / range
175 };
176 Rec* fRecs;
177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000179 const uint16_t* getCache16() const;
180 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181
reed@google.com7c2f27d2011-03-07 19:29:00 +0000182 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000183 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185private:
186 enum {
187 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
188
reed@android.com1c12abe2009-07-02 15:01:02 +0000189 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 };
191 SkColor fStorage[(kStorageSize + 3) >> 2];
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000192 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
193 bool fColorsAreOpaque;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
reed@google.com7c2f27d2011-03-07 19:29:00 +0000195 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
196 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197
reed@google.com7c2f27d2011-03-07 19:29:00 +0000198 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
199 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000200 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 +0000201
reed@android.com512a8762009-12-14 15:25:36 +0000202 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000203 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
204 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000205 void setCacheAlpha(U8CPU alpha) const;
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000206 void initCommon();
reed@android.com512a8762009-12-14 15:25:36 +0000207
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 typedef SkShader INHERITED;
209};
210
reed@android.com41bccf52009-04-03 13:33:51 +0000211static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 SkASSERT(x >= 0 && x <= SK_Scalar1);
213
214#ifdef SK_SCALAR_IS_FLOAT
215 return (unsigned)(x * 0xFFFF);
216#else
217 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
218#endif
219}
220
reed@android.com41bccf52009-04-03 13:33:51 +0000221Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
222 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 SkASSERT(colorCount > 1);
224
225 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
226
227 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000228 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
231 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
232 fTileMode = mode;
233 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000234
reed@android.com41bccf52009-04-03 13:33:51 +0000235 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000236 fCache32 = NULL;
237 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238
reed@android.com41bccf52009-04-03 13:33:51 +0000239 /* Note: we let the caller skip the first and/or last position.
240 i.e. pos[0] = 0.3, pos[1] = 0.7
241 In these cases, we insert dummy entries to ensure that the final data
242 will be bracketed by [0, 1].
243 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
244
245 Thus colorCount (the caller's value, and fColorCount (our value) may
246 differ by up to 2. In the above example:
247 colorCount = 2
248 fColorCount = 4
249 */
250 fColorCount = colorCount;
251 // check if we need to add in dummy start and/or end position/colors
252 bool dummyFirst = false;
253 bool dummyLast = false;
254 if (pos) {
255 dummyFirst = pos[0] != 0;
256 dummyLast = pos[colorCount - 1] != SK_Scalar1;
257 fColorCount += dummyFirst + dummyLast;
258 }
259
260 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000261 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000262 fOrigColors = reinterpret_cast<SkColor*>(
263 sk_malloc_throw(size * fColorCount));
264 }
265 else {
266 fOrigColors = fStorage;
267 }
268
269 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 {
reed@android.com41bccf52009-04-03 13:33:51 +0000271 SkColor* origColors = fOrigColors;
272 if (dummyFirst) {
273 *origColors++ = colors[0];
274 }
275 memcpy(origColors, colors, colorCount * sizeof(SkColor));
276 if (dummyLast) {
277 origColors += colorCount;
278 *origColors = colors[colorCount - 1];
279 }
280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281
reed@android.com1c12abe2009-07-02 15:01:02 +0000282 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000283 if (fColorCount > 2) {
284 Rec* recs = fRecs;
285 recs->fPos = 0;
286 // recs->fScale = 0; // unused;
287 recs += 1;
288 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 /* We need to convert the user's array of relative positions into
290 fixed-point positions and scale factors. We need these results
291 to be strictly monotonic (no two values equal or out of order).
292 Hence this complex loop that just jams a zero for the scale
293 value if it sees a segment out of order, and it assures that
294 we start at 0 and end at 1.0
295 */
296 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000297 int startIndex = dummyFirst ? 0 : 1;
298 int count = colorCount + dummyLast;
299 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 // force the last value to be 1.0
301 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000302 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000304 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 }
reed@android.com41bccf52009-04-03 13:33:51 +0000307 // pin curr withing range
308 if (curr < 0) {
309 curr = 0;
310 } else if (curr > SK_Fixed1) {
311 curr = SK_Fixed1;
312 }
313 recs->fPos = curr;
314 if (curr > prev) {
315 recs->fScale = (1 << 24) / (curr - prev);
316 } else {
317 recs->fScale = 0; // ignore this segment
318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 // get ready for the next value
320 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000321 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 }
reed@android.com41bccf52009-04-03 13:33:51 +0000323 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 SkFixed dp = SK_Fixed1 / (colorCount - 1);
325 SkFixed p = dp;
326 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000327 for (int i = 1; i < colorCount; i++) {
328 recs->fPos = p;
329 recs->fScale = scale;
330 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 p += dp;
332 }
333 }
334 }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000335 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336}
337
338Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000339 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 fCacheAlpha = 256;
341
342 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
343
344 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000345 fCache32 = NULL;
346 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
reed@android.com41bccf52009-04-03 13:33:51 +0000348 int colorCount = fColorCount = buffer.readU32();
349 if (colorCount > kColorStorageCount) {
350 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
351 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
352 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000354 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356
357 fTileMode = (TileMode)buffer.readU8();
358 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000359 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 if (colorCount > 2) {
361 Rec* recs = fRecs;
362 recs[0].fPos = 0;
363 for (int i = 1; i < colorCount; i++) {
364 recs[i].fPos = buffer.readS32();
365 recs[i].fScale = buffer.readU32();
366 }
367 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000368 SkReadMatrix(&buffer, &fPtsToUnit);
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000369 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370}
371
reed@android.com41bccf52009-04-03 13:33:51 +0000372Gradient_Shader::~Gradient_Shader() {
373 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000375 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000376 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000377 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000379 }
reed@google.com82065d62011-02-07 15:30:46 +0000380 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381}
382
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000383void Gradient_Shader::initCommon() {
384 fFlags = 0;
385 unsigned colorAlpha = 0xFF;
386 for (int i = 0; i < fColorCount; i++) {
387 colorAlpha &= SkColorGetA(fOrigColors[i]);
388 }
389 fColorsAreOpaque = colorAlpha == 0xFF;
390}
391
reed@android.com41bccf52009-04-03 13:33:51 +0000392void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 this->INHERITED::flatten(buffer);
394 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000395 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
397 buffer.write8(fTileMode);
398 if (fColorCount > 2) {
399 Rec* recs = fRecs;
400 for (int i = 1; i < fColorCount; i++) {
401 buffer.write32(recs[i].fPos);
402 buffer.write32(recs[i].fScale);
403 }
404 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000405 SkWriteMatrix(&buffer, fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406}
407
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000408bool Gradient_Shader::isOpaque() const {
409 return fColorsAreOpaque;
410}
411
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412bool Gradient_Shader::setContext(const SkBitmap& device,
413 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000414 const SkMatrix& matrix) {
415 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000417 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418
419 const SkMatrix& inverse = this->getTotalInverse();
420
421 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
422 return false;
423 }
424
425 fDstToIndexProc = fDstToIndex.getMapXYProc();
426 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
427
428 // now convert our colors in to PMColors
429 unsigned paintAlpha = this->getPaintAlpha();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430
431 fFlags = this->INHERITED::getFlags();
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000432 if (fColorsAreOpaque && paintAlpha == 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 fFlags |= kOpaqueAlpha_Flag;
434 }
435 // we can do span16 as long as our individual colors are opaque,
436 // regardless of the paint's alpha
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000437 if (fColorsAreOpaque) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 fFlags |= kHasSpan16_Flag;
439 }
440
reed@google.com95eed982011-07-05 17:01:56 +0000441 this->setCacheAlpha(paintAlpha);
442 return true;
443}
444
445void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 // if the new alpha differs from the previous time we were called, inval our cache
447 // this will trigger the cache to be rebuilt.
448 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000449 if (fCacheAlpha != alpha) {
450 fCache16 = NULL; // inval the cache
451 fCache32 = NULL; // inval the cache
452 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000453 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000454 if (fCache32PixelRef) {
455 fCache32PixelRef->notifyPixelsChanged();
456 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458}
459
reed@android.com41bccf52009-04-03 13:33:51 +0000460static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 SkASSERT(a == SkToU8(a));
462 SkASSERT(b == SkToU8(b));
463 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 return a + ((b - a) * scale >> 8);
465}
466
reed@android.com41bccf52009-04-03 13:33:51 +0000467static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
468 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469#if 0
470 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
471 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
472 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
473 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
474
475 return SkPackARGB32(a, r, g, b);
476#else
477 int otherBlend = 256 - blend;
478
479#if 0
480 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
481 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
482 SkASSERT((t0 & t1) == 0);
483 return t0 | t1;
484#else
485 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
486 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
487#endif
488
489#endif
490}
491
492#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
493
reed@android.com41bccf52009-04-03 13:33:51 +0000494/** We take the original colors, not our premultiplied PMColors, since we can
495 build a 16bit table as long as the original colors are opaque, even if the
496 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497*/
reed@android.com512a8762009-12-14 15:25:36 +0000498void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
499 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 SkASSERT(count > 1);
501 SkASSERT(SkColorGetA(c0) == 0xFF);
502 SkASSERT(SkColorGetA(c1) == 0xFF);
503
504 SkFixed r = SkColorGetR(c0);
505 SkFixed g = SkColorGetG(c0);
506 SkFixed b = SkColorGetB(c0);
507
508 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
509 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
510 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
511
512 r = SkIntToFixed(r) + 0x8000;
513 g = SkIntToFixed(g) + 0x8000;
514 b = SkIntToFixed(b) + 0x8000;
515
516 do {
517 unsigned rr = r >> 16;
518 unsigned gg = g >> 16;
519 unsigned bb = b >> 16;
520 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000521 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 cache += 1;
523 r += dr;
524 g += dg;
525 b += db;
526 } while (--count != 0);
527}
528
reed@google.com55b8e8c2011-01-13 16:22:35 +0000529/*
530 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
531 * semantics of how we 2x2 dither 32->16
532 */
533static inline U8CPU dither_fixed_to_8(SkFixed n) {
534 n >>= 8;
535 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
536}
537
538/*
539 * For dithering with premultiply, we want to ceiling the alpha component,
540 * to ensure that it is always >= any color component.
541 */
542static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
543 n >>= 8;
544 return ((n << 1) - (n | (n >> 8))) >> 8;
545}
546
547void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
548 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 SkASSERT(count > 1);
550
reed@android.com1c12abe2009-07-02 15:01:02 +0000551 // need to apply paintAlpha to our two endpoints
552 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
553 SkFixed da;
554 {
555 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
556 da = SkIntToFixed(tmp - a) / (count - 1);
557 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558
reed@android.com1c12abe2009-07-02 15:01:02 +0000559 SkFixed r = SkColorGetR(c0);
560 SkFixed g = SkColorGetG(c0);
561 SkFixed b = SkColorGetB(c0);
562 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
563 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
564 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565
566 a = SkIntToFixed(a) + 0x8000;
567 r = SkIntToFixed(r) + 0x8000;
568 g = SkIntToFixed(g) + 0x8000;
569 b = SkIntToFixed(b) + 0x8000;
570
571 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000572 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000573 cache[kCache32Count] =
574 SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
575 dither_fixed_to_8(r),
576 dither_fixed_to_8(g),
577 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000578 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 a += da;
580 r += dr;
581 g += dg;
582 b += db;
583 } while (--count != 0);
584}
585
reed@android.com41bccf52009-04-03 13:33:51 +0000586static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 SkASSERT((unsigned)x <= SK_Fixed1);
588 return x - (x >> 16);
589}
590
reed@android.com200645d2009-12-14 16:41:57 +0000591static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000592 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000593 if (6 == bits) {
594 return (x << 10) | (x << 4) | (x >> 2);
595 }
596 if (8 == bits) {
597 return (x << 8) | x;
598 }
599 sk_throw();
600 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601}
602
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000603/** We duplicate the last value in each half of the cache so that
604 interpolation doesn't have to special-case being at the last point.
605*/
606static void complete_16bit_cache(uint16_t* cache, int stride) {
607 cache[stride - 1] = cache[stride - 2];
608 cache[2 * stride - 1] = cache[2 * stride - 2];
609}
610
reed@google.com7c2f27d2011-03-07 19:29:00 +0000611const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000612 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000613 // double the count for dither entries
614 const int entryCount = kCache16Count * 2;
615 const size_t allocSize = sizeof(uint16_t) * entryCount;
616
reed@android.com3c9b2a42009-08-27 19:28:37 +0000617 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000618 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000619 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000621 if (fColorCount == 2) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000622 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
623 kGradient16Length);
reed@android.com41bccf52009-04-03 13:33:51 +0000624 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 Rec* rec = fRecs;
626 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000627 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000628 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 SkASSERT(nextIndex < kCache16Count);
630
631 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000632 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 prevIndex = nextIndex;
634 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000635 // one extra space left over at the end for complete_16bit_cache()
636 SkASSERT(prevIndex == kGradient16Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 }
638
reed@android.com41bccf52009-04-03 13:33:51 +0000639 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000640 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 uint16_t* linear = fCache16; // just computed linear data
642 uint16_t* mapped = fCache16Storage; // storage for mapped data
643 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000644 for (int i = 0; i < kGradient16Length; i++) {
reed@android.com200645d2009-12-14 16:41:57 +0000645 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000647 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 }
649 sk_free(fCache16);
650 fCache16 = fCache16Storage;
651 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000652 complete_16bit_cache(fCache16, kCache16Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 }
654 return fCache16;
655}
656
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000657/** We duplicate the last value in each half of the cache so that
658 interpolation doesn't have to special-case being at the last point.
659*/
660static void complete_32bit_cache(SkPMColor* cache, int stride) {
661 cache[stride - 1] = cache[stride - 2];
662 cache[2 * stride - 1] = cache[2 * stride - 2];
663}
664
reed@google.com7c2f27d2011-03-07 19:29:00 +0000665const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000666 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000667 // double the count for dither entries
668 const int entryCount = kCache32Count * 2;
669 const size_t allocSize = sizeof(SkPMColor) * entryCount;
670
reed@google.comdc731fd2010-12-23 15:19:47 +0000671 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000672 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
673 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000674 }
675 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000676 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000677 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000678 kGradient32Length, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000679 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 Rec* rec = fRecs;
681 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000682 for (int i = 1; i < fColorCount; i++) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000683 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000684 SkASSERT(nextIndex < kGradient32Length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685
686 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000687 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
688 fOrigColors[i],
689 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 prevIndex = nextIndex;
691 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000692 SkASSERT(prevIndex == kGradient32Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693 }
694
reed@android.com41bccf52009-04-03 13:33:51 +0000695 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000696 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000697 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000699 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000701 for (int i = 0; i < kGradient32Length; i++) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000702 int index = map->mapUnit16((i << 8) | i) >> 8;
703 mapped[i] = linear[index];
704 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000705 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000706 fCache32PixelRef->unref();
707 fCache32PixelRef = newPR;
708 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000710 complete_32bit_cache(fCache32, kCache32Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 }
712 return fCache32;
713}
714
reed@google.comdc731fd2010-12-23 15:19:47 +0000715/*
716 * Because our caller might rebuild the same (logically the same) gradient
717 * over and over, we'd like to return exactly the same "bitmap" if possible,
718 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
719 * To do that, we maintain a private cache of built-bitmaps, based on our
720 * colors and positions. Note: we don't try to flatten the fMapper, so if one
721 * is present, we skip the cache for now.
722 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000723void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000724 // our caller assumes no external alpha, so we ensure that our cache is
725 // built with 0xFF
726 this->setCacheAlpha(0xFF);
727
reed@google.comdc731fd2010-12-23 15:19:47 +0000728 // don't have a way to put the mapper into our cache-key yet
729 if (fMapper) {
730 // force our cahce32pixelref to be built
731 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000732 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000733 bitmap->setPixelRef(fCache32PixelRef);
734 return;
735 }
736
737 // build our key: [numColors + colors[] + {positions[]} ]
738 int count = 1 + fColorCount;
739 if (fColorCount > 2) {
740 count += fColorCount - 1; // fRecs[].fPos
741 }
742
743 SkAutoSTMalloc<16, int32_t> storage(count);
744 int32_t* buffer = storage.get();
745
746 *buffer++ = fColorCount;
747 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
748 buffer += fColorCount;
749 if (fColorCount > 2) {
750 for (int i = 1; i < fColorCount; i++) {
751 *buffer++ = fRecs[i].fPos;
752 }
753 }
754 SkASSERT(buffer - storage.get() == count);
755
756 ///////////////////////////////////
757
758 static SkMutex gMutex;
759 static SkBitmapCache* gCache;
760 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
761 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
762 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000763
reed@google.comdc731fd2010-12-23 15:19:47 +0000764 if (NULL == gCache) {
765 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
766 }
767 size_t size = count * sizeof(int32_t);
768
769 if (!gCache->find(storage.get(), size, bitmap)) {
770 // force our cahce32pixelref to be built
771 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000772 // Only expose the linear section of the cache; don't let the caller
773 // know about the padding at the end to make interpolation faster.
774 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000775 bitmap->setPixelRef(fCache32PixelRef);
776
777 gCache->add(storage.get(), size, *bitmap);
778 }
779}
780
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000781void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
782 if (info) {
783 if (info->fColorCount >= fColorCount) {
784 if (info->fColors) {
785 memcpy(info->fColors, fOrigColors,
786 fColorCount * sizeof(SkColor));
787 }
788 if (info->fColorOffsets) {
789 if (fColorCount == 2) {
790 info->fColorOffsets[0] = 0;
791 info->fColorOffsets[1] = SK_Scalar1;
792 } else if (fColorCount > 2) {
793 for (int i = 0; i < fColorCount; i++)
794 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
795 }
796 }
797 }
798 info->fColorCount = fColorCount;
799 info->fTileMode = fTileMode;
800 }
801}
802
reed@google.com61eb0402011-04-15 12:11:12 +0000803///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804
reed@android.com41bccf52009-04-03 13:33:51 +0000805static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 SkVector vec = pts[1] - pts[0];
807 SkScalar mag = vec.length();
808 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
809
810 vec.scale(inv);
811 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
812 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
813 matrix->postScale(inv, inv);
814}
815
816///////////////////////////////////////////////////////////////////////////////
817
818class Linear_Gradient : public Gradient_Shader {
819public:
820 Linear_Gradient(const SkPoint pts[2],
821 const SkColor colors[], const SkScalar pos[], int colorCount,
822 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000823 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
824 fStart(pts[0]),
825 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 {
827 pts_to_unit_matrix(pts, &fPtsToUnit);
828 }
reed@android.com9b46e772009-06-05 12:24:41 +0000829
reed@google.com7716afb2011-12-07 15:17:50 +0000830 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
831 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
832 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
833 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
834 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
835 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836
reed@google.com55b8e8c2011-01-13 16:22:35 +0000837 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 return SkNEW_ARGS(Linear_Gradient, (buffer));
839 }
840
reed@google.com7716afb2011-12-07 15:17:50 +0000841 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000842 this->INHERITED::flatten(buffer);
843 buffer.writeScalar(fStart.fX);
844 buffer.writeScalar(fStart.fY);
845 buffer.writeScalar(fEnd.fX);
846 buffer.writeScalar(fEnd.fY);
847 }
848
caryclark@google.comd26147a2011-12-15 14:16:43 +0000849 SK_DECLARE_FLATTENABLE_REGISTRAR()
850
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000852 Linear_Gradient(SkFlattenableReadBuffer& buffer)
853 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +0000854 fStart(unflatten_point(buffer)),
855 fEnd(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000856 }
reed@google.com7716afb2011-12-07 15:17:50 +0000857 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858
859private:
860 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000861 const SkPoint fStart;
862 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863};
864
reed@android.com5119bdb2009-06-12 21:27:03 +0000865bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
866 const SkMatrix& matrix) {
867 if (!this->INHERITED::setContext(device, paint, matrix)) {
868 return false;
869 }
870
871 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
872 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000873 fFlags |= SkShader::kConstInY32_Flag;
874 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
875 // only claim this if we do have a 16bit mode (i.e. none of our
876 // colors have alpha), and if we are not dithering (which obviously
877 // is not const in Y).
878 fFlags |= SkShader::kConstInY16_Flag;
879 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000880 }
881 return true;
882}
883
reed@google.com5eb158d2011-04-15 15:50:34 +0000884#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000885 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000886 unsigned fi = fx >> Gradient_Shader::kCache32Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000887 SkASSERT(fi <= 0xFF); \
888 fx += dx; \
889 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000890 toggle ^= Gradient_Shader::kDitherStride32; \
reed@google.com13659f12011-04-18 19:59:38 +0000891 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000892
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000893namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +0000894
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000895typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000896 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000897 int toggle, int count);
898
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000899// lerp is unnecessary if there are no sharp discontinuities in the
900// gradient - which must be true if there are only 2 colors - but for
901// vertical gradients it's so cheap we do it anyway.
902void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
903 SkPMColor* SK_RESTRICT dstC,
904 const SkPMColor* SK_RESTRICT cache,
905 int toggle, int count) {
906 // We're a vertical gradient, so no change in a span.
907 // If colors change sharply across the gradient, dithering is
908 // insufficient (it subsamples the color space) and we need to lerp.
909 unsigned fullIndex = proc(fx);
910 unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
911 unsigned remainder = fullIndex & Gradient_Shader::kLerpRemainderMask32;
912 SkPMColor lerp =
913 SkFastFourByteInterp(
914 cache[toggle + fi + 1],
915 cache[toggle + fi], remainder);
916 SkPMColor dlerp =
917 SkFastFourByteInterp(
918 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi + 1],
919 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi], remainder);
920 sk_memset32_dither(dstC, lerp, dlerp, count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000921}
922
923void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
924 SkPMColor* SK_RESTRICT dstC,
925 const SkPMColor* SK_RESTRICT cache,
926 int toggle, int count) {
927 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000928 range.init(fx, dx, count, 0, Gradient_Shader::kGradient32Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000929
930 if ((count = range.fCount0) > 0) {
931 sk_memset32_dither(dstC,
932 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000933 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000934 count);
935 dstC += count;
936 }
937 if ((count = range.fCount1) > 0) {
938 int unroll = count >> 3;
939 fx = range.fFx1;
940 for (int i = 0; i < unroll; i++) {
941 NO_CHECK_ITER; NO_CHECK_ITER;
942 NO_CHECK_ITER; NO_CHECK_ITER;
943 NO_CHECK_ITER; NO_CHECK_ITER;
944 NO_CHECK_ITER; NO_CHECK_ITER;
945 }
946 if ((count &= 7) > 0) {
947 do {
948 NO_CHECK_ITER;
949 } while (--count != 0);
950 }
951 }
952 if ((count = range.fCount2) > 0) {
953 sk_memset32_dither(dstC,
954 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000955 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000956 count);
957 }
958}
959
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000960void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
961 SkPMColor* SK_RESTRICT dstC,
962 const SkPMColor* SK_RESTRICT cache,
963 int toggle, int count) {
964 do {
965 unsigned fi = mirror_8bits(fx >> 8);
966 SkASSERT(fi <= 0xFF);
967 fx += dx;
968 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000969 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000970 } while (--count != 0);
971}
972
973void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
974 SkPMColor* SK_RESTRICT dstC,
975 const SkPMColor* SK_RESTRICT cache,
976 int toggle, int count) {
977 do {
978 unsigned fi = repeat_8bits(fx >> 8);
979 SkASSERT(fi <= 0xFF);
980 fx += dx;
981 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000982 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000983 } while (--count != 0);
984}
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000985
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000986}
987
988void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
989 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 SkASSERT(count > 0);
991
992 SkPoint srcPt;
993 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
994 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000995 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com0e734bd2011-12-08 17:24:44 +0000996#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000997 int toggle = ((x ^ y) & 1) * kDitherStride32;
reed@google.com0e734bd2011-12-08 17:24:44 +0000998#else
999 int toggle = 0;
reed@google.com0e734bd2011-12-08 17:24:44 +00001000#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001
reed@android.comc552a432009-06-12 20:02:50 +00001002 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001003 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1004 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1006
reed@android.comc552a432009-06-12 20:02:50 +00001007 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 SkFixed dxStorage[1];
1009 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1010 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +00001011 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1013 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1014 }
1015
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001016 LinearShadeProc shadeProc = shadeSpan_linear_repeat;
reed@android.comc552a432009-06-12 20:02:50 +00001017 if (SkFixedNearlyZero(dx)) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001018 shadeProc = shadeSpan_linear_vertical_lerp;
reed@android.comc552a432009-06-12 20:02:50 +00001019 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001020 shadeProc = shadeSpan_linear_clamp;
reed@android.comc552a432009-06-12 20:02:50 +00001021 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001022 shadeProc = shadeSpan_linear_mirror;
reed@android.comc552a432009-06-12 20:02:50 +00001023 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001026 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comc552a432009-06-12 20:02:50 +00001027 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 SkScalar dstX = SkIntToScalar(x);
1029 SkScalar dstY = SkIntToScalar(y);
1030 do {
1031 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1032 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1033 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001034 *dstC++ = cache[toggle + (fi >> kCache32Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001035 toggle ^= Gradient_Shader::kDitherStride32;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 dstX += SK_Scalar1;
1037 } while (--count != 0);
1038 }
1039}
1040
reed@google.com55b8e8c2011-01-13 16:22:35 +00001041SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +00001042 SkMatrix* matrix,
1043 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +00001044 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001046 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 }
1048 if (matrix) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001049 matrix->setScale(SkIntToScalar(kGradient32Length), SK_Scalar1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 matrix->preConcat(fPtsToUnit);
1051 }
1052 if (xy) {
1053 xy[0] = fTileMode;
1054 xy[1] = kClamp_TileMode;
1055 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001056 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057}
1058
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001059SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
1060 if (info) {
1061 commonAsAGradient(info);
1062 info->fPoint[0] = fStart;
1063 info->fPoint[1] = fEnd;
1064 }
1065 return kLinear_GradientType;
1066}
1067
reed@android.com3c9b2a42009-08-27 19:28:37 +00001068static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
1069 int count) {
1070 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 *dst++ = value;
1072 count -= 1;
1073 SkTSwap(value, other);
1074 }
1075
1076 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001077
reed@android.com3c9b2a42009-08-27 19:28:37 +00001078 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001080 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082
reed@google.com5eb158d2011-04-15 15:50:34 +00001083#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +00001084 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001085 unsigned fi = fx >> Gradient_Shader::kCache16Shift; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001086 SkASSERT(fi < Gradient_Shader::kCache16Count); \
reed@google.com5eb158d2011-04-15 15:50:34 +00001087 fx += dx; \
1088 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001089 toggle ^= Gradient_Shader::kDitherStride16; \
reed@google.com13659f12011-04-18 19:59:38 +00001090 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +00001091
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001092namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +00001093
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001094typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001095 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001096 int toggle, int count);
1097
1098void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
1099 uint16_t* SK_RESTRICT dstC,
1100 const uint16_t* SK_RESTRICT cache,
1101 int toggle, int count) {
1102 // we're a vertical gradient, so no change in a span
1103 unsigned fi = proc(fx) >> Gradient_Shader::kCache16Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001104 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001105 dither_memset16(dstC, cache[toggle + fi],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001106 cache[(toggle ^ Gradient_Shader::kDitherStride16) + fi], count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001107
1108}
1109
1110void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
1111 uint16_t* SK_RESTRICT dstC,
1112 const uint16_t* SK_RESTRICT cache,
1113 int toggle, int count) {
1114 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001115 range.init(fx, dx, count, 0, Gradient_Shader::kGradient16Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001116
1117 if ((count = range.fCount0) > 0) {
1118 dither_memset16(dstC,
1119 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001120 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001121 count);
1122 dstC += count;
1123 }
1124 if ((count = range.fCount1) > 0) {
1125 int unroll = count >> 3;
1126 fx = range.fFx1;
1127 for (int i = 0; i < unroll; i++) {
1128 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1129 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1130 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1131 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1132 }
1133 if ((count &= 7) > 0) {
1134 do {
1135 NO_CHECK_ITER_16;
1136 } while (--count != 0);
1137 }
1138 }
1139 if ((count = range.fCount2) > 0) {
1140 dither_memset16(dstC,
1141 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001142 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001143 count);
1144 }
1145}
1146
1147void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
1148 uint16_t* SK_RESTRICT dstC,
1149 const uint16_t* SK_RESTRICT cache,
1150 int toggle, int count) {
1151 do {
1152 unsigned fi = mirror_bits(fx >> Gradient_Shader::kCache16Shift,
1153 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001154 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001155 fx += dx;
1156 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001157 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001158 } while (--count != 0);
1159}
1160
1161void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
1162 uint16_t* SK_RESTRICT dstC,
1163 const uint16_t* SK_RESTRICT cache,
1164 int toggle, int count) {
1165 SkASSERT(proc == repeat_tileproc);
1166 do {
1167 unsigned fi = repeat_bits(fx >> Gradient_Shader::kCache16Shift,
1168 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001169 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001170 fx += dx;
1171 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001172 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001173 } while (--count != 0);
1174}
1175}
1176
1177void Linear_Gradient::shadeSpan16(int x, int y,
1178 uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 SkASSERT(count > 0);
1180
1181 SkPoint srcPt;
1182 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1183 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001184 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001185 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001187 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001188 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1189 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1191
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001192 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 SkFixed dxStorage[1];
1194 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1195 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001196 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1198 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1199 }
1200
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001201 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001202 if (SkFixedNearlyZero(dx)) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001203 shadeProc = shadeSpan16_linear_vertical;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001204 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001205 shadeProc = shadeSpan16_linear_clamp;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001206 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001207 shadeProc = shadeSpan16_linear_mirror;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001208 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001211 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001212 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 SkScalar dstX = SkIntToScalar(x);
1214 SkScalar dstY = SkIntToScalar(y);
1215 do {
1216 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1217 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1218 SkASSERT(fi <= 0xFFFF);
1219
reed@android.com512a8762009-12-14 15:25:36 +00001220 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001222 toggle ^= Gradient_Shader::kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223
1224 dstX += SK_Scalar1;
1225 } while (--count != 0);
1226 }
1227}
1228
1229///////////////////////////////////////////////////////////////////////////////
1230
1231#define kSQRT_TABLE_BITS 11
1232#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1233
1234#include "SkRadialGradient_Table.h"
1235
1236#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1237
1238#include <stdio.h>
1239
reed@google.com61eb0402011-04-15 12:11:12 +00001240void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1242
1243 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1244 SkASSERT(file);
1245 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1246
reed@google.com61eb0402011-04-15 12:11:12 +00001247 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1248 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251
1252 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1253
1254 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001255 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001257 }
1258 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001260 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 }
1262 ::fprintf(file, "};\n");
1263 ::fclose(file);
1264}
1265
1266#endif
1267
1268
reed@google.com61eb0402011-04-15 12:11:12 +00001269static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1270 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 SkScalar inv = SkScalarInvert(radius);
1272
1273 matrix->setTranslate(-center.fX, -center.fY);
1274 matrix->postScale(inv, inv);
1275}
1276
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001277
1278namespace {
1279
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001280typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
1281 SkScalar sfy, SkScalar sdy,
1282 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001283 int toggle, int count);
1284
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001285void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
1286 SkScalar sfy, SkScalar sdy,
1287 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001288 int toggle, int count) {
1289 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
1290
1291 /* knock these down so we can pin against +- 0x7FFF, which is an
1292 immediate load, rather than 0xFFFF which is slower. This is a
1293 compromise, since it reduces our precision, but that appears
1294 to be visually OK. If we decide this is OK for all of our cases,
1295 we could (it seems) put this scale-down into fDstToIndex,
1296 to avoid having to do these extra shifts each time.
1297 */
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001298 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1299 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1300 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1301 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001302 // might perform this check for the other modes,
1303 // but the win will be a smaller % of the total
1304 if (dy == 0) {
1305 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1306 fy *= fy;
1307 do {
1308 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1309 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1310 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1311 fx += dx;
1312 *dstC++ = cache[toggle +
1313 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001314 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001315 } while (--count != 0);
1316 } else {
1317 do {
1318 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1319 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1320 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1321 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1322 fx += dx;
1323 fy += dy;
1324 *dstC++ = cache[toggle +
1325 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001326 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001327 } while (--count != 0);
1328 }
1329}
1330
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001331void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
1332 SkScalar sfy, SkScalar sdy,
1333 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001334 int toggle, int count) {
1335 do {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001336#ifdef SK_SCALAR_IS_FLOAT
1337 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1338 SkFixed dist = SkFloatToFixed(fdist);
1339#else
1340 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1341 SkFixedSquare(sfy);
1342 if (magnitudeSquared < 0) // Overflow.
1343 magnitudeSquared = SK_FixedMax;
1344 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1345#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001346 unsigned fi = mirror_tileproc(dist);
1347 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001348 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001349 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001350 sfx += sdx;
1351 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001352 } while (--count != 0);
1353}
1354
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001355void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
1356 SkScalar sfy, SkScalar sdy,
1357 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001358 int toggle, int count) {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001359 SkFixed fx = SkScalarToFixed(sfx);
1360 SkFixed dx = SkScalarToFixed(sdx);
1361 SkFixed fy = SkScalarToFixed(sfy);
1362 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001363 do {
1364 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1365 unsigned fi = repeat_tileproc(dist);
1366 SkASSERT(fi <= 0xFFFF);
1367 fx += dx;
1368 fy += dy;
1369 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001370 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001371 } while (--count != 0);
1372}
1373
1374}
1375
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376class Radial_Gradient : public Gradient_Shader {
1377public:
1378 Radial_Gradient(const SkPoint& center, SkScalar radius,
1379 const SkColor colors[], const SkScalar pos[], int colorCount,
1380 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001381 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1382 fCenter(center),
1383 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 {
1385 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1386 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1387
1388 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1389 }
reed@google.com61eb0402011-04-15 12:11:12 +00001390
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001391 virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
1392 SK_OVERRIDE;
1393 virtual void shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
1394 int count) SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 SkASSERT(count > 0);
1396
1397 SkPoint srcPt;
1398 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1399 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001400 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001401 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402
reed@android.com3c9b2a42009-08-27 19:28:37 +00001403 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001404 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1405 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001406
1407 SkScalar sdx = fDstToIndex.getScaleX();
1408 SkScalar sdy = fDstToIndex.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409
reed@android.com3c9b2a42009-08-27 19:28:37 +00001410 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 SkFixed storage[2];
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001412 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1413 &storage[0], &storage[1]);
1414 sdx = SkFixedToScalar(storage[0]);
1415 sdy = SkFixedToScalar(storage[1]);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001416 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 }
1419
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001420 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001421 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001422 shadeProc = shadeSpan16_radial_clamp;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001423 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001424 shadeProc = shadeSpan16_radial_mirror;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001425 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 }
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001428 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
1429 cache, toggle, count);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001430 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 SkScalar dstX = SkIntToScalar(x);
1432 SkScalar dstY = SkIntToScalar(y);
1433 do {
1434 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1435 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1436 SkASSERT(fi <= 0xFFFF);
1437
1438 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001440 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441
1442 dstX += SK_Scalar1;
1443 } while (--count != 0);
1444 }
1445 }
1446
reed@google.com55b8e8c2011-01-13 16:22:35 +00001447 virtual BitmapType asABitmap(SkBitmap* bitmap,
1448 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001449 TileMode* xy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001450 SkScalar* twoPointRadialParams)
1451 const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001452 if (bitmap) {
1453 this->commonAsABitmap(bitmap);
1454 }
1455 if (matrix) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001456 matrix->setScale(SkIntToScalar(kGradient32Length),
1457 SkIntToScalar(kGradient32Length));
reed@google.comdc731fd2010-12-23 15:19:47 +00001458 matrix->preConcat(fPtsToUnit);
1459 }
1460 if (xy) {
1461 xy[0] = fTileMode;
1462 xy[1] = kClamp_TileMode;
1463 }
1464 return kRadial_BitmapType;
1465 }
reed@google.com7716afb2011-12-07 15:17:50 +00001466 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001467 if (info) {
1468 commonAsAGradient(info);
1469 info->fPoint[0] = fCenter;
1470 info->fRadius[0] = fRadius;
1471 }
1472 return kRadial_GradientType;
1473 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001474
reed@google.com8e6d9142011-12-07 15:30:34 +00001475 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 return SkNEW_ARGS(Radial_Gradient, (buffer));
1477 }
1478
reed@google.com7716afb2011-12-07 15:17:50 +00001479 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001480 this->INHERITED::flatten(buffer);
1481 buffer.writeScalar(fCenter.fX);
1482 buffer.writeScalar(fCenter.fY);
1483 buffer.writeScalar(fRadius);
1484 }
1485
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001487 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1488 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001489 fCenter(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001490 fRadius(buffer.readScalar()) {
1491 }
reed@google.com7716afb2011-12-07 15:17:50 +00001492 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493
1494private:
1495 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001496 const SkPoint fCenter;
1497 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498};
1499
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001500namespace {
1501
1502inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001503 // fast, overly-conservative test: checks unit square instead
1504 // of unit circle
1505 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
1506 (fx <= -SK_FixedHalf && dx <= 0);
1507 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
1508 (fy <= -SK_FixedHalf && dy <= 0);
1509
1510 return xClamped || yClamped;
1511}
1512
1513// Return true if (fx * fy) is always inside the unit circle
1514// SkPin32 is expensive, but so are all the SkFixedMul in this test,
1515// so it shouldn't be run if count is small.
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001516inline bool no_need_for_radial_pin(int fx, int dx,
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001517 int fy, int dy, int count) {
1518 SkASSERT(count > 0);
1519 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1520 return false;
1521 }
1522 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
1523 return false;
1524 }
1525 fx += (count - 1) * dx;
1526 fy += (count - 1) * dy;
1527 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1528 return false;
1529 }
1530 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
1531}
1532
1533#define UNPINNED_RADIAL_STEP \
1534 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001535 *dstC++ = cache[sqrt_table[fi] >> Gradient_Shader::kSqrt32Shift]; \
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001536 fx += dx; \
1537 fy += dy;
1538
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001539typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
1540 SkScalar sfy, SkScalar sdy,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001541 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001542 int count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001543
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001544// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001545void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
1546 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001547 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001548 int count) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001549 // Floating point seems to be slower than fixed point,
1550 // even when we have float hardware.
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001551 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001552 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1553 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1554 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1555 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001556 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001557 sk_memset32(dstC, cache[Gradient_Shader::kGradient32Length], count);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001558 } else if ((count > 4) &&
1559 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
1560 unsigned fi;
1561 // 4x unroll appears to be no faster than 2x unroll on Linux
1562 while (count > 1) {
1563 UNPINNED_RADIAL_STEP;
1564 UNPINNED_RADIAL_STEP;
1565 count -= 2;
1566 }
1567 if (count) {
1568 UNPINNED_RADIAL_STEP;
1569 }
1570 }
1571 else {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001572 // Specializing for dy == 0 gains us 25% on Skia benchmarks
1573 if (dy == 0) {
1574 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1575 yy *= yy;
1576 do {
1577 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1578 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
1579 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1580 *dstC++ = cache[sqrt_table[fi] >>
1581 Gradient_Shader::kSqrt32Shift];
1582 fx += dx;
1583 } while (--count != 0);
1584 } else {
1585 do {
1586 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1587 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1588 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1589 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1590 *dstC++ = cache[sqrt_table[fi] >>
1591 Gradient_Shader::kSqrt32Shift];
1592 fx += dx;
1593 fy += dy;
1594 } while (--count != 0);
1595 }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001596 }
1597}
1598
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001599// Unrolling this loop doesn't seem to help (when float); we're stalling to
1600// get the results of the sqrt (?), and don't have enough extra registers to
1601// have many in flight.
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001602void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
1603 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001604 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001605 int count) {
1606 do {
tomhudson@google.comf925ba42012-01-11 21:54:26 +00001607#ifdef SK_SCALAR_IS_FLOAT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001608 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1609 SkFixed dist = SkFloatToFixed(fdist);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001610#else
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001611 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1612 SkFixedSquare(sfy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001613 if (magnitudeSquared < 0) // Overflow.
1614 magnitudeSquared = SK_FixedMax;
1615 SkFixed dist = SkFixedSqrt(magnitudeSquared);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001616#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001617 unsigned fi = mirror_tileproc(dist);
1618 SkASSERT(fi <= 0xFFFF);
1619 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001620 sfx += sdx;
1621 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001622 } while (--count != 0);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001623}
1624
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001625void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
1626 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001627 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001628 int count) {
1629 SkFixed fx = SkScalarToFixed(sfx);
1630 SkFixed dx = SkScalarToFixed(sdx);
1631 SkFixed fy = SkScalarToFixed(sfy);
1632 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001633 do {
1634 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1635 SkFixedSquare(fy);
1636 if (magnitudeSquared < 0) // Overflow.
1637 magnitudeSquared = SK_FixedMax;
1638 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1639 unsigned fi = repeat_tileproc(dist);
1640 SkASSERT(fi <= 0xFFFF);
1641 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
1642 fx += dx;
1643 fy += dy;
1644 } while (--count != 0);
1645}
1646}
1647
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001648void Radial_Gradient::shadeSpan(int x, int y,
1649 SkPMColor* SK_RESTRICT dstC, int count) {
1650 SkASSERT(count > 0);
1651
1652 SkPoint srcPt;
1653 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1654 TileProc proc = fTileProc;
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001655 const SkPMColor* SK_RESTRICT cache = this->getCache32();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001656
1657 if (fDstToIndexClass != kPerspective_MatrixClass) {
1658 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1659 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001660 SkScalar sdx = fDstToIndex.getScaleX();
1661 SkScalar sdy = fDstToIndex.getSkewY();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001662
1663 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1664 SkFixed storage[2];
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001665 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1666 &storage[0], &storage[1]);
1667 sdx = SkFixedToScalar(storage[0]);
1668 sdy = SkFixedToScalar(storage[1]);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001669 } else {
1670 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001671 }
1672
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001673 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001674 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001675 shadeProc = shadeSpan_radial_clamp;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001676 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001677 shadeProc = shadeSpan_radial_mirror;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001678 } else {
1679 SkASSERT(proc == repeat_tileproc);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001680 }
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001681 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001682 } else { // perspective case
1683 SkScalar dstX = SkIntToScalar(x);
1684 SkScalar dstY = SkIntToScalar(y);
1685 do {
1686 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1687 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1688 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001689 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001690 dstX += SK_Scalar1;
1691 } while (--count != 0);
1692 }
1693}
1694
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001695/* Two-point radial gradients are specified by two circles, each with a center
1696 point and radius. The gradient can be considered to be a series of
1697 concentric circles, with the color interpolated from the start circle
1698 (at t=0) to the end circle (at t=1).
1699
1700 For each point (x, y) in the span, we want to find the
1701 interpolated circle that intersects that point. The center
1702 of the desired circle (Cx, Cy) falls at some distance t
1703 along the line segment between the start point (Sx, Sy) and
1704 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001705
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001706 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1707 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001708
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001709 The radius of the desired circle (r) is also a linear interpolation t
1710 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001711
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001712 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001713
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001714 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001715
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001716 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001717
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001718 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001719
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001720 (x - ((1 - t) * Sx + t * Ex))^2
1721 + (y - ((1 - t) * Sy + t * Ey))^2
1722 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001723
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001724 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001725
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001726 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1727 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1728 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001729
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001730 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1731
1732 [Dx^2 + Dy^2 - Dr^2)] * t^2
1733 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1734 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001735
1736 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001737 possible circles on which the point may fall. Solving for t yields
1738 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001739
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001740 If a<0, the start circle is entirely contained in the
1741 end circle, and one of the roots will be <0 or >1 (off the line
1742 segment). If a>0, the start circle falls at least partially
1743 outside the end circle (or vice versa), and the gradient
1744 defines a "tube" where a point may be on one circle (on the
1745 inside of the tube) or the other (outside of the tube). We choose
1746 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001747
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001748 In order to keep the math to within the limits of fixed point,
1749 we divide the entire quadratic by Dr^2, and replace
1750 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001751
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001752 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1753 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1754 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001755
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001756 (x' and y' are computed by appending the subtract and scale to the
1757 fDstToIndex matrix in the constructor).
1758
1759 Since the 'A' component of the quadratic is independent of x' and y', it
1760 is precomputed in the constructor. Since the 'B' component is linear in
1761 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001762 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001763 a perspective projection), it must be computed in the loop.
1764
1765*/
1766
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001767namespace {
1768
1769inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1770 SkScalar sr2d2, SkScalar foura,
1771 SkScalar oneOverTwoA, bool posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001772 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001773 if (0 == foura) {
1774 return SkScalarToFixed(SkScalarDiv(-c, b));
1775 }
1776
reed@google.com84e9c082011-04-13 17:44:24 +00001777 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001778 if (discrim < 0) {
1779 discrim = -discrim;
1780 }
reed@google.com84e9c082011-04-13 17:44:24 +00001781 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1782 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001783 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001784 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001785 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001786 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001787 }
reed@google.com84e9c082011-04-13 17:44:24 +00001788 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001789}
1790
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001791typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
1792 SkScalar fy, SkScalar dy,
1793 SkScalar b, SkScalar db,
1794 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001795 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001796 int count);
1797
1798void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
1799 SkScalar fy, SkScalar dy,
1800 SkScalar b, SkScalar db,
1801 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001802 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001803 int count) {
1804 for (; count > 0; --count) {
1805 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1806 fOneOverTwoA, posRoot);
1807 SkFixed index = SkClampMax(t, 0xFFFF);
1808 SkASSERT(index <= 0xFFFF);
1809 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1810 fx += dx;
1811 fy += dy;
1812 b += db;
1813 }
1814}
1815void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
1816 SkScalar fy, SkScalar dy,
1817 SkScalar b, SkScalar db,
1818 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001819 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001820 int count) {
1821 for (; count > 0; --count) {
1822 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1823 fOneOverTwoA, posRoot);
1824 SkFixed index = mirror_tileproc(t);
1825 SkASSERT(index <= 0xFFFF);
1826 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1827 fx += dx;
1828 fy += dy;
1829 b += db;
1830 }
1831}
1832
1833void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
1834 SkScalar fy, SkScalar dy,
1835 SkScalar b, SkScalar db,
1836 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001837 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001838 int count) {
1839 for (; count > 0; --count) {
1840 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1841 fOneOverTwoA, posRoot);
1842 SkFixed index = repeat_tileproc(t);
1843 SkASSERT(index <= 0xFFFF);
1844 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1845 fx += dx;
1846 fy += dy;
1847 b += db;
1848 }
1849}
1850
1851
1852
1853}
1854
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001855class Two_Point_Radial_Gradient : public Gradient_Shader {
1856public:
1857 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1858 const SkPoint& end, SkScalar endRadius,
1859 const SkColor colors[], const SkScalar pos[],
1860 int colorCount, SkShader::TileMode mode,
1861 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001862 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1863 fCenter1(start),
1864 fCenter2(end),
1865 fRadius1(startRadius),
1866 fRadius2(endRadius) {
1867 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001868 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001869
1870 virtual BitmapType asABitmap(SkBitmap* bitmap,
1871 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001872 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001873 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001874 if (bitmap) {
1875 this->commonAsABitmap(bitmap);
1876 }
1877 SkScalar diffL = 0; // just to avoid gcc warning
1878 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001879 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001880 SkScalarSquare(fDiff.fY));
1881 }
1882 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001883 if (diffL) {
1884 SkScalar invDiffL = SkScalarInvert(diffL);
1885 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1886 SkScalarMul(invDiffL, fDiff.fX));
1887 } else {
1888 matrix->reset();
1889 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001890 matrix->preConcat(fPtsToUnit);
1891 }
1892 if (xy) {
1893 xy[0] = fTileMode;
1894 xy[1] = kClamp_TileMode;
1895 }
1896 if (NULL != twoPointRadialParams) {
1897 twoPointRadialParams[0] = diffL;
1898 twoPointRadialParams[1] = fStartRadius;
1899 twoPointRadialParams[2] = fDiffRadius;
1900 }
1901 return kTwoPointRadial_BitmapType;
1902 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001903
reed@google.com8e6d9142011-12-07 15:30:34 +00001904 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001905 if (info) {
1906 commonAsAGradient(info);
1907 info->fPoint[0] = fCenter1;
1908 info->fPoint[1] = fCenter2;
1909 info->fRadius[0] = fRadius1;
1910 info->fRadius[1] = fRadius2;
1911 }
1912 return kRadial2_GradientType;
1913 }
1914
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001915 virtual void shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
1916 int count) SK_OVERRIDE {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001917 SkASSERT(count > 0);
1918
1919 // Zero difference between radii: fill with transparent black.
1920 if (fDiffRadius == 0) {
1921 sk_bzero(dstC, count * sizeof(*dstC));
1922 return;
1923 }
1924 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1925 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001926 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001927
1928 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001929 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001930 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001931 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001932 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1933 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001934 SkScalar dx, fx = srcPt.fX;
1935 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001936
reed@google.com61eb0402011-04-15 12:11:12 +00001937 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001938 SkFixed fixedX, fixedY;
1939 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1940 dx = SkFixedToScalar(fixedX);
1941 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001942 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001943 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001944 dx = fDstToIndex.getScaleX();
1945 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001946 }
reed@google.com84e9c082011-04-13 17:44:24 +00001947 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1948 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1949 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1950 SkScalarMul(fDiff.fY, dy)) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001951
1952 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
reed@google.com61eb0402011-04-15 12:11:12 +00001953 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001954 shadeProc = shadeSpan_twopoint_clamp;
reed@google.com61eb0402011-04-15 12:11:12 +00001955 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001956 shadeProc = shadeSpan_twopoint_mirror;
reed@google.com61eb0402011-04-15 12:11:12 +00001957 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001958 SkASSERT(proc == repeat_tileproc);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001959 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001960 (*shadeProc)(fx, dx, fy, dy, b, db,
1961 fSr2D2, foura, fOneOverTwoA, posRoot,
1962 dstC, cache, count);
reed@google.com61eb0402011-04-15 12:11:12 +00001963 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001964 SkScalar dstX = SkIntToScalar(x);
1965 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001966 for (; count > 0; --count) {
1967 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001968 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001969 SkScalar fx = srcPt.fX;
1970 SkScalar fy = srcPt.fY;
1971 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1972 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001973 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1974 fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001975 SkFixed index = proc(t);
1976 SkASSERT(index <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001977 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
reed@android.com6c59a172009-09-22 20:24:05 +00001978 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001979 }
1980 }
1981 }
1982
reed@android.com6c59a172009-09-22 20:24:05 +00001983 virtual bool setContext(const SkBitmap& device,
1984 const SkPaint& paint,
reed@google.com8e6d9142011-12-07 15:30:34 +00001985 const SkMatrix& matrix) SK_OVERRIDE {
1986 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com6c59a172009-09-22 20:24:05 +00001987 return false;
1988 }
1989
1990 // we don't have a span16 proc
1991 fFlags &= ~kHasSpan16_Flag;
1992 return true;
1993 }
1994
reed@google.com8e6d9142011-12-07 15:30:34 +00001995 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001996 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1997 }
1998
reed@google.com7716afb2011-12-07 15:17:50 +00001999 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
reed@android.combcfc7332009-11-10 16:19:39 +00002000 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002001 buffer.writeScalar(fCenter1.fX);
2002 buffer.writeScalar(fCenter1.fY);
2003 buffer.writeScalar(fCenter2.fX);
2004 buffer.writeScalar(fCenter2.fY);
2005 buffer.writeScalar(fRadius1);
2006 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00002007 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002008
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002009protected:
reed@android.combcfc7332009-11-10 16:19:39 +00002010 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002011 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00002012 fCenter1(unflatten_point(buffer)),
2013 fCenter2(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002014 fRadius1(buffer.readScalar()),
2015 fRadius2(buffer.readScalar()) {
2016 init();
reed@android.combcfc7332009-11-10 16:19:39 +00002017 };
reed@google.com7716afb2011-12-07 15:17:50 +00002018 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002019
2020private:
2021 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002022 const SkPoint fCenter1;
2023 const SkPoint fCenter2;
2024 const SkScalar fRadius1;
2025 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002026 SkPoint fDiff;
2027 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002028
2029 void init() {
2030 fDiff = fCenter1 - fCenter2;
2031 fDiffRadius = fRadius2 - fRadius1;
2032 SkScalar inv = SkScalarInvert(fDiffRadius);
2033 fDiff.fX = SkScalarMul(fDiff.fX, inv);
2034 fDiff.fY = SkScalarMul(fDiff.fY, inv);
2035 fStartRadius = SkScalarMul(fRadius1, inv);
2036 fSr2D2 = SkScalarSquare(fStartRadius);
2037 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00002038 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002039
2040 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
2041 fPtsToUnit.postScale(inv, inv);
2042 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002043};
2044
reed@android.com8a1c16f2008-12-17 15:59:43 +00002045///////////////////////////////////////////////////////////////////////////////
2046
2047class Sweep_Gradient : public Gradient_Shader {
2048public:
2049 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
2050 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002051 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
2052 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00002053 {
2054 fPtsToUnit.setTranslate(-cx, -cy);
2055 }
reed@google.com7716afb2011-12-07 15:17:50 +00002056 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
2057 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002058
2059 virtual BitmapType asABitmap(SkBitmap* bitmap,
2060 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00002061 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00002062 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00002063 if (bitmap) {
2064 this->commonAsABitmap(bitmap);
2065 }
2066 if (matrix) {
2067 *matrix = fPtsToUnit;
2068 }
2069 if (xy) {
2070 xy[0] = fTileMode;
2071 xy[1] = kClamp_TileMode;
2072 }
2073 return kSweep_BitmapType;
2074 }
2075
reed@google.com7716afb2011-12-07 15:17:50 +00002076 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002077 if (info) {
2078 commonAsAGradient(info);
2079 info->fPoint[0] = fCenter;
2080 }
2081 return kSweep_GradientType;
2082 }
2083
reed@google.com8e6d9142011-12-07 15:30:34 +00002084 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 return SkNEW_ARGS(Sweep_Gradient, (buffer));
2086 }
2087
reed@google.com7716afb2011-12-07 15:17:50 +00002088 virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002089 this->INHERITED::flatten(buffer);
2090 buffer.writeScalar(fCenter.fX);
2091 buffer.writeScalar(fCenter.fY);
2092 }
2093
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002095 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
2096 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00002097 fCenter(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002098 }
2099
reed@google.com7716afb2011-12-07 15:17:50 +00002100 virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101
2102private:
2103 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002104 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105};
2106
2107#ifdef COMPUTE_SWEEP_TABLE
2108#define PI 3.14159265
2109static bool gSweepTableReady;
2110static uint8_t gSweepTable[65];
2111
2112/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
2113 We scale the results to [0..32]
2114*/
reed@google.com61eb0402011-04-15 12:11:12 +00002115static const uint8_t* build_sweep_table() {
2116 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 const int N = 65;
2118 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002119
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120 for (int i = 0; i < N; i++)
2121 {
2122 double arg = i / DENOM;
2123 double v = atan(arg);
2124 int iv = (int)round(v * DENOM * 2 / PI);
2125// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
2126 printf("%d, ", iv);
2127 gSweepTable[i] = iv;
2128 }
2129 gSweepTableReady = true;
2130 }
2131 return gSweepTable;
2132}
2133#else
2134static const uint8_t gSweepTable[] = {
2135 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
2136 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
2137 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
2138 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
2139 32
2140};
2141static const uint8_t* build_sweep_table() { return gSweepTable; }
2142#endif
2143
2144// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
2145// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
2146// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
2147
2148//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00002149static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150 SkASSERT(numer <= denom);
2151 SkASSERT(numer > 0);
2152 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002153
reed@android.com8a1c16f2008-12-17 15:59:43 +00002154 int nbits = SkCLZ(numer);
2155 int dbits = SkCLZ(denom);
2156 int bits = 6 - nbits + dbits;
2157 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002158
reed@google.com61eb0402011-04-15 12:11:12 +00002159 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002161 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162
2163 denom <<= dbits - 1;
2164 numer <<= nbits - 1;
2165
2166 unsigned result = 0;
2167
2168 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00002169 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00002171 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00002173 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002174
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00002176 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177 // make room for the rest of the answer bits
2178 result <<= bits;
2179 switch (bits) {
2180 case 6:
2181 if ((numer = (numer << 1) - denom) >= 0)
2182 result |= 32;
2183 else
2184 numer += denom;
2185 case 5:
2186 if ((numer = (numer << 1) - denom) >= 0)
2187 result |= 16;
2188 else
2189 numer += denom;
2190 case 4:
2191 if ((numer = (numer << 1) - denom) >= 0)
2192 result |= 8;
2193 else
2194 numer += denom;
2195 case 3:
2196 if ((numer = (numer << 1) - denom) >= 0)
2197 result |= 4;
2198 else
2199 numer += denom;
2200 case 2:
2201 if ((numer = (numer << 1) - denom) >= 0)
2202 result |= 2;
2203 else
2204 numer += denom;
2205 case 1:
2206 default: // not strictly need, but makes GCC make better ARM code
2207 if ((numer = (numer << 1) - denom) >= 0)
2208 result |= 1;
2209 else
2210 numer += denom;
2211 }
2212 }
2213 return result;
2214}
2215
2216// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00002217static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218#ifdef SK_DEBUG
2219 {
2220 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00002221 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 gOnce = true;
2223 SkASSERT(div_64(55, 55) == 64);
2224 SkASSERT(div_64(128, 256) == 32);
2225 SkASSERT(div_64(2326528, 4685824) == 31);
2226 SkASSERT(div_64(753664, 5210112) == 9);
2227 SkASSERT(div_64(229376, 4882432) == 3);
2228 SkASSERT(div_64(2, 64) == 2);
2229 SkASSERT(div_64(1, 64) == 1);
2230 // test that we handle underflow correctly
2231 SkASSERT(div_64(12345, 0x54321234) == 0);
2232 }
2233 }
2234#endif
2235
2236 SkASSERT(y > 0 && x > 0);
2237 const uint8_t* table = build_sweep_table();
2238
2239 unsigned result;
2240 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00002241 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242 // first part of the atan(v) = PI/2 - atan(1/v) identity
2243 // since our div_64 and table want v <= 1, where v = y/x
2244 SkTSwap<SkFixed>(x, y);
2245 }
2246
2247 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002248
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249#ifdef SK_DEBUG
2250 {
2251 unsigned result2 = SkDivBits(y, x, 6);
2252 SkASSERT(result2 == result ||
2253 (result == 1 && result2 == 0));
2254 }
2255#endif
2256
2257 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2258 result = table[result];
2259
reed@google.com61eb0402011-04-15 12:11:12 +00002260 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 // complete the atan(v) = PI/2 - atan(1/v) identity
2262 result = 64 - result;
2263 // pin to 63
2264 result -= result >> 6;
2265 }
2266
2267 SkASSERT(result <= 63);
2268 return result;
2269}
2270
2271// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com51baf5a2011-09-21 13:38:36 +00002272#ifdef SK_SCALAR_IS_FLOAT
2273static unsigned SkATan2_255(float y, float x) {
2274 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
2275 static const float g255Over2PI = 40.584510488433314f;
2276
2277 float result = sk_float_atan2(y, x);
2278 if (result < 0) {
2279 result += 2 * SK_ScalarPI;
2280 }
2281 SkASSERT(result >= 0);
2282 // since our value is always >= 0, we can cast to int, which is faster than
2283 // calling floorf()
2284 int ir = (int)(result * g255Over2PI);
2285 SkASSERT(ir >= 0 && ir <= 255);
2286 return ir;
2287}
2288#else
reed@google.com61eb0402011-04-15 12:11:12 +00002289static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2290 if (x == 0) {
2291 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002293 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294 return y < 0 ? 192 : 64;
2295 }
reed@google.com61eb0402011-04-15 12:11:12 +00002296 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002298 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002299
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 /* Find the right quadrant for x,y
2301 Since atan_0_90 only handles the first quadrant, we rotate x,y
2302 appropriately before calling it, and then add the right amount
2303 to account for the real quadrant.
2304 quadrant 0 : add 0 | x > 0 && y > 0
2305 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2306 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2307 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002308
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 map x<0 to (1 << 6)
2310 map y<0 to (3 << 6)
2311 add = map_x ^ map_y
2312 */
2313 int xsign = x >> 31;
2314 int ysign = y >> 31;
2315 int add = ((-xsign) ^ (ysign & 3)) << 6;
2316
2317#ifdef SK_DEBUG
2318 if (0 == add)
2319 SkASSERT(x > 0 && y > 0);
2320 else if (64 == add)
2321 SkASSERT(x < 0 && y > 0);
2322 else if (128 == add)
2323 SkASSERT(x < 0 && y < 0);
2324 else if (192 == add)
2325 SkASSERT(x > 0 && y < 0);
2326 else
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002327 SkDEBUGFAIL("bad value for add");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002329
reed@android.com8a1c16f2008-12-17 15:59:43 +00002330 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2331 where we need to rotate x,y by 90 or -90
2332 */
2333 x = (x ^ xsign) - xsign;
2334 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002335 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002337 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338
2339 unsigned result = add + atan_0_90(y, x);
2340 SkASSERT(result < 256);
2341 return result;
2342}
reed@google.com51baf5a2011-09-21 13:38:36 +00002343#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002345void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
2346 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002347 SkMatrix::MapXYProc proc = fDstToIndexProc;
2348 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002349 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002350 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002351
reed@google.com61eb0402011-04-15 12:11:12 +00002352 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2354 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002355 SkScalar dx, fx = srcPt.fX;
2356 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002357
reed@google.com61eb0402011-04-15 12:11:12 +00002358 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359 SkFixed storage[2];
2360 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2361 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002362 dx = SkFixedToScalar(storage[0]);
2363 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002364 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002366 dx = matrix.getScaleX();
2367 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002368 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002369
reed@google.com61eb0402011-04-15 12:11:12 +00002370 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 *dstC++ = cache[SkATan2_255(fy, fx)];
2372 fx += dx;
2373 fy += dy;
2374 }
reed@google.com61eb0402011-04-15 12:11:12 +00002375 } else { // perspective case
2376 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com51baf5a2011-09-21 13:38:36 +00002378 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2379 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380 }
2381 }
2382}
2383
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002384void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
2385 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002386 SkMatrix::MapXYProc proc = fDstToIndexProc;
2387 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002388 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002389 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 SkPoint srcPt;
2391
reed@google.com61eb0402011-04-15 12:11:12 +00002392 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002393 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2394 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002395 SkScalar dx, fx = srcPt.fX;
2396 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002397
reed@google.com61eb0402011-04-15 12:11:12 +00002398 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 SkFixed storage[2];
2400 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2401 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002402 dx = SkFixedToScalar(storage[0]);
2403 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002404 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002406 dx = matrix.getScaleX();
2407 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002409
reed@google.com61eb0402011-04-15 12:11:12 +00002410 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2412 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002413 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002414 fx += dx;
2415 fy += dy;
2416 }
reed@google.com61eb0402011-04-15 12:11:12 +00002417 } else { // perspective case
2418 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2420 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002421
reed@google.com51baf5a2011-09-21 13:38:36 +00002422 int index = SkATan2_255(srcPt.fY, srcPt.fX);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423 index >>= (8 - kCache16Bits);
2424 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002425 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426 }
2427 }
2428}
2429
reed@google.com61eb0402011-04-15 12:11:12 +00002430///////////////////////////////////////////////////////////////////////////////
2431///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432
2433// assumes colors is SkColor* and pos is SkScalar*
2434#define EXPAND_1_COLOR(count) \
2435 SkColor tmp[2]; \
2436 do { \
2437 if (1 == count) { \
2438 tmp[0] = tmp[1] = colors[0]; \
2439 colors = tmp; \
2440 pos = NULL; \
2441 count = 2; \
2442 } \
2443 } while (0)
2444
reed@google.com61eb0402011-04-15 12:11:12 +00002445SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2446 const SkColor colors[],
2447 const SkScalar pos[], int colorCount,
2448 SkShader::TileMode mode,
2449 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450 if (NULL == pts || NULL == colors || colorCount < 1) {
2451 return NULL;
2452 }
2453 EXPAND_1_COLOR(colorCount);
2454
reed@android.comab840b82009-07-01 17:00:03 +00002455 return SkNEW_ARGS(Linear_Gradient,
2456 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457}
2458
reed@google.com61eb0402011-04-15 12:11:12 +00002459SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2460 const SkColor colors[],
2461 const SkScalar pos[], int colorCount,
2462 SkShader::TileMode mode,
2463 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002464 if (radius <= 0 || NULL == colors || colorCount < 1) {
2465 return NULL;
2466 }
2467 EXPAND_1_COLOR(colorCount);
2468
reed@android.comab840b82009-07-01 17:00:03 +00002469 return SkNEW_ARGS(Radial_Gradient,
2470 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471}
2472
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002473SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2474 SkScalar startRadius,
2475 const SkPoint& end,
2476 SkScalar endRadius,
2477 const SkColor colors[],
2478 const SkScalar pos[],
2479 int colorCount,
2480 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002481 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002482 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2483 return NULL;
2484 }
2485 EXPAND_1_COLOR(colorCount);
2486
2487 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002488 (start, startRadius, end, endRadius, colors, pos,
2489 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002490}
2491
reed@android.com8a1c16f2008-12-17 15:59:43 +00002492SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2493 const SkColor colors[],
2494 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002495 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002496 if (NULL == colors || count < 1) {
2497 return NULL;
2498 }
2499 EXPAND_1_COLOR(count);
2500
2501 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2502}
2503
caryclark@google.comd26147a2011-12-15 14:16:43 +00002504SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
2505 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Linear_Gradient)
2506 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Radial_Gradient)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002507
caryclark@google.comd26147a2011-12-15 14:16:43 +00002508 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sweep_Gradient)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002509
caryclark@google.comd26147a2011-12-15 14:16:43 +00002510 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Radial_Gradient)
2511SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END