blob: 7a55bbbcc751097fb1b508423eb9c54b401adbef [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"
rileya@google.com03c1c352012-07-20 20:02:43 +000018#include "../gpu/effects/GrGradientEffects.h"
19#include "../gpu/GrSamplerState.h"
20#include "../gpu/SkGr.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021
reed@google.com0e734bd2011-12-08 17:24:44 +000022#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
23 #define USE_DITHER_32BIT_GRADIENT
24#endif
25
reed@google.com5eb158d2011-04-15 15:50:34 +000026static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
27 int count) {
28 if (count > 0) {
29 if (v0 == v1) {
30 sk_memset32(dst, v0, count);
31 } else {
32 int pairs = count >> 1;
33 for (int i = 0; i < pairs; i++) {
34 *dst++ = v0;
35 *dst++ = v1;
36 }
37 if (count & 1) {
38 *dst = v0;
39 }
40 }
41 }
42}
43
reed@google.comc98a0aa2012-02-02 19:33:08 +000044// Clamp
reed@android.com8a1c16f2008-12-17 15:59:43 +000045
reed@android.com41bccf52009-04-03 13:33:51 +000046static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000047 return SkClampMax(x, 0xFFFF);
48}
49
reed@google.comc98a0aa2012-02-02 19:33:08 +000050// Repeat
51
reed@android.com41bccf52009-04-03 13:33:51 +000052static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 return x & 0xFFFF;
54}
55
reed@google.comc98a0aa2012-02-02 19:33:08 +000056static inline int repeat_bits(int x, const int bits) {
57 return x & ((1 << bits) - 1);
58}
59
60static inline int repeat_8bits(int x) {
61 return x & 0xFF;
62}
63
64// Mirror
65
epoger@google.com5468c902012-02-02 20:41:45 +000066// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
67// See http://code.google.com/p/skia/issues/detail?id=472
68#if defined(_MSC_VER) && (_MSC_VER >= 1600)
69#pragma optimize("", off)
70#endif
71
reed@android.com41bccf52009-04-03 13:33:51 +000072static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 int s = x << 15 >> 31;
74 return (x ^ s) & 0xFFFF;
75}
76
reed@android.com200645d2009-12-14 16:41:57 +000077static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000078#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000079 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000081 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000082#else
reed@android.com200645d2009-12-14 16:41:57 +000083 int s = x << (31 - bits) >> 31;
84 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085#endif
86}
87
reed@android.com41bccf52009-04-03 13:33:51 +000088static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000089#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000090 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000092 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 return x & 255;
94#else
95 int s = x << 23 >> 31;
96 return (x ^ s) & 0xFF;
97#endif
98}
99
epoger@google.com5468c902012-02-02 20:41:45 +0000100#if defined(_MSC_VER) && (_MSC_VER >= 1600)
101#pragma optimize("", on)
102#endif
103
reed@google.com61eb0402011-04-15 12:11:12 +0000104///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105
reed@google.comc98a0aa2012-02-02 19:33:08 +0000106typedef SkFixed (*TileProc)(SkFixed);
107
108static const TileProc gTileProcs[] = {
109 clamp_tileproc,
110 repeat_tileproc,
111 mirror_tileproc
112};
113
114///////////////////////////////////////////////////////////////////////////////
reed@google.comc98a0aa2012-02-02 19:33:08 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116class Gradient_Shader : public SkShader {
117public:
118 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000119 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 virtual ~Gradient_Shader();
121
122 // overrides
reed@google.com7716afb2011-12-07 15:17:50 +0000123 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
124 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000125 virtual bool isOpaque() const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000127 enum {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000128 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
129 /// it, use a larger cache.
130 kCache16Bits = 8,
131 kGradient16Length = (1 << kCache16Bits),
132 /// Each cache gets 1 extra entry at the end so we don't have to
133 /// test for end-of-cache in lerps. This is also the value used
134 /// to stride *writes* into the dither cache; it must not be zero.
135 /// Total space for a cache is 2x kCache16Count entries: one
136 /// regular cache, one for dithering.
137 kCache16Count = kGradient16Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000138 kCache16Shift = 16 - kCache16Bits,
139 kSqrt16Shift = 8 - kCache16Bits,
140
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000141 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
142 /// it, use a larger cache.
143 kCache32Bits = 8,
144 kGradient32Length = (1 << kCache32Bits),
145 /// Each cache gets 1 extra entry at the end so we don't have to
146 /// test for end-of-cache in lerps. This is also the value used
147 /// to stride *writes* into the dither cache; it must not be zero.
148 /// Total space for a cache is 2x kCache32Count entries: one
149 /// regular cache, one for dithering.
150 kCache32Count = kGradient32Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000151 kCache32Shift = 16 - kCache32Bits,
152 kSqrt32Shift = 8 - kCache32Bits,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000153
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000154 /// This value is used to *read* the dither cache; it may be 0
155 /// if dithering is disabled.
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000156#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000157 kDitherStride32 = kCache32Count,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000158#else
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000159 kDitherStride32 = 0,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000160#endif
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000161 kDitherStride16 = kCache16Count,
162 kLerpRemainderMask32 = (1 << (16 - kCache32Bits)) - 1
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000163 };
164
165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166protected:
167 Gradient_Shader(SkFlattenableReadBuffer& );
djsollen@google.com54924242012-03-29 15:18:04 +0000168 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 SkUnitMapper* fMapper;
171 SkMatrix fPtsToUnit; // set by subclass
172 SkMatrix fDstToIndex;
173 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 TileMode fTileMode;
175 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000176 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 uint8_t fDstToIndexClass;
178 uint8_t fFlags;
179
180 struct Rec {
181 SkFixed fPos; // 0...1
182 uint32_t fScale; // (1 << 24) / range
183 };
184 Rec* fRecs;
185
reed@google.com7c2f27d2011-03-07 19:29:00 +0000186 const uint16_t* getCache16() const;
187 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
reed@google.com7c2f27d2011-03-07 19:29:00 +0000189 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000190 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192private:
193 enum {
194 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
195
reed@android.com1c12abe2009-07-02 15:01:02 +0000196 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 };
198 SkColor fStorage[(kStorageSize + 3) >> 2];
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000199 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
200 bool fColorsAreOpaque;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201
reed@google.com7c2f27d2011-03-07 19:29:00 +0000202 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
203 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
reed@google.com7c2f27d2011-03-07 19:29:00 +0000205 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
206 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000207 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 +0000208
reed@android.com512a8762009-12-14 15:25:36 +0000209 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000210 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
211 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000212 void setCacheAlpha(U8CPU alpha) const;
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000213 void initCommon();
reed@android.com512a8762009-12-14 15:25:36 +0000214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 typedef SkShader INHERITED;
216};
217
reed@android.com41bccf52009-04-03 13:33:51 +0000218Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
219 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 SkASSERT(colorCount > 1);
221
222 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
223
224 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000225 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
228 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
229 fTileMode = mode;
230 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000231
reed@android.com41bccf52009-04-03 13:33:51 +0000232 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000233 fCache32 = NULL;
234 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235
reed@android.com41bccf52009-04-03 13:33:51 +0000236 /* Note: we let the caller skip the first and/or last position.
237 i.e. pos[0] = 0.3, pos[1] = 0.7
238 In these cases, we insert dummy entries to ensure that the final data
239 will be bracketed by [0, 1].
240 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
241
242 Thus colorCount (the caller's value, and fColorCount (our value) may
243 differ by up to 2. In the above example:
244 colorCount = 2
245 fColorCount = 4
246 */
247 fColorCount = colorCount;
248 // check if we need to add in dummy start and/or end position/colors
249 bool dummyFirst = false;
250 bool dummyLast = false;
251 if (pos) {
252 dummyFirst = pos[0] != 0;
253 dummyLast = pos[colorCount - 1] != SK_Scalar1;
254 fColorCount += dummyFirst + dummyLast;
255 }
256
257 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000258 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000259 fOrigColors = reinterpret_cast<SkColor*>(
260 sk_malloc_throw(size * fColorCount));
261 }
262 else {
263 fOrigColors = fStorage;
264 }
265
266 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 {
reed@android.com41bccf52009-04-03 13:33:51 +0000268 SkColor* origColors = fOrigColors;
269 if (dummyFirst) {
270 *origColors++ = colors[0];
271 }
272 memcpy(origColors, colors, colorCount * sizeof(SkColor));
273 if (dummyLast) {
274 origColors += colorCount;
275 *origColors = colors[colorCount - 1];
276 }
277 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278
reed@android.com1c12abe2009-07-02 15:01:02 +0000279 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000280 if (fColorCount > 2) {
281 Rec* recs = fRecs;
282 recs->fPos = 0;
283 // recs->fScale = 0; // unused;
284 recs += 1;
285 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 /* We need to convert the user's array of relative positions into
287 fixed-point positions and scale factors. We need these results
288 to be strictly monotonic (no two values equal or out of order).
289 Hence this complex loop that just jams a zero for the scale
290 value if it sees a segment out of order, and it assures that
291 we start at 0 and end at 1.0
292 */
293 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000294 int startIndex = dummyFirst ? 0 : 1;
295 int count = colorCount + dummyLast;
296 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 // force the last value to be 1.0
298 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000299 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000301 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 }
reed@android.com41bccf52009-04-03 13:33:51 +0000304 // pin curr withing range
305 if (curr < 0) {
306 curr = 0;
307 } else if (curr > SK_Fixed1) {
308 curr = SK_Fixed1;
309 }
310 recs->fPos = curr;
311 if (curr > prev) {
312 recs->fScale = (1 << 24) / (curr - prev);
313 } else {
314 recs->fScale = 0; // ignore this segment
315 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 // get ready for the next value
317 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000318 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 }
reed@android.com41bccf52009-04-03 13:33:51 +0000320 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 SkFixed dp = SK_Fixed1 / (colorCount - 1);
322 SkFixed p = dp;
323 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000324 for (int i = 1; i < colorCount; i++) {
325 recs->fPos = p;
326 recs->fScale = scale;
327 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 p += dp;
329 }
330 }
331 }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000332 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333}
334
335Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000336 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 fCacheAlpha = 256;
338
339 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
340
341 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000342 fCache32 = NULL;
343 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344
reed@android.com41bccf52009-04-03 13:33:51 +0000345 int colorCount = fColorCount = buffer.readU32();
346 if (colorCount > kColorStorageCount) {
347 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
348 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
349 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000351 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354 fTileMode = (TileMode)buffer.readU8();
355 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000356 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 if (colorCount > 2) {
358 Rec* recs = fRecs;
359 recs[0].fPos = 0;
360 for (int i = 1; i < colorCount; i++) {
361 recs[i].fPos = buffer.readS32();
362 recs[i].fScale = buffer.readU32();
363 }
364 }
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000365 buffer.readMatrix(&fPtsToUnit);
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000366 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367}
368
reed@android.com41bccf52009-04-03 13:33:51 +0000369Gradient_Shader::~Gradient_Shader() {
370 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000372 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000373 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000374 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000376 }
reed@google.com82065d62011-02-07 15:30:46 +0000377 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378}
379
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000380void Gradient_Shader::initCommon() {
381 fFlags = 0;
382 unsigned colorAlpha = 0xFF;
383 for (int i = 0; i < fColorCount; i++) {
384 colorAlpha &= SkColorGetA(fOrigColors[i]);
385 }
386 fColorsAreOpaque = colorAlpha == 0xFF;
387}
388
djsollen@google.com54924242012-03-29 15:18:04 +0000389void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 this->INHERITED::flatten(buffer);
391 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000392 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
394 buffer.write8(fTileMode);
395 if (fColorCount > 2) {
396 Rec* recs = fRecs;
397 for (int i = 1; i < fColorCount; i++) {
398 buffer.write32(recs[i].fPos);
399 buffer.write32(recs[i].fScale);
400 }
401 }
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000402 buffer.writeMatrix(fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403}
404
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000405bool Gradient_Shader::isOpaque() const {
406 return fColorsAreOpaque;
407}
408
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409bool Gradient_Shader::setContext(const SkBitmap& device,
410 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000411 const SkMatrix& matrix) {
412 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000414 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415
416 const SkMatrix& inverse = this->getTotalInverse();
417
418 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
419 return false;
420 }
421
422 fDstToIndexProc = fDstToIndex.getMapXYProc();
423 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
424
425 // now convert our colors in to PMColors
426 unsigned paintAlpha = this->getPaintAlpha();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427
428 fFlags = this->INHERITED::getFlags();
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000429 if (fColorsAreOpaque && paintAlpha == 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 fFlags |= kOpaqueAlpha_Flag;
431 }
432 // we can do span16 as long as our individual colors are opaque,
433 // regardless of the paint's alpha
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000434 if (fColorsAreOpaque) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 fFlags |= kHasSpan16_Flag;
436 }
437
reed@google.com95eed982011-07-05 17:01:56 +0000438 this->setCacheAlpha(paintAlpha);
439 return true;
440}
441
442void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 // if the new alpha differs from the previous time we were called, inval our cache
444 // this will trigger the cache to be rebuilt.
445 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000446 if (fCacheAlpha != alpha) {
447 fCache16 = NULL; // inval the cache
448 fCache32 = NULL; // inval the cache
449 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000450 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000451 if (fCache32PixelRef) {
452 fCache32PixelRef->notifyPixelsChanged();
453 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455}
456
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
458
reed@android.com41bccf52009-04-03 13:33:51 +0000459/** We take the original colors, not our premultiplied PMColors, since we can
460 build a 16bit table as long as the original colors are opaque, even if the
461 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462*/
reed@android.com512a8762009-12-14 15:25:36 +0000463void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
464 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 SkASSERT(count > 1);
466 SkASSERT(SkColorGetA(c0) == 0xFF);
467 SkASSERT(SkColorGetA(c1) == 0xFF);
468
469 SkFixed r = SkColorGetR(c0);
470 SkFixed g = SkColorGetG(c0);
471 SkFixed b = SkColorGetB(c0);
472
473 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
474 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
475 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
476
477 r = SkIntToFixed(r) + 0x8000;
478 g = SkIntToFixed(g) + 0x8000;
479 b = SkIntToFixed(b) + 0x8000;
480
481 do {
482 unsigned rr = r >> 16;
483 unsigned gg = g >> 16;
484 unsigned bb = b >> 16;
485 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000486 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 cache += 1;
488 r += dr;
489 g += dg;
490 b += db;
491 } while (--count != 0);
492}
493
reed@google.com55b8e8c2011-01-13 16:22:35 +0000494/*
495 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
496 * semantics of how we 2x2 dither 32->16
497 */
498static inline U8CPU dither_fixed_to_8(SkFixed n) {
499 n >>= 8;
500 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
501}
502
503/*
504 * For dithering with premultiply, we want to ceiling the alpha component,
505 * to ensure that it is always >= any color component.
506 */
507static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
508 n >>= 8;
509 return ((n << 1) - (n | (n >> 8))) >> 8;
510}
511
512void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
513 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 SkASSERT(count > 1);
515
reed@android.com1c12abe2009-07-02 15:01:02 +0000516 // need to apply paintAlpha to our two endpoints
517 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
518 SkFixed da;
519 {
520 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
521 da = SkIntToFixed(tmp - a) / (count - 1);
522 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523
reed@android.com1c12abe2009-07-02 15:01:02 +0000524 SkFixed r = SkColorGetR(c0);
525 SkFixed g = SkColorGetG(c0);
526 SkFixed b = SkColorGetB(c0);
527 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
528 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
529 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530
531 a = SkIntToFixed(a) + 0x8000;
532 r = SkIntToFixed(r) + 0x8000;
533 g = SkIntToFixed(g) + 0x8000;
534 b = SkIntToFixed(b) + 0x8000;
535
536 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000537 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000538 cache[kCache32Count] =
539 SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
540 dither_fixed_to_8(r),
541 dither_fixed_to_8(g),
542 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000543 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 a += da;
545 r += dr;
546 g += dg;
547 b += db;
548 } while (--count != 0);
549}
550
reed@android.com41bccf52009-04-03 13:33:51 +0000551static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 SkASSERT((unsigned)x <= SK_Fixed1);
553 return x - (x >> 16);
554}
555
reed@android.com200645d2009-12-14 16:41:57 +0000556static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000557 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000558 if (6 == bits) {
559 return (x << 10) | (x << 4) | (x >> 2);
560 }
561 if (8 == bits) {
562 return (x << 8) | x;
563 }
564 sk_throw();
565 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566}
567
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000568/** We duplicate the last value in each half of the cache so that
569 interpolation doesn't have to special-case being at the last point.
570*/
571static void complete_16bit_cache(uint16_t* cache, int stride) {
572 cache[stride - 1] = cache[stride - 2];
573 cache[2 * stride - 1] = cache[2 * stride - 2];
574}
575
reed@google.com7c2f27d2011-03-07 19:29:00 +0000576const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000577 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000578 // double the count for dither entries
579 const int entryCount = kCache16Count * 2;
580 const size_t allocSize = sizeof(uint16_t) * entryCount;
581
reed@android.com3c9b2a42009-08-27 19:28:37 +0000582 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000583 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000586 if (fColorCount == 2) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000587 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
588 kGradient16Length);
reed@android.com41bccf52009-04-03 13:33:51 +0000589 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 Rec* rec = fRecs;
591 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000592 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000593 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkASSERT(nextIndex < kCache16Count);
595
596 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000597 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 prevIndex = nextIndex;
599 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000600 // one extra space left over at the end for complete_16bit_cache()
601 SkASSERT(prevIndex == kGradient16Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 }
603
reed@android.com41bccf52009-04-03 13:33:51 +0000604 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000605 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 uint16_t* linear = fCache16; // just computed linear data
607 uint16_t* mapped = fCache16Storage; // storage for mapped data
608 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000609 for (int i = 0; i < kGradient16Length; i++) {
reed@android.com200645d2009-12-14 16:41:57 +0000610 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000612 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 }
614 sk_free(fCache16);
615 fCache16 = fCache16Storage;
616 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000617 complete_16bit_cache(fCache16, kCache16Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 }
619 return fCache16;
620}
621
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000622/** We duplicate the last value in each half of the cache so that
623 interpolation doesn't have to special-case being at the last point.
624*/
625static void complete_32bit_cache(SkPMColor* cache, int stride) {
626 cache[stride - 1] = cache[stride - 2];
627 cache[2 * stride - 1] = cache[2 * stride - 2];
628}
629
reed@google.com7c2f27d2011-03-07 19:29:00 +0000630const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000631 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000632 // double the count for dither entries
633 const int entryCount = kCache32Count * 2;
634 const size_t allocSize = sizeof(SkPMColor) * entryCount;
635
reed@google.comdc731fd2010-12-23 15:19:47 +0000636 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000637 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
638 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000639 }
640 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000641 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000642 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000643 kGradient32Length, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000644 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 Rec* rec = fRecs;
646 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000647 for (int i = 1; i < fColorCount; i++) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000648 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000649 SkASSERT(nextIndex < kGradient32Length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650
651 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000652 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
653 fOrigColors[i],
654 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 prevIndex = nextIndex;
656 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000657 SkASSERT(prevIndex == kGradient32Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 }
659
reed@android.com41bccf52009-04-03 13:33:51 +0000660 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000661 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000662 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000664 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000666 for (int i = 0; i < kGradient32Length; i++) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000667 int index = map->mapUnit16((i << 8) | i) >> 8;
668 mapped[i] = linear[index];
669 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000670 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000671 fCache32PixelRef->unref();
672 fCache32PixelRef = newPR;
673 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000675 complete_32bit_cache(fCache32, kCache32Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 }
677 return fCache32;
678}
679
reed@google.comdc731fd2010-12-23 15:19:47 +0000680/*
681 * Because our caller might rebuild the same (logically the same) gradient
682 * over and over, we'd like to return exactly the same "bitmap" if possible,
683 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
684 * To do that, we maintain a private cache of built-bitmaps, based on our
685 * colors and positions. Note: we don't try to flatten the fMapper, so if one
686 * is present, we skip the cache for now.
687 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000688void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000689 // our caller assumes no external alpha, so we ensure that our cache is
690 // built with 0xFF
691 this->setCacheAlpha(0xFF);
692
reed@google.comdc731fd2010-12-23 15:19:47 +0000693 // don't have a way to put the mapper into our cache-key yet
694 if (fMapper) {
695 // force our cahce32pixelref to be built
696 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000697 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000698 bitmap->setPixelRef(fCache32PixelRef);
699 return;
700 }
701
702 // build our key: [numColors + colors[] + {positions[]} ]
703 int count = 1 + fColorCount;
704 if (fColorCount > 2) {
705 count += fColorCount - 1; // fRecs[].fPos
706 }
707
708 SkAutoSTMalloc<16, int32_t> storage(count);
709 int32_t* buffer = storage.get();
710
711 *buffer++ = fColorCount;
712 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
713 buffer += fColorCount;
714 if (fColorCount > 2) {
715 for (int i = 1; i < fColorCount; i++) {
716 *buffer++ = fRecs[i].fPos;
717 }
718 }
719 SkASSERT(buffer - storage.get() == count);
720
721 ///////////////////////////////////
722
digit@google.com1771cbf2012-01-26 21:26:40 +0000723 SK_DECLARE_STATIC_MUTEX(gMutex);
reed@google.comdc731fd2010-12-23 15:19:47 +0000724 static SkBitmapCache* gCache;
725 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
726 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
727 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000728
reed@google.comdc731fd2010-12-23 15:19:47 +0000729 if (NULL == gCache) {
tomhudson@google.com060ef182012-07-24 12:22:40 +0000730 gCache = SkNEW_ARGS(SkBitmapCache, (MAX_NUM_CACHED_GRADIENT_BITMAPS));
reed@google.comdc731fd2010-12-23 15:19:47 +0000731 }
732 size_t size = count * sizeof(int32_t);
733
734 if (!gCache->find(storage.get(), size, bitmap)) {
735 // force our cahce32pixelref to be built
736 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000737 // Only expose the linear section of the cache; don't let the caller
738 // know about the padding at the end to make interpolation faster.
739 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000740 bitmap->setPixelRef(fCache32PixelRef);
741
742 gCache->add(storage.get(), size, *bitmap);
743 }
744}
745
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000746void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
747 if (info) {
748 if (info->fColorCount >= fColorCount) {
749 if (info->fColors) {
750 memcpy(info->fColors, fOrigColors,
751 fColorCount * sizeof(SkColor));
752 }
753 if (info->fColorOffsets) {
754 if (fColorCount == 2) {
755 info->fColorOffsets[0] = 0;
756 info->fColorOffsets[1] = SK_Scalar1;
757 } else if (fColorCount > 2) {
758 for (int i = 0; i < fColorCount; i++)
759 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
760 }
761 }
762 }
763 info->fColorCount = fColorCount;
764 info->fTileMode = fTileMode;
765 }
766}
767
reed@google.com61eb0402011-04-15 12:11:12 +0000768///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769
reed@android.com41bccf52009-04-03 13:33:51 +0000770static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 SkVector vec = pts[1] - pts[0];
772 SkScalar mag = vec.length();
773 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
774
775 vec.scale(inv);
776 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
777 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
778 matrix->postScale(inv, inv);
779}
780
781///////////////////////////////////////////////////////////////////////////////
782
783class Linear_Gradient : public Gradient_Shader {
784public:
785 Linear_Gradient(const SkPoint pts[2],
786 const SkColor colors[], const SkScalar pos[], int colorCount,
787 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000788 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
789 fStart(pts[0]),
790 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 {
792 pts_to_unit_matrix(pts, &fPtsToUnit);
793 }
reed@android.com9b46e772009-06-05 12:24:41 +0000794
reed@google.com7716afb2011-12-07 15:17:50 +0000795 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
796 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
797 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
rileya@google.com91f319c2012-07-25 17:18:31 +0000798 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
reed@google.com7716afb2011-12-07 15:17:50 +0000799 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
rileya@google.com03c1c352012-07-20 20:02:43 +0000800 virtual GrCustomStage* asNewCustomStage(GrContext* context,
801 GrSamplerState* sampler) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802
djsollen@google.com54924242012-03-29 15:18:04 +0000803 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Linear_Gradient)
804
805protected:
806 Linear_Gradient(SkFlattenableReadBuffer& buffer)
807 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000808 fStart(buffer.readPoint()),
809 fEnd(buffer.readPoint()) {
djsollen@google.com54924242012-03-29 15:18:04 +0000810 }
811 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000812 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000813 buffer.writePoint(fStart);
814 buffer.writePoint(fEnd);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000815 }
816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817private:
818 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000819 const SkPoint fStart;
820 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821};
822
reed@android.com5119bdb2009-06-12 21:27:03 +0000823bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
824 const SkMatrix& matrix) {
825 if (!this->INHERITED::setContext(device, paint, matrix)) {
826 return false;
827 }
828
829 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
830 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000831 fFlags |= SkShader::kConstInY32_Flag;
832 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
833 // only claim this if we do have a 16bit mode (i.e. none of our
834 // colors have alpha), and if we are not dithering (which obviously
835 // is not const in Y).
836 fFlags |= SkShader::kConstInY16_Flag;
837 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000838 }
839 return true;
840}
841
reed@google.com5eb158d2011-04-15 15:50:34 +0000842#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000843 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000844 unsigned fi = fx >> Gradient_Shader::kCache32Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000845 SkASSERT(fi <= 0xFF); \
846 fx += dx; \
847 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000848 toggle ^= Gradient_Shader::kDitherStride32; \
reed@google.com13659f12011-04-18 19:59:38 +0000849 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000850
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000851namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +0000852
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000853typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000854 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000855 int toggle, int count);
856
tomhudson@google.com13a847a2012-01-20 13:59:14 +0000857// This function is deprecated, and will be replaced by
858// shadeSpan_linear_vertical_lerp() once Chrome has been weaned off of it.
859void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
860 SkPMColor* SK_RESTRICT dstC,
861 const SkPMColor* SK_RESTRICT cache,
862 int toggle, int count) {
863 // We're a vertical gradient, so no change in a span.
864 // If colors change sharply across the gradient, dithering is
865 // insufficient (it subsamples the color space) and we need to lerp.
866 unsigned fullIndex = proc(fx);
867 unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
868 sk_memset32_dither(dstC,
869 cache[toggle + fi],
870 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
871 count);
872}
873
874// Linear interpolation (lerp) is unnecessary if there are no sharp
875// discontinuities in the gradient - which must be true if there are
876// only 2 colors - but it's cheap.
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000877void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
878 SkPMColor* SK_RESTRICT dstC,
879 const SkPMColor* SK_RESTRICT cache,
880 int toggle, int count) {
881 // We're a vertical gradient, so no change in a span.
882 // If colors change sharply across the gradient, dithering is
883 // insufficient (it subsamples the color space) and we need to lerp.
884 unsigned fullIndex = proc(fx);
885 unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
886 unsigned remainder = fullIndex & Gradient_Shader::kLerpRemainderMask32;
887 SkPMColor lerp =
888 SkFastFourByteInterp(
889 cache[toggle + fi + 1],
890 cache[toggle + fi], remainder);
891 SkPMColor dlerp =
892 SkFastFourByteInterp(
893 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi + 1],
894 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi], remainder);
895 sk_memset32_dither(dstC, lerp, dlerp, count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000896}
897
898void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
899 SkPMColor* SK_RESTRICT dstC,
900 const SkPMColor* SK_RESTRICT cache,
901 int toggle, int count) {
902 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000903 range.init(fx, dx, count, 0, Gradient_Shader::kGradient32Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000904
905 if ((count = range.fCount0) > 0) {
906 sk_memset32_dither(dstC,
907 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000908 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000909 count);
910 dstC += count;
911 }
912 if ((count = range.fCount1) > 0) {
913 int unroll = count >> 3;
914 fx = range.fFx1;
915 for (int i = 0; i < unroll; i++) {
916 NO_CHECK_ITER; NO_CHECK_ITER;
917 NO_CHECK_ITER; NO_CHECK_ITER;
918 NO_CHECK_ITER; NO_CHECK_ITER;
919 NO_CHECK_ITER; NO_CHECK_ITER;
920 }
921 if ((count &= 7) > 0) {
922 do {
923 NO_CHECK_ITER;
924 } while (--count != 0);
925 }
926 }
927 if ((count = range.fCount2) > 0) {
928 sk_memset32_dither(dstC,
929 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000930 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000931 count);
932 }
933}
934
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000935void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
936 SkPMColor* SK_RESTRICT dstC,
937 const SkPMColor* SK_RESTRICT cache,
938 int toggle, int count) {
939 do {
940 unsigned fi = mirror_8bits(fx >> 8);
941 SkASSERT(fi <= 0xFF);
942 fx += dx;
943 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000944 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000945 } while (--count != 0);
946}
947
948void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
949 SkPMColor* SK_RESTRICT dstC,
950 const SkPMColor* SK_RESTRICT cache,
951 int toggle, int count) {
952 do {
953 unsigned fi = repeat_8bits(fx >> 8);
954 SkASSERT(fi <= 0xFF);
955 fx += dx;
956 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000957 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000958 } while (--count != 0);
959}
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000960
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000961}
962
963void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
964 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 SkASSERT(count > 0);
966
967 SkPoint srcPt;
968 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
969 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000970 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com0e734bd2011-12-08 17:24:44 +0000971#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000972 int toggle = ((x ^ y) & 1) * kDitherStride32;
reed@google.com0e734bd2011-12-08 17:24:44 +0000973#else
974 int toggle = 0;
reed@google.com0e734bd2011-12-08 17:24:44 +0000975#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976
reed@android.comc552a432009-06-12 20:02:50 +0000977 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000978 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
979 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
981
reed@android.comc552a432009-06-12 20:02:50 +0000982 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 SkFixed dxStorage[1];
984 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
985 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000986 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
988 dx = SkScalarToFixed(fDstToIndex.getScaleX());
989 }
990
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000991 LinearShadeProc shadeProc = shadeSpan_linear_repeat;
reed@android.comc552a432009-06-12 20:02:50 +0000992 if (SkFixedNearlyZero(dx)) {
tomhudson@google.com13a847a2012-01-20 13:59:14 +0000993#ifdef SK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS
994 if (fColorCount > 2) {
995 shadeProc = shadeSpan_linear_vertical_lerp;
996 } else {
997 shadeProc = shadeSpan_linear_vertical;
998 }
999#else
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001000 shadeProc = shadeSpan_linear_vertical_lerp;
tomhudson@google.com13a847a2012-01-20 13:59:14 +00001001#endif
reed@android.comc552a432009-06-12 20:02:50 +00001002 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001003 shadeProc = shadeSpan_linear_clamp;
reed@android.comc552a432009-06-12 20:02:50 +00001004 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001005 shadeProc = shadeSpan_linear_mirror;
reed@android.comc552a432009-06-12 20:02:50 +00001006 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001009 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comc552a432009-06-12 20:02:50 +00001010 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 SkScalar dstX = SkIntToScalar(x);
1012 SkScalar dstY = SkIntToScalar(y);
1013 do {
1014 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1015 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1016 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001017 *dstC++ = cache[toggle + (fi >> kCache32Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001018 toggle ^= Gradient_Shader::kDitherStride32;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 dstX += SK_Scalar1;
1020 } while (--count != 0);
1021 }
1022}
1023
reed@google.com55b8e8c2011-01-13 16:22:35 +00001024SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +00001025 SkMatrix* matrix,
rileya@google.com91f319c2012-07-25 17:18:31 +00001026 TileMode xy[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001028 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 }
1030 if (matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 matrix->preConcat(fPtsToUnit);
1032 }
1033 if (xy) {
1034 xy[0] = fTileMode;
1035 xy[1] = kClamp_TileMode;
1036 }
rileya@google.com22e57f92012-07-19 15:16:19 +00001037 return kLinear_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038}
1039
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001040SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
1041 if (info) {
1042 commonAsAGradient(info);
1043 info->fPoint[0] = fStart;
1044 info->fPoint[1] = fEnd;
1045 }
1046 return kLinear_GradientType;
1047}
1048
rileya@google.com03c1c352012-07-20 20:02:43 +00001049GrCustomStage* Linear_Gradient::asNewCustomStage(GrContext* context,
1050 GrSamplerState* sampler) const {
1051 SkASSERT(NULL != context && NULL != sampler);
1052 sampler->matrix()->preConcat(fPtsToUnit);
1053 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
1054 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
1055 sampler->setFilter(GrSamplerState::kBilinear_Filter);
rileya@google.com91f319c2012-07-25 17:18:31 +00001056 return SkNEW_ARGS(GrLinearGradient, (context, *this, sampler));
rileya@google.com03c1c352012-07-20 20:02:43 +00001057}
1058
reed@android.com3c9b2a42009-08-27 19:28:37 +00001059static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
1060 int count) {
1061 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 *dst++ = value;
1063 count -= 1;
1064 SkTSwap(value, other);
1065 }
1066
1067 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001068
reed@android.com3c9b2a42009-08-27 19:28:37 +00001069 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001071 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073
reed@google.com5eb158d2011-04-15 15:50:34 +00001074#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +00001075 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001076 unsigned fi = fx >> Gradient_Shader::kCache16Shift; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001077 SkASSERT(fi < Gradient_Shader::kCache16Count); \
reed@google.com5eb158d2011-04-15 15:50:34 +00001078 fx += dx; \
1079 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001080 toggle ^= Gradient_Shader::kDitherStride16; \
reed@google.com13659f12011-04-18 19:59:38 +00001081 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +00001082
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001083namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +00001084
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001085typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001086 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001087 int toggle, int count);
1088
1089void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
1090 uint16_t* SK_RESTRICT dstC,
1091 const uint16_t* SK_RESTRICT cache,
1092 int toggle, int count) {
1093 // we're a vertical gradient, so no change in a span
1094 unsigned fi = proc(fx) >> Gradient_Shader::kCache16Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001095 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001096 dither_memset16(dstC, cache[toggle + fi],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001097 cache[(toggle ^ Gradient_Shader::kDitherStride16) + fi], count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001098
1099}
1100
1101void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
1102 uint16_t* SK_RESTRICT dstC,
1103 const uint16_t* SK_RESTRICT cache,
1104 int toggle, int count) {
1105 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001106 range.init(fx, dx, count, 0, Gradient_Shader::kGradient16Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001107
1108 if ((count = range.fCount0) > 0) {
1109 dither_memset16(dstC,
1110 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001111 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001112 count);
1113 dstC += count;
1114 }
1115 if ((count = range.fCount1) > 0) {
1116 int unroll = count >> 3;
1117 fx = range.fFx1;
1118 for (int i = 0; i < unroll; i++) {
1119 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1120 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1121 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1122 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1123 }
1124 if ((count &= 7) > 0) {
1125 do {
1126 NO_CHECK_ITER_16;
1127 } while (--count != 0);
1128 }
1129 }
1130 if ((count = range.fCount2) > 0) {
1131 dither_memset16(dstC,
1132 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001133 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001134 count);
1135 }
1136}
1137
1138void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
1139 uint16_t* SK_RESTRICT dstC,
1140 const uint16_t* SK_RESTRICT cache,
1141 int toggle, int count) {
1142 do {
1143 unsigned fi = mirror_bits(fx >> Gradient_Shader::kCache16Shift,
1144 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001145 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001146 fx += dx;
1147 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001148 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001149 } while (--count != 0);
1150}
1151
1152void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
1153 uint16_t* SK_RESTRICT dstC,
1154 const uint16_t* SK_RESTRICT cache,
1155 int toggle, int count) {
1156 SkASSERT(proc == repeat_tileproc);
1157 do {
1158 unsigned fi = repeat_bits(fx >> Gradient_Shader::kCache16Shift,
1159 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001160 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001161 fx += dx;
1162 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001163 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001164 } while (--count != 0);
1165}
1166}
1167
1168void Linear_Gradient::shadeSpan16(int x, int y,
1169 uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 SkASSERT(count > 0);
1171
1172 SkPoint srcPt;
1173 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1174 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001175 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001176 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001178 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001179 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1180 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1182
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001183 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 SkFixed dxStorage[1];
1185 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1186 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001187 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1189 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1190 }
1191
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001192 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001193 if (SkFixedNearlyZero(dx)) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001194 shadeProc = shadeSpan16_linear_vertical;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001195 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001196 shadeProc = shadeSpan16_linear_clamp;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001197 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001198 shadeProc = shadeSpan16_linear_mirror;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001199 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001202 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001203 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 SkScalar dstX = SkIntToScalar(x);
1205 SkScalar dstY = SkIntToScalar(y);
1206 do {
1207 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1208 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1209 SkASSERT(fi <= 0xFFFF);
1210
reed@android.com512a8762009-12-14 15:25:36 +00001211 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001213 toggle ^= Gradient_Shader::kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214
1215 dstX += SK_Scalar1;
1216 } while (--count != 0);
1217 }
1218}
1219
1220///////////////////////////////////////////////////////////////////////////////
1221
1222#define kSQRT_TABLE_BITS 11
1223#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1224
1225#include "SkRadialGradient_Table.h"
1226
1227#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1228
1229#include <stdio.h>
1230
reed@google.com61eb0402011-04-15 12:11:12 +00001231void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1233
1234 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1235 SkASSERT(file);
1236 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1237
reed@google.com61eb0402011-04-15 12:11:12 +00001238 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1239 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001241 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242
1243 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1244
1245 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001246 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001248 }
1249 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 }
1253 ::fprintf(file, "};\n");
1254 ::fclose(file);
1255}
1256
1257#endif
1258
1259
reed@google.com61eb0402011-04-15 12:11:12 +00001260static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1261 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 SkScalar inv = SkScalarInvert(radius);
1263
1264 matrix->setTranslate(-center.fX, -center.fY);
1265 matrix->postScale(inv, inv);
1266}
1267
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001268
1269namespace {
1270
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001271typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
1272 SkScalar sfy, SkScalar sdy,
1273 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001274 int toggle, int count);
1275
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001276void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
1277 SkScalar sfy, SkScalar sdy,
1278 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001279 int toggle, int count) {
1280 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
1281
1282 /* knock these down so we can pin against +- 0x7FFF, which is an
1283 immediate load, rather than 0xFFFF which is slower. This is a
1284 compromise, since it reduces our precision, but that appears
1285 to be visually OK. If we decide this is OK for all of our cases,
1286 we could (it seems) put this scale-down into fDstToIndex,
1287 to avoid having to do these extra shifts each time.
1288 */
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001289 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1290 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1291 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1292 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001293 // might perform this check for the other modes,
1294 // but the win will be a smaller % of the total
1295 if (dy == 0) {
1296 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1297 fy *= fy;
1298 do {
1299 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1300 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1301 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1302 fx += dx;
1303 *dstC++ = cache[toggle +
1304 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001305 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001306 } while (--count != 0);
1307 } else {
1308 do {
1309 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1310 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1311 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1312 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1313 fx += dx;
1314 fy += dy;
1315 *dstC++ = cache[toggle +
1316 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001317 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001318 } while (--count != 0);
1319 }
1320}
1321
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001322void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
1323 SkScalar sfy, SkScalar sdy,
1324 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001325 int toggle, int count) {
1326 do {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001327#ifdef SK_SCALAR_IS_FLOAT
1328 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1329 SkFixed dist = SkFloatToFixed(fdist);
1330#else
1331 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1332 SkFixedSquare(sfy);
1333 if (magnitudeSquared < 0) // Overflow.
1334 magnitudeSquared = SK_FixedMax;
1335 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1336#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001337 unsigned fi = mirror_tileproc(dist);
1338 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001339 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001340 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001341 sfx += sdx;
1342 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001343 } while (--count != 0);
1344}
1345
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001346void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
1347 SkScalar sfy, SkScalar sdy,
1348 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001349 int toggle, int count) {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001350 SkFixed fx = SkScalarToFixed(sfx);
1351 SkFixed dx = SkScalarToFixed(sdx);
1352 SkFixed fy = SkScalarToFixed(sfy);
1353 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001354 do {
1355 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1356 unsigned fi = repeat_tileproc(dist);
1357 SkASSERT(fi <= 0xFFFF);
1358 fx += dx;
1359 fy += dy;
1360 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001361 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001362 } while (--count != 0);
1363}
1364
1365}
1366
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367class Radial_Gradient : public Gradient_Shader {
1368public:
1369 Radial_Gradient(const SkPoint& center, SkScalar radius,
1370 const SkColor colors[], const SkScalar pos[], int colorCount,
1371 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001372 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1373 fCenter(center),
1374 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 {
1376 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1377 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1378
1379 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1380 }
reed@google.com61eb0402011-04-15 12:11:12 +00001381
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001382 virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
1383 SK_OVERRIDE;
reed@google.com7096dc62012-05-11 18:01:50 +00001384 virtual void shadeSpan16(int x, int y, uint16_t* dstCParam,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001385 int count) SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 SkASSERT(count > 0);
1387
reed@google.com7096dc62012-05-11 18:01:50 +00001388 uint16_t* SK_RESTRICT dstC = dstCParam;
1389
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 SkPoint srcPt;
1391 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1392 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001393 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001394 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395
reed@android.com3c9b2a42009-08-27 19:28:37 +00001396 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001397 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1398 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001399
1400 SkScalar sdx = fDstToIndex.getScaleX();
1401 SkScalar sdy = fDstToIndex.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402
reed@android.com3c9b2a42009-08-27 19:28:37 +00001403 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 SkFixed storage[2];
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001405 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1406 &storage[0], &storage[1]);
1407 sdx = SkFixedToScalar(storage[0]);
1408 sdy = SkFixedToScalar(storage[1]);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001409 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 }
1412
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001413 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001414 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001415 shadeProc = shadeSpan16_radial_clamp;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001416 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001417 shadeProc = shadeSpan16_radial_mirror;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001418 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 }
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001421 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
1422 cache, toggle, count);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001423 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 SkScalar dstX = SkIntToScalar(x);
1425 SkScalar dstY = SkIntToScalar(y);
1426 do {
1427 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1428 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1429 SkASSERT(fi <= 0xFFFF);
1430
1431 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001433 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434
1435 dstX += SK_Scalar1;
1436 } while (--count != 0);
1437 }
1438 }
1439
reed@google.com55b8e8c2011-01-13 16:22:35 +00001440 virtual BitmapType asABitmap(SkBitmap* bitmap,
1441 SkMatrix* matrix,
rileya@google.com91f319c2012-07-25 17:18:31 +00001442 TileMode* xy) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001443 if (bitmap) {
1444 this->commonAsABitmap(bitmap);
1445 }
1446 if (matrix) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001447 matrix->setScale(SkIntToScalar(kGradient32Length),
1448 SkIntToScalar(kGradient32Length));
reed@google.comdc731fd2010-12-23 15:19:47 +00001449 matrix->preConcat(fPtsToUnit);
1450 }
1451 if (xy) {
1452 xy[0] = fTileMode;
1453 xy[1] = kClamp_TileMode;
1454 }
1455 return kRadial_BitmapType;
1456 }
rileya@google.com03c1c352012-07-20 20:02:43 +00001457
reed@google.com7716afb2011-12-07 15:17:50 +00001458 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001459 if (info) {
1460 commonAsAGradient(info);
1461 info->fPoint[0] = fCenter;
1462 info->fRadius[0] = fRadius;
1463 }
1464 return kRadial_GradientType;
1465 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001466
rileya@google.com03c1c352012-07-20 20:02:43 +00001467 virtual GrCustomStage* asNewCustomStage(GrContext* context,
1468 GrSamplerState* sampler) const SK_OVERRIDE {
1469 SkASSERT(NULL != context && NULL != sampler);
1470 sampler->matrix()->preConcat(fPtsToUnit);
1471 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
1472 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
1473 sampler->setFilter(GrSamplerState::kBilinear_Filter);
rileya@google.com91f319c2012-07-25 17:18:31 +00001474 return SkNEW_ARGS(GrRadialGradient, (context, *this, sampler));
rileya@google.com03c1c352012-07-20 20:02:43 +00001475 }
1476
djsollen@google.comba28d032012-03-26 17:57:35 +00001477 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Radial_Gradient)
1478
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001480 Radial_Gradient(SkFlattenableReadBuffer& buffer)
djsollen@google.com54924242012-03-29 15:18:04 +00001481 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001482 fCenter(buffer.readPoint()),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001483 fRadius(buffer.readScalar()) {
1484 }
djsollen@google.com54924242012-03-29 15:18:04 +00001485 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
1486 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001487 buffer.writePoint(fCenter);
djsollen@google.com54924242012-03-29 15:18:04 +00001488 buffer.writeScalar(fRadius);
1489 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490
1491private:
1492 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001493 const SkPoint fCenter;
1494 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495};
1496
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001497namespace {
1498
1499inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001500 // fast, overly-conservative test: checks unit square instead
1501 // of unit circle
1502 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
1503 (fx <= -SK_FixedHalf && dx <= 0);
1504 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
1505 (fy <= -SK_FixedHalf && dy <= 0);
1506
1507 return xClamped || yClamped;
1508}
1509
1510// Return true if (fx * fy) is always inside the unit circle
1511// SkPin32 is expensive, but so are all the SkFixedMul in this test,
1512// so it shouldn't be run if count is small.
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001513inline bool no_need_for_radial_pin(int fx, int dx,
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001514 int fy, int dy, int count) {
1515 SkASSERT(count > 0);
1516 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1517 return false;
1518 }
1519 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
1520 return false;
1521 }
1522 fx += (count - 1) * dx;
1523 fy += (count - 1) * dy;
1524 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1525 return false;
1526 }
1527 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
1528}
1529
1530#define UNPINNED_RADIAL_STEP \
1531 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001532 *dstC++ = cache[toggle + \
1533 (sqrt_table[fi] >> Gradient_Shader::kSqrt32Shift)]; \
1534 toggle ^= Gradient_Shader::kDitherStride32; \
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001535 fx += dx; \
1536 fy += dy;
1537
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001538typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
1539 SkScalar sfy, SkScalar sdy,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001540 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001541 int count, int toggle);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001542
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001543// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001544void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
1545 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001546 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001547 int count, int toggle) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001548 // Floating point seems to be slower than fixed point,
1549 // even when we have float hardware.
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001550 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001551 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1552 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1553 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1554 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001555 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001556 unsigned fi = Gradient_Shader::kGradient32Length;
1557 sk_memset32_dither(dstC,
1558 cache[toggle + fi],
1559 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
1560 count);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001561 } else if ((count > 4) &&
1562 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
1563 unsigned fi;
1564 // 4x unroll appears to be no faster than 2x unroll on Linux
1565 while (count > 1) {
1566 UNPINNED_RADIAL_STEP;
1567 UNPINNED_RADIAL_STEP;
1568 count -= 2;
1569 }
1570 if (count) {
1571 UNPINNED_RADIAL_STEP;
1572 }
1573 }
1574 else {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001575 // Specializing for dy == 0 gains us 25% on Skia benchmarks
1576 if (dy == 0) {
1577 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1578 yy *= yy;
1579 do {
1580 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1581 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
1582 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001583 *dstC++ = cache[toggle + (sqrt_table[fi] >>
1584 Gradient_Shader::kSqrt32Shift)];
1585 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001586 fx += dx;
1587 } while (--count != 0);
1588 } else {
1589 do {
1590 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1591 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1592 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1593 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001594 *dstC++ = cache[toggle + (sqrt_table[fi] >>
1595 Gradient_Shader::kSqrt32Shift)];
1596 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001597 fx += dx;
1598 fy += dy;
1599 } while (--count != 0);
1600 }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001601 }
1602}
1603
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001604// Unrolling this loop doesn't seem to help (when float); we're stalling to
1605// get the results of the sqrt (?), and don't have enough extra registers to
1606// have many in flight.
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001607void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
1608 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001609 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001610 int count, int toggle) {
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001611 do {
tomhudson@google.comf925ba42012-01-11 21:54:26 +00001612#ifdef SK_SCALAR_IS_FLOAT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001613 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1614 SkFixed dist = SkFloatToFixed(fdist);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001615#else
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001616 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1617 SkFixedSquare(sfy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001618 if (magnitudeSquared < 0) // Overflow.
1619 magnitudeSquared = SK_FixedMax;
1620 SkFixed dist = SkFixedSqrt(magnitudeSquared);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001621#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001622 unsigned fi = mirror_tileproc(dist);
1623 SkASSERT(fi <= 0xFFFF);
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001624 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
1625 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001626 sfx += sdx;
1627 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001628 } while (--count != 0);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001629}
1630
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001631void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
1632 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001633 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001634 int count, int toggle) {
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001635 SkFixed fx = SkScalarToFixed(sfx);
1636 SkFixed dx = SkScalarToFixed(sdx);
1637 SkFixed fy = SkScalarToFixed(sfy);
1638 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001639 do {
1640 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1641 SkFixedSquare(fy);
1642 if (magnitudeSquared < 0) // Overflow.
1643 magnitudeSquared = SK_FixedMax;
1644 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1645 unsigned fi = repeat_tileproc(dist);
1646 SkASSERT(fi <= 0xFFFF);
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001647 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
1648 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001649 fx += dx;
1650 fy += dy;
1651 } while (--count != 0);
1652}
1653}
1654
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001655void Radial_Gradient::shadeSpan(int x, int y,
1656 SkPMColor* SK_RESTRICT dstC, int count) {
1657 SkASSERT(count > 0);
1658
1659 SkPoint srcPt;
1660 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1661 TileProc proc = fTileProc;
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001662 const SkPMColor* SK_RESTRICT cache = this->getCache32();
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001663#ifdef USE_DITHER_32BIT_GRADIENT
1664 int toggle = ((x ^ y) & 1) * Gradient_Shader::kDitherStride32;
1665#else
1666 int toggle = 0;
1667#endif
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001668
1669 if (fDstToIndexClass != kPerspective_MatrixClass) {
1670 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1671 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001672 SkScalar sdx = fDstToIndex.getScaleX();
1673 SkScalar sdy = fDstToIndex.getSkewY();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001674
1675 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1676 SkFixed storage[2];
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001677 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1678 &storage[0], &storage[1]);
1679 sdx = SkFixedToScalar(storage[0]);
1680 sdy = SkFixedToScalar(storage[1]);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001681 } else {
1682 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001683 }
1684
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001685 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001686 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001687 shadeProc = shadeSpan_radial_clamp;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001688 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001689 shadeProc = shadeSpan_radial_mirror;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001690 } else {
1691 SkASSERT(proc == repeat_tileproc);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001692 }
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001693 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001694 } else { // perspective case
1695 SkScalar dstX = SkIntToScalar(x);
1696 SkScalar dstY = SkIntToScalar(y);
1697 do {
1698 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1699 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1700 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001701 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001702 dstX += SK_Scalar1;
1703 } while (--count != 0);
1704 }
1705}
1706
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001707/* Two-point radial gradients are specified by two circles, each with a center
1708 point and radius. The gradient can be considered to be a series of
1709 concentric circles, with the color interpolated from the start circle
1710 (at t=0) to the end circle (at t=1).
1711
1712 For each point (x, y) in the span, we want to find the
1713 interpolated circle that intersects that point. The center
1714 of the desired circle (Cx, Cy) falls at some distance t
1715 along the line segment between the start point (Sx, Sy) and
1716 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001717
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001718 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1719 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001720
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001721 The radius of the desired circle (r) is also a linear interpolation t
1722 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001723
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001724 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001725
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001726 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001727
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001728 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001729
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001730 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001731
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001732 (x - ((1 - t) * Sx + t * Ex))^2
1733 + (y - ((1 - t) * Sy + t * Ey))^2
1734 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001735
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001736 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001737
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001738 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1739 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1740 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001741
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001742 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1743
1744 [Dx^2 + Dy^2 - Dr^2)] * t^2
1745 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1746 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001747
1748 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001749 possible circles on which the point may fall. Solving for t yields
1750 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001751
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001752 If a<0, the start circle is entirely contained in the
1753 end circle, and one of the roots will be <0 or >1 (off the line
1754 segment). If a>0, the start circle falls at least partially
1755 outside the end circle (or vice versa), and the gradient
1756 defines a "tube" where a point may be on one circle (on the
1757 inside of the tube) or the other (outside of the tube). We choose
1758 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001759
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001760 In order to keep the math to within the limits of fixed point,
1761 we divide the entire quadratic by Dr^2, and replace
1762 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001763
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001764 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1765 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1766 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001767
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001768 (x' and y' are computed by appending the subtract and scale to the
1769 fDstToIndex matrix in the constructor).
1770
1771 Since the 'A' component of the quadratic is independent of x' and y', it
1772 is precomputed in the constructor. Since the 'B' component is linear in
1773 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001774 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001775 a perspective projection), it must be computed in the loop.
1776
1777*/
1778
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001779namespace {
1780
1781inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1782 SkScalar sr2d2, SkScalar foura,
1783 SkScalar oneOverTwoA, bool posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001784 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001785 if (0 == foura) {
1786 return SkScalarToFixed(SkScalarDiv(-c, b));
1787 }
1788
reed@google.com84e9c082011-04-13 17:44:24 +00001789 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001790 if (discrim < 0) {
1791 discrim = -discrim;
1792 }
reed@google.com84e9c082011-04-13 17:44:24 +00001793 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1794 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001795 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001796 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001797 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001798 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001799 }
reed@google.com84e9c082011-04-13 17:44:24 +00001800 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001801}
1802
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001803typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
1804 SkScalar fy, SkScalar dy,
1805 SkScalar b, SkScalar db,
1806 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001807 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001808 int count);
1809
1810void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
1811 SkScalar fy, SkScalar dy,
1812 SkScalar b, SkScalar db,
1813 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001814 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001815 int count) {
1816 for (; count > 0; --count) {
1817 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1818 fOneOverTwoA, posRoot);
1819 SkFixed index = SkClampMax(t, 0xFFFF);
1820 SkASSERT(index <= 0xFFFF);
1821 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1822 fx += dx;
1823 fy += dy;
1824 b += db;
1825 }
1826}
1827void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
1828 SkScalar fy, SkScalar dy,
1829 SkScalar b, SkScalar db,
1830 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001831 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001832 int count) {
1833 for (; count > 0; --count) {
1834 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1835 fOneOverTwoA, posRoot);
1836 SkFixed index = mirror_tileproc(t);
1837 SkASSERT(index <= 0xFFFF);
1838 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1839 fx += dx;
1840 fy += dy;
1841 b += db;
1842 }
1843}
1844
1845void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
1846 SkScalar fy, SkScalar dy,
1847 SkScalar b, SkScalar db,
1848 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001849 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001850 int count) {
1851 for (; count > 0; --count) {
1852 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1853 fOneOverTwoA, posRoot);
1854 SkFixed index = repeat_tileproc(t);
1855 SkASSERT(index <= 0xFFFF);
1856 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1857 fx += dx;
1858 fy += dy;
1859 b += db;
1860 }
1861}
1862
1863
1864
1865}
1866
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001867class Two_Point_Radial_Gradient : public Gradient_Shader {
1868public:
1869 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1870 const SkPoint& end, SkScalar endRadius,
1871 const SkColor colors[], const SkScalar pos[],
1872 int colorCount, SkShader::TileMode mode,
1873 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001874 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1875 fCenter1(start),
1876 fCenter2(end),
1877 fRadius1(startRadius),
1878 fRadius2(endRadius) {
1879 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001880 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001881
1882 virtual BitmapType asABitmap(SkBitmap* bitmap,
1883 SkMatrix* matrix,
rileya@google.com91f319c2012-07-25 17:18:31 +00001884 TileMode* xy) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001885 if (bitmap) {
1886 this->commonAsABitmap(bitmap);
1887 }
1888 SkScalar diffL = 0; // just to avoid gcc warning
rileya@google.com91f319c2012-07-25 17:18:31 +00001889 if (matrix) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001890 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001891 SkScalarSquare(fDiff.fY));
1892 }
1893 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001894 if (diffL) {
1895 SkScalar invDiffL = SkScalarInvert(diffL);
1896 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1897 SkScalarMul(invDiffL, fDiff.fX));
1898 } else {
1899 matrix->reset();
1900 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001901 matrix->preConcat(fPtsToUnit);
1902 }
1903 if (xy) {
1904 xy[0] = fTileMode;
1905 xy[1] = kClamp_TileMode;
1906 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001907 return kTwoPointRadial_BitmapType;
1908 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001909
reed@google.com8e6d9142011-12-07 15:30:34 +00001910 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001911 if (info) {
1912 commonAsAGradient(info);
1913 info->fPoint[0] = fCenter1;
1914 info->fPoint[1] = fCenter2;
1915 info->fRadius[0] = fRadius1;
1916 info->fRadius[1] = fRadius2;
1917 }
1918 return kRadial2_GradientType;
1919 }
1920
rileya@google.com03c1c352012-07-20 20:02:43 +00001921 virtual GrCustomStage* asNewCustomStage(GrContext* context,
1922 GrSamplerState* sampler) const SK_OVERRIDE {
1923 SkASSERT(NULL != context && NULL != sampler);
1924 SkScalar diffLen = fDiff.length();
1925 if (0 != diffLen) {
1926 SkScalar invDiffLen = SkScalarInvert(diffLen);
1927 sampler->matrix()->setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
rileya@google.com91f319c2012-07-25 17:18:31 +00001928 SkScalarMul(invDiffLen, fDiff.fX));
rileya@google.com03c1c352012-07-20 20:02:43 +00001929 } else {
1930 sampler->matrix()->reset();
1931 }
1932 sampler->matrix()->preConcat(fPtsToUnit);
1933 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
1934 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
1935 sampler->setFilter(GrSamplerState::kBilinear_Filter);
rileya@google.com91f319c2012-07-25 17:18:31 +00001936 return SkNEW_ARGS(GrRadial2Gradient, (context, *this, sampler,
1937 diffLen, fStartRadius, fDiffRadius));
rileya@google.com03c1c352012-07-20 20:02:43 +00001938 }
1939
reed@google.com7096dc62012-05-11 18:01:50 +00001940 virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001941 int count) SK_OVERRIDE {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001942 SkASSERT(count > 0);
1943
reed@google.com7096dc62012-05-11 18:01:50 +00001944 SkPMColor* SK_RESTRICT dstC = dstCParam;
1945
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001946 // Zero difference between radii: fill with transparent black.
1947 if (fDiffRadius == 0) {
1948 sk_bzero(dstC, count * sizeof(*dstC));
1949 return;
1950 }
1951 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1952 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001953 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001954
1955 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001956 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001957 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001958 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001959 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1960 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001961 SkScalar dx, fx = srcPt.fX;
1962 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001963
reed@google.com61eb0402011-04-15 12:11:12 +00001964 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001965 SkFixed fixedX, fixedY;
1966 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1967 dx = SkFixedToScalar(fixedX);
1968 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001969 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001970 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001971 dx = fDstToIndex.getScaleX();
1972 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001973 }
reed@google.com84e9c082011-04-13 17:44:24 +00001974 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1975 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1976 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1977 SkScalarMul(fDiff.fY, dy)) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001978
1979 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
reed@google.com61eb0402011-04-15 12:11:12 +00001980 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001981 shadeProc = shadeSpan_twopoint_clamp;
reed@google.com61eb0402011-04-15 12:11:12 +00001982 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001983 shadeProc = shadeSpan_twopoint_mirror;
reed@google.com61eb0402011-04-15 12:11:12 +00001984 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001985 SkASSERT(proc == repeat_tileproc);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001986 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001987 (*shadeProc)(fx, dx, fy, dy, b, db,
1988 fSr2D2, foura, fOneOverTwoA, posRoot,
1989 dstC, cache, count);
reed@google.com61eb0402011-04-15 12:11:12 +00001990 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001991 SkScalar dstX = SkIntToScalar(x);
1992 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001993 for (; count > 0; --count) {
1994 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001995 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001996 SkScalar fx = srcPt.fX;
1997 SkScalar fy = srcPt.fY;
1998 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1999 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00002000 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
2001 fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002002 SkFixed index = proc(t);
2003 SkASSERT(index <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00002004 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
reed@android.com6c59a172009-09-22 20:24:05 +00002005 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002006 }
2007 }
2008 }
2009
reed@android.com6c59a172009-09-22 20:24:05 +00002010 virtual bool setContext(const SkBitmap& device,
2011 const SkPaint& paint,
reed@google.com8e6d9142011-12-07 15:30:34 +00002012 const SkMatrix& matrix) SK_OVERRIDE {
2013 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com6c59a172009-09-22 20:24:05 +00002014 return false;
2015 }
2016
reed@google.com070a8002012-06-05 17:15:30 +00002017 // For now, we might have divided by zero, so detect that
2018 if (0 == fDiffRadius) {
2019 return false;
2020 }
2021
reed@android.com6c59a172009-09-22 20:24:05 +00002022 // we don't have a span16 proc
2023 fFlags &= ~kHasSpan16_Flag;
2024 return true;
2025 }
2026
djsollen@google.com54924242012-03-29 15:18:04 +00002027 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Radial_Gradient)
2028
2029protected:
2030 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
2031 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002032 fCenter1(buffer.readPoint()),
2033 fCenter2(buffer.readPoint()),
djsollen@google.com54924242012-03-29 15:18:04 +00002034 fRadius1(buffer.readScalar()),
2035 fRadius2(buffer.readScalar()) {
2036 init();
2037 };
2038
2039 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
reed@android.combcfc7332009-11-10 16:19:39 +00002040 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002041 buffer.writePoint(fCenter1);
2042 buffer.writePoint(fCenter2);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002043 buffer.writeScalar(fRadius1);
2044 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00002045 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002046
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002047private:
2048 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002049 const SkPoint fCenter1;
2050 const SkPoint fCenter2;
2051 const SkScalar fRadius1;
2052 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002053 SkPoint fDiff;
2054 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002055
2056 void init() {
2057 fDiff = fCenter1 - fCenter2;
2058 fDiffRadius = fRadius2 - fRadius1;
reed@google.comb83f7972012-06-05 17:39:22 +00002059 // hack to avoid zero-divide for now
2060 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002061 fDiff.fX = SkScalarMul(fDiff.fX, inv);
2062 fDiff.fY = SkScalarMul(fDiff.fY, inv);
2063 fStartRadius = SkScalarMul(fRadius1, inv);
2064 fSr2D2 = SkScalarSquare(fStartRadius);
2065 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00002066 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002067
2068 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
2069 fPtsToUnit.postScale(inv, inv);
2070 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002071};
2072
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073///////////////////////////////////////////////////////////////////////////////
2074
reed@google.comcb7be692012-06-06 20:31:56 +00002075static int valid_divide(float numer, float denom, float* ratio) {
2076 SkASSERT(ratio);
2077 if (0 == denom) {
2078 return 0;
2079 }
2080 *ratio = numer / denom;
2081 return 1;
2082}
2083
2084// Return the number of distinct real roots, and write them into roots[] in
2085// ascending order
2086static int find_quad_roots(float A, float B, float C, float roots[2]) {
2087 SkASSERT(roots);
2088
2089 if (A == 0) {
2090 return valid_divide(-C, B, roots);
2091 }
2092
reed@google.comcb7be692012-06-06 20:31:56 +00002093 float R = B*B - 4*A*C;
reed@google.comaca29ae2012-06-07 14:20:52 +00002094 if (R < 0) {
reed@google.comcb7be692012-06-06 20:31:56 +00002095 return 0;
2096 }
2097 R = sk_float_sqrt(R);
reed@google.comaca29ae2012-06-07 14:20:52 +00002098
2099#if 1
2100 float Q = B;
2101 if (Q < 0) {
2102 Q -= R;
2103 } else {
2104 Q += R;
reed@google.comcb7be692012-06-06 20:31:56 +00002105 }
reed@google.comaca29ae2012-06-07 14:20:52 +00002106#else
2107 // on 10.6 this was much slower than the above branch :(
2108 float Q = B + copysignf(R, B);
2109#endif
2110 Q *= -0.5f;
2111 if (0 == Q) {
2112 roots[0] = 0;
2113 return 1;
2114 }
2115
2116 float r0 = Q / A;
2117 float r1 = C / Q;
2118 roots[0] = r0 < r1 ? r0 : r1;
2119 roots[1] = r0 > r1 ? r0 : r1;
2120 return 2;
reed@google.comcb7be692012-06-06 20:31:56 +00002121}
2122
2123static float lerp(float x, float dx, float t) {
2124 return x + t * dx;
2125}
2126
2127static float sqr(float x) { return x * x; }
2128
2129struct TwoPtRadial {
2130 enum {
2131 kDontDrawT = 0x80000000
2132 };
2133
2134 float fCenterX, fCenterY;
2135 float fDCenterX, fDCenterY;
2136 float fRadius;
2137 float fDRadius;
2138 float fA;
2139 float fRadius2;
2140 float fRDR;
2141
2142 void init(const SkPoint& center0, SkScalar rad0,
2143 const SkPoint& center1, SkScalar rad1) {
2144 fCenterX = SkScalarToFloat(center0.fX);
2145 fCenterY = SkScalarToFloat(center0.fY);
2146 fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
2147 fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
2148 fRadius = SkScalarToFloat(rad0);
2149 fDRadius = SkScalarToFloat(rad1) - fRadius;
2150
mike@reedtribe.org23113dd2012-06-09 02:03:40 +00002151 fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
reed@google.comcb7be692012-06-06 20:31:56 +00002152 fRadius2 = sqr(fRadius);
2153 fRDR = fRadius * fDRadius;
2154 }
2155
2156 // used by setup and nextT
2157 float fRelX, fRelY, fIncX, fIncY;
2158 float fB, fDB;
2159
2160 void setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
2161 fRelX = SkScalarToFloat(fx) - fCenterX;
2162 fRelY = SkScalarToFloat(fy) - fCenterY;
2163 fIncX = SkScalarToFloat(dfx);
2164 fIncY = SkScalarToFloat(dfy);
2165 fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR);
2166 fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY);
2167 }
2168
2169 SkFixed nextT() {
2170 float roots[2];
2171
reed@google.comaca29ae2012-06-07 14:20:52 +00002172 float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
reed@google.comcb7be692012-06-06 20:31:56 +00002173 int countRoots = find_quad_roots(fA, fB, C, roots);
2174
2175 fRelX += fIncX;
2176 fRelY += fIncY;
2177 fB += fDB;
2178
2179 if (0 == countRoots) {
2180 return kDontDrawT;
2181 }
2182
2183 // Prefer the bigger t value if both give a radius(t) > 0
2184 // find_quad_roots returns the values sorted, so we start with the last
2185 float t = roots[countRoots - 1];
2186 float r = lerp(fRadius, fDRadius, t);
2187 if (r <= 0) {
2188 t = roots[0]; // might be the same as roots[countRoots-1]
2189 r = lerp(fRadius, fDRadius, t);
2190 if (r <= 0) {
2191 return kDontDrawT;
2192 }
2193 }
2194 return SkFloatToFixed(t);
2195 }
2196
2197 static bool DontDrawT(SkFixed t) {
reed@google.comaca29ae2012-06-07 14:20:52 +00002198 return kDontDrawT == (uint32_t)t;
reed@google.comcb7be692012-06-06 20:31:56 +00002199 }
2200};
2201
2202typedef void (*TwoPointRadialProc)(TwoPtRadial* rec, SkPMColor* dstC,
2203 const SkPMColor* cache, int count);
2204
reed@google.comaca29ae2012-06-07 14:20:52 +00002205static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
2206 const SkPMColor* SK_RESTRICT cache, int count) {
reed@google.comcb7be692012-06-06 20:31:56 +00002207 for (; count > 0; --count) {
2208 SkFixed t = rec->nextT();
2209 if (TwoPtRadial::DontDrawT(t)) {
2210 *dstC++ = 0;
2211 } else {
2212 SkFixed index = SkClampMax(t, 0xFFFF);
2213 SkASSERT(index <= 0xFFFF);
2214 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
2215 }
2216 }
2217}
2218
reed@google.comaca29ae2012-06-07 14:20:52 +00002219static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
2220 const SkPMColor* SK_RESTRICT cache, int count) {
reed@google.comcb7be692012-06-06 20:31:56 +00002221 for (; count > 0; --count) {
2222 SkFixed t = rec->nextT();
2223 if (TwoPtRadial::DontDrawT(t)) {
2224 *dstC++ = 0;
2225 } else {
2226 SkFixed index = repeat_tileproc(t);
2227 SkASSERT(index <= 0xFFFF);
2228 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
2229 }
2230 }
2231}
2232
reed@google.comaca29ae2012-06-07 14:20:52 +00002233static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
2234 const SkPMColor* SK_RESTRICT cache, int count) {
reed@google.comcb7be692012-06-06 20:31:56 +00002235 for (; count > 0; --count) {
2236 SkFixed t = rec->nextT();
2237 if (TwoPtRadial::DontDrawT(t)) {
2238 *dstC++ = 0;
2239 } else {
2240 SkFixed index = mirror_tileproc(t);
2241 SkASSERT(index <= 0xFFFF);
2242 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
2243 }
2244 }
2245}
2246
2247class Two_Point_Conical_Gradient : public Gradient_Shader {
2248 TwoPtRadial fRec;
2249
2250 void init() {
2251 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2);
2252 fPtsToUnit.reset();
2253 }
2254public:
2255 Two_Point_Conical_Gradient(const SkPoint& start, SkScalar startRadius,
2256 const SkPoint& end, SkScalar endRadius,
2257 const SkColor colors[], const SkScalar pos[],
2258 int colorCount, SkShader::TileMode mode,
2259 SkUnitMapper* mapper)
2260 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
2261 fCenter1(start),
2262 fCenter2(end),
2263 fRadius1(startRadius),
2264 fRadius2(endRadius) {
2265 // this is degenerate, and should be caught by our caller
2266 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
2267 this->init();
2268 }
2269
2270 virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
2271 int count) SK_OVERRIDE {
2272 SkASSERT(count > 0);
2273
2274 SkPMColor* SK_RESTRICT dstC = dstCParam;
2275
2276 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
2277 TileProc proc = fTileProc;
2278 const SkPMColor* SK_RESTRICT cache = this->getCache32();
2279
2280 TwoPointRadialProc shadeProc = twopoint_repeat;
2281 if (proc == clamp_tileproc) {
2282 shadeProc = twopoint_clamp;
2283 } else if (proc == mirror_tileproc) {
2284 shadeProc = twopoint_mirror;
2285 } else {
2286 SkASSERT(proc == repeat_tileproc);
2287 }
2288
2289 if (fDstToIndexClass != kPerspective_MatrixClass) {
2290 SkPoint srcPt;
2291 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
2292 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2293 SkScalar dx, fx = srcPt.fX;
2294 SkScalar dy, fy = srcPt.fY;
2295
2296 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
2297 SkFixed fixedX, fixedY;
2298 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
2299 dx = SkFixedToScalar(fixedX);
2300 dy = SkFixedToScalar(fixedY);
2301 } else {
2302 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
2303 dx = fDstToIndex.getScaleX();
2304 dy = fDstToIndex.getSkewY();
2305 }
2306
2307 fRec.setup(fx, fy, dx, dy);
2308 (*shadeProc)(&fRec, dstC, cache, count);
2309 } else { // perspective case
2310 SkScalar dstX = SkIntToScalar(x);
2311 SkScalar dstY = SkIntToScalar(y);
2312 for (; count > 0; --count) {
2313 SkPoint srcPt;
2314 dstProc(fDstToIndex, dstX, dstY, &srcPt);
2315 dstX += SK_Scalar1;
2316
2317 fRec.setup(srcPt.fX, srcPt.fY, 0, 0);
2318 (*shadeProc)(&fRec, dstC, cache, 1);
2319 }
2320 }
2321 }
2322
2323 virtual bool setContext(const SkBitmap& device,
2324 const SkPaint& paint,
2325 const SkMatrix& matrix) SK_OVERRIDE {
2326 if (!this->INHERITED::setContext(device, paint, matrix)) {
2327 return false;
2328 }
2329
2330 // we don't have a span16 proc
2331 fFlags &= ~kHasSpan16_Flag;
2332
2333 // in general, we might discard based on computed-radius, so clear
2334 // this flag (todo: sometimes we can detect that we never discard...)
2335 fFlags &= ~kOpaqueAlpha_Flag;
2336
2337 return true;
2338 }
reed@google.com83226972012-06-07 20:26:47 +00002339
rileya@google.com3e332582012-07-03 13:43:35 +00002340 virtual BitmapType asABitmap(SkBitmap* bitmap,
2341 SkMatrix* matrix,
rileya@google.com91f319c2012-07-25 17:18:31 +00002342 TileMode* xy) const {
rileya@google.com3e332582012-07-03 13:43:35 +00002343
2344 SkPoint diff = fCenter2 - fCenter1;
2345 SkScalar diffRadius = fRadius2 - fRadius1;
2346 SkScalar startRadius = fRadius1;
2347 SkScalar diffLen = 0;
2348
2349 if (bitmap) {
2350 this->commonAsABitmap(bitmap);
2351 }
rileya@google.com91f319c2012-07-25 17:18:31 +00002352 if (matrix) {
rileya@google.com3e332582012-07-03 13:43:35 +00002353 diffLen = diff.length();
2354 }
2355 if (matrix) {
2356 if (diffLen) {
2357 SkScalar invDiffLen = SkScalarInvert(diffLen);
2358 // rotate to align circle centers with the x-axis
2359 matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
2360 SkScalarMul(invDiffLen, diff.fX));
2361 } else {
2362 matrix->reset();
2363 }
2364 matrix->preTranslate(-fCenter1.fX, -fCenter1.fY);
2365 }
2366 if (xy) {
2367 xy[0] = fTileMode;
2368 xy[1] = kClamp_TileMode;
2369 }
rileya@google.com3e332582012-07-03 13:43:35 +00002370 return kTwoPointConical_BitmapType;
2371 }
2372
2373
reed@google.com83226972012-06-07 20:26:47 +00002374 SkShader::GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
2375 if (info) {
2376 commonAsAGradient(info);
2377 info->fPoint[0] = fCenter1;
2378 info->fPoint[1] = fCenter2;
2379 info->fRadius[0] = fRadius1;
2380 info->fRadius[1] = fRadius2;
2381 }
2382 return kConical_GradientType;
2383 }
2384
rileya@google.com03c1c352012-07-20 20:02:43 +00002385 virtual GrCustomStage* asNewCustomStage(GrContext* context,
2386 GrSamplerState* sampler) const SK_OVERRIDE {
2387 SkASSERT(NULL != context && NULL != sampler);
2388 SkPoint diff = fCenter2 - fCenter1;
2389 SkScalar diffLen = diff.length();
2390 if (0 != diffLen) {
2391 SkScalar invDiffLen = SkScalarInvert(diffLen);
2392 sampler->matrix()->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
2393 SkScalarMul(invDiffLen, diff.fX));
2394 } else {
2395 sampler->matrix()->reset();
2396 }
2397 sampler->matrix()->preTranslate(-fCenter1.fX, -fCenter1.fY);
2398 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
2399 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
2400 sampler->setFilter(GrSamplerState::kBilinear_Filter);
rileya@google.com91f319c2012-07-25 17:18:31 +00002401 return SkNEW_ARGS(GrConical2Gradient, (context, *this, sampler,
2402 diffLen, fRadius1, fRadius2 - fRadius1));
rileya@google.com03c1c352012-07-20 20:02:43 +00002403 }
2404
reed@google.comcb7be692012-06-06 20:31:56 +00002405 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Conical_Gradient)
2406
2407protected:
2408 Two_Point_Conical_Gradient(SkFlattenableReadBuffer& buffer)
2409 : INHERITED(buffer),
2410 fCenter1(buffer.readPoint()),
2411 fCenter2(buffer.readPoint()),
2412 fRadius1(buffer.readScalar()),
2413 fRadius2(buffer.readScalar()) {
2414 this->init();
2415 };
2416
2417 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
2418 this->INHERITED::flatten(buffer);
2419 buffer.writePoint(fCenter1);
2420 buffer.writePoint(fCenter2);
2421 buffer.writeScalar(fRadius1);
2422 buffer.writeScalar(fRadius2);
2423 }
2424
2425private:
2426 typedef Gradient_Shader INHERITED;
2427 const SkPoint fCenter1;
2428 const SkPoint fCenter2;
2429 const SkScalar fRadius1;
2430 const SkScalar fRadius2;
2431};
2432
2433///////////////////////////////////////////////////////////////////////////////
2434
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435class Sweep_Gradient : public Gradient_Shader {
2436public:
2437 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
2438 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002439 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
2440 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441 {
2442 fPtsToUnit.setTranslate(-cx, -cy);
2443 }
reed@google.com7716afb2011-12-07 15:17:50 +00002444 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
2445 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002446
2447 virtual BitmapType asABitmap(SkBitmap* bitmap,
2448 SkMatrix* matrix,
rileya@google.com91f319c2012-07-25 17:18:31 +00002449 TileMode* xy) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00002450 if (bitmap) {
2451 this->commonAsABitmap(bitmap);
2452 }
2453 if (matrix) {
2454 *matrix = fPtsToUnit;
2455 }
2456 if (xy) {
2457 xy[0] = fTileMode;
2458 xy[1] = kClamp_TileMode;
2459 }
2460 return kSweep_BitmapType;
2461 }
2462
reed@google.com7716afb2011-12-07 15:17:50 +00002463 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002464 if (info) {
2465 commonAsAGradient(info);
2466 info->fPoint[0] = fCenter;
2467 }
2468 return kSweep_GradientType;
2469 }
2470
rileya@google.com03c1c352012-07-20 20:02:43 +00002471 virtual GrCustomStage* asNewCustomStage(GrContext* context,
2472 GrSamplerState* sampler) const SK_OVERRIDE {
2473 sampler->matrix()->preConcat(fPtsToUnit);
2474 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
2475 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
2476 sampler->setFilter(GrSamplerState::kBilinear_Filter);
rileya@google.com91f319c2012-07-25 17:18:31 +00002477 return SkNEW_ARGS(GrSweepGradient, (context, *this, sampler));
rileya@google.com03c1c352012-07-20 20:02:43 +00002478 }
2479
djsollen@google.comba28d032012-03-26 17:57:35 +00002480 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sweep_Gradient)
2481
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002483 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
djsollen@google.com54924242012-03-29 15:18:04 +00002484 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002485 fCenter(buffer.readPoint()) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002486 }
djsollen@google.com54924242012-03-29 15:18:04 +00002487 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
2488 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002489 buffer.writePoint(fCenter);
djsollen@google.com54924242012-03-29 15:18:04 +00002490 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002491
reed@android.com8a1c16f2008-12-17 15:59:43 +00002492private:
2493 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002494 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002495};
2496
caryclark@google.com383d5d42012-06-06 12:09:18 +00002497#ifndef SK_SCALAR_IS_FLOAT
reed@android.com8a1c16f2008-12-17 15:59:43 +00002498#ifdef COMPUTE_SWEEP_TABLE
2499#define PI 3.14159265
2500static bool gSweepTableReady;
2501static uint8_t gSweepTable[65];
2502
2503/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
2504 We scale the results to [0..32]
2505*/
reed@google.com61eb0402011-04-15 12:11:12 +00002506static const uint8_t* build_sweep_table() {
2507 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002508 const int N = 65;
2509 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002510
reed@android.com8a1c16f2008-12-17 15:59:43 +00002511 for (int i = 0; i < N; i++)
2512 {
2513 double arg = i / DENOM;
2514 double v = atan(arg);
2515 int iv = (int)round(v * DENOM * 2 / PI);
2516// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
2517 printf("%d, ", iv);
2518 gSweepTable[i] = iv;
2519 }
2520 gSweepTableReady = true;
2521 }
2522 return gSweepTable;
2523}
2524#else
2525static const uint8_t gSweepTable[] = {
2526 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
2527 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
2528 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
2529 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
2530 32
2531};
2532static const uint8_t* build_sweep_table() { return gSweepTable; }
2533#endif
caryclark@google.com383d5d42012-06-06 12:09:18 +00002534#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535
2536// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
2537// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
2538// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
2539
2540//unsigned div_64(int numer, int denom);
caryclark@google.com383d5d42012-06-06 12:09:18 +00002541#ifndef SK_SCALAR_IS_FLOAT
reed@google.com61eb0402011-04-15 12:11:12 +00002542static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002543 SkASSERT(numer <= denom);
2544 SkASSERT(numer > 0);
2545 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002546
reed@android.com8a1c16f2008-12-17 15:59:43 +00002547 int nbits = SkCLZ(numer);
2548 int dbits = SkCLZ(denom);
2549 int bits = 6 - nbits + dbits;
2550 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002551
reed@google.com61eb0402011-04-15 12:11:12 +00002552 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00002553 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002554 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002555
2556 denom <<= dbits - 1;
2557 numer <<= nbits - 1;
2558
2559 unsigned result = 0;
2560
2561 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00002562 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002563 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00002564 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002565 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00002566 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002567
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00002569 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002570 // make room for the rest of the answer bits
2571 result <<= bits;
2572 switch (bits) {
2573 case 6:
2574 if ((numer = (numer << 1) - denom) >= 0)
2575 result |= 32;
2576 else
2577 numer += denom;
2578 case 5:
2579 if ((numer = (numer << 1) - denom) >= 0)
2580 result |= 16;
2581 else
2582 numer += denom;
2583 case 4:
2584 if ((numer = (numer << 1) - denom) >= 0)
2585 result |= 8;
2586 else
2587 numer += denom;
2588 case 3:
2589 if ((numer = (numer << 1) - denom) >= 0)
2590 result |= 4;
2591 else
2592 numer += denom;
2593 case 2:
2594 if ((numer = (numer << 1) - denom) >= 0)
2595 result |= 2;
2596 else
2597 numer += denom;
2598 case 1:
2599 default: // not strictly need, but makes GCC make better ARM code
2600 if ((numer = (numer << 1) - denom) >= 0)
2601 result |= 1;
2602 else
2603 numer += denom;
2604 }
2605 }
2606 return result;
2607}
caryclark@google.com383d5d42012-06-06 12:09:18 +00002608#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002609
2610// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
caryclark@google.com383d5d42012-06-06 12:09:18 +00002611#ifndef SK_SCALAR_IS_FLOAT
reed@google.com61eb0402011-04-15 12:11:12 +00002612static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002613#ifdef SK_DEBUG
2614 {
2615 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00002616 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002617 gOnce = true;
2618 SkASSERT(div_64(55, 55) == 64);
2619 SkASSERT(div_64(128, 256) == 32);
2620 SkASSERT(div_64(2326528, 4685824) == 31);
2621 SkASSERT(div_64(753664, 5210112) == 9);
2622 SkASSERT(div_64(229376, 4882432) == 3);
2623 SkASSERT(div_64(2, 64) == 2);
2624 SkASSERT(div_64(1, 64) == 1);
2625 // test that we handle underflow correctly
2626 SkASSERT(div_64(12345, 0x54321234) == 0);
2627 }
2628 }
2629#endif
2630
2631 SkASSERT(y > 0 && x > 0);
2632 const uint8_t* table = build_sweep_table();
2633
2634 unsigned result;
2635 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00002636 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002637 // first part of the atan(v) = PI/2 - atan(1/v) identity
2638 // since our div_64 and table want v <= 1, where v = y/x
2639 SkTSwap<SkFixed>(x, y);
2640 }
2641
2642 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002643
reed@android.com8a1c16f2008-12-17 15:59:43 +00002644#ifdef SK_DEBUG
2645 {
2646 unsigned result2 = SkDivBits(y, x, 6);
2647 SkASSERT(result2 == result ||
2648 (result == 1 && result2 == 0));
2649 }
2650#endif
2651
2652 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2653 result = table[result];
2654
reed@google.com61eb0402011-04-15 12:11:12 +00002655 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002656 // complete the atan(v) = PI/2 - atan(1/v) identity
2657 result = 64 - result;
2658 // pin to 63
2659 result -= result >> 6;
2660 }
2661
2662 SkASSERT(result <= 63);
2663 return result;
2664}
caryclark@google.com383d5d42012-06-06 12:09:18 +00002665#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002666
2667// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com51baf5a2011-09-21 13:38:36 +00002668#ifdef SK_SCALAR_IS_FLOAT
2669static unsigned SkATan2_255(float y, float x) {
2670 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
2671 static const float g255Over2PI = 40.584510488433314f;
2672
2673 float result = sk_float_atan2(y, x);
2674 if (result < 0) {
2675 result += 2 * SK_ScalarPI;
2676 }
2677 SkASSERT(result >= 0);
2678 // since our value is always >= 0, we can cast to int, which is faster than
2679 // calling floorf()
2680 int ir = (int)(result * g255Over2PI);
2681 SkASSERT(ir >= 0 && ir <= 255);
2682 return ir;
2683}
2684#else
reed@google.com61eb0402011-04-15 12:11:12 +00002685static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2686 if (x == 0) {
2687 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002688 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002689 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002690 return y < 0 ? 192 : 64;
2691 }
reed@google.com61eb0402011-04-15 12:11:12 +00002692 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002693 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002694 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002695
reed@android.com8a1c16f2008-12-17 15:59:43 +00002696 /* Find the right quadrant for x,y
2697 Since atan_0_90 only handles the first quadrant, we rotate x,y
2698 appropriately before calling it, and then add the right amount
2699 to account for the real quadrant.
2700 quadrant 0 : add 0 | x > 0 && y > 0
2701 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2702 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2703 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002704
reed@android.com8a1c16f2008-12-17 15:59:43 +00002705 map x<0 to (1 << 6)
2706 map y<0 to (3 << 6)
2707 add = map_x ^ map_y
2708 */
2709 int xsign = x >> 31;
2710 int ysign = y >> 31;
2711 int add = ((-xsign) ^ (ysign & 3)) << 6;
2712
2713#ifdef SK_DEBUG
2714 if (0 == add)
2715 SkASSERT(x > 0 && y > 0);
2716 else if (64 == add)
2717 SkASSERT(x < 0 && y > 0);
2718 else if (128 == add)
2719 SkASSERT(x < 0 && y < 0);
2720 else if (192 == add)
2721 SkASSERT(x > 0 && y < 0);
2722 else
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002723 SkDEBUGFAIL("bad value for add");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002724#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002725
reed@android.com8a1c16f2008-12-17 15:59:43 +00002726 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2727 where we need to rotate x,y by 90 or -90
2728 */
2729 x = (x ^ xsign) - xsign;
2730 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002731 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002732 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002733 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002734
2735 unsigned result = add + atan_0_90(y, x);
2736 SkASSERT(result < 256);
2737 return result;
2738}
reed@google.com51baf5a2011-09-21 13:38:36 +00002739#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002740
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002741void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
2742 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002743 SkMatrix::MapXYProc proc = fDstToIndexProc;
2744 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002745 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002746 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002747
reed@google.com61eb0402011-04-15 12:11:12 +00002748 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002749 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2750 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002751 SkScalar dx, fx = srcPt.fX;
2752 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002753
reed@google.com61eb0402011-04-15 12:11:12 +00002754 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002755 SkFixed storage[2];
2756 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2757 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002758 dx = SkFixedToScalar(storage[0]);
2759 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002760 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002761 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002762 dx = matrix.getScaleX();
2763 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002764 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002765
reed@google.com61eb0402011-04-15 12:11:12 +00002766 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002767 *dstC++ = cache[SkATan2_255(fy, fx)];
2768 fx += dx;
2769 fy += dy;
2770 }
reed@google.com61eb0402011-04-15 12:11:12 +00002771 } else { // perspective case
2772 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002773 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com51baf5a2011-09-21 13:38:36 +00002774 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2775 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002776 }
2777 }
2778}
2779
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002780void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
2781 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002782 SkMatrix::MapXYProc proc = fDstToIndexProc;
2783 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002784 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002785 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002786 SkPoint srcPt;
2787
reed@google.com61eb0402011-04-15 12:11:12 +00002788 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002789 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2790 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002791 SkScalar dx, fx = srcPt.fX;
2792 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002793
reed@google.com61eb0402011-04-15 12:11:12 +00002794 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002795 SkFixed storage[2];
2796 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2797 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002798 dx = SkFixedToScalar(storage[0]);
2799 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002800 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002801 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002802 dx = matrix.getScaleX();
2803 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002804 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002805
reed@google.com61eb0402011-04-15 12:11:12 +00002806 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002807 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2808 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002809 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002810 fx += dx;
2811 fy += dy;
2812 }
reed@google.com61eb0402011-04-15 12:11:12 +00002813 } else { // perspective case
2814 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002815 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2816 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002817
reed@google.com51baf5a2011-09-21 13:38:36 +00002818 int index = SkATan2_255(srcPt.fY, srcPt.fX);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002819 index >>= (8 - kCache16Bits);
2820 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002821 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002822 }
2823 }
2824}
2825
reed@google.com61eb0402011-04-15 12:11:12 +00002826///////////////////////////////////////////////////////////////////////////////
2827///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002828
reed@google.comcb7be692012-06-06 20:31:56 +00002829#include "SkEmptyShader.h"
2830
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831// assumes colors is SkColor* and pos is SkScalar*
2832#define EXPAND_1_COLOR(count) \
2833 SkColor tmp[2]; \
2834 do { \
2835 if (1 == count) { \
2836 tmp[0] = tmp[1] = colors[0]; \
2837 colors = tmp; \
2838 pos = NULL; \
2839 count = 2; \
2840 } \
2841 } while (0)
2842
reed@google.com61eb0402011-04-15 12:11:12 +00002843SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2844 const SkColor colors[],
2845 const SkScalar pos[], int colorCount,
2846 SkShader::TileMode mode,
2847 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848 if (NULL == pts || NULL == colors || colorCount < 1) {
2849 return NULL;
2850 }
2851 EXPAND_1_COLOR(colorCount);
2852
reed@android.comab840b82009-07-01 17:00:03 +00002853 return SkNEW_ARGS(Linear_Gradient,
2854 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002855}
2856
reed@google.com61eb0402011-04-15 12:11:12 +00002857SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2858 const SkColor colors[],
2859 const SkScalar pos[], int colorCount,
2860 SkShader::TileMode mode,
2861 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 if (radius <= 0 || NULL == colors || colorCount < 1) {
2863 return NULL;
2864 }
2865 EXPAND_1_COLOR(colorCount);
2866
reed@android.comab840b82009-07-01 17:00:03 +00002867 return SkNEW_ARGS(Radial_Gradient,
2868 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002869}
2870
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002871SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2872 SkScalar startRadius,
2873 const SkPoint& end,
2874 SkScalar endRadius,
2875 const SkColor colors[],
2876 const SkScalar pos[],
2877 int colorCount,
2878 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002879 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002880 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2881 return NULL;
2882 }
2883 EXPAND_1_COLOR(colorCount);
reed@google.comcb7be692012-06-06 20:31:56 +00002884
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002885 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002886 (start, startRadius, end, endRadius, colors, pos,
2887 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002888}
2889
reed@google.comcb7be692012-06-06 20:31:56 +00002890SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
2891 SkScalar startRadius,
2892 const SkPoint& end,
2893 SkScalar endRadius,
2894 const SkColor colors[],
2895 const SkScalar pos[],
2896 int colorCount,
2897 SkShader::TileMode mode,
2898 SkUnitMapper* mapper) {
2899 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2900 return NULL;
2901 }
2902 if (start == end && startRadius == endRadius) {
2903 return SkNEW(SkEmptyShader);
2904 }
2905
2906 return SkNEW_ARGS(Two_Point_Conical_Gradient,
2907 (start, startRadius, end, endRadius, colors, pos,
2908 colorCount, mode, mapper));
2909}
2910
reed@android.com8a1c16f2008-12-17 15:59:43 +00002911SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2912 const SkColor colors[],
2913 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002914 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002915 if (NULL == colors || count < 1) {
2916 return NULL;
2917 }
2918 EXPAND_1_COLOR(count);
2919
2920 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2921}
2922
caryclark@google.comd26147a2011-12-15 14:16:43 +00002923SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
2924 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Linear_Gradient)
2925 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Radial_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002926 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sweep_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002927 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Radial_Gradient)
scroggo@google.comdb15a422012-06-11 16:51:45 +00002928 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Conical_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002929SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END