blob: 6de820bef89d5de27147d523600bd6ccb92c7e39 [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.comc98a0aa2012-02-02 19:33:08 +000041// Clamp
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
reed@android.com41bccf52009-04-03 13:33:51 +000043static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 return SkClampMax(x, 0xFFFF);
45}
46
reed@google.comc98a0aa2012-02-02 19:33:08 +000047// Repeat
48
reed@android.com41bccf52009-04-03 13:33:51 +000049static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 return x & 0xFFFF;
51}
52
reed@google.comc98a0aa2012-02-02 19:33:08 +000053static inline int repeat_bits(int x, const int bits) {
54 return x & ((1 << bits) - 1);
55}
56
57static inline int repeat_8bits(int x) {
58 return x & 0xFF;
59}
60
61// Mirror
62
epoger@google.com5468c902012-02-02 20:41:45 +000063// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
64// See http://code.google.com/p/skia/issues/detail?id=472
65#if defined(_MSC_VER) && (_MSC_VER >= 1600)
66#pragma optimize("", off)
67#endif
68
reed@android.com41bccf52009-04-03 13:33:51 +000069static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 int s = x << 15 >> 31;
71 return (x ^ s) & 0xFFFF;
72}
73
reed@android.com200645d2009-12-14 16:41:57 +000074static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000075#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000076 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000078 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000079#else
reed@android.com200645d2009-12-14 16:41:57 +000080 int s = x << (31 - bits) >> 31;
81 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000082#endif
83}
84
reed@android.com41bccf52009-04-03 13:33:51 +000085static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000087 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000089 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 return x & 255;
91#else
92 int s = x << 23 >> 31;
93 return (x ^ s) & 0xFF;
94#endif
95}
96
epoger@google.com5468c902012-02-02 20:41:45 +000097#if defined(_MSC_VER) && (_MSC_VER >= 1600)
98#pragma optimize("", on)
99#endif
100
reed@google.com61eb0402011-04-15 12:11:12 +0000101///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102
reed@google.comc98a0aa2012-02-02 19:33:08 +0000103typedef SkFixed (*TileProc)(SkFixed);
104
105static const TileProc gTileProcs[] = {
106 clamp_tileproc,
107 repeat_tileproc,
108 mirror_tileproc
109};
110
111///////////////////////////////////////////////////////////////////////////////
112///////////////////////////////////////////////////////////////////////////////
113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114class Gradient_Shader : public SkShader {
115public:
116 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000117 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 virtual ~Gradient_Shader();
119
120 // overrides
reed@google.com7716afb2011-12-07 15:17:50 +0000121 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
122 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000123 virtual bool isOpaque() const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000125 enum {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000126 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
127 /// it, use a larger cache.
128 kCache16Bits = 8,
129 kGradient16Length = (1 << kCache16Bits),
130 /// Each cache gets 1 extra entry at the end so we don't have to
131 /// test for end-of-cache in lerps. This is also the value used
132 /// to stride *writes* into the dither cache; it must not be zero.
133 /// Total space for a cache is 2x kCache16Count entries: one
134 /// regular cache, one for dithering.
135 kCache16Count = kGradient16Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000136 kCache16Shift = 16 - kCache16Bits,
137 kSqrt16Shift = 8 - kCache16Bits,
138
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000139 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
140 /// it, use a larger cache.
141 kCache32Bits = 8,
142 kGradient32Length = (1 << kCache32Bits),
143 /// Each cache gets 1 extra entry at the end so we don't have to
144 /// test for end-of-cache in lerps. This is also the value used
145 /// to stride *writes* into the dither cache; it must not be zero.
146 /// Total space for a cache is 2x kCache32Count entries: one
147 /// regular cache, one for dithering.
148 kCache32Count = kGradient32Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000149 kCache32Shift = 16 - kCache32Bits,
150 kSqrt32Shift = 8 - kCache32Bits,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000151
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000152 /// This value is used to *read* the dither cache; it may be 0
153 /// if dithering is disabled.
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000154#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000155 kDitherStride32 = kCache32Count,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000156#else
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000157 kDitherStride32 = 0,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000158#endif
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000159 kDitherStride16 = kCache16Count,
160 kLerpRemainderMask32 = (1 << (16 - kCache32Bits)) - 1
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000161 };
162
163
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164protected:
165 Gradient_Shader(SkFlattenableReadBuffer& );
djsollen@google.com54924242012-03-29 15:18:04 +0000166 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 SkUnitMapper* fMapper;
169 SkMatrix fPtsToUnit; // set by subclass
170 SkMatrix fDstToIndex;
171 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 TileMode fTileMode;
173 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000174 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 uint8_t fDstToIndexClass;
176 uint8_t fFlags;
177
178 struct Rec {
179 SkFixed fPos; // 0...1
180 uint32_t fScale; // (1 << 24) / range
181 };
182 Rec* fRecs;
183
reed@google.com7c2f27d2011-03-07 19:29:00 +0000184 const uint16_t* getCache16() const;
185 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186
reed@google.com7c2f27d2011-03-07 19:29:00 +0000187 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000188 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000189
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190private:
191 enum {
192 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
193
reed@android.com1c12abe2009-07-02 15:01:02 +0000194 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 };
196 SkColor fStorage[(kStorageSize + 3) >> 2];
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000197 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
198 bool fColorsAreOpaque;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199
reed@google.com7c2f27d2011-03-07 19:29:00 +0000200 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
201 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
reed@google.com7c2f27d2011-03-07 19:29:00 +0000203 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
204 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000205 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 +0000206
reed@android.com512a8762009-12-14 15:25:36 +0000207 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000208 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
209 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000210 void setCacheAlpha(U8CPU alpha) const;
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000211 void initCommon();
reed@android.com512a8762009-12-14 15:25:36 +0000212
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 typedef SkShader INHERITED;
214};
215
reed@android.com41bccf52009-04-03 13:33:51 +0000216Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
217 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 SkASSERT(colorCount > 1);
219
220 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
221
222 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000223 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
226 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
227 fTileMode = mode;
228 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000229
reed@android.com41bccf52009-04-03 13:33:51 +0000230 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000231 fCache32 = NULL;
232 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233
reed@android.com41bccf52009-04-03 13:33:51 +0000234 /* Note: we let the caller skip the first and/or last position.
235 i.e. pos[0] = 0.3, pos[1] = 0.7
236 In these cases, we insert dummy entries to ensure that the final data
237 will be bracketed by [0, 1].
238 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
239
240 Thus colorCount (the caller's value, and fColorCount (our value) may
241 differ by up to 2. In the above example:
242 colorCount = 2
243 fColorCount = 4
244 */
245 fColorCount = colorCount;
246 // check if we need to add in dummy start and/or end position/colors
247 bool dummyFirst = false;
248 bool dummyLast = false;
249 if (pos) {
250 dummyFirst = pos[0] != 0;
251 dummyLast = pos[colorCount - 1] != SK_Scalar1;
252 fColorCount += dummyFirst + dummyLast;
253 }
254
255 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000256 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000257 fOrigColors = reinterpret_cast<SkColor*>(
258 sk_malloc_throw(size * fColorCount));
259 }
260 else {
261 fOrigColors = fStorage;
262 }
263
264 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 {
reed@android.com41bccf52009-04-03 13:33:51 +0000266 SkColor* origColors = fOrigColors;
267 if (dummyFirst) {
268 *origColors++ = colors[0];
269 }
270 memcpy(origColors, colors, colorCount * sizeof(SkColor));
271 if (dummyLast) {
272 origColors += colorCount;
273 *origColors = colors[colorCount - 1];
274 }
275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276
reed@android.com1c12abe2009-07-02 15:01:02 +0000277 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000278 if (fColorCount > 2) {
279 Rec* recs = fRecs;
280 recs->fPos = 0;
281 // recs->fScale = 0; // unused;
282 recs += 1;
283 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 /* We need to convert the user's array of relative positions into
285 fixed-point positions and scale factors. We need these results
286 to be strictly monotonic (no two values equal or out of order).
287 Hence this complex loop that just jams a zero for the scale
288 value if it sees a segment out of order, and it assures that
289 we start at 0 and end at 1.0
290 */
291 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000292 int startIndex = dummyFirst ? 0 : 1;
293 int count = colorCount + dummyLast;
294 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 // force the last value to be 1.0
296 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000297 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000299 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 }
reed@android.com41bccf52009-04-03 13:33:51 +0000302 // pin curr withing range
303 if (curr < 0) {
304 curr = 0;
305 } else if (curr > SK_Fixed1) {
306 curr = SK_Fixed1;
307 }
308 recs->fPos = curr;
309 if (curr > prev) {
310 recs->fScale = (1 << 24) / (curr - prev);
311 } else {
312 recs->fScale = 0; // ignore this segment
313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 // get ready for the next value
315 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000316 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 }
reed@android.com41bccf52009-04-03 13:33:51 +0000318 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 SkFixed dp = SK_Fixed1 / (colorCount - 1);
320 SkFixed p = dp;
321 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000322 for (int i = 1; i < colorCount; i++) {
323 recs->fPos = p;
324 recs->fScale = scale;
325 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 p += dp;
327 }
328 }
329 }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000330 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331}
332
333Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000334 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 fCacheAlpha = 256;
336
337 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
338
339 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000340 fCache32 = NULL;
341 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342
reed@android.com41bccf52009-04-03 13:33:51 +0000343 int colorCount = fColorCount = buffer.readU32();
344 if (colorCount > kColorStorageCount) {
345 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
346 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
347 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351
352 fTileMode = (TileMode)buffer.readU8();
353 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000354 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 if (colorCount > 2) {
356 Rec* recs = fRecs;
357 recs[0].fPos = 0;
358 for (int i = 1; i < colorCount; i++) {
359 recs[i].fPos = buffer.readS32();
360 recs[i].fScale = buffer.readU32();
361 }
362 }
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000363 buffer.readMatrix(&fPtsToUnit);
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000364 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365}
366
reed@android.com41bccf52009-04-03 13:33:51 +0000367Gradient_Shader::~Gradient_Shader() {
368 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000370 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000371 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000372 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000374 }
reed@google.com82065d62011-02-07 15:30:46 +0000375 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376}
377
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000378void Gradient_Shader::initCommon() {
379 fFlags = 0;
380 unsigned colorAlpha = 0xFF;
381 for (int i = 0; i < fColorCount; i++) {
382 colorAlpha &= SkColorGetA(fOrigColors[i]);
383 }
384 fColorsAreOpaque = colorAlpha == 0xFF;
385}
386
djsollen@google.com54924242012-03-29 15:18:04 +0000387void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 this->INHERITED::flatten(buffer);
389 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000390 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
392 buffer.write8(fTileMode);
393 if (fColorCount > 2) {
394 Rec* recs = fRecs;
395 for (int i = 1; i < fColorCount; i++) {
396 buffer.write32(recs[i].fPos);
397 buffer.write32(recs[i].fScale);
398 }
399 }
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000400 buffer.writeMatrix(fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401}
402
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000403bool Gradient_Shader::isOpaque() const {
404 return fColorsAreOpaque;
405}
406
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407bool Gradient_Shader::setContext(const SkBitmap& device,
408 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000409 const SkMatrix& matrix) {
410 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000412 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413
414 const SkMatrix& inverse = this->getTotalInverse();
415
416 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
417 return false;
418 }
419
420 fDstToIndexProc = fDstToIndex.getMapXYProc();
421 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
422
423 // now convert our colors in to PMColors
424 unsigned paintAlpha = this->getPaintAlpha();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425
426 fFlags = this->INHERITED::getFlags();
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000427 if (fColorsAreOpaque && paintAlpha == 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 fFlags |= kOpaqueAlpha_Flag;
429 }
430 // we can do span16 as long as our individual colors are opaque,
431 // regardless of the paint's alpha
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000432 if (fColorsAreOpaque) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 fFlags |= kHasSpan16_Flag;
434 }
435
reed@google.com95eed982011-07-05 17:01:56 +0000436 this->setCacheAlpha(paintAlpha);
437 return true;
438}
439
440void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 // if the new alpha differs from the previous time we were called, inval our cache
442 // this will trigger the cache to be rebuilt.
443 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000444 if (fCacheAlpha != alpha) {
445 fCache16 = NULL; // inval the cache
446 fCache32 = NULL; // inval the cache
447 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000448 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000449 if (fCache32PixelRef) {
450 fCache32PixelRef->notifyPixelsChanged();
451 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453}
454
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
456
reed@android.com41bccf52009-04-03 13:33:51 +0000457/** We take the original colors, not our premultiplied PMColors, since we can
458 build a 16bit table as long as the original colors are opaque, even if the
459 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460*/
reed@android.com512a8762009-12-14 15:25:36 +0000461void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
462 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 SkASSERT(count > 1);
464 SkASSERT(SkColorGetA(c0) == 0xFF);
465 SkASSERT(SkColorGetA(c1) == 0xFF);
466
467 SkFixed r = SkColorGetR(c0);
468 SkFixed g = SkColorGetG(c0);
469 SkFixed b = SkColorGetB(c0);
470
471 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
472 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
473 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
474
475 r = SkIntToFixed(r) + 0x8000;
476 g = SkIntToFixed(g) + 0x8000;
477 b = SkIntToFixed(b) + 0x8000;
478
479 do {
480 unsigned rr = r >> 16;
481 unsigned gg = g >> 16;
482 unsigned bb = b >> 16;
483 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000484 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 cache += 1;
486 r += dr;
487 g += dg;
488 b += db;
489 } while (--count != 0);
490}
491
reed@google.com55b8e8c2011-01-13 16:22:35 +0000492/*
493 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
494 * semantics of how we 2x2 dither 32->16
495 */
496static inline U8CPU dither_fixed_to_8(SkFixed n) {
497 n >>= 8;
498 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
499}
500
501/*
502 * For dithering with premultiply, we want to ceiling the alpha component,
503 * to ensure that it is always >= any color component.
504 */
505static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
506 n >>= 8;
507 return ((n << 1) - (n | (n >> 8))) >> 8;
508}
509
510void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
511 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 SkASSERT(count > 1);
513
reed@android.com1c12abe2009-07-02 15:01:02 +0000514 // need to apply paintAlpha to our two endpoints
515 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
516 SkFixed da;
517 {
518 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
519 da = SkIntToFixed(tmp - a) / (count - 1);
520 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521
reed@android.com1c12abe2009-07-02 15:01:02 +0000522 SkFixed r = SkColorGetR(c0);
523 SkFixed g = SkColorGetG(c0);
524 SkFixed b = SkColorGetB(c0);
525 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
526 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
527 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528
529 a = SkIntToFixed(a) + 0x8000;
530 r = SkIntToFixed(r) + 0x8000;
531 g = SkIntToFixed(g) + 0x8000;
532 b = SkIntToFixed(b) + 0x8000;
533
534 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000535 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000536 cache[kCache32Count] =
537 SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
538 dither_fixed_to_8(r),
539 dither_fixed_to_8(g),
540 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000541 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 a += da;
543 r += dr;
544 g += dg;
545 b += db;
546 } while (--count != 0);
547}
548
reed@android.com41bccf52009-04-03 13:33:51 +0000549static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 SkASSERT((unsigned)x <= SK_Fixed1);
551 return x - (x >> 16);
552}
553
reed@android.com200645d2009-12-14 16:41:57 +0000554static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000555 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000556 if (6 == bits) {
557 return (x << 10) | (x << 4) | (x >> 2);
558 }
559 if (8 == bits) {
560 return (x << 8) | x;
561 }
562 sk_throw();
563 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564}
565
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000566/** We duplicate the last value in each half of the cache so that
567 interpolation doesn't have to special-case being at the last point.
568*/
569static void complete_16bit_cache(uint16_t* cache, int stride) {
570 cache[stride - 1] = cache[stride - 2];
571 cache[2 * stride - 1] = cache[2 * stride - 2];
572}
573
reed@google.com7c2f27d2011-03-07 19:29:00 +0000574const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000575 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000576 // double the count for dither entries
577 const int entryCount = kCache16Count * 2;
578 const size_t allocSize = sizeof(uint16_t) * entryCount;
579
reed@android.com3c9b2a42009-08-27 19:28:37 +0000580 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000581 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000582 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000584 if (fColorCount == 2) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000585 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
586 kGradient16Length);
reed@android.com41bccf52009-04-03 13:33:51 +0000587 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 Rec* rec = fRecs;
589 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000590 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000591 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 SkASSERT(nextIndex < kCache16Count);
593
594 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000595 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 prevIndex = nextIndex;
597 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000598 // one extra space left over at the end for complete_16bit_cache()
599 SkASSERT(prevIndex == kGradient16Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 }
601
reed@android.com41bccf52009-04-03 13:33:51 +0000602 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000603 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 uint16_t* linear = fCache16; // just computed linear data
605 uint16_t* mapped = fCache16Storage; // storage for mapped data
606 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000607 for (int i = 0; i < kGradient16Length; i++) {
reed@android.com200645d2009-12-14 16:41:57 +0000608 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000610 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 }
612 sk_free(fCache16);
613 fCache16 = fCache16Storage;
614 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000615 complete_16bit_cache(fCache16, kCache16Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 }
617 return fCache16;
618}
619
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000620/** We duplicate the last value in each half of the cache so that
621 interpolation doesn't have to special-case being at the last point.
622*/
623static void complete_32bit_cache(SkPMColor* cache, int stride) {
624 cache[stride - 1] = cache[stride - 2];
625 cache[2 * stride - 1] = cache[2 * stride - 2];
626}
627
reed@google.com7c2f27d2011-03-07 19:29:00 +0000628const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000629 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000630 // double the count for dither entries
631 const int entryCount = kCache32Count * 2;
632 const size_t allocSize = sizeof(SkPMColor) * entryCount;
633
reed@google.comdc731fd2010-12-23 15:19:47 +0000634 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000635 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
636 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000637 }
638 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000639 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000640 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000641 kGradient32Length, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000642 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 Rec* rec = fRecs;
644 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000645 for (int i = 1; i < fColorCount; i++) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000646 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000647 SkASSERT(nextIndex < kGradient32Length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648
649 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000650 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
651 fOrigColors[i],
652 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 prevIndex = nextIndex;
654 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000655 SkASSERT(prevIndex == kGradient32Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 }
657
reed@android.com41bccf52009-04-03 13:33:51 +0000658 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000659 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000660 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000662 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000664 for (int i = 0; i < kGradient32Length; i++) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000665 int index = map->mapUnit16((i << 8) | i) >> 8;
666 mapped[i] = linear[index];
667 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000668 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000669 fCache32PixelRef->unref();
670 fCache32PixelRef = newPR;
671 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000673 complete_32bit_cache(fCache32, kCache32Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 }
675 return fCache32;
676}
677
reed@google.comdc731fd2010-12-23 15:19:47 +0000678/*
679 * Because our caller might rebuild the same (logically the same) gradient
680 * over and over, we'd like to return exactly the same "bitmap" if possible,
681 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
682 * To do that, we maintain a private cache of built-bitmaps, based on our
683 * colors and positions. Note: we don't try to flatten the fMapper, so if one
684 * is present, we skip the cache for now.
685 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000686void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000687 // our caller assumes no external alpha, so we ensure that our cache is
688 // built with 0xFF
689 this->setCacheAlpha(0xFF);
690
reed@google.comdc731fd2010-12-23 15:19:47 +0000691 // don't have a way to put the mapper into our cache-key yet
692 if (fMapper) {
693 // force our cahce32pixelref to be built
694 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000695 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000696 bitmap->setPixelRef(fCache32PixelRef);
697 return;
698 }
699
700 // build our key: [numColors + colors[] + {positions[]} ]
701 int count = 1 + fColorCount;
702 if (fColorCount > 2) {
703 count += fColorCount - 1; // fRecs[].fPos
704 }
705
706 SkAutoSTMalloc<16, int32_t> storage(count);
707 int32_t* buffer = storage.get();
708
709 *buffer++ = fColorCount;
710 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
711 buffer += fColorCount;
712 if (fColorCount > 2) {
713 for (int i = 1; i < fColorCount; i++) {
714 *buffer++ = fRecs[i].fPos;
715 }
716 }
717 SkASSERT(buffer - storage.get() == count);
718
719 ///////////////////////////////////
720
digit@google.com1771cbf2012-01-26 21:26:40 +0000721 SK_DECLARE_STATIC_MUTEX(gMutex);
reed@google.comdc731fd2010-12-23 15:19:47 +0000722 static SkBitmapCache* gCache;
723 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
724 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
725 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000726
reed@google.comdc731fd2010-12-23 15:19:47 +0000727 if (NULL == gCache) {
728 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
729 }
730 size_t size = count * sizeof(int32_t);
731
732 if (!gCache->find(storage.get(), size, bitmap)) {
733 // force our cahce32pixelref to be built
734 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000735 // Only expose the linear section of the cache; don't let the caller
736 // know about the padding at the end to make interpolation faster.
737 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000738 bitmap->setPixelRef(fCache32PixelRef);
739
740 gCache->add(storage.get(), size, *bitmap);
741 }
742}
743
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000744void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
745 if (info) {
746 if (info->fColorCount >= fColorCount) {
747 if (info->fColors) {
748 memcpy(info->fColors, fOrigColors,
749 fColorCount * sizeof(SkColor));
750 }
751 if (info->fColorOffsets) {
752 if (fColorCount == 2) {
753 info->fColorOffsets[0] = 0;
754 info->fColorOffsets[1] = SK_Scalar1;
755 } else if (fColorCount > 2) {
756 for (int i = 0; i < fColorCount; i++)
757 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
758 }
759 }
760 }
761 info->fColorCount = fColorCount;
762 info->fTileMode = fTileMode;
763 }
764}
765
reed@google.com61eb0402011-04-15 12:11:12 +0000766///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767
reed@android.com41bccf52009-04-03 13:33:51 +0000768static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 SkVector vec = pts[1] - pts[0];
770 SkScalar mag = vec.length();
771 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
772
773 vec.scale(inv);
774 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
775 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
776 matrix->postScale(inv, inv);
777}
778
779///////////////////////////////////////////////////////////////////////////////
780
781class Linear_Gradient : public Gradient_Shader {
782public:
783 Linear_Gradient(const SkPoint pts[2],
784 const SkColor colors[], const SkScalar pos[], int colorCount,
785 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000786 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
787 fStart(pts[0]),
788 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 {
790 pts_to_unit_matrix(pts, &fPtsToUnit);
791 }
reed@android.com9b46e772009-06-05 12:24:41 +0000792
reed@google.com7716afb2011-12-07 15:17:50 +0000793 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
794 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
795 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
796 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
797 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
798 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799
djsollen@google.com54924242012-03-29 15:18:04 +0000800 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Linear_Gradient)
801
802protected:
803 Linear_Gradient(SkFlattenableReadBuffer& buffer)
804 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000805 fStart(buffer.readPoint()),
806 fEnd(buffer.readPoint()) {
djsollen@google.com54924242012-03-29 15:18:04 +0000807 }
808 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000809 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000810 buffer.writePoint(fStart);
811 buffer.writePoint(fEnd);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000812 }
813
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814private:
815 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000816 const SkPoint fStart;
817 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818};
819
reed@android.com5119bdb2009-06-12 21:27:03 +0000820bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
821 const SkMatrix& matrix) {
822 if (!this->INHERITED::setContext(device, paint, matrix)) {
823 return false;
824 }
825
826 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
827 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000828 fFlags |= SkShader::kConstInY32_Flag;
829 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
830 // only claim this if we do have a 16bit mode (i.e. none of our
831 // colors have alpha), and if we are not dithering (which obviously
832 // is not const in Y).
833 fFlags |= SkShader::kConstInY16_Flag;
834 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000835 }
836 return true;
837}
838
reed@google.com5eb158d2011-04-15 15:50:34 +0000839#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000840 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000841 unsigned fi = fx >> Gradient_Shader::kCache32Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000842 SkASSERT(fi <= 0xFF); \
843 fx += dx; \
844 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000845 toggle ^= Gradient_Shader::kDitherStride32; \
reed@google.com13659f12011-04-18 19:59:38 +0000846 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000847
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000848namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +0000849
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000850typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000851 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000852 int toggle, int count);
853
tomhudson@google.com13a847a2012-01-20 13:59:14 +0000854// This function is deprecated, and will be replaced by
855// shadeSpan_linear_vertical_lerp() once Chrome has been weaned off of it.
856void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
857 SkPMColor* SK_RESTRICT dstC,
858 const SkPMColor* SK_RESTRICT cache,
859 int toggle, int count) {
860 // We're a vertical gradient, so no change in a span.
861 // If colors change sharply across the gradient, dithering is
862 // insufficient (it subsamples the color space) and we need to lerp.
863 unsigned fullIndex = proc(fx);
864 unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
865 sk_memset32_dither(dstC,
866 cache[toggle + fi],
867 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
868 count);
869}
870
871// Linear interpolation (lerp) is unnecessary if there are no sharp
872// discontinuities in the gradient - which must be true if there are
873// only 2 colors - but it's cheap.
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000874void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
875 SkPMColor* SK_RESTRICT dstC,
876 const SkPMColor* SK_RESTRICT cache,
877 int toggle, int count) {
878 // We're a vertical gradient, so no change in a span.
879 // If colors change sharply across the gradient, dithering is
880 // insufficient (it subsamples the color space) and we need to lerp.
881 unsigned fullIndex = proc(fx);
882 unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
883 unsigned remainder = fullIndex & Gradient_Shader::kLerpRemainderMask32;
884 SkPMColor lerp =
885 SkFastFourByteInterp(
886 cache[toggle + fi + 1],
887 cache[toggle + fi], remainder);
888 SkPMColor dlerp =
889 SkFastFourByteInterp(
890 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi + 1],
891 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi], remainder);
892 sk_memset32_dither(dstC, lerp, dlerp, count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000893}
894
895void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
896 SkPMColor* SK_RESTRICT dstC,
897 const SkPMColor* SK_RESTRICT cache,
898 int toggle, int count) {
899 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000900 range.init(fx, dx, count, 0, Gradient_Shader::kGradient32Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000901
902 if ((count = range.fCount0) > 0) {
903 sk_memset32_dither(dstC,
904 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000905 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000906 count);
907 dstC += count;
908 }
909 if ((count = range.fCount1) > 0) {
910 int unroll = count >> 3;
911 fx = range.fFx1;
912 for (int i = 0; i < unroll; i++) {
913 NO_CHECK_ITER; NO_CHECK_ITER;
914 NO_CHECK_ITER; NO_CHECK_ITER;
915 NO_CHECK_ITER; NO_CHECK_ITER;
916 NO_CHECK_ITER; NO_CHECK_ITER;
917 }
918 if ((count &= 7) > 0) {
919 do {
920 NO_CHECK_ITER;
921 } while (--count != 0);
922 }
923 }
924 if ((count = range.fCount2) > 0) {
925 sk_memset32_dither(dstC,
926 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000927 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000928 count);
929 }
930}
931
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000932void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
933 SkPMColor* SK_RESTRICT dstC,
934 const SkPMColor* SK_RESTRICT cache,
935 int toggle, int count) {
936 do {
937 unsigned fi = mirror_8bits(fx >> 8);
938 SkASSERT(fi <= 0xFF);
939 fx += dx;
940 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000941 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000942 } while (--count != 0);
943}
944
945void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
946 SkPMColor* SK_RESTRICT dstC,
947 const SkPMColor* SK_RESTRICT cache,
948 int toggle, int count) {
949 do {
950 unsigned fi = repeat_8bits(fx >> 8);
951 SkASSERT(fi <= 0xFF);
952 fx += dx;
953 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000954 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000955 } while (--count != 0);
956}
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000957
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000958}
959
960void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
961 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 SkASSERT(count > 0);
963
964 SkPoint srcPt;
965 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
966 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000967 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com0e734bd2011-12-08 17:24:44 +0000968#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000969 int toggle = ((x ^ y) & 1) * kDitherStride32;
reed@google.com0e734bd2011-12-08 17:24:44 +0000970#else
971 int toggle = 0;
reed@google.com0e734bd2011-12-08 17:24:44 +0000972#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973
reed@android.comc552a432009-06-12 20:02:50 +0000974 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000975 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
976 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
978
reed@android.comc552a432009-06-12 20:02:50 +0000979 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 SkFixed dxStorage[1];
981 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
982 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000983 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
985 dx = SkScalarToFixed(fDstToIndex.getScaleX());
986 }
987
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000988 LinearShadeProc shadeProc = shadeSpan_linear_repeat;
reed@android.comc552a432009-06-12 20:02:50 +0000989 if (SkFixedNearlyZero(dx)) {
tomhudson@google.com13a847a2012-01-20 13:59:14 +0000990#ifdef SK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS
991 if (fColorCount > 2) {
992 shadeProc = shadeSpan_linear_vertical_lerp;
993 } else {
994 shadeProc = shadeSpan_linear_vertical;
995 }
996#else
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000997 shadeProc = shadeSpan_linear_vertical_lerp;
tomhudson@google.com13a847a2012-01-20 13:59:14 +0000998#endif
reed@android.comc552a432009-06-12 20:02:50 +0000999 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001000 shadeProc = shadeSpan_linear_clamp;
reed@android.comc552a432009-06-12 20:02:50 +00001001 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001002 shadeProc = shadeSpan_linear_mirror;
reed@android.comc552a432009-06-12 20:02:50 +00001003 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001006 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comc552a432009-06-12 20:02:50 +00001007 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 SkScalar dstX = SkIntToScalar(x);
1009 SkScalar dstY = SkIntToScalar(y);
1010 do {
1011 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1012 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1013 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001014 *dstC++ = cache[toggle + (fi >> kCache32Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001015 toggle ^= Gradient_Shader::kDitherStride32;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 dstX += SK_Scalar1;
1017 } while (--count != 0);
1018 }
1019}
1020
reed@google.com55b8e8c2011-01-13 16:22:35 +00001021SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +00001022 SkMatrix* matrix,
1023 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +00001024 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001026 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 }
1028 if (matrix) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001029 matrix->setScale(SkIntToScalar(kGradient32Length), SK_Scalar1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 matrix->preConcat(fPtsToUnit);
1031 }
1032 if (xy) {
1033 xy[0] = fTileMode;
1034 xy[1] = kClamp_TileMode;
1035 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001036 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037}
1038
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001039SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
1040 if (info) {
1041 commonAsAGradient(info);
1042 info->fPoint[0] = fStart;
1043 info->fPoint[1] = fEnd;
1044 }
1045 return kLinear_GradientType;
1046}
1047
reed@android.com3c9b2a42009-08-27 19:28:37 +00001048static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
1049 int count) {
1050 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 *dst++ = value;
1052 count -= 1;
1053 SkTSwap(value, other);
1054 }
1055
1056 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001057
reed@android.com3c9b2a42009-08-27 19:28:37 +00001058 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001060 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062
reed@google.com5eb158d2011-04-15 15:50:34 +00001063#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +00001064 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001065 unsigned fi = fx >> Gradient_Shader::kCache16Shift; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001066 SkASSERT(fi < Gradient_Shader::kCache16Count); \
reed@google.com5eb158d2011-04-15 15:50:34 +00001067 fx += dx; \
1068 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001069 toggle ^= Gradient_Shader::kDitherStride16; \
reed@google.com13659f12011-04-18 19:59:38 +00001070 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +00001071
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001072namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +00001073
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001074typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001075 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001076 int toggle, int count);
1077
1078void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
1079 uint16_t* SK_RESTRICT dstC,
1080 const uint16_t* SK_RESTRICT cache,
1081 int toggle, int count) {
1082 // we're a vertical gradient, so no change in a span
1083 unsigned fi = proc(fx) >> Gradient_Shader::kCache16Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001084 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001085 dither_memset16(dstC, cache[toggle + fi],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001086 cache[(toggle ^ Gradient_Shader::kDitherStride16) + fi], count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001087
1088}
1089
1090void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
1091 uint16_t* SK_RESTRICT dstC,
1092 const uint16_t* SK_RESTRICT cache,
1093 int toggle, int count) {
1094 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001095 range.init(fx, dx, count, 0, Gradient_Shader::kGradient16Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001096
1097 if ((count = range.fCount0) > 0) {
1098 dither_memset16(dstC,
1099 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001100 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001101 count);
1102 dstC += count;
1103 }
1104 if ((count = range.fCount1) > 0) {
1105 int unroll = count >> 3;
1106 fx = range.fFx1;
1107 for (int i = 0; i < unroll; i++) {
1108 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1109 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1110 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1111 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1112 }
1113 if ((count &= 7) > 0) {
1114 do {
1115 NO_CHECK_ITER_16;
1116 } while (--count != 0);
1117 }
1118 }
1119 if ((count = range.fCount2) > 0) {
1120 dither_memset16(dstC,
1121 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001122 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001123 count);
1124 }
1125}
1126
1127void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
1128 uint16_t* SK_RESTRICT dstC,
1129 const uint16_t* SK_RESTRICT cache,
1130 int toggle, int count) {
1131 do {
1132 unsigned fi = mirror_bits(fx >> Gradient_Shader::kCache16Shift,
1133 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001134 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001135 fx += dx;
1136 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001137 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001138 } while (--count != 0);
1139}
1140
1141void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
1142 uint16_t* SK_RESTRICT dstC,
1143 const uint16_t* SK_RESTRICT cache,
1144 int toggle, int count) {
1145 SkASSERT(proc == repeat_tileproc);
1146 do {
1147 unsigned fi = repeat_bits(fx >> Gradient_Shader::kCache16Shift,
1148 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001149 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001150 fx += dx;
1151 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001152 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001153 } while (--count != 0);
1154}
1155}
1156
1157void Linear_Gradient::shadeSpan16(int x, int y,
1158 uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159 SkASSERT(count > 0);
1160
1161 SkPoint srcPt;
1162 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1163 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001164 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001165 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001167 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001168 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1169 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1171
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001172 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 SkFixed dxStorage[1];
1174 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1175 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001176 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1178 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1179 }
1180
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001181 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001182 if (SkFixedNearlyZero(dx)) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001183 shadeProc = shadeSpan16_linear_vertical;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001184 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001185 shadeProc = shadeSpan16_linear_clamp;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001186 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001187 shadeProc = shadeSpan16_linear_mirror;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001188 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001191 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001192 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 SkScalar dstX = SkIntToScalar(x);
1194 SkScalar dstY = SkIntToScalar(y);
1195 do {
1196 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1197 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1198 SkASSERT(fi <= 0xFFFF);
1199
reed@android.com512a8762009-12-14 15:25:36 +00001200 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001202 toggle ^= Gradient_Shader::kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203
1204 dstX += SK_Scalar1;
1205 } while (--count != 0);
1206 }
1207}
1208
1209///////////////////////////////////////////////////////////////////////////////
1210
1211#define kSQRT_TABLE_BITS 11
1212#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1213
1214#include "SkRadialGradient_Table.h"
1215
1216#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1217
1218#include <stdio.h>
1219
reed@google.com61eb0402011-04-15 12:11:12 +00001220void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1222
1223 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1224 SkASSERT(file);
1225 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1226
reed@google.com61eb0402011-04-15 12:11:12 +00001227 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1228 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001230 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231
1232 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1233
1234 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001235 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001237 }
1238 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001240 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 }
1242 ::fprintf(file, "};\n");
1243 ::fclose(file);
1244}
1245
1246#endif
1247
1248
reed@google.com61eb0402011-04-15 12:11:12 +00001249static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1250 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 SkScalar inv = SkScalarInvert(radius);
1252
1253 matrix->setTranslate(-center.fX, -center.fY);
1254 matrix->postScale(inv, inv);
1255}
1256
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001257
1258namespace {
1259
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001260typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
1261 SkScalar sfy, SkScalar sdy,
1262 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001263 int toggle, int count);
1264
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001265void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
1266 SkScalar sfy, SkScalar sdy,
1267 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001268 int toggle, int count) {
1269 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
1270
1271 /* knock these down so we can pin against +- 0x7FFF, which is an
1272 immediate load, rather than 0xFFFF which is slower. This is a
1273 compromise, since it reduces our precision, but that appears
1274 to be visually OK. If we decide this is OK for all of our cases,
1275 we could (it seems) put this scale-down into fDstToIndex,
1276 to avoid having to do these extra shifts each time.
1277 */
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001278 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1279 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1280 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1281 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001282 // might perform this check for the other modes,
1283 // but the win will be a smaller % of the total
1284 if (dy == 0) {
1285 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1286 fy *= fy;
1287 do {
1288 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1289 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1290 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1291 fx += dx;
1292 *dstC++ = cache[toggle +
1293 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001294 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001295 } while (--count != 0);
1296 } else {
1297 do {
1298 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1299 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1300 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1301 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1302 fx += dx;
1303 fy += dy;
1304 *dstC++ = cache[toggle +
1305 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001306 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001307 } while (--count != 0);
1308 }
1309}
1310
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001311void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
1312 SkScalar sfy, SkScalar sdy,
1313 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001314 int toggle, int count) {
1315 do {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001316#ifdef SK_SCALAR_IS_FLOAT
1317 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1318 SkFixed dist = SkFloatToFixed(fdist);
1319#else
1320 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1321 SkFixedSquare(sfy);
1322 if (magnitudeSquared < 0) // Overflow.
1323 magnitudeSquared = SK_FixedMax;
1324 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1325#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001326 unsigned fi = mirror_tileproc(dist);
1327 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001328 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001329 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001330 sfx += sdx;
1331 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001332 } while (--count != 0);
1333}
1334
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001335void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
1336 SkScalar sfy, SkScalar sdy,
1337 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001338 int toggle, int count) {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001339 SkFixed fx = SkScalarToFixed(sfx);
1340 SkFixed dx = SkScalarToFixed(sdx);
1341 SkFixed fy = SkScalarToFixed(sfy);
1342 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001343 do {
1344 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1345 unsigned fi = repeat_tileproc(dist);
1346 SkASSERT(fi <= 0xFFFF);
1347 fx += dx;
1348 fy += dy;
1349 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001350 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001351 } while (--count != 0);
1352}
1353
1354}
1355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356class Radial_Gradient : public Gradient_Shader {
1357public:
1358 Radial_Gradient(const SkPoint& center, SkScalar radius,
1359 const SkColor colors[], const SkScalar pos[], int colorCount,
1360 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001361 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1362 fCenter(center),
1363 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 {
1365 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1366 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1367
1368 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1369 }
reed@google.com61eb0402011-04-15 12:11:12 +00001370
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001371 virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
1372 SK_OVERRIDE;
reed@google.com7096dc62012-05-11 18:01:50 +00001373 virtual void shadeSpan16(int x, int y, uint16_t* dstCParam,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001374 int count) SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 SkASSERT(count > 0);
1376
reed@google.com7096dc62012-05-11 18:01:50 +00001377 uint16_t* SK_RESTRICT dstC = dstCParam;
1378
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 SkPoint srcPt;
1380 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1381 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001382 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001383 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384
reed@android.com3c9b2a42009-08-27 19:28:37 +00001385 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001386 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1387 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001388
1389 SkScalar sdx = fDstToIndex.getScaleX();
1390 SkScalar sdy = fDstToIndex.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391
reed@android.com3c9b2a42009-08-27 19:28:37 +00001392 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 SkFixed storage[2];
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001394 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1395 &storage[0], &storage[1]);
1396 sdx = SkFixedToScalar(storage[0]);
1397 sdy = SkFixedToScalar(storage[1]);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001398 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 }
1401
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001402 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001403 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001404 shadeProc = shadeSpan16_radial_clamp;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001405 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001406 shadeProc = shadeSpan16_radial_mirror;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001407 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 }
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001410 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
1411 cache, toggle, count);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001412 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 SkScalar dstX = SkIntToScalar(x);
1414 SkScalar dstY = SkIntToScalar(y);
1415 do {
1416 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1417 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1418 SkASSERT(fi <= 0xFFFF);
1419
1420 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001422 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423
1424 dstX += SK_Scalar1;
1425 } while (--count != 0);
1426 }
1427 }
1428
reed@google.com55b8e8c2011-01-13 16:22:35 +00001429 virtual BitmapType asABitmap(SkBitmap* bitmap,
1430 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001431 TileMode* xy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001432 SkScalar* twoPointRadialParams)
1433 const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001434 if (bitmap) {
1435 this->commonAsABitmap(bitmap);
1436 }
1437 if (matrix) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001438 matrix->setScale(SkIntToScalar(kGradient32Length),
1439 SkIntToScalar(kGradient32Length));
reed@google.comdc731fd2010-12-23 15:19:47 +00001440 matrix->preConcat(fPtsToUnit);
1441 }
1442 if (xy) {
1443 xy[0] = fTileMode;
1444 xy[1] = kClamp_TileMode;
1445 }
1446 return kRadial_BitmapType;
1447 }
reed@google.com7716afb2011-12-07 15:17:50 +00001448 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001449 if (info) {
1450 commonAsAGradient(info);
1451 info->fPoint[0] = fCenter;
1452 info->fRadius[0] = fRadius;
1453 }
1454 return kRadial_GradientType;
1455 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001456
djsollen@google.comba28d032012-03-26 17:57:35 +00001457 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Radial_Gradient)
1458
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001460 Radial_Gradient(SkFlattenableReadBuffer& buffer)
djsollen@google.com54924242012-03-29 15:18:04 +00001461 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001462 fCenter(buffer.readPoint()),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001463 fRadius(buffer.readScalar()) {
1464 }
djsollen@google.com54924242012-03-29 15:18:04 +00001465 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
1466 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001467 buffer.writePoint(fCenter);
djsollen@google.com54924242012-03-29 15:18:04 +00001468 buffer.writeScalar(fRadius);
1469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470
1471private:
1472 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001473 const SkPoint fCenter;
1474 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475};
1476
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001477namespace {
1478
1479inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001480 // fast, overly-conservative test: checks unit square instead
1481 // of unit circle
1482 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
1483 (fx <= -SK_FixedHalf && dx <= 0);
1484 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
1485 (fy <= -SK_FixedHalf && dy <= 0);
1486
1487 return xClamped || yClamped;
1488}
1489
1490// Return true if (fx * fy) is always inside the unit circle
1491// SkPin32 is expensive, but so are all the SkFixedMul in this test,
1492// so it shouldn't be run if count is small.
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001493inline bool no_need_for_radial_pin(int fx, int dx,
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001494 int fy, int dy, int count) {
1495 SkASSERT(count > 0);
1496 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1497 return false;
1498 }
1499 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
1500 return false;
1501 }
1502 fx += (count - 1) * dx;
1503 fy += (count - 1) * dy;
1504 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1505 return false;
1506 }
1507 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
1508}
1509
1510#define UNPINNED_RADIAL_STEP \
1511 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001512 *dstC++ = cache[toggle + \
1513 (sqrt_table[fi] >> Gradient_Shader::kSqrt32Shift)]; \
1514 toggle ^= Gradient_Shader::kDitherStride32; \
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001515 fx += dx; \
1516 fy += dy;
1517
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001518typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
1519 SkScalar sfy, SkScalar sdy,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001520 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001521 int count, int toggle);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001522
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001523// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001524void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
1525 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001526 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001527 int count, int toggle) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001528 // Floating point seems to be slower than fixed point,
1529 // even when we have float hardware.
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001530 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001531 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1532 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1533 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1534 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001535 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001536 unsigned fi = Gradient_Shader::kGradient32Length;
1537 sk_memset32_dither(dstC,
1538 cache[toggle + fi],
1539 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
1540 count);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001541 } else if ((count > 4) &&
1542 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
1543 unsigned fi;
1544 // 4x unroll appears to be no faster than 2x unroll on Linux
1545 while (count > 1) {
1546 UNPINNED_RADIAL_STEP;
1547 UNPINNED_RADIAL_STEP;
1548 count -= 2;
1549 }
1550 if (count) {
1551 UNPINNED_RADIAL_STEP;
1552 }
1553 }
1554 else {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001555 // Specializing for dy == 0 gains us 25% on Skia benchmarks
1556 if (dy == 0) {
1557 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1558 yy *= yy;
1559 do {
1560 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1561 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
1562 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001563 *dstC++ = cache[toggle + (sqrt_table[fi] >>
1564 Gradient_Shader::kSqrt32Shift)];
1565 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001566 fx += dx;
1567 } while (--count != 0);
1568 } else {
1569 do {
1570 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1571 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1572 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1573 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001574 *dstC++ = cache[toggle + (sqrt_table[fi] >>
1575 Gradient_Shader::kSqrt32Shift)];
1576 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001577 fx += dx;
1578 fy += dy;
1579 } while (--count != 0);
1580 }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001581 }
1582}
1583
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001584// Unrolling this loop doesn't seem to help (when float); we're stalling to
1585// get the results of the sqrt (?), and don't have enough extra registers to
1586// have many in flight.
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001587void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
1588 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001589 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001590 int count, int toggle) {
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001591 do {
tomhudson@google.comf925ba42012-01-11 21:54:26 +00001592#ifdef SK_SCALAR_IS_FLOAT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001593 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1594 SkFixed dist = SkFloatToFixed(fdist);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001595#else
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001596 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1597 SkFixedSquare(sfy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001598 if (magnitudeSquared < 0) // Overflow.
1599 magnitudeSquared = SK_FixedMax;
1600 SkFixed dist = SkFixedSqrt(magnitudeSquared);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001601#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001602 unsigned fi = mirror_tileproc(dist);
1603 SkASSERT(fi <= 0xFFFF);
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001604 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
1605 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001606 sfx += sdx;
1607 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001608 } while (--count != 0);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001609}
1610
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001611void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
1612 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001613 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001614 int count, int toggle) {
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001615 SkFixed fx = SkScalarToFixed(sfx);
1616 SkFixed dx = SkScalarToFixed(sdx);
1617 SkFixed fy = SkScalarToFixed(sfy);
1618 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001619 do {
1620 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1621 SkFixedSquare(fy);
1622 if (magnitudeSquared < 0) // Overflow.
1623 magnitudeSquared = SK_FixedMax;
1624 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1625 unsigned fi = repeat_tileproc(dist);
1626 SkASSERT(fi <= 0xFFFF);
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001627 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
1628 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001629 fx += dx;
1630 fy += dy;
1631 } while (--count != 0);
1632}
1633}
1634
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001635void Radial_Gradient::shadeSpan(int x, int y,
1636 SkPMColor* SK_RESTRICT dstC, int count) {
1637 SkASSERT(count > 0);
1638
1639 SkPoint srcPt;
1640 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1641 TileProc proc = fTileProc;
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001642 const SkPMColor* SK_RESTRICT cache = this->getCache32();
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001643#ifdef USE_DITHER_32BIT_GRADIENT
1644 int toggle = ((x ^ y) & 1) * Gradient_Shader::kDitherStride32;
1645#else
1646 int toggle = 0;
1647#endif
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001648
1649 if (fDstToIndexClass != kPerspective_MatrixClass) {
1650 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1651 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001652 SkScalar sdx = fDstToIndex.getScaleX();
1653 SkScalar sdy = fDstToIndex.getSkewY();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001654
1655 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1656 SkFixed storage[2];
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001657 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1658 &storage[0], &storage[1]);
1659 sdx = SkFixedToScalar(storage[0]);
1660 sdy = SkFixedToScalar(storage[1]);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001661 } else {
1662 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001663 }
1664
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001665 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001666 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001667 shadeProc = shadeSpan_radial_clamp;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001668 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001669 shadeProc = shadeSpan_radial_mirror;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001670 } else {
1671 SkASSERT(proc == repeat_tileproc);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001672 }
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001673 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001674 } else { // perspective case
1675 SkScalar dstX = SkIntToScalar(x);
1676 SkScalar dstY = SkIntToScalar(y);
1677 do {
1678 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1679 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1680 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001681 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001682 dstX += SK_Scalar1;
1683 } while (--count != 0);
1684 }
1685}
1686
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001687/* Two-point radial gradients are specified by two circles, each with a center
1688 point and radius. The gradient can be considered to be a series of
1689 concentric circles, with the color interpolated from the start circle
1690 (at t=0) to the end circle (at t=1).
1691
1692 For each point (x, y) in the span, we want to find the
1693 interpolated circle that intersects that point. The center
1694 of the desired circle (Cx, Cy) falls at some distance t
1695 along the line segment between the start point (Sx, Sy) and
1696 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001697
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001698 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1699 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001700
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001701 The radius of the desired circle (r) is also a linear interpolation t
1702 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001703
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001704 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001705
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001706 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001707
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001708 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001709
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001710 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001711
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001712 (x - ((1 - t) * Sx + t * Ex))^2
1713 + (y - ((1 - t) * Sy + t * Ey))^2
1714 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001715
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001716 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001717
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001718 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1719 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1720 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001721
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001722 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1723
1724 [Dx^2 + Dy^2 - Dr^2)] * t^2
1725 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1726 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001727
1728 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001729 possible circles on which the point may fall. Solving for t yields
1730 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001731
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001732 If a<0, the start circle is entirely contained in the
1733 end circle, and one of the roots will be <0 or >1 (off the line
1734 segment). If a>0, the start circle falls at least partially
1735 outside the end circle (or vice versa), and the gradient
1736 defines a "tube" where a point may be on one circle (on the
1737 inside of the tube) or the other (outside of the tube). We choose
1738 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001739
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001740 In order to keep the math to within the limits of fixed point,
1741 we divide the entire quadratic by Dr^2, and replace
1742 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001743
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001744 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1745 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1746 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001747
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001748 (x' and y' are computed by appending the subtract and scale to the
1749 fDstToIndex matrix in the constructor).
1750
1751 Since the 'A' component of the quadratic is independent of x' and y', it
1752 is precomputed in the constructor. Since the 'B' component is linear in
1753 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001754 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001755 a perspective projection), it must be computed in the loop.
1756
1757*/
1758
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001759namespace {
1760
1761inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1762 SkScalar sr2d2, SkScalar foura,
1763 SkScalar oneOverTwoA, bool posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001764 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001765 if (0 == foura) {
1766 return SkScalarToFixed(SkScalarDiv(-c, b));
1767 }
1768
reed@google.com84e9c082011-04-13 17:44:24 +00001769 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001770 if (discrim < 0) {
1771 discrim = -discrim;
1772 }
reed@google.com84e9c082011-04-13 17:44:24 +00001773 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1774 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001775 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001776 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001777 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001778 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001779 }
reed@google.com84e9c082011-04-13 17:44:24 +00001780 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001781}
1782
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001783typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
1784 SkScalar fy, SkScalar dy,
1785 SkScalar b, SkScalar db,
1786 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001787 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001788 int count);
1789
1790void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
1791 SkScalar fy, SkScalar dy,
1792 SkScalar b, SkScalar db,
1793 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001794 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001795 int count) {
1796 for (; count > 0; --count) {
1797 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1798 fOneOverTwoA, posRoot);
1799 SkFixed index = SkClampMax(t, 0xFFFF);
1800 SkASSERT(index <= 0xFFFF);
1801 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1802 fx += dx;
1803 fy += dy;
1804 b += db;
1805 }
1806}
1807void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
1808 SkScalar fy, SkScalar dy,
1809 SkScalar b, SkScalar db,
1810 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001811 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001812 int count) {
1813 for (; count > 0; --count) {
1814 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1815 fOneOverTwoA, posRoot);
1816 SkFixed index = mirror_tileproc(t);
1817 SkASSERT(index <= 0xFFFF);
1818 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1819 fx += dx;
1820 fy += dy;
1821 b += db;
1822 }
1823}
1824
1825void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
1826 SkScalar fy, SkScalar dy,
1827 SkScalar b, SkScalar db,
1828 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001829 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001830 int count) {
1831 for (; count > 0; --count) {
1832 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1833 fOneOverTwoA, posRoot);
1834 SkFixed index = repeat_tileproc(t);
1835 SkASSERT(index <= 0xFFFF);
1836 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1837 fx += dx;
1838 fy += dy;
1839 b += db;
1840 }
1841}
1842
1843
1844
1845}
1846
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001847class Two_Point_Radial_Gradient : public Gradient_Shader {
1848public:
1849 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1850 const SkPoint& end, SkScalar endRadius,
1851 const SkColor colors[], const SkScalar pos[],
1852 int colorCount, SkShader::TileMode mode,
1853 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001854 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1855 fCenter1(start),
1856 fCenter2(end),
1857 fRadius1(startRadius),
1858 fRadius2(endRadius) {
1859 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001860 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001861
1862 virtual BitmapType asABitmap(SkBitmap* bitmap,
1863 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001864 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001865 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001866 if (bitmap) {
1867 this->commonAsABitmap(bitmap);
1868 }
1869 SkScalar diffL = 0; // just to avoid gcc warning
1870 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001871 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001872 SkScalarSquare(fDiff.fY));
1873 }
1874 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001875 if (diffL) {
1876 SkScalar invDiffL = SkScalarInvert(diffL);
1877 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1878 SkScalarMul(invDiffL, fDiff.fX));
1879 } else {
1880 matrix->reset();
1881 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001882 matrix->preConcat(fPtsToUnit);
1883 }
1884 if (xy) {
1885 xy[0] = fTileMode;
1886 xy[1] = kClamp_TileMode;
1887 }
1888 if (NULL != twoPointRadialParams) {
1889 twoPointRadialParams[0] = diffL;
1890 twoPointRadialParams[1] = fStartRadius;
1891 twoPointRadialParams[2] = fDiffRadius;
1892 }
1893 return kTwoPointRadial_BitmapType;
1894 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001895
reed@google.com8e6d9142011-12-07 15:30:34 +00001896 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001897 if (info) {
1898 commonAsAGradient(info);
1899 info->fPoint[0] = fCenter1;
1900 info->fPoint[1] = fCenter2;
1901 info->fRadius[0] = fRadius1;
1902 info->fRadius[1] = fRadius2;
1903 }
1904 return kRadial2_GradientType;
1905 }
1906
reed@google.com7096dc62012-05-11 18:01:50 +00001907 virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001908 int count) SK_OVERRIDE {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001909 SkASSERT(count > 0);
1910
reed@google.com7096dc62012-05-11 18:01:50 +00001911 SkPMColor* SK_RESTRICT dstC = dstCParam;
1912
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001913 // Zero difference between radii: fill with transparent black.
1914 if (fDiffRadius == 0) {
1915 sk_bzero(dstC, count * sizeof(*dstC));
1916 return;
1917 }
1918 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1919 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001920 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001921
1922 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001923 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001924 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001925 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001926 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1927 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001928 SkScalar dx, fx = srcPt.fX;
1929 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001930
reed@google.com61eb0402011-04-15 12:11:12 +00001931 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001932 SkFixed fixedX, fixedY;
1933 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1934 dx = SkFixedToScalar(fixedX);
1935 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001936 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001937 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001938 dx = fDstToIndex.getScaleX();
1939 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001940 }
reed@google.com84e9c082011-04-13 17:44:24 +00001941 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1942 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1943 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1944 SkScalarMul(fDiff.fY, dy)) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001945
1946 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
reed@google.com61eb0402011-04-15 12:11:12 +00001947 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001948 shadeProc = shadeSpan_twopoint_clamp;
reed@google.com61eb0402011-04-15 12:11:12 +00001949 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001950 shadeProc = shadeSpan_twopoint_mirror;
reed@google.com61eb0402011-04-15 12:11:12 +00001951 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001952 SkASSERT(proc == repeat_tileproc);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001953 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001954 (*shadeProc)(fx, dx, fy, dy, b, db,
1955 fSr2D2, foura, fOneOverTwoA, posRoot,
1956 dstC, cache, count);
reed@google.com61eb0402011-04-15 12:11:12 +00001957 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001958 SkScalar dstX = SkIntToScalar(x);
1959 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001960 for (; count > 0; --count) {
1961 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001962 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001963 SkScalar fx = srcPt.fX;
1964 SkScalar fy = srcPt.fY;
1965 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1966 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001967 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1968 fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001969 SkFixed index = proc(t);
1970 SkASSERT(index <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001971 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
reed@android.com6c59a172009-09-22 20:24:05 +00001972 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001973 }
1974 }
1975 }
1976
reed@android.com6c59a172009-09-22 20:24:05 +00001977 virtual bool setContext(const SkBitmap& device,
1978 const SkPaint& paint,
reed@google.com8e6d9142011-12-07 15:30:34 +00001979 const SkMatrix& matrix) SK_OVERRIDE {
1980 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com6c59a172009-09-22 20:24:05 +00001981 return false;
1982 }
1983
1984 // we don't have a span16 proc
1985 fFlags &= ~kHasSpan16_Flag;
1986 return true;
1987 }
1988
djsollen@google.com54924242012-03-29 15:18:04 +00001989 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Radial_Gradient)
1990
1991protected:
1992 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
1993 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001994 fCenter1(buffer.readPoint()),
1995 fCenter2(buffer.readPoint()),
djsollen@google.com54924242012-03-29 15:18:04 +00001996 fRadius1(buffer.readScalar()),
1997 fRadius2(buffer.readScalar()) {
1998 init();
1999 };
2000
2001 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
reed@android.combcfc7332009-11-10 16:19:39 +00002002 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002003 buffer.writePoint(fCenter1);
2004 buffer.writePoint(fCenter2);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002005 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 +00002009private:
2010 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002011 const SkPoint fCenter1;
2012 const SkPoint fCenter2;
2013 const SkScalar fRadius1;
2014 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002015 SkPoint fDiff;
2016 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002017
2018 void init() {
2019 fDiff = fCenter1 - fCenter2;
2020 fDiffRadius = fRadius2 - fRadius1;
2021 SkScalar inv = SkScalarInvert(fDiffRadius);
2022 fDiff.fX = SkScalarMul(fDiff.fX, inv);
2023 fDiff.fY = SkScalarMul(fDiff.fY, inv);
2024 fStartRadius = SkScalarMul(fRadius1, inv);
2025 fSr2D2 = SkScalarSquare(fStartRadius);
2026 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00002027 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002028
2029 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
2030 fPtsToUnit.postScale(inv, inv);
2031 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002032};
2033
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034///////////////////////////////////////////////////////////////////////////////
2035
2036class Sweep_Gradient : public Gradient_Shader {
2037public:
2038 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
2039 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002040 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
2041 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042 {
2043 fPtsToUnit.setTranslate(-cx, -cy);
2044 }
reed@google.com7716afb2011-12-07 15:17:50 +00002045 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
2046 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002047
2048 virtual BitmapType asABitmap(SkBitmap* bitmap,
2049 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00002050 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00002051 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00002052 if (bitmap) {
2053 this->commonAsABitmap(bitmap);
2054 }
2055 if (matrix) {
2056 *matrix = fPtsToUnit;
2057 }
2058 if (xy) {
2059 xy[0] = fTileMode;
2060 xy[1] = kClamp_TileMode;
2061 }
2062 return kSweep_BitmapType;
2063 }
2064
reed@google.com7716afb2011-12-07 15:17:50 +00002065 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002066 if (info) {
2067 commonAsAGradient(info);
2068 info->fPoint[0] = fCenter;
2069 }
2070 return kSweep_GradientType;
2071 }
2072
djsollen@google.comba28d032012-03-26 17:57:35 +00002073 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sweep_Gradient)
2074
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002076 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
djsollen@google.com54924242012-03-29 15:18:04 +00002077 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002078 fCenter(buffer.readPoint()) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002079 }
djsollen@google.com54924242012-03-29 15:18:04 +00002080 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
2081 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002082 buffer.writePoint(fCenter);
djsollen@google.com54924242012-03-29 15:18:04 +00002083 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002084
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085private:
2086 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002087 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088};
2089
2090#ifdef COMPUTE_SWEEP_TABLE
2091#define PI 3.14159265
2092static bool gSweepTableReady;
2093static uint8_t gSweepTable[65];
2094
2095/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
2096 We scale the results to [0..32]
2097*/
reed@google.com61eb0402011-04-15 12:11:12 +00002098static const uint8_t* build_sweep_table() {
2099 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 const int N = 65;
2101 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002102
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 for (int i = 0; i < N; i++)
2104 {
2105 double arg = i / DENOM;
2106 double v = atan(arg);
2107 int iv = (int)round(v * DENOM * 2 / PI);
2108// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
2109 printf("%d, ", iv);
2110 gSweepTable[i] = iv;
2111 }
2112 gSweepTableReady = true;
2113 }
2114 return gSweepTable;
2115}
2116#else
2117static const uint8_t gSweepTable[] = {
2118 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
2119 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
2120 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
2121 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
2122 32
2123};
2124static const uint8_t* build_sweep_table() { return gSweepTable; }
2125#endif
2126
2127// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
2128// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
2129// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
2130
2131//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00002132static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133 SkASSERT(numer <= denom);
2134 SkASSERT(numer > 0);
2135 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002136
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137 int nbits = SkCLZ(numer);
2138 int dbits = SkCLZ(denom);
2139 int bits = 6 - nbits + dbits;
2140 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002141
reed@google.com61eb0402011-04-15 12:11:12 +00002142 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002144 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145
2146 denom <<= dbits - 1;
2147 numer <<= nbits - 1;
2148
2149 unsigned result = 0;
2150
2151 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00002152 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00002154 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002155 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00002156 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002157
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00002159 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 // make room for the rest of the answer bits
2161 result <<= bits;
2162 switch (bits) {
2163 case 6:
2164 if ((numer = (numer << 1) - denom) >= 0)
2165 result |= 32;
2166 else
2167 numer += denom;
2168 case 5:
2169 if ((numer = (numer << 1) - denom) >= 0)
2170 result |= 16;
2171 else
2172 numer += denom;
2173 case 4:
2174 if ((numer = (numer << 1) - denom) >= 0)
2175 result |= 8;
2176 else
2177 numer += denom;
2178 case 3:
2179 if ((numer = (numer << 1) - denom) >= 0)
2180 result |= 4;
2181 else
2182 numer += denom;
2183 case 2:
2184 if ((numer = (numer << 1) - denom) >= 0)
2185 result |= 2;
2186 else
2187 numer += denom;
2188 case 1:
2189 default: // not strictly need, but makes GCC make better ARM code
2190 if ((numer = (numer << 1) - denom) >= 0)
2191 result |= 1;
2192 else
2193 numer += denom;
2194 }
2195 }
2196 return result;
2197}
2198
2199// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00002200static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201#ifdef SK_DEBUG
2202 {
2203 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00002204 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 gOnce = true;
2206 SkASSERT(div_64(55, 55) == 64);
2207 SkASSERT(div_64(128, 256) == 32);
2208 SkASSERT(div_64(2326528, 4685824) == 31);
2209 SkASSERT(div_64(753664, 5210112) == 9);
2210 SkASSERT(div_64(229376, 4882432) == 3);
2211 SkASSERT(div_64(2, 64) == 2);
2212 SkASSERT(div_64(1, 64) == 1);
2213 // test that we handle underflow correctly
2214 SkASSERT(div_64(12345, 0x54321234) == 0);
2215 }
2216 }
2217#endif
2218
2219 SkASSERT(y > 0 && x > 0);
2220 const uint8_t* table = build_sweep_table();
2221
2222 unsigned result;
2223 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00002224 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225 // first part of the atan(v) = PI/2 - atan(1/v) identity
2226 // since our div_64 and table want v <= 1, where v = y/x
2227 SkTSwap<SkFixed>(x, y);
2228 }
2229
2230 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002231
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232#ifdef SK_DEBUG
2233 {
2234 unsigned result2 = SkDivBits(y, x, 6);
2235 SkASSERT(result2 == result ||
2236 (result == 1 && result2 == 0));
2237 }
2238#endif
2239
2240 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2241 result = table[result];
2242
reed@google.com61eb0402011-04-15 12:11:12 +00002243 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244 // complete the atan(v) = PI/2 - atan(1/v) identity
2245 result = 64 - result;
2246 // pin to 63
2247 result -= result >> 6;
2248 }
2249
2250 SkASSERT(result <= 63);
2251 return result;
2252}
2253
2254// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com51baf5a2011-09-21 13:38:36 +00002255#ifdef SK_SCALAR_IS_FLOAT
2256static unsigned SkATan2_255(float y, float x) {
2257 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
2258 static const float g255Over2PI = 40.584510488433314f;
2259
2260 float result = sk_float_atan2(y, x);
2261 if (result < 0) {
2262 result += 2 * SK_ScalarPI;
2263 }
2264 SkASSERT(result >= 0);
2265 // since our value is always >= 0, we can cast to int, which is faster than
2266 // calling floorf()
2267 int ir = (int)(result * g255Over2PI);
2268 SkASSERT(ir >= 0 && ir <= 255);
2269 return ir;
2270}
2271#else
reed@google.com61eb0402011-04-15 12:11:12 +00002272static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2273 if (x == 0) {
2274 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 return y < 0 ? 192 : 64;
2278 }
reed@google.com61eb0402011-04-15 12:11:12 +00002279 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002281 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002282
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283 /* Find the right quadrant for x,y
2284 Since atan_0_90 only handles the first quadrant, we rotate x,y
2285 appropriately before calling it, and then add the right amount
2286 to account for the real quadrant.
2287 quadrant 0 : add 0 | x > 0 && y > 0
2288 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2289 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2290 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002291
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 map x<0 to (1 << 6)
2293 map y<0 to (3 << 6)
2294 add = map_x ^ map_y
2295 */
2296 int xsign = x >> 31;
2297 int ysign = y >> 31;
2298 int add = ((-xsign) ^ (ysign & 3)) << 6;
2299
2300#ifdef SK_DEBUG
2301 if (0 == add)
2302 SkASSERT(x > 0 && y > 0);
2303 else if (64 == add)
2304 SkASSERT(x < 0 && y > 0);
2305 else if (128 == add)
2306 SkASSERT(x < 0 && y < 0);
2307 else if (192 == add)
2308 SkASSERT(x > 0 && y < 0);
2309 else
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002310 SkDEBUGFAIL("bad value for add");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002312
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2314 where we need to rotate x,y by 90 or -90
2315 */
2316 x = (x ^ xsign) - xsign;
2317 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002318 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002319 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321
2322 unsigned result = add + atan_0_90(y, x);
2323 SkASSERT(result < 256);
2324 return result;
2325}
reed@google.com51baf5a2011-09-21 13:38:36 +00002326#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002327
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002328void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
2329 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002330 SkMatrix::MapXYProc proc = fDstToIndexProc;
2331 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002332 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002334
reed@google.com61eb0402011-04-15 12:11:12 +00002335 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2337 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002338 SkScalar dx, fx = srcPt.fX;
2339 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002340
reed@google.com61eb0402011-04-15 12:11:12 +00002341 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002342 SkFixed storage[2];
2343 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2344 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002345 dx = SkFixedToScalar(storage[0]);
2346 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002347 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002349 dx = matrix.getScaleX();
2350 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002352
reed@google.com61eb0402011-04-15 12:11:12 +00002353 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354 *dstC++ = cache[SkATan2_255(fy, fx)];
2355 fx += dx;
2356 fy += dy;
2357 }
reed@google.com61eb0402011-04-15 12:11:12 +00002358 } else { // perspective case
2359 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com51baf5a2011-09-21 13:38:36 +00002361 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2362 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002363 }
2364 }
2365}
2366
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002367void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
2368 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 SkMatrix::MapXYProc proc = fDstToIndexProc;
2370 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002371 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002372 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373 SkPoint srcPt;
2374
reed@google.com61eb0402011-04-15 12:11:12 +00002375 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2377 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002378 SkScalar dx, fx = srcPt.fX;
2379 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002380
reed@google.com61eb0402011-04-15 12:11:12 +00002381 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382 SkFixed storage[2];
2383 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2384 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002385 dx = SkFixedToScalar(storage[0]);
2386 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002387 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002389 dx = matrix.getScaleX();
2390 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002391 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002392
reed@google.com61eb0402011-04-15 12:11:12 +00002393 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2395 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002396 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002397 fx += dx;
2398 fy += dy;
2399 }
reed@google.com61eb0402011-04-15 12:11:12 +00002400 } else { // perspective case
2401 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2403 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002404
reed@google.com51baf5a2011-09-21 13:38:36 +00002405 int index = SkATan2_255(srcPt.fY, srcPt.fX);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002406 index >>= (8 - kCache16Bits);
2407 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002408 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409 }
2410 }
2411}
2412
reed@google.com61eb0402011-04-15 12:11:12 +00002413///////////////////////////////////////////////////////////////////////////////
2414///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002415
2416// assumes colors is SkColor* and pos is SkScalar*
2417#define EXPAND_1_COLOR(count) \
2418 SkColor tmp[2]; \
2419 do { \
2420 if (1 == count) { \
2421 tmp[0] = tmp[1] = colors[0]; \
2422 colors = tmp; \
2423 pos = NULL; \
2424 count = 2; \
2425 } \
2426 } while (0)
2427
reed@google.com61eb0402011-04-15 12:11:12 +00002428SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2429 const SkColor colors[],
2430 const SkScalar pos[], int colorCount,
2431 SkShader::TileMode mode,
2432 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433 if (NULL == pts || NULL == colors || colorCount < 1) {
2434 return NULL;
2435 }
2436 EXPAND_1_COLOR(colorCount);
2437
reed@android.comab840b82009-07-01 17:00:03 +00002438 return SkNEW_ARGS(Linear_Gradient,
2439 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440}
2441
reed@google.com61eb0402011-04-15 12:11:12 +00002442SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2443 const SkColor colors[],
2444 const SkScalar pos[], int colorCount,
2445 SkShader::TileMode mode,
2446 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002447 if (radius <= 0 || NULL == colors || colorCount < 1) {
2448 return NULL;
2449 }
2450 EXPAND_1_COLOR(colorCount);
2451
reed@android.comab840b82009-07-01 17:00:03 +00002452 return SkNEW_ARGS(Radial_Gradient,
2453 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002454}
2455
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002456SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2457 SkScalar startRadius,
2458 const SkPoint& end,
2459 SkScalar endRadius,
2460 const SkColor colors[],
2461 const SkScalar pos[],
2462 int colorCount,
2463 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002464 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002465 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2466 return NULL;
2467 }
2468 EXPAND_1_COLOR(colorCount);
2469
2470 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002471 (start, startRadius, end, endRadius, colors, pos,
2472 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002473}
2474
reed@android.com8a1c16f2008-12-17 15:59:43 +00002475SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2476 const SkColor colors[],
2477 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002478 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002479 if (NULL == colors || count < 1) {
2480 return NULL;
2481 }
2482 EXPAND_1_COLOR(count);
2483
2484 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2485}
2486
caryclark@google.comd26147a2011-12-15 14:16:43 +00002487SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
2488 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Linear_Gradient)
2489 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Radial_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002490 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sweep_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002491 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Radial_Gradient)
2492SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END