blob: 8838e77eb081e3b3320b772bf8e7ef20cc5fa60a [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///////////////////////////////////////////////////////////////////////////////
115///////////////////////////////////////////////////////////////////////////////
116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117class Gradient_Shader : public SkShader {
118public:
119 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000120 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 virtual ~Gradient_Shader();
122
123 // overrides
reed@google.com7716afb2011-12-07 15:17:50 +0000124 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
125 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000126 virtual bool isOpaque() const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000128 enum {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000129 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
130 /// it, use a larger cache.
131 kCache16Bits = 8,
132 kGradient16Length = (1 << kCache16Bits),
133 /// Each cache gets 1 extra entry at the end so we don't have to
134 /// test for end-of-cache in lerps. This is also the value used
135 /// to stride *writes* into the dither cache; it must not be zero.
136 /// Total space for a cache is 2x kCache16Count entries: one
137 /// regular cache, one for dithering.
138 kCache16Count = kGradient16Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000139 kCache16Shift = 16 - kCache16Bits,
140 kSqrt16Shift = 8 - kCache16Bits,
141
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000142 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
143 /// it, use a larger cache.
144 kCache32Bits = 8,
145 kGradient32Length = (1 << kCache32Bits),
146 /// Each cache gets 1 extra entry at the end so we don't have to
147 /// test for end-of-cache in lerps. This is also the value used
148 /// to stride *writes* into the dither cache; it must not be zero.
149 /// Total space for a cache is 2x kCache32Count entries: one
150 /// regular cache, one for dithering.
151 kCache32Count = kGradient32Length + 1,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000152 kCache32Shift = 16 - kCache32Bits,
153 kSqrt32Shift = 8 - kCache32Bits,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000154
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000155 /// This value is used to *read* the dither cache; it may be 0
156 /// if dithering is disabled.
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000157#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000158 kDitherStride32 = kCache32Count,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000159#else
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000160 kDitherStride32 = 0,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000161#endif
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000162 kDitherStride16 = kCache16Count,
163 kLerpRemainderMask32 = (1 << (16 - kCache32Bits)) - 1
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000164 };
165
166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167protected:
168 Gradient_Shader(SkFlattenableReadBuffer& );
djsollen@google.com54924242012-03-29 15:18:04 +0000169 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 SkUnitMapper* fMapper;
172 SkMatrix fPtsToUnit; // set by subclass
173 SkMatrix fDstToIndex;
174 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 TileMode fTileMode;
176 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000177 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 uint8_t fDstToIndexClass;
179 uint8_t fFlags;
180
181 struct Rec {
182 SkFixed fPos; // 0...1
183 uint32_t fScale; // (1 << 24) / range
184 };
185 Rec* fRecs;
186
reed@google.com7c2f27d2011-03-07 19:29:00 +0000187 const uint16_t* getCache16() const;
188 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189
reed@google.com7c2f27d2011-03-07 19:29:00 +0000190 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000191 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193private:
194 enum {
195 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
196
reed@android.com1c12abe2009-07-02 15:01:02 +0000197 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 };
199 SkColor fStorage[(kStorageSize + 3) >> 2];
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000200 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
201 bool fColorsAreOpaque;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
reed@google.com7c2f27d2011-03-07 19:29:00 +0000203 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
204 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205
reed@google.com7c2f27d2011-03-07 19:29:00 +0000206 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
207 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000208 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 +0000209
reed@android.com512a8762009-12-14 15:25:36 +0000210 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000211 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
212 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000213 void setCacheAlpha(U8CPU alpha) const;
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000214 void initCommon();
reed@android.com512a8762009-12-14 15:25:36 +0000215
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 typedef SkShader INHERITED;
217};
218
reed@android.com41bccf52009-04-03 13:33:51 +0000219Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
220 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 SkASSERT(colorCount > 1);
222
223 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
224
225 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000226 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
229 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
230 fTileMode = mode;
231 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000232
reed@android.com41bccf52009-04-03 13:33:51 +0000233 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000234 fCache32 = NULL;
235 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236
reed@android.com41bccf52009-04-03 13:33:51 +0000237 /* Note: we let the caller skip the first and/or last position.
238 i.e. pos[0] = 0.3, pos[1] = 0.7
239 In these cases, we insert dummy entries to ensure that the final data
240 will be bracketed by [0, 1].
241 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
242
243 Thus colorCount (the caller's value, and fColorCount (our value) may
244 differ by up to 2. In the above example:
245 colorCount = 2
246 fColorCount = 4
247 */
248 fColorCount = colorCount;
249 // check if we need to add in dummy start and/or end position/colors
250 bool dummyFirst = false;
251 bool dummyLast = false;
252 if (pos) {
253 dummyFirst = pos[0] != 0;
254 dummyLast = pos[colorCount - 1] != SK_Scalar1;
255 fColorCount += dummyFirst + dummyLast;
256 }
257
258 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000259 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000260 fOrigColors = reinterpret_cast<SkColor*>(
261 sk_malloc_throw(size * fColorCount));
262 }
263 else {
264 fOrigColors = fStorage;
265 }
266
267 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 {
reed@android.com41bccf52009-04-03 13:33:51 +0000269 SkColor* origColors = fOrigColors;
270 if (dummyFirst) {
271 *origColors++ = colors[0];
272 }
273 memcpy(origColors, colors, colorCount * sizeof(SkColor));
274 if (dummyLast) {
275 origColors += colorCount;
276 *origColors = colors[colorCount - 1];
277 }
278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279
reed@android.com1c12abe2009-07-02 15:01:02 +0000280 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000281 if (fColorCount > 2) {
282 Rec* recs = fRecs;
283 recs->fPos = 0;
284 // recs->fScale = 0; // unused;
285 recs += 1;
286 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 /* We need to convert the user's array of relative positions into
288 fixed-point positions and scale factors. We need these results
289 to be strictly monotonic (no two values equal or out of order).
290 Hence this complex loop that just jams a zero for the scale
291 value if it sees a segment out of order, and it assures that
292 we start at 0 and end at 1.0
293 */
294 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000295 int startIndex = dummyFirst ? 0 : 1;
296 int count = colorCount + dummyLast;
297 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 // force the last value to be 1.0
299 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000300 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000302 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 }
reed@android.com41bccf52009-04-03 13:33:51 +0000305 // pin curr withing range
306 if (curr < 0) {
307 curr = 0;
308 } else if (curr > SK_Fixed1) {
309 curr = SK_Fixed1;
310 }
311 recs->fPos = curr;
312 if (curr > prev) {
313 recs->fScale = (1 << 24) / (curr - prev);
314 } else {
315 recs->fScale = 0; // ignore this segment
316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 // get ready for the next value
318 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000319 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 }
reed@android.com41bccf52009-04-03 13:33:51 +0000321 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 SkFixed dp = SK_Fixed1 / (colorCount - 1);
323 SkFixed p = dp;
324 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000325 for (int i = 1; i < colorCount; i++) {
326 recs->fPos = p;
327 recs->fScale = scale;
328 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 p += dp;
330 }
331 }
332 }
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000333 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334}
335
336Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000337 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 fCacheAlpha = 256;
339
340 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
341
342 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000343 fCache32 = NULL;
344 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345
reed@android.com41bccf52009-04-03 13:33:51 +0000346 int colorCount = fColorCount = buffer.readU32();
347 if (colorCount > kColorStorageCount) {
348 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
349 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
350 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354
355 fTileMode = (TileMode)buffer.readU8();
356 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000357 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 if (colorCount > 2) {
359 Rec* recs = fRecs;
360 recs[0].fPos = 0;
361 for (int i = 1; i < colorCount; i++) {
362 recs[i].fPos = buffer.readS32();
363 recs[i].fScale = buffer.readU32();
364 }
365 }
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000366 buffer.readMatrix(&fPtsToUnit);
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000367 this->initCommon();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368}
369
reed@android.com41bccf52009-04-03 13:33:51 +0000370Gradient_Shader::~Gradient_Shader() {
371 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000373 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000374 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000375 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000377 }
reed@google.com82065d62011-02-07 15:30:46 +0000378 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379}
380
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000381void Gradient_Shader::initCommon() {
382 fFlags = 0;
383 unsigned colorAlpha = 0xFF;
384 for (int i = 0; i < fColorCount; i++) {
385 colorAlpha &= SkColorGetA(fOrigColors[i]);
386 }
387 fColorsAreOpaque = colorAlpha == 0xFF;
388}
389
djsollen@google.com54924242012-03-29 15:18:04 +0000390void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 this->INHERITED::flatten(buffer);
392 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000393 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
395 buffer.write8(fTileMode);
396 if (fColorCount > 2) {
397 Rec* recs = fRecs;
398 for (int i = 1; i < fColorCount; i++) {
399 buffer.write32(recs[i].fPos);
400 buffer.write32(recs[i].fScale);
401 }
402 }
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000403 buffer.writeMatrix(fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404}
405
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000406bool Gradient_Shader::isOpaque() const {
407 return fColorsAreOpaque;
408}
409
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410bool Gradient_Shader::setContext(const SkBitmap& device,
411 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000412 const SkMatrix& matrix) {
413 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000415 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416
417 const SkMatrix& inverse = this->getTotalInverse();
418
419 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
420 return false;
421 }
422
423 fDstToIndexProc = fDstToIndex.getMapXYProc();
424 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
425
426 // now convert our colors in to PMColors
427 unsigned paintAlpha = this->getPaintAlpha();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428
429 fFlags = this->INHERITED::getFlags();
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000430 if (fColorsAreOpaque && paintAlpha == 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 fFlags |= kOpaqueAlpha_Flag;
432 }
433 // we can do span16 as long as our individual colors are opaque,
434 // regardless of the paint's alpha
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000435 if (fColorsAreOpaque) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 fFlags |= kHasSpan16_Flag;
437 }
438
reed@google.com95eed982011-07-05 17:01:56 +0000439 this->setCacheAlpha(paintAlpha);
440 return true;
441}
442
443void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 // if the new alpha differs from the previous time we were called, inval our cache
445 // this will trigger the cache to be rebuilt.
446 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000447 if (fCacheAlpha != alpha) {
448 fCache16 = NULL; // inval the cache
449 fCache32 = NULL; // inval the cache
450 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000451 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000452 if (fCache32PixelRef) {
453 fCache32PixelRef->notifyPixelsChanged();
454 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456}
457
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
459
reed@android.com41bccf52009-04-03 13:33:51 +0000460/** We take the original colors, not our premultiplied PMColors, since we can
461 build a 16bit table as long as the original colors are opaque, even if the
462 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463*/
reed@android.com512a8762009-12-14 15:25:36 +0000464void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
465 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 SkASSERT(count > 1);
467 SkASSERT(SkColorGetA(c0) == 0xFF);
468 SkASSERT(SkColorGetA(c1) == 0xFF);
469
470 SkFixed r = SkColorGetR(c0);
471 SkFixed g = SkColorGetG(c0);
472 SkFixed b = SkColorGetB(c0);
473
474 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
475 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
476 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
477
478 r = SkIntToFixed(r) + 0x8000;
479 g = SkIntToFixed(g) + 0x8000;
480 b = SkIntToFixed(b) + 0x8000;
481
482 do {
483 unsigned rr = r >> 16;
484 unsigned gg = g >> 16;
485 unsigned bb = b >> 16;
486 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000487 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 cache += 1;
489 r += dr;
490 g += dg;
491 b += db;
492 } while (--count != 0);
493}
494
reed@google.com55b8e8c2011-01-13 16:22:35 +0000495/*
496 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
497 * semantics of how we 2x2 dither 32->16
498 */
499static inline U8CPU dither_fixed_to_8(SkFixed n) {
500 n >>= 8;
501 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
502}
503
504/*
505 * For dithering with premultiply, we want to ceiling the alpha component,
506 * to ensure that it is always >= any color component.
507 */
508static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
509 n >>= 8;
510 return ((n << 1) - (n | (n >> 8))) >> 8;
511}
512
513void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
514 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 SkASSERT(count > 1);
516
reed@android.com1c12abe2009-07-02 15:01:02 +0000517 // need to apply paintAlpha to our two endpoints
518 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
519 SkFixed da;
520 {
521 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
522 da = SkIntToFixed(tmp - a) / (count - 1);
523 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524
reed@android.com1c12abe2009-07-02 15:01:02 +0000525 SkFixed r = SkColorGetR(c0);
526 SkFixed g = SkColorGetG(c0);
527 SkFixed b = SkColorGetB(c0);
528 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
529 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
530 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531
532 a = SkIntToFixed(a) + 0x8000;
533 r = SkIntToFixed(r) + 0x8000;
534 g = SkIntToFixed(g) + 0x8000;
535 b = SkIntToFixed(b) + 0x8000;
536
537 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000538 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000539 cache[kCache32Count] =
540 SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
541 dither_fixed_to_8(r),
542 dither_fixed_to_8(g),
543 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000544 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 a += da;
546 r += dr;
547 g += dg;
548 b += db;
549 } while (--count != 0);
550}
551
reed@android.com41bccf52009-04-03 13:33:51 +0000552static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 SkASSERT((unsigned)x <= SK_Fixed1);
554 return x - (x >> 16);
555}
556
reed@android.com200645d2009-12-14 16:41:57 +0000557static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000558 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000559 if (6 == bits) {
560 return (x << 10) | (x << 4) | (x >> 2);
561 }
562 if (8 == bits) {
563 return (x << 8) | x;
564 }
565 sk_throw();
566 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567}
568
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000569/** We duplicate the last value in each half of the cache so that
570 interpolation doesn't have to special-case being at the last point.
571*/
572static void complete_16bit_cache(uint16_t* cache, int stride) {
573 cache[stride - 1] = cache[stride - 2];
574 cache[2 * stride - 1] = cache[2 * stride - 2];
575}
576
reed@google.com7c2f27d2011-03-07 19:29:00 +0000577const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000578 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000579 // double the count for dither entries
580 const int entryCount = kCache16Count * 2;
581 const size_t allocSize = sizeof(uint16_t) * entryCount;
582
reed@android.com3c9b2a42009-08-27 19:28:37 +0000583 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000584 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000585 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000587 if (fColorCount == 2) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000588 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
589 kGradient16Length);
reed@android.com41bccf52009-04-03 13:33:51 +0000590 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 Rec* rec = fRecs;
592 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000593 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000594 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 SkASSERT(nextIndex < kCache16Count);
596
597 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000598 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 prevIndex = nextIndex;
600 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000601 // one extra space left over at the end for complete_16bit_cache()
602 SkASSERT(prevIndex == kGradient16Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 }
604
reed@android.com41bccf52009-04-03 13:33:51 +0000605 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000606 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 uint16_t* linear = fCache16; // just computed linear data
608 uint16_t* mapped = fCache16Storage; // storage for mapped data
609 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000610 for (int i = 0; i < kGradient16Length; i++) {
reed@android.com200645d2009-12-14 16:41:57 +0000611 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000613 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 }
615 sk_free(fCache16);
616 fCache16 = fCache16Storage;
617 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000618 complete_16bit_cache(fCache16, kCache16Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 }
620 return fCache16;
621}
622
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000623/** We duplicate the last value in each half of the cache so that
624 interpolation doesn't have to special-case being at the last point.
625*/
626static void complete_32bit_cache(SkPMColor* cache, int stride) {
627 cache[stride - 1] = cache[stride - 2];
628 cache[2 * stride - 1] = cache[2 * stride - 2];
629}
630
reed@google.com7c2f27d2011-03-07 19:29:00 +0000631const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000632 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000633 // double the count for dither entries
634 const int entryCount = kCache32Count * 2;
635 const size_t allocSize = sizeof(SkPMColor) * entryCount;
636
reed@google.comdc731fd2010-12-23 15:19:47 +0000637 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000638 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
639 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000640 }
641 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000642 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000643 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000644 kGradient32Length, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000645 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 Rec* rec = fRecs;
647 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000648 for (int i = 1; i < fColorCount; i++) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000649 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000650 SkASSERT(nextIndex < kGradient32Length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651
652 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000653 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
654 fOrigColors[i],
655 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 prevIndex = nextIndex;
657 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000658 SkASSERT(prevIndex == kGradient32Length - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 }
660
reed@android.com41bccf52009-04-03 13:33:51 +0000661 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000662 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000663 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000665 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 SkUnitMapper* map = fMapper;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000667 for (int i = 0; i < kGradient32Length; i++) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000668 int index = map->mapUnit16((i << 8) | i) >> 8;
669 mapped[i] = linear[index];
670 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000671 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000672 fCache32PixelRef->unref();
673 fCache32PixelRef = newPR;
674 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 }
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000676 complete_32bit_cache(fCache32, kCache32Count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 }
678 return fCache32;
679}
680
reed@google.comdc731fd2010-12-23 15:19:47 +0000681/*
682 * Because our caller might rebuild the same (logically the same) gradient
683 * over and over, we'd like to return exactly the same "bitmap" if possible,
684 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
685 * To do that, we maintain a private cache of built-bitmaps, based on our
686 * colors and positions. Note: we don't try to flatten the fMapper, so if one
687 * is present, we skip the cache for now.
688 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000689void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000690 // our caller assumes no external alpha, so we ensure that our cache is
691 // built with 0xFF
692 this->setCacheAlpha(0xFF);
693
reed@google.comdc731fd2010-12-23 15:19:47 +0000694 // don't have a way to put the mapper into our cache-key yet
695 if (fMapper) {
696 // force our cahce32pixelref to be built
697 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000698 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000699 bitmap->setPixelRef(fCache32PixelRef);
700 return;
701 }
702
703 // build our key: [numColors + colors[] + {positions[]} ]
704 int count = 1 + fColorCount;
705 if (fColorCount > 2) {
706 count += fColorCount - 1; // fRecs[].fPos
707 }
708
709 SkAutoSTMalloc<16, int32_t> storage(count);
710 int32_t* buffer = storage.get();
711
712 *buffer++ = fColorCount;
713 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
714 buffer += fColorCount;
715 if (fColorCount > 2) {
716 for (int i = 1; i < fColorCount; i++) {
717 *buffer++ = fRecs[i].fPos;
718 }
719 }
720 SkASSERT(buffer - storage.get() == count);
721
722 ///////////////////////////////////
723
digit@google.com1771cbf2012-01-26 21:26:40 +0000724 SK_DECLARE_STATIC_MUTEX(gMutex);
reed@google.comdc731fd2010-12-23 15:19:47 +0000725 static SkBitmapCache* gCache;
726 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
727 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
728 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000729
reed@google.comdc731fd2010-12-23 15:19:47 +0000730 if (NULL == gCache) {
731 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
732 }
733 size_t size = count * sizeof(int32_t);
734
735 if (!gCache->find(storage.get(), size, bitmap)) {
736 // force our cahce32pixelref to be built
737 (void)this->getCache32();
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000738 // Only expose the linear section of the cache; don't let the caller
739 // know about the padding at the end to make interpolation faster.
740 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
reed@google.comdc731fd2010-12-23 15:19:47 +0000741 bitmap->setPixelRef(fCache32PixelRef);
742
743 gCache->add(storage.get(), size, *bitmap);
744 }
745}
746
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000747void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
748 if (info) {
749 if (info->fColorCount >= fColorCount) {
750 if (info->fColors) {
751 memcpy(info->fColors, fOrigColors,
752 fColorCount * sizeof(SkColor));
753 }
754 if (info->fColorOffsets) {
755 if (fColorCount == 2) {
756 info->fColorOffsets[0] = 0;
757 info->fColorOffsets[1] = SK_Scalar1;
758 } else if (fColorCount > 2) {
759 for (int i = 0; i < fColorCount; i++)
760 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
761 }
762 }
763 }
764 info->fColorCount = fColorCount;
765 info->fTileMode = fTileMode;
766 }
767}
768
reed@google.com61eb0402011-04-15 12:11:12 +0000769///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770
reed@android.com41bccf52009-04-03 13:33:51 +0000771static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 SkVector vec = pts[1] - pts[0];
773 SkScalar mag = vec.length();
774 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
775
776 vec.scale(inv);
777 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
778 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
779 matrix->postScale(inv, inv);
780}
781
782///////////////////////////////////////////////////////////////////////////////
783
784class Linear_Gradient : public Gradient_Shader {
785public:
786 Linear_Gradient(const SkPoint pts[2],
787 const SkColor colors[], const SkScalar pos[], int colorCount,
788 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000789 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
790 fStart(pts[0]),
791 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 {
793 pts_to_unit_matrix(pts, &fPtsToUnit);
794 }
reed@android.com9b46e772009-06-05 12:24:41 +0000795
reed@google.com7716afb2011-12-07 15:17:50 +0000796 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
797 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
798 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
799 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
800 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
801 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
rileya@google.com03c1c352012-07-20 20:02:43 +0000802 virtual GrCustomStage* asNewCustomStage(GrContext* context,
803 GrSamplerState* sampler) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804
djsollen@google.com54924242012-03-29 15:18:04 +0000805 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Linear_Gradient)
806
807protected:
808 Linear_Gradient(SkFlattenableReadBuffer& buffer)
809 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000810 fStart(buffer.readPoint()),
811 fEnd(buffer.readPoint()) {
djsollen@google.com54924242012-03-29 15:18:04 +0000812 }
813 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000814 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +0000815 buffer.writePoint(fStart);
816 buffer.writePoint(fEnd);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000817 }
818
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819private:
820 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000821 const SkPoint fStart;
822 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823};
824
reed@android.com5119bdb2009-06-12 21:27:03 +0000825bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
826 const SkMatrix& matrix) {
827 if (!this->INHERITED::setContext(device, paint, matrix)) {
828 return false;
829 }
830
831 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
832 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000833 fFlags |= SkShader::kConstInY32_Flag;
834 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
835 // only claim this if we do have a 16bit mode (i.e. none of our
836 // colors have alpha), and if we are not dithering (which obviously
837 // is not const in Y).
838 fFlags |= SkShader::kConstInY16_Flag;
839 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000840 }
841 return true;
842}
843
reed@google.com5eb158d2011-04-15 15:50:34 +0000844#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000845 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000846 unsigned fi = fx >> Gradient_Shader::kCache32Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000847 SkASSERT(fi <= 0xFF); \
848 fx += dx; \
849 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000850 toggle ^= Gradient_Shader::kDitherStride32; \
reed@google.com13659f12011-04-18 19:59:38 +0000851 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000852
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000853namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +0000854
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000855typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +0000856 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000857 int toggle, int count);
858
tomhudson@google.com13a847a2012-01-20 13:59:14 +0000859// This function is deprecated, and will be replaced by
860// shadeSpan_linear_vertical_lerp() once Chrome has been weaned off of it.
861void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
862 SkPMColor* SK_RESTRICT dstC,
863 const SkPMColor* SK_RESTRICT cache,
864 int toggle, int count) {
865 // We're a vertical gradient, so no change in a span.
866 // If colors change sharply across the gradient, dithering is
867 // insufficient (it subsamples the color space) and we need to lerp.
868 unsigned fullIndex = proc(fx);
869 unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
870 sk_memset32_dither(dstC,
871 cache[toggle + fi],
872 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
873 count);
874}
875
876// Linear interpolation (lerp) is unnecessary if there are no sharp
877// discontinuities in the gradient - which must be true if there are
878// only 2 colors - but it's cheap.
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000879void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
880 SkPMColor* SK_RESTRICT dstC,
881 const SkPMColor* SK_RESTRICT cache,
882 int toggle, int count) {
883 // We're a vertical gradient, so no change in a span.
884 // If colors change sharply across the gradient, dithering is
885 // insufficient (it subsamples the color space) and we need to lerp.
886 unsigned fullIndex = proc(fx);
887 unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
888 unsigned remainder = fullIndex & Gradient_Shader::kLerpRemainderMask32;
889 SkPMColor lerp =
890 SkFastFourByteInterp(
891 cache[toggle + fi + 1],
892 cache[toggle + fi], remainder);
893 SkPMColor dlerp =
894 SkFastFourByteInterp(
895 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi + 1],
896 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi], remainder);
897 sk_memset32_dither(dstC, lerp, dlerp, count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000898}
899
900void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
901 SkPMColor* SK_RESTRICT dstC,
902 const SkPMColor* SK_RESTRICT cache,
903 int toggle, int count) {
904 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000905 range.init(fx, dx, count, 0, Gradient_Shader::kGradient32Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000906
907 if ((count = range.fCount0) > 0) {
908 sk_memset32_dither(dstC,
909 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000910 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000911 count);
912 dstC += count;
913 }
914 if ((count = range.fCount1) > 0) {
915 int unroll = count >> 3;
916 fx = range.fFx1;
917 for (int i = 0; i < unroll; i++) {
918 NO_CHECK_ITER; NO_CHECK_ITER;
919 NO_CHECK_ITER; NO_CHECK_ITER;
920 NO_CHECK_ITER; NO_CHECK_ITER;
921 NO_CHECK_ITER; NO_CHECK_ITER;
922 }
923 if ((count &= 7) > 0) {
924 do {
925 NO_CHECK_ITER;
926 } while (--count != 0);
927 }
928 }
929 if ((count = range.fCount2) > 0) {
930 sk_memset32_dither(dstC,
931 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000932 cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000933 count);
934 }
935}
936
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000937void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
938 SkPMColor* SK_RESTRICT dstC,
939 const SkPMColor* SK_RESTRICT cache,
940 int toggle, int count) {
941 do {
942 unsigned fi = mirror_8bits(fx >> 8);
943 SkASSERT(fi <= 0xFF);
944 fx += dx;
945 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000946 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000947 } while (--count != 0);
948}
949
950void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
951 SkPMColor* SK_RESTRICT dstC,
952 const SkPMColor* SK_RESTRICT cache,
953 int toggle, int count) {
954 do {
955 unsigned fi = repeat_8bits(fx >> 8);
956 SkASSERT(fi <= 0xFF);
957 fx += dx;
958 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000959 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000960 } while (--count != 0);
961}
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000962
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000963}
964
965void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
966 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 SkASSERT(count > 0);
968
969 SkPoint srcPt;
970 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
971 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +0000972 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com0e734bd2011-12-08 17:24:44 +0000973#ifdef USE_DITHER_32BIT_GRADIENT
tomhudson@google.com13e812c2012-01-18 21:28:01 +0000974 int toggle = ((x ^ y) & 1) * kDitherStride32;
reed@google.com0e734bd2011-12-08 17:24:44 +0000975#else
976 int toggle = 0;
reed@google.com0e734bd2011-12-08 17:24:44 +0000977#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978
reed@android.comc552a432009-06-12 20:02:50 +0000979 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000980 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
981 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
983
reed@android.comc552a432009-06-12 20:02:50 +0000984 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 SkFixed dxStorage[1];
986 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
987 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000988 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
990 dx = SkScalarToFixed(fDstToIndex.getScaleX());
991 }
992
tomhudson@google.come8c984d2012-01-09 13:45:36 +0000993 LinearShadeProc shadeProc = shadeSpan_linear_repeat;
reed@android.comc552a432009-06-12 20:02:50 +0000994 if (SkFixedNearlyZero(dx)) {
tomhudson@google.com13a847a2012-01-20 13:59:14 +0000995#ifdef SK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS
996 if (fColorCount > 2) {
997 shadeProc = shadeSpan_linear_vertical_lerp;
998 } else {
999 shadeProc = shadeSpan_linear_vertical;
1000 }
1001#else
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001002 shadeProc = shadeSpan_linear_vertical_lerp;
tomhudson@google.com13a847a2012-01-20 13:59:14 +00001003#endif
reed@android.comc552a432009-06-12 20:02:50 +00001004 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001005 shadeProc = shadeSpan_linear_clamp;
reed@android.comc552a432009-06-12 20:02:50 +00001006 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001007 shadeProc = shadeSpan_linear_mirror;
reed@android.comc552a432009-06-12 20:02:50 +00001008 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001011 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comc552a432009-06-12 20:02:50 +00001012 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 SkScalar dstX = SkIntToScalar(x);
1014 SkScalar dstY = SkIntToScalar(y);
1015 do {
1016 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1017 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1018 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001019 *dstC++ = cache[toggle + (fi >> kCache32Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001020 toggle ^= Gradient_Shader::kDitherStride32;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 dstX += SK_Scalar1;
1022 } while (--count != 0);
1023 }
1024}
1025
reed@google.com55b8e8c2011-01-13 16:22:35 +00001026SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +00001027 SkMatrix* matrix,
1028 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +00001029 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001031 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 }
1033 if (matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 matrix->preConcat(fPtsToUnit);
1035 }
1036 if (xy) {
1037 xy[0] = fTileMode;
1038 xy[1] = kClamp_TileMode;
1039 }
rileya@google.com22e57f92012-07-19 15:16:19 +00001040 return kLinear_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041}
1042
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001043SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
1044 if (info) {
1045 commonAsAGradient(info);
1046 info->fPoint[0] = fStart;
1047 info->fPoint[1] = fEnd;
1048 }
1049 return kLinear_GradientType;
1050}
1051
rileya@google.com03c1c352012-07-20 20:02:43 +00001052GrCustomStage* Linear_Gradient::asNewCustomStage(GrContext* context,
1053 GrSamplerState* sampler) const {
1054 SkASSERT(NULL != context && NULL != sampler);
1055 sampler->matrix()->preConcat(fPtsToUnit);
1056 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
1057 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
1058 sampler->setFilter(GrSamplerState::kBilinear_Filter);
1059 return SkNEW_ARGS(GrLinearGradient, (context, *this));
1060}
1061
reed@android.com3c9b2a42009-08-27 19:28:37 +00001062static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
1063 int count) {
1064 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 *dst++ = value;
1066 count -= 1;
1067 SkTSwap(value, other);
1068 }
1069
1070 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001071
reed@android.com3c9b2a42009-08-27 19:28:37 +00001072 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001074 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076
reed@google.com5eb158d2011-04-15 15:50:34 +00001077#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +00001078 do { \
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001079 unsigned fi = fx >> Gradient_Shader::kCache16Shift; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001080 SkASSERT(fi < Gradient_Shader::kCache16Count); \
reed@google.com5eb158d2011-04-15 15:50:34 +00001081 fx += dx; \
1082 *dstC++ = cache[toggle + fi]; \
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001083 toggle ^= Gradient_Shader::kDitherStride16; \
reed@google.com13659f12011-04-18 19:59:38 +00001084 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +00001085
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001086namespace {
reed@google.com5eb158d2011-04-15 15:50:34 +00001087
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001088typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001089 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001090 int toggle, int count);
1091
1092void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
1093 uint16_t* SK_RESTRICT dstC,
1094 const uint16_t* SK_RESTRICT cache,
1095 int toggle, int count) {
1096 // we're a vertical gradient, so no change in a span
1097 unsigned fi = proc(fx) >> Gradient_Shader::kCache16Shift;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001098 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001099 dither_memset16(dstC, cache[toggle + fi],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001100 cache[(toggle ^ Gradient_Shader::kDitherStride16) + fi], count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001101
1102}
1103
1104void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
1105 uint16_t* SK_RESTRICT dstC,
1106 const uint16_t* SK_RESTRICT cache,
1107 int toggle, int count) {
1108 SkClampRange range;
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001109 range.init(fx, dx, count, 0, Gradient_Shader::kGradient16Length);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001110
1111 if ((count = range.fCount0) > 0) {
1112 dither_memset16(dstC,
1113 cache[toggle + range.fV0],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001114 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV0],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001115 count);
1116 dstC += count;
1117 }
1118 if ((count = range.fCount1) > 0) {
1119 int unroll = count >> 3;
1120 fx = range.fFx1;
1121 for (int i = 0; i < unroll; i++) {
1122 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1123 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1124 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1125 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1126 }
1127 if ((count &= 7) > 0) {
1128 do {
1129 NO_CHECK_ITER_16;
1130 } while (--count != 0);
1131 }
1132 }
1133 if ((count = range.fCount2) > 0) {
1134 dither_memset16(dstC,
1135 cache[toggle + range.fV1],
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001136 cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV1],
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001137 count);
1138 }
1139}
1140
1141void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
1142 uint16_t* SK_RESTRICT dstC,
1143 const uint16_t* SK_RESTRICT cache,
1144 int toggle, int count) {
1145 do {
1146 unsigned fi = mirror_bits(fx >> Gradient_Shader::kCache16Shift,
1147 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001148 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001149 fx += dx;
1150 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001151 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001152 } while (--count != 0);
1153}
1154
1155void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
1156 uint16_t* SK_RESTRICT dstC,
1157 const uint16_t* SK_RESTRICT cache,
1158 int toggle, int count) {
1159 SkASSERT(proc == repeat_tileproc);
1160 do {
1161 unsigned fi = repeat_bits(fx >> Gradient_Shader::kCache16Shift,
1162 Gradient_Shader::kCache16Bits);
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001163 SkASSERT(fi < Gradient_Shader::kCache16Count);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001164 fx += dx;
1165 *dstC++ = cache[toggle + fi];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001166 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001167 } while (--count != 0);
1168}
1169}
1170
1171void Linear_Gradient::shadeSpan16(int x, int y,
1172 uint16_t* SK_RESTRICT dstC, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 SkASSERT(count > 0);
1174
1175 SkPoint srcPt;
1176 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1177 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001178 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001179 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001181 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001182 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1183 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1185
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001186 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 SkFixed dxStorage[1];
1188 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1189 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001190 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1192 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1193 }
1194
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001195 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001196 if (SkFixedNearlyZero(dx)) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001197 shadeProc = shadeSpan16_linear_vertical;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001198 } else if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001199 shadeProc = shadeSpan16_linear_clamp;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001200 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001201 shadeProc = shadeSpan16_linear_mirror;
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001202 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001205 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001206 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 SkScalar dstX = SkIntToScalar(x);
1208 SkScalar dstY = SkIntToScalar(y);
1209 do {
1210 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1211 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1212 SkASSERT(fi <= 0xFFFF);
1213
reed@android.com512a8762009-12-14 15:25:36 +00001214 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001216 toggle ^= Gradient_Shader::kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217
1218 dstX += SK_Scalar1;
1219 } while (--count != 0);
1220 }
1221}
1222
1223///////////////////////////////////////////////////////////////////////////////
1224
1225#define kSQRT_TABLE_BITS 11
1226#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1227
1228#include "SkRadialGradient_Table.h"
1229
1230#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1231
1232#include <stdio.h>
1233
reed@google.com61eb0402011-04-15 12:11:12 +00001234void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1236
1237 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1238 SkASSERT(file);
1239 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1240
reed@google.com61eb0402011-04-15 12:11:12 +00001241 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1242 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001244 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245
1246 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1247
1248 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001249 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001251 }
1252 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001254 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 }
1256 ::fprintf(file, "};\n");
1257 ::fclose(file);
1258}
1259
1260#endif
1261
1262
reed@google.com61eb0402011-04-15 12:11:12 +00001263static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1264 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 SkScalar inv = SkScalarInvert(radius);
1266
1267 matrix->setTranslate(-center.fX, -center.fY);
1268 matrix->postScale(inv, inv);
1269}
1270
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001271
1272namespace {
1273
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001274typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
1275 SkScalar sfy, SkScalar sdy,
1276 uint16_t* dstC, const uint16_t* cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001277 int toggle, int count);
1278
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001279void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
1280 SkScalar sfy, SkScalar sdy,
1281 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001282 int toggle, int count) {
1283 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
1284
1285 /* knock these down so we can pin against +- 0x7FFF, which is an
1286 immediate load, rather than 0xFFFF which is slower. This is a
1287 compromise, since it reduces our precision, but that appears
1288 to be visually OK. If we decide this is OK for all of our cases,
1289 we could (it seems) put this scale-down into fDstToIndex,
1290 to avoid having to do these extra shifts each time.
1291 */
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001292 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1293 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1294 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1295 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001296 // might perform this check for the other modes,
1297 // but the win will be a smaller % of the total
1298 if (dy == 0) {
1299 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1300 fy *= fy;
1301 do {
1302 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1303 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1304 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1305 fx += dx;
1306 *dstC++ = cache[toggle +
1307 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001308 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001309 } while (--count != 0);
1310 } else {
1311 do {
1312 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1313 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1314 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1315 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1316 fx += dx;
1317 fy += dy;
1318 *dstC++ = cache[toggle +
1319 (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001320 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001321 } while (--count != 0);
1322 }
1323}
1324
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001325void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
1326 SkScalar sfy, SkScalar sdy,
1327 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001328 int toggle, int count) {
1329 do {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001330#ifdef SK_SCALAR_IS_FLOAT
1331 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1332 SkFixed dist = SkFloatToFixed(fdist);
1333#else
1334 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1335 SkFixedSquare(sfy);
1336 if (magnitudeSquared < 0) // Overflow.
1337 magnitudeSquared = SK_FixedMax;
1338 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1339#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001340 unsigned fi = mirror_tileproc(dist);
1341 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001342 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001343 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001344 sfx += sdx;
1345 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001346 } while (--count != 0);
1347}
1348
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001349void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
1350 SkScalar sfy, SkScalar sdy,
1351 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001352 int toggle, int count) {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001353 SkFixed fx = SkScalarToFixed(sfx);
1354 SkFixed dx = SkScalarToFixed(sdx);
1355 SkFixed fy = SkScalarToFixed(sfy);
1356 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001357 do {
1358 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1359 unsigned fi = repeat_tileproc(dist);
1360 SkASSERT(fi <= 0xFFFF);
1361 fx += dx;
1362 fy += dy;
1363 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001364 toggle ^= Gradient_Shader::kDitherStride16;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001365 } while (--count != 0);
1366}
1367
1368}
1369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370class Radial_Gradient : public Gradient_Shader {
1371public:
1372 Radial_Gradient(const SkPoint& center, SkScalar radius,
1373 const SkColor colors[], const SkScalar pos[], int colorCount,
1374 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001375 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1376 fCenter(center),
1377 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 {
1379 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1380 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1381
1382 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1383 }
reed@google.com61eb0402011-04-15 12:11:12 +00001384
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001385 virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
1386 SK_OVERRIDE;
reed@google.com7096dc62012-05-11 18:01:50 +00001387 virtual void shadeSpan16(int x, int y, uint16_t* dstCParam,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001388 int count) SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 SkASSERT(count > 0);
1390
reed@google.com7096dc62012-05-11 18:01:50 +00001391 uint16_t* SK_RESTRICT dstC = dstCParam;
1392
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 SkPoint srcPt;
1394 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1395 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001396 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001397 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398
reed@android.com3c9b2a42009-08-27 19:28:37 +00001399 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001400 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1401 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001402
1403 SkScalar sdx = fDstToIndex.getScaleX();
1404 SkScalar sdy = fDstToIndex.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405
reed@android.com3c9b2a42009-08-27 19:28:37 +00001406 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 SkFixed storage[2];
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001408 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1409 &storage[0], &storage[1]);
1410 sdx = SkFixedToScalar(storage[0]);
1411 sdy = SkFixedToScalar(storage[1]);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001412 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 }
1415
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001416 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001417 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001418 shadeProc = shadeSpan16_radial_clamp;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001419 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001420 shadeProc = shadeSpan16_radial_mirror;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001421 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 SkASSERT(proc == repeat_tileproc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 }
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001424 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
1425 cache, toggle, count);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001426 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 SkScalar dstX = SkIntToScalar(x);
1428 SkScalar dstY = SkIntToScalar(y);
1429 do {
1430 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1431 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1432 SkASSERT(fi <= 0xFFFF);
1433
1434 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001436 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437
1438 dstX += SK_Scalar1;
1439 } while (--count != 0);
1440 }
1441 }
1442
reed@google.com55b8e8c2011-01-13 16:22:35 +00001443 virtual BitmapType asABitmap(SkBitmap* bitmap,
1444 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001445 TileMode* xy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001446 SkScalar* twoPointRadialParams)
1447 const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00001448 if (bitmap) {
1449 this->commonAsABitmap(bitmap);
1450 }
1451 if (matrix) {
tomhudson@google.com13e812c2012-01-18 21:28:01 +00001452 matrix->setScale(SkIntToScalar(kGradient32Length),
1453 SkIntToScalar(kGradient32Length));
reed@google.comdc731fd2010-12-23 15:19:47 +00001454 matrix->preConcat(fPtsToUnit);
1455 }
1456 if (xy) {
1457 xy[0] = fTileMode;
1458 xy[1] = kClamp_TileMode;
1459 }
1460 return kRadial_BitmapType;
1461 }
rileya@google.com03c1c352012-07-20 20:02:43 +00001462
reed@google.com7716afb2011-12-07 15:17:50 +00001463 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001464 if (info) {
1465 commonAsAGradient(info);
1466 info->fPoint[0] = fCenter;
1467 info->fRadius[0] = fRadius;
1468 }
1469 return kRadial_GradientType;
1470 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001471
rileya@google.com03c1c352012-07-20 20:02:43 +00001472 virtual GrCustomStage* asNewCustomStage(GrContext* context,
1473 GrSamplerState* sampler) const SK_OVERRIDE {
1474 SkASSERT(NULL != context && NULL != sampler);
1475 sampler->matrix()->preConcat(fPtsToUnit);
1476 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
1477 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
1478 sampler->setFilter(GrSamplerState::kBilinear_Filter);
1479 return SkNEW_ARGS(GrRadialGradient, (context, *this));
1480 }
1481
djsollen@google.comba28d032012-03-26 17:57:35 +00001482 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Radial_Gradient)
1483
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001485 Radial_Gradient(SkFlattenableReadBuffer& buffer)
djsollen@google.com54924242012-03-29 15:18:04 +00001486 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001487 fCenter(buffer.readPoint()),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001488 fRadius(buffer.readScalar()) {
1489 }
djsollen@google.com54924242012-03-29 15:18:04 +00001490 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
1491 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001492 buffer.writePoint(fCenter);
djsollen@google.com54924242012-03-29 15:18:04 +00001493 buffer.writeScalar(fRadius);
1494 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495
1496private:
1497 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001498 const SkPoint fCenter;
1499 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500};
1501
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001502namespace {
1503
1504inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001505 // fast, overly-conservative test: checks unit square instead
1506 // of unit circle
1507 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
1508 (fx <= -SK_FixedHalf && dx <= 0);
1509 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
1510 (fy <= -SK_FixedHalf && dy <= 0);
1511
1512 return xClamped || yClamped;
1513}
1514
1515// Return true if (fx * fy) is always inside the unit circle
1516// SkPin32 is expensive, but so are all the SkFixedMul in this test,
1517// so it shouldn't be run if count is small.
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001518inline bool no_need_for_radial_pin(int fx, int dx,
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001519 int fy, int dy, int count) {
1520 SkASSERT(count > 0);
1521 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1522 return false;
1523 }
1524 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
1525 return false;
1526 }
1527 fx += (count - 1) * dx;
1528 fy += (count - 1) * dy;
1529 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
1530 return false;
1531 }
1532 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
1533}
1534
1535#define UNPINNED_RADIAL_STEP \
1536 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001537 *dstC++ = cache[toggle + \
1538 (sqrt_table[fi] >> Gradient_Shader::kSqrt32Shift)]; \
1539 toggle ^= Gradient_Shader::kDitherStride32; \
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001540 fx += dx; \
1541 fy += dy;
1542
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001543typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
1544 SkScalar sfy, SkScalar sdy,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001545 SkPMColor* dstC, const SkPMColor* cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001546 int count, int toggle);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001547
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001548// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001549void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
1550 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001551 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001552 int count, int toggle) {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001553 // Floating point seems to be slower than fixed point,
1554 // even when we have float hardware.
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001555 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001556 SkFixed fx = SkScalarToFixed(sfx) >> 1;
1557 SkFixed dx = SkScalarToFixed(sdx) >> 1;
1558 SkFixed fy = SkScalarToFixed(sfy) >> 1;
1559 SkFixed dy = SkScalarToFixed(sdy) >> 1;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001560 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001561 unsigned fi = Gradient_Shader::kGradient32Length;
1562 sk_memset32_dither(dstC,
1563 cache[toggle + fi],
1564 cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
1565 count);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001566 } else if ((count > 4) &&
1567 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
1568 unsigned fi;
1569 // 4x unroll appears to be no faster than 2x unroll on Linux
1570 while (count > 1) {
1571 UNPINNED_RADIAL_STEP;
1572 UNPINNED_RADIAL_STEP;
1573 count -= 2;
1574 }
1575 if (count) {
1576 UNPINNED_RADIAL_STEP;
1577 }
1578 }
1579 else {
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001580 // Specializing for dy == 0 gains us 25% on Skia benchmarks
1581 if (dy == 0) {
1582 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1583 yy *= yy;
1584 do {
1585 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1586 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
1587 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001588 *dstC++ = cache[toggle + (sqrt_table[fi] >>
1589 Gradient_Shader::kSqrt32Shift)];
1590 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001591 fx += dx;
1592 } while (--count != 0);
1593 } else {
1594 do {
1595 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1596 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1597 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1598 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001599 *dstC++ = cache[toggle + (sqrt_table[fi] >>
1600 Gradient_Shader::kSqrt32Shift)];
1601 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001602 fx += dx;
1603 fy += dy;
1604 } while (--count != 0);
1605 }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001606 }
1607}
1608
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001609// Unrolling this loop doesn't seem to help (when float); we're stalling to
1610// get the results of the sqrt (?), and don't have enough extra registers to
1611// have many in flight.
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001612void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
1613 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001614 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001615 int count, int toggle) {
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001616 do {
tomhudson@google.comf925ba42012-01-11 21:54:26 +00001617#ifdef SK_SCALAR_IS_FLOAT
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001618 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
1619 SkFixed dist = SkFloatToFixed(fdist);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001620#else
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001621 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
1622 SkFixedSquare(sfy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001623 if (magnitudeSquared < 0) // Overflow.
1624 magnitudeSquared = SK_FixedMax;
1625 SkFixed dist = SkFixedSqrt(magnitudeSquared);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001626#endif
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001627 unsigned fi = mirror_tileproc(dist);
1628 SkASSERT(fi <= 0xFFFF);
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001629 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
1630 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001631 sfx += sdx;
1632 sfy += sdy;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001633 } while (--count != 0);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001634}
1635
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001636void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
1637 SkScalar sfy, SkScalar sdy,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001638 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001639 int count, int toggle) {
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001640 SkFixed fx = SkScalarToFixed(sfx);
1641 SkFixed dx = SkScalarToFixed(sdx);
1642 SkFixed fy = SkScalarToFixed(sfy);
1643 SkFixed dy = SkScalarToFixed(sdy);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001644 do {
1645 SkFixed magnitudeSquared = SkFixedSquare(fx) +
1646 SkFixedSquare(fy);
1647 if (magnitudeSquared < 0) // Overflow.
1648 magnitudeSquared = SK_FixedMax;
1649 SkFixed dist = SkFixedSqrt(magnitudeSquared);
1650 unsigned fi = repeat_tileproc(dist);
1651 SkASSERT(fi <= 0xFFFF);
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001652 *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
1653 toggle ^= Gradient_Shader::kDitherStride32;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001654 fx += dx;
1655 fy += dy;
1656 } while (--count != 0);
1657}
1658}
1659
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001660void Radial_Gradient::shadeSpan(int x, int y,
1661 SkPMColor* SK_RESTRICT dstC, int count) {
1662 SkASSERT(count > 0);
1663
1664 SkPoint srcPt;
1665 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1666 TileProc proc = fTileProc;
tomhudson@google.com3e0f22c2011-09-30 14:31:28 +00001667 const SkPMColor* SK_RESTRICT cache = this->getCache32();
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001668#ifdef USE_DITHER_32BIT_GRADIENT
1669 int toggle = ((x ^ y) & 1) * Gradient_Shader::kDitherStride32;
1670#else
1671 int toggle = 0;
1672#endif
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001673
1674 if (fDstToIndexClass != kPerspective_MatrixClass) {
1675 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1676 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001677 SkScalar sdx = fDstToIndex.getScaleX();
1678 SkScalar sdy = fDstToIndex.getSkewY();
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001679
1680 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1681 SkFixed storage[2];
tomhudson@google.com51a283b2012-01-12 16:21:43 +00001682 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
1683 &storage[0], &storage[1]);
1684 sdx = SkFixedToScalar(storage[0]);
1685 sdy = SkFixedToScalar(storage[1]);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001686 } else {
1687 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001688 }
1689
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001690 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001691 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001692 shadeProc = shadeSpan_radial_clamp;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001693 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001694 shadeProc = shadeSpan_radial_mirror;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001695 } else {
1696 SkASSERT(proc == repeat_tileproc);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001697 }
tomhudson@google.com480e21f2012-02-02 14:11:18 +00001698 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001699 } else { // perspective case
1700 SkScalar dstX = SkIntToScalar(x);
1701 SkScalar dstY = SkIntToScalar(y);
1702 do {
1703 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1704 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1705 SkASSERT(fi <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001706 *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
tomhudson@google.com5ea050f2011-09-26 15:03:55 +00001707 dstX += SK_Scalar1;
1708 } while (--count != 0);
1709 }
1710}
1711
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001712/* Two-point radial gradients are specified by two circles, each with a center
1713 point and radius. The gradient can be considered to be a series of
1714 concentric circles, with the color interpolated from the start circle
1715 (at t=0) to the end circle (at t=1).
1716
1717 For each point (x, y) in the span, we want to find the
1718 interpolated circle that intersects that point. The center
1719 of the desired circle (Cx, Cy) falls at some distance t
1720 along the line segment between the start point (Sx, Sy) and
1721 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001722
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001723 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1724 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001725
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001726 The radius of the desired circle (r) is also a linear interpolation t
1727 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001728
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001729 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001730
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001731 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001732
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001733 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001734
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001735 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001736
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001737 (x - ((1 - t) * Sx + t * Ex))^2
1738 + (y - ((1 - t) * Sy + t * Ey))^2
1739 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001740
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001741 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001742
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001743 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1744 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1745 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001746
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001747 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1748
1749 [Dx^2 + Dy^2 - Dr^2)] * t^2
1750 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1751 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001752
1753 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001754 possible circles on which the point may fall. Solving for t yields
1755 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001756
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001757 If a<0, the start circle is entirely contained in the
1758 end circle, and one of the roots will be <0 or >1 (off the line
1759 segment). If a>0, the start circle falls at least partially
1760 outside the end circle (or vice versa), and the gradient
1761 defines a "tube" where a point may be on one circle (on the
1762 inside of the tube) or the other (outside of the tube). We choose
1763 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001764
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001765 In order to keep the math to within the limits of fixed point,
1766 we divide the entire quadratic by Dr^2, and replace
1767 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001768
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001769 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1770 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1771 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001772
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001773 (x' and y' are computed by appending the subtract and scale to the
1774 fDstToIndex matrix in the constructor).
1775
1776 Since the 'A' component of the quadratic is independent of x' and y', it
1777 is precomputed in the constructor. Since the 'B' component is linear in
1778 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001779 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001780 a perspective projection), it must be computed in the loop.
1781
1782*/
1783
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001784namespace {
1785
1786inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1787 SkScalar sr2d2, SkScalar foura,
1788 SkScalar oneOverTwoA, bool posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001789 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001790 if (0 == foura) {
1791 return SkScalarToFixed(SkScalarDiv(-c, b));
1792 }
1793
reed@google.com84e9c082011-04-13 17:44:24 +00001794 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001795 if (discrim < 0) {
1796 discrim = -discrim;
1797 }
reed@google.com84e9c082011-04-13 17:44:24 +00001798 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1799 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001800 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001801 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001802 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001803 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001804 }
reed@google.com84e9c082011-04-13 17:44:24 +00001805 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001806}
1807
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001808typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
1809 SkScalar fy, SkScalar dy,
1810 SkScalar b, SkScalar db,
1811 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001812 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001813 int count);
1814
1815void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
1816 SkScalar fy, SkScalar dy,
1817 SkScalar b, SkScalar db,
1818 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001819 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001820 int count) {
1821 for (; count > 0; --count) {
1822 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1823 fOneOverTwoA, posRoot);
1824 SkFixed index = SkClampMax(t, 0xFFFF);
1825 SkASSERT(index <= 0xFFFF);
1826 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1827 fx += dx;
1828 fy += dy;
1829 b += db;
1830 }
1831}
1832void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
1833 SkScalar fy, SkScalar dy,
1834 SkScalar b, SkScalar db,
1835 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001836 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001837 int count) {
1838 for (; count > 0; --count) {
1839 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1840 fOneOverTwoA, posRoot);
1841 SkFixed index = mirror_tileproc(t);
1842 SkASSERT(index <= 0xFFFF);
1843 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1844 fx += dx;
1845 fy += dy;
1846 b += db;
1847 }
1848}
1849
1850void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
1851 SkScalar fy, SkScalar dy,
1852 SkScalar b, SkScalar db,
1853 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
tomhudson@google.com6a8df702012-01-12 20:13:49 +00001854 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001855 int count) {
1856 for (; count > 0; --count) {
1857 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
1858 fOneOverTwoA, posRoot);
1859 SkFixed index = repeat_tileproc(t);
1860 SkASSERT(index <= 0xFFFF);
1861 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
1862 fx += dx;
1863 fy += dy;
1864 b += db;
1865 }
1866}
1867
1868
1869
1870}
1871
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001872class Two_Point_Radial_Gradient : public Gradient_Shader {
1873public:
1874 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1875 const SkPoint& end, SkScalar endRadius,
1876 const SkColor colors[], const SkScalar pos[],
1877 int colorCount, SkShader::TileMode mode,
1878 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001879 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1880 fCenter1(start),
1881 fCenter2(end),
1882 fRadius1(startRadius),
1883 fRadius2(endRadius) {
1884 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001885 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001886
1887 virtual BitmapType asABitmap(SkBitmap* bitmap,
1888 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001889 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001890 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001891 if (bitmap) {
1892 this->commonAsABitmap(bitmap);
1893 }
1894 SkScalar diffL = 0; // just to avoid gcc warning
1895 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001896 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001897 SkScalarSquare(fDiff.fY));
1898 }
1899 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001900 if (diffL) {
1901 SkScalar invDiffL = SkScalarInvert(diffL);
1902 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1903 SkScalarMul(invDiffL, fDiff.fX));
1904 } else {
1905 matrix->reset();
1906 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001907 matrix->preConcat(fPtsToUnit);
1908 }
1909 if (xy) {
1910 xy[0] = fTileMode;
1911 xy[1] = kClamp_TileMode;
1912 }
1913 if (NULL != twoPointRadialParams) {
1914 twoPointRadialParams[0] = diffL;
1915 twoPointRadialParams[1] = fStartRadius;
1916 twoPointRadialParams[2] = fDiffRadius;
1917 }
1918 return kTwoPointRadial_BitmapType;
1919 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001920
reed@google.com8e6d9142011-12-07 15:30:34 +00001921 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001922 if (info) {
1923 commonAsAGradient(info);
1924 info->fPoint[0] = fCenter1;
1925 info->fPoint[1] = fCenter2;
1926 info->fRadius[0] = fRadius1;
1927 info->fRadius[1] = fRadius2;
1928 }
1929 return kRadial2_GradientType;
1930 }
1931
rileya@google.com03c1c352012-07-20 20:02:43 +00001932 virtual GrCustomStage* asNewCustomStage(GrContext* context,
1933 GrSamplerState* sampler) const SK_OVERRIDE {
1934 SkASSERT(NULL != context && NULL != sampler);
1935 SkScalar diffLen = fDiff.length();
1936 if (0 != diffLen) {
1937 SkScalar invDiffLen = SkScalarInvert(diffLen);
1938 sampler->matrix()->setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
1939 SkScalarMul(invDiffLen, fDiff.fX));
1940 } else {
1941 sampler->matrix()->reset();
1942 }
1943 sampler->matrix()->preConcat(fPtsToUnit);
1944 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
1945 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
1946 sampler->setFilter(GrSamplerState::kBilinear_Filter);
1947 return SkNEW_ARGS(GrRadial2Gradient, (context, *this));
1948 }
1949
reed@google.com7096dc62012-05-11 18:01:50 +00001950 virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001951 int count) SK_OVERRIDE {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001952 SkASSERT(count > 0);
1953
reed@google.com7096dc62012-05-11 18:01:50 +00001954 SkPMColor* SK_RESTRICT dstC = dstCParam;
1955
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001956 // Zero difference between radii: fill with transparent black.
1957 if (fDiffRadius == 0) {
1958 sk_bzero(dstC, count * sizeof(*dstC));
1959 return;
1960 }
1961 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1962 TileProc proc = fTileProc;
reed@google.comdd5bd672011-09-20 15:56:13 +00001963 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001964
1965 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001966 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001967 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001968 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001969 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1970 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001971 SkScalar dx, fx = srcPt.fX;
1972 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001973
reed@google.com61eb0402011-04-15 12:11:12 +00001974 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001975 SkFixed fixedX, fixedY;
1976 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1977 dx = SkFixedToScalar(fixedX);
1978 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001979 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001980 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001981 dx = fDstToIndex.getScaleX();
1982 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001983 }
reed@google.com84e9c082011-04-13 17:44:24 +00001984 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1985 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1986 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1987 SkScalarMul(fDiff.fY, dy)) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001988
1989 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
reed@google.com61eb0402011-04-15 12:11:12 +00001990 if (proc == clamp_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001991 shadeProc = shadeSpan_twopoint_clamp;
reed@google.com61eb0402011-04-15 12:11:12 +00001992 } else if (proc == mirror_tileproc) {
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001993 shadeProc = shadeSpan_twopoint_mirror;
reed@google.com61eb0402011-04-15 12:11:12 +00001994 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001995 SkASSERT(proc == repeat_tileproc);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001996 }
tomhudson@google.come8c984d2012-01-09 13:45:36 +00001997 (*shadeProc)(fx, dx, fy, dy, b, db,
1998 fSr2D2, foura, fOneOverTwoA, posRoot,
1999 dstC, cache, count);
reed@google.com61eb0402011-04-15 12:11:12 +00002000 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00002001 SkScalar dstX = SkIntToScalar(x);
2002 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002003 for (; count > 0; --count) {
2004 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00002005 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00002006 SkScalar fx = srcPt.fX;
2007 SkScalar fy = srcPt.fY;
2008 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
2009 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
tomhudson@google.come8c984d2012-01-09 13:45:36 +00002010 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
2011 fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002012 SkFixed index = proc(t);
2013 SkASSERT(index <= 0xFFFF);
tomhudson@google.come8c984d2012-01-09 13:45:36 +00002014 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
reed@android.com6c59a172009-09-22 20:24:05 +00002015 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002016 }
2017 }
2018 }
2019
reed@android.com6c59a172009-09-22 20:24:05 +00002020 virtual bool setContext(const SkBitmap& device,
2021 const SkPaint& paint,
reed@google.com8e6d9142011-12-07 15:30:34 +00002022 const SkMatrix& matrix) SK_OVERRIDE {
2023 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com6c59a172009-09-22 20:24:05 +00002024 return false;
2025 }
2026
reed@google.com070a8002012-06-05 17:15:30 +00002027 // For now, we might have divided by zero, so detect that
2028 if (0 == fDiffRadius) {
2029 return false;
2030 }
2031
reed@android.com6c59a172009-09-22 20:24:05 +00002032 // we don't have a span16 proc
2033 fFlags &= ~kHasSpan16_Flag;
2034 return true;
2035 }
2036
djsollen@google.com54924242012-03-29 15:18:04 +00002037 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Radial_Gradient)
2038
2039protected:
2040 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
2041 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002042 fCenter1(buffer.readPoint()),
2043 fCenter2(buffer.readPoint()),
djsollen@google.com54924242012-03-29 15:18:04 +00002044 fRadius1(buffer.readScalar()),
2045 fRadius2(buffer.readScalar()) {
2046 init();
2047 };
2048
2049 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
reed@android.combcfc7332009-11-10 16:19:39 +00002050 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002051 buffer.writePoint(fCenter1);
2052 buffer.writePoint(fCenter2);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002053 buffer.writeScalar(fRadius1);
2054 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00002055 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002056
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002057private:
2058 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002059 const SkPoint fCenter1;
2060 const SkPoint fCenter2;
2061 const SkScalar fRadius1;
2062 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002063 SkPoint fDiff;
2064 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002065
2066 void init() {
2067 fDiff = fCenter1 - fCenter2;
2068 fDiffRadius = fRadius2 - fRadius1;
reed@google.comb83f7972012-06-05 17:39:22 +00002069 // hack to avoid zero-divide for now
2070 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002071 fDiff.fX = SkScalarMul(fDiff.fX, inv);
2072 fDiff.fY = SkScalarMul(fDiff.fY, inv);
2073 fStartRadius = SkScalarMul(fRadius1, inv);
2074 fSr2D2 = SkScalarSquare(fStartRadius);
2075 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00002076 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002077
2078 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
2079 fPtsToUnit.postScale(inv, inv);
2080 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002081};
2082
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083///////////////////////////////////////////////////////////////////////////////
2084
reed@google.comcb7be692012-06-06 20:31:56 +00002085static int valid_divide(float numer, float denom, float* ratio) {
2086 SkASSERT(ratio);
2087 if (0 == denom) {
2088 return 0;
2089 }
2090 *ratio = numer / denom;
2091 return 1;
2092}
2093
2094// Return the number of distinct real roots, and write them into roots[] in
2095// ascending order
2096static int find_quad_roots(float A, float B, float C, float roots[2]) {
2097 SkASSERT(roots);
2098
2099 if (A == 0) {
2100 return valid_divide(-C, B, roots);
2101 }
2102
reed@google.comcb7be692012-06-06 20:31:56 +00002103 float R = B*B - 4*A*C;
reed@google.comaca29ae2012-06-07 14:20:52 +00002104 if (R < 0) {
reed@google.comcb7be692012-06-06 20:31:56 +00002105 return 0;
2106 }
2107 R = sk_float_sqrt(R);
reed@google.comaca29ae2012-06-07 14:20:52 +00002108
2109#if 1
2110 float Q = B;
2111 if (Q < 0) {
2112 Q -= R;
2113 } else {
2114 Q += R;
reed@google.comcb7be692012-06-06 20:31:56 +00002115 }
reed@google.comaca29ae2012-06-07 14:20:52 +00002116#else
2117 // on 10.6 this was much slower than the above branch :(
2118 float Q = B + copysignf(R, B);
2119#endif
2120 Q *= -0.5f;
2121 if (0 == Q) {
2122 roots[0] = 0;
2123 return 1;
2124 }
2125
2126 float r0 = Q / A;
2127 float r1 = C / Q;
2128 roots[0] = r0 < r1 ? r0 : r1;
2129 roots[1] = r0 > r1 ? r0 : r1;
2130 return 2;
reed@google.comcb7be692012-06-06 20:31:56 +00002131}
2132
2133static float lerp(float x, float dx, float t) {
2134 return x + t * dx;
2135}
2136
2137static float sqr(float x) { return x * x; }
2138
2139struct TwoPtRadial {
2140 enum {
2141 kDontDrawT = 0x80000000
2142 };
2143
2144 float fCenterX, fCenterY;
2145 float fDCenterX, fDCenterY;
2146 float fRadius;
2147 float fDRadius;
2148 float fA;
2149 float fRadius2;
2150 float fRDR;
2151
2152 void init(const SkPoint& center0, SkScalar rad0,
2153 const SkPoint& center1, SkScalar rad1) {
2154 fCenterX = SkScalarToFloat(center0.fX);
2155 fCenterY = SkScalarToFloat(center0.fY);
2156 fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
2157 fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
2158 fRadius = SkScalarToFloat(rad0);
2159 fDRadius = SkScalarToFloat(rad1) - fRadius;
2160
mike@reedtribe.org23113dd2012-06-09 02:03:40 +00002161 fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
reed@google.comcb7be692012-06-06 20:31:56 +00002162 fRadius2 = sqr(fRadius);
2163 fRDR = fRadius * fDRadius;
2164 }
2165
2166 // used by setup and nextT
2167 float fRelX, fRelY, fIncX, fIncY;
2168 float fB, fDB;
2169
2170 void setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
2171 fRelX = SkScalarToFloat(fx) - fCenterX;
2172 fRelY = SkScalarToFloat(fy) - fCenterY;
2173 fIncX = SkScalarToFloat(dfx);
2174 fIncY = SkScalarToFloat(dfy);
2175 fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR);
2176 fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY);
2177 }
2178
2179 SkFixed nextT() {
2180 float roots[2];
2181
reed@google.comaca29ae2012-06-07 14:20:52 +00002182 float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
reed@google.comcb7be692012-06-06 20:31:56 +00002183 int countRoots = find_quad_roots(fA, fB, C, roots);
2184
2185 fRelX += fIncX;
2186 fRelY += fIncY;
2187 fB += fDB;
2188
2189 if (0 == countRoots) {
2190 return kDontDrawT;
2191 }
2192
2193 // Prefer the bigger t value if both give a radius(t) > 0
2194 // find_quad_roots returns the values sorted, so we start with the last
2195 float t = roots[countRoots - 1];
2196 float r = lerp(fRadius, fDRadius, t);
2197 if (r <= 0) {
2198 t = roots[0]; // might be the same as roots[countRoots-1]
2199 r = lerp(fRadius, fDRadius, t);
2200 if (r <= 0) {
2201 return kDontDrawT;
2202 }
2203 }
2204 return SkFloatToFixed(t);
2205 }
2206
2207 static bool DontDrawT(SkFixed t) {
reed@google.comaca29ae2012-06-07 14:20:52 +00002208 return kDontDrawT == (uint32_t)t;
reed@google.comcb7be692012-06-06 20:31:56 +00002209 }
2210};
2211
2212typedef void (*TwoPointRadialProc)(TwoPtRadial* rec, SkPMColor* dstC,
2213 const SkPMColor* cache, int count);
2214
reed@google.comaca29ae2012-06-07 14:20:52 +00002215static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
2216 const SkPMColor* SK_RESTRICT cache, int count) {
reed@google.comcb7be692012-06-06 20:31:56 +00002217 for (; count > 0; --count) {
2218 SkFixed t = rec->nextT();
2219 if (TwoPtRadial::DontDrawT(t)) {
2220 *dstC++ = 0;
2221 } else {
2222 SkFixed index = SkClampMax(t, 0xFFFF);
2223 SkASSERT(index <= 0xFFFF);
2224 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
2225 }
2226 }
2227}
2228
reed@google.comaca29ae2012-06-07 14:20:52 +00002229static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
2230 const SkPMColor* SK_RESTRICT cache, int count) {
reed@google.comcb7be692012-06-06 20:31:56 +00002231 for (; count > 0; --count) {
2232 SkFixed t = rec->nextT();
2233 if (TwoPtRadial::DontDrawT(t)) {
2234 *dstC++ = 0;
2235 } else {
2236 SkFixed index = repeat_tileproc(t);
2237 SkASSERT(index <= 0xFFFF);
2238 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
2239 }
2240 }
2241}
2242
reed@google.comaca29ae2012-06-07 14:20:52 +00002243static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
2244 const SkPMColor* SK_RESTRICT cache, int count) {
reed@google.comcb7be692012-06-06 20:31:56 +00002245 for (; count > 0; --count) {
2246 SkFixed t = rec->nextT();
2247 if (TwoPtRadial::DontDrawT(t)) {
2248 *dstC++ = 0;
2249 } else {
2250 SkFixed index = mirror_tileproc(t);
2251 SkASSERT(index <= 0xFFFF);
2252 *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
2253 }
2254 }
2255}
2256
2257class Two_Point_Conical_Gradient : public Gradient_Shader {
2258 TwoPtRadial fRec;
2259
2260 void init() {
2261 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2);
2262 fPtsToUnit.reset();
2263 }
2264public:
2265 Two_Point_Conical_Gradient(const SkPoint& start, SkScalar startRadius,
2266 const SkPoint& end, SkScalar endRadius,
2267 const SkColor colors[], const SkScalar pos[],
2268 int colorCount, SkShader::TileMode mode,
2269 SkUnitMapper* mapper)
2270 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
2271 fCenter1(start),
2272 fCenter2(end),
2273 fRadius1(startRadius),
2274 fRadius2(endRadius) {
2275 // this is degenerate, and should be caught by our caller
2276 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
2277 this->init();
2278 }
2279
2280 virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
2281 int count) SK_OVERRIDE {
2282 SkASSERT(count > 0);
2283
2284 SkPMColor* SK_RESTRICT dstC = dstCParam;
2285
2286 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
2287 TileProc proc = fTileProc;
2288 const SkPMColor* SK_RESTRICT cache = this->getCache32();
2289
2290 TwoPointRadialProc shadeProc = twopoint_repeat;
2291 if (proc == clamp_tileproc) {
2292 shadeProc = twopoint_clamp;
2293 } else if (proc == mirror_tileproc) {
2294 shadeProc = twopoint_mirror;
2295 } else {
2296 SkASSERT(proc == repeat_tileproc);
2297 }
2298
2299 if (fDstToIndexClass != kPerspective_MatrixClass) {
2300 SkPoint srcPt;
2301 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
2302 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2303 SkScalar dx, fx = srcPt.fX;
2304 SkScalar dy, fy = srcPt.fY;
2305
2306 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
2307 SkFixed fixedX, fixedY;
2308 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
2309 dx = SkFixedToScalar(fixedX);
2310 dy = SkFixedToScalar(fixedY);
2311 } else {
2312 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
2313 dx = fDstToIndex.getScaleX();
2314 dy = fDstToIndex.getSkewY();
2315 }
2316
2317 fRec.setup(fx, fy, dx, dy);
2318 (*shadeProc)(&fRec, dstC, cache, count);
2319 } else { // perspective case
2320 SkScalar dstX = SkIntToScalar(x);
2321 SkScalar dstY = SkIntToScalar(y);
2322 for (; count > 0; --count) {
2323 SkPoint srcPt;
2324 dstProc(fDstToIndex, dstX, dstY, &srcPt);
2325 dstX += SK_Scalar1;
2326
2327 fRec.setup(srcPt.fX, srcPt.fY, 0, 0);
2328 (*shadeProc)(&fRec, dstC, cache, 1);
2329 }
2330 }
2331 }
2332
2333 virtual bool setContext(const SkBitmap& device,
2334 const SkPaint& paint,
2335 const SkMatrix& matrix) SK_OVERRIDE {
2336 if (!this->INHERITED::setContext(device, paint, matrix)) {
2337 return false;
2338 }
2339
2340 // we don't have a span16 proc
2341 fFlags &= ~kHasSpan16_Flag;
2342
2343 // in general, we might discard based on computed-radius, so clear
2344 // this flag (todo: sometimes we can detect that we never discard...)
2345 fFlags &= ~kOpaqueAlpha_Flag;
2346
2347 return true;
2348 }
reed@google.com83226972012-06-07 20:26:47 +00002349
rileya@google.com3e332582012-07-03 13:43:35 +00002350 virtual BitmapType asABitmap(SkBitmap* bitmap,
2351 SkMatrix* matrix,
2352 TileMode* xy,
2353 SkScalar* twoPointRadialParams) const {
2354
2355 SkPoint diff = fCenter2 - fCenter1;
2356 SkScalar diffRadius = fRadius2 - fRadius1;
2357 SkScalar startRadius = fRadius1;
2358 SkScalar diffLen = 0;
2359
2360 if (bitmap) {
2361 this->commonAsABitmap(bitmap);
2362 }
2363 if (matrix || twoPointRadialParams) {
2364 diffLen = diff.length();
2365 }
2366 if (matrix) {
2367 if (diffLen) {
2368 SkScalar invDiffLen = SkScalarInvert(diffLen);
2369 // rotate to align circle centers with the x-axis
2370 matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
2371 SkScalarMul(invDiffLen, diff.fX));
2372 } else {
2373 matrix->reset();
2374 }
2375 matrix->preTranslate(-fCenter1.fX, -fCenter1.fY);
2376 }
2377 if (xy) {
2378 xy[0] = fTileMode;
2379 xy[1] = kClamp_TileMode;
2380 }
2381 if (NULL != twoPointRadialParams) {
2382 twoPointRadialParams[0] = diffLen;
2383 twoPointRadialParams[1] = startRadius;
2384 twoPointRadialParams[2] = diffRadius;
2385 }
2386
2387 return kTwoPointConical_BitmapType;
2388 }
2389
2390
reed@google.com83226972012-06-07 20:26:47 +00002391 SkShader::GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
2392 if (info) {
2393 commonAsAGradient(info);
2394 info->fPoint[0] = fCenter1;
2395 info->fPoint[1] = fCenter2;
2396 info->fRadius[0] = fRadius1;
2397 info->fRadius[1] = fRadius2;
2398 }
2399 return kConical_GradientType;
2400 }
2401
rileya@google.com03c1c352012-07-20 20:02:43 +00002402 virtual GrCustomStage* asNewCustomStage(GrContext* context,
2403 GrSamplerState* sampler) const SK_OVERRIDE {
2404 SkASSERT(NULL != context && NULL != sampler);
2405 SkPoint diff = fCenter2 - fCenter1;
2406 SkScalar diffLen = diff.length();
2407 if (0 != diffLen) {
2408 SkScalar invDiffLen = SkScalarInvert(diffLen);
2409 sampler->matrix()->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
2410 SkScalarMul(invDiffLen, diff.fX));
2411 } else {
2412 sampler->matrix()->reset();
2413 }
2414 sampler->matrix()->preTranslate(-fCenter1.fX, -fCenter1.fY);
2415 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
2416 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
2417 sampler->setFilter(GrSamplerState::kBilinear_Filter);
2418 return SkNEW_ARGS(GrConical2Gradient, (context, *this));
2419 }
2420
reed@google.comcb7be692012-06-06 20:31:56 +00002421 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Conical_Gradient)
2422
2423protected:
2424 Two_Point_Conical_Gradient(SkFlattenableReadBuffer& buffer)
2425 : INHERITED(buffer),
2426 fCenter1(buffer.readPoint()),
2427 fCenter2(buffer.readPoint()),
2428 fRadius1(buffer.readScalar()),
2429 fRadius2(buffer.readScalar()) {
2430 this->init();
2431 };
2432
2433 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
2434 this->INHERITED::flatten(buffer);
2435 buffer.writePoint(fCenter1);
2436 buffer.writePoint(fCenter2);
2437 buffer.writeScalar(fRadius1);
2438 buffer.writeScalar(fRadius2);
2439 }
2440
2441private:
2442 typedef Gradient_Shader INHERITED;
2443 const SkPoint fCenter1;
2444 const SkPoint fCenter2;
2445 const SkScalar fRadius1;
2446 const SkScalar fRadius2;
2447};
2448
2449///////////////////////////////////////////////////////////////////////////////
2450
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451class Sweep_Gradient : public Gradient_Shader {
2452public:
2453 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
2454 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002455 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
2456 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457 {
2458 fPtsToUnit.setTranslate(-cx, -cy);
2459 }
reed@google.com7716afb2011-12-07 15:17:50 +00002460 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
2461 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002462
2463 virtual BitmapType asABitmap(SkBitmap* bitmap,
2464 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00002465 TileMode* xy,
reed@google.com7716afb2011-12-07 15:17:50 +00002466 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
reed@google.comdc731fd2010-12-23 15:19:47 +00002467 if (bitmap) {
2468 this->commonAsABitmap(bitmap);
2469 }
2470 if (matrix) {
2471 *matrix = fPtsToUnit;
2472 }
2473 if (xy) {
2474 xy[0] = fTileMode;
2475 xy[1] = kClamp_TileMode;
2476 }
2477 return kSweep_BitmapType;
2478 }
2479
reed@google.com7716afb2011-12-07 15:17:50 +00002480 virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002481 if (info) {
2482 commonAsAGradient(info);
2483 info->fPoint[0] = fCenter;
2484 }
2485 return kSweep_GradientType;
2486 }
2487
rileya@google.com03c1c352012-07-20 20:02:43 +00002488 virtual GrCustomStage* asNewCustomStage(GrContext* context,
2489 GrSamplerState* sampler) const SK_OVERRIDE {
2490 sampler->matrix()->preConcat(fPtsToUnit);
2491 sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
2492 sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
2493 sampler->setFilter(GrSamplerState::kBilinear_Filter);
2494 return SkNEW_ARGS(GrSweepGradient, (context, *this));
2495 }
2496
djsollen@google.comba28d032012-03-26 17:57:35 +00002497 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sweep_Gradient)
2498
reed@android.com8a1c16f2008-12-17 15:59:43 +00002499protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002500 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
djsollen@google.com54924242012-03-29 15:18:04 +00002501 : INHERITED(buffer),
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002502 fCenter(buffer.readPoint()) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002503 }
djsollen@google.com54924242012-03-29 15:18:04 +00002504 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
2505 this->INHERITED::flatten(buffer);
djsollen@google.com2b2ede32012-04-12 13:24:04 +00002506 buffer.writePoint(fCenter);
djsollen@google.com54924242012-03-29 15:18:04 +00002507 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002508
reed@android.com8a1c16f2008-12-17 15:59:43 +00002509private:
2510 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00002511 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002512};
2513
caryclark@google.com383d5d42012-06-06 12:09:18 +00002514#ifndef SK_SCALAR_IS_FLOAT
reed@android.com8a1c16f2008-12-17 15:59:43 +00002515#ifdef COMPUTE_SWEEP_TABLE
2516#define PI 3.14159265
2517static bool gSweepTableReady;
2518static uint8_t gSweepTable[65];
2519
2520/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
2521 We scale the results to [0..32]
2522*/
reed@google.com61eb0402011-04-15 12:11:12 +00002523static const uint8_t* build_sweep_table() {
2524 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002525 const int N = 65;
2526 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002527
reed@android.com8a1c16f2008-12-17 15:59:43 +00002528 for (int i = 0; i < N; i++)
2529 {
2530 double arg = i / DENOM;
2531 double v = atan(arg);
2532 int iv = (int)round(v * DENOM * 2 / PI);
2533// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
2534 printf("%d, ", iv);
2535 gSweepTable[i] = iv;
2536 }
2537 gSweepTableReady = true;
2538 }
2539 return gSweepTable;
2540}
2541#else
2542static const uint8_t gSweepTable[] = {
2543 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
2544 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
2545 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
2546 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
2547 32
2548};
2549static const uint8_t* build_sweep_table() { return gSweepTable; }
2550#endif
caryclark@google.com383d5d42012-06-06 12:09:18 +00002551#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002552
2553// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
2554// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
2555// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
2556
2557//unsigned div_64(int numer, int denom);
caryclark@google.com383d5d42012-06-06 12:09:18 +00002558#ifndef SK_SCALAR_IS_FLOAT
reed@google.com61eb0402011-04-15 12:11:12 +00002559static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002560 SkASSERT(numer <= denom);
2561 SkASSERT(numer > 0);
2562 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002563
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564 int nbits = SkCLZ(numer);
2565 int dbits = SkCLZ(denom);
2566 int bits = 6 - nbits + dbits;
2567 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002568
reed@google.com61eb0402011-04-15 12:11:12 +00002569 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00002570 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002571 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002572
2573 denom <<= dbits - 1;
2574 numer <<= nbits - 1;
2575
2576 unsigned result = 0;
2577
2578 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00002579 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002580 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00002581 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002582 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00002583 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002584
reed@android.com8a1c16f2008-12-17 15:59:43 +00002585 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00002586 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002587 // make room for the rest of the answer bits
2588 result <<= bits;
2589 switch (bits) {
2590 case 6:
2591 if ((numer = (numer << 1) - denom) >= 0)
2592 result |= 32;
2593 else
2594 numer += denom;
2595 case 5:
2596 if ((numer = (numer << 1) - denom) >= 0)
2597 result |= 16;
2598 else
2599 numer += denom;
2600 case 4:
2601 if ((numer = (numer << 1) - denom) >= 0)
2602 result |= 8;
2603 else
2604 numer += denom;
2605 case 3:
2606 if ((numer = (numer << 1) - denom) >= 0)
2607 result |= 4;
2608 else
2609 numer += denom;
2610 case 2:
2611 if ((numer = (numer << 1) - denom) >= 0)
2612 result |= 2;
2613 else
2614 numer += denom;
2615 case 1:
2616 default: // not strictly need, but makes GCC make better ARM code
2617 if ((numer = (numer << 1) - denom) >= 0)
2618 result |= 1;
2619 else
2620 numer += denom;
2621 }
2622 }
2623 return result;
2624}
caryclark@google.com383d5d42012-06-06 12:09:18 +00002625#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002626
2627// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
caryclark@google.com383d5d42012-06-06 12:09:18 +00002628#ifndef SK_SCALAR_IS_FLOAT
reed@google.com61eb0402011-04-15 12:11:12 +00002629static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002630#ifdef SK_DEBUG
2631 {
2632 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00002633 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002634 gOnce = true;
2635 SkASSERT(div_64(55, 55) == 64);
2636 SkASSERT(div_64(128, 256) == 32);
2637 SkASSERT(div_64(2326528, 4685824) == 31);
2638 SkASSERT(div_64(753664, 5210112) == 9);
2639 SkASSERT(div_64(229376, 4882432) == 3);
2640 SkASSERT(div_64(2, 64) == 2);
2641 SkASSERT(div_64(1, 64) == 1);
2642 // test that we handle underflow correctly
2643 SkASSERT(div_64(12345, 0x54321234) == 0);
2644 }
2645 }
2646#endif
2647
2648 SkASSERT(y > 0 && x > 0);
2649 const uint8_t* table = build_sweep_table();
2650
2651 unsigned result;
2652 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00002653 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002654 // first part of the atan(v) = PI/2 - atan(1/v) identity
2655 // since our div_64 and table want v <= 1, where v = y/x
2656 SkTSwap<SkFixed>(x, y);
2657 }
2658
2659 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002660
reed@android.com8a1c16f2008-12-17 15:59:43 +00002661#ifdef SK_DEBUG
2662 {
2663 unsigned result2 = SkDivBits(y, x, 6);
2664 SkASSERT(result2 == result ||
2665 (result == 1 && result2 == 0));
2666 }
2667#endif
2668
2669 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2670 result = table[result];
2671
reed@google.com61eb0402011-04-15 12:11:12 +00002672 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002673 // complete the atan(v) = PI/2 - atan(1/v) identity
2674 result = 64 - result;
2675 // pin to 63
2676 result -= result >> 6;
2677 }
2678
2679 SkASSERT(result <= 63);
2680 return result;
2681}
caryclark@google.com383d5d42012-06-06 12:09:18 +00002682#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002683
2684// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com51baf5a2011-09-21 13:38:36 +00002685#ifdef SK_SCALAR_IS_FLOAT
2686static unsigned SkATan2_255(float y, float x) {
2687 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
2688 static const float g255Over2PI = 40.584510488433314f;
2689
2690 float result = sk_float_atan2(y, x);
2691 if (result < 0) {
2692 result += 2 * SK_ScalarPI;
2693 }
2694 SkASSERT(result >= 0);
2695 // since our value is always >= 0, we can cast to int, which is faster than
2696 // calling floorf()
2697 int ir = (int)(result * g255Over2PI);
2698 SkASSERT(ir >= 0 && ir <= 255);
2699 return ir;
2700}
2701#else
reed@google.com61eb0402011-04-15 12:11:12 +00002702static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2703 if (x == 0) {
2704 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002705 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002706 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002707 return y < 0 ? 192 : 64;
2708 }
reed@google.com61eb0402011-04-15 12:11:12 +00002709 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002710 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002711 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002712
reed@android.com8a1c16f2008-12-17 15:59:43 +00002713 /* Find the right quadrant for x,y
2714 Since atan_0_90 only handles the first quadrant, we rotate x,y
2715 appropriately before calling it, and then add the right amount
2716 to account for the real quadrant.
2717 quadrant 0 : add 0 | x > 0 && y > 0
2718 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2719 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2720 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002721
reed@android.com8a1c16f2008-12-17 15:59:43 +00002722 map x<0 to (1 << 6)
2723 map y<0 to (3 << 6)
2724 add = map_x ^ map_y
2725 */
2726 int xsign = x >> 31;
2727 int ysign = y >> 31;
2728 int add = ((-xsign) ^ (ysign & 3)) << 6;
2729
2730#ifdef SK_DEBUG
2731 if (0 == add)
2732 SkASSERT(x > 0 && y > 0);
2733 else if (64 == add)
2734 SkASSERT(x < 0 && y > 0);
2735 else if (128 == add)
2736 SkASSERT(x < 0 && y < 0);
2737 else if (192 == add)
2738 SkASSERT(x > 0 && y < 0);
2739 else
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002740 SkDEBUGFAIL("bad value for add");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002741#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002742
reed@android.com8a1c16f2008-12-17 15:59:43 +00002743 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2744 where we need to rotate x,y by 90 or -90
2745 */
2746 x = (x ^ xsign) - xsign;
2747 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002748 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002749 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002750 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002751
2752 unsigned result = add + atan_0_90(y, x);
2753 SkASSERT(result < 256);
2754 return result;
2755}
reed@google.com51baf5a2011-09-21 13:38:36 +00002756#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002757
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002758void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
2759 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002760 SkMatrix::MapXYProc proc = fDstToIndexProc;
2761 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002762 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002763 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002764
reed@google.com61eb0402011-04-15 12:11:12 +00002765 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002766 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2767 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002768 SkScalar dx, fx = srcPt.fX;
2769 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002770
reed@google.com61eb0402011-04-15 12:11:12 +00002771 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002772 SkFixed storage[2];
2773 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2774 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002775 dx = SkFixedToScalar(storage[0]);
2776 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002777 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002778 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002779 dx = matrix.getScaleX();
2780 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002781 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002782
reed@google.com61eb0402011-04-15 12:11:12 +00002783 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002784 *dstC++ = cache[SkATan2_255(fy, fx)];
2785 fx += dx;
2786 fy += dy;
2787 }
reed@google.com61eb0402011-04-15 12:11:12 +00002788 } else { // perspective case
2789 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002790 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com51baf5a2011-09-21 13:38:36 +00002791 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2792 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002793 }
2794 }
2795}
2796
tomhudson@google.com6a8df702012-01-12 20:13:49 +00002797void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
2798 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002799 SkMatrix::MapXYProc proc = fDstToIndexProc;
2800 const SkMatrix& matrix = fDstToIndex;
reed@google.comdd5bd672011-09-20 15:56:13 +00002801 const uint16_t* SK_RESTRICT cache = this->getCache16();
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002802 int toggle = ((x ^ y) & 1) * kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002803 SkPoint srcPt;
2804
reed@google.com61eb0402011-04-15 12:11:12 +00002805 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002806 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2807 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com51baf5a2011-09-21 13:38:36 +00002808 SkScalar dx, fx = srcPt.fX;
2809 SkScalar dy, fy = srcPt.fY;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002810
reed@google.com61eb0402011-04-15 12:11:12 +00002811 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002812 SkFixed storage[2];
2813 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2814 &storage[0], &storage[1]);
reed@google.com51baf5a2011-09-21 13:38:36 +00002815 dx = SkFixedToScalar(storage[0]);
2816 dy = SkFixedToScalar(storage[1]);
reed@google.com61eb0402011-04-15 12:11:12 +00002817 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002818 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com51baf5a2011-09-21 13:38:36 +00002819 dx = matrix.getScaleX();
2820 dy = matrix.getSkewY();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002821 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002822
reed@google.com61eb0402011-04-15 12:11:12 +00002823 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002824 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2825 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002826 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002827 fx += dx;
2828 fy += dy;
2829 }
reed@google.com61eb0402011-04-15 12:11:12 +00002830 } else { // perspective case
2831 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002832 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2833 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002834
reed@google.com51baf5a2011-09-21 13:38:36 +00002835 int index = SkATan2_255(srcPt.fY, srcPt.fX);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002836 index >>= (8 - kCache16Bits);
2837 *dstC++ = cache[toggle + index];
tomhudson@google.com13e812c2012-01-18 21:28:01 +00002838 toggle ^= kDitherStride16;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002839 }
2840 }
2841}
2842
reed@google.com61eb0402011-04-15 12:11:12 +00002843///////////////////////////////////////////////////////////////////////////////
2844///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002845
reed@google.comcb7be692012-06-06 20:31:56 +00002846#include "SkEmptyShader.h"
2847
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848// assumes colors is SkColor* and pos is SkScalar*
2849#define EXPAND_1_COLOR(count) \
2850 SkColor tmp[2]; \
2851 do { \
2852 if (1 == count) { \
2853 tmp[0] = tmp[1] = colors[0]; \
2854 colors = tmp; \
2855 pos = NULL; \
2856 count = 2; \
2857 } \
2858 } while (0)
2859
reed@google.com61eb0402011-04-15 12:11:12 +00002860SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2861 const SkColor colors[],
2862 const SkScalar pos[], int colorCount,
2863 SkShader::TileMode mode,
2864 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 if (NULL == pts || NULL == colors || colorCount < 1) {
2866 return NULL;
2867 }
2868 EXPAND_1_COLOR(colorCount);
2869
reed@android.comab840b82009-07-01 17:00:03 +00002870 return SkNEW_ARGS(Linear_Gradient,
2871 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002872}
2873
reed@google.com61eb0402011-04-15 12:11:12 +00002874SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2875 const SkColor colors[],
2876 const SkScalar pos[], int colorCount,
2877 SkShader::TileMode mode,
2878 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002879 if (radius <= 0 || NULL == colors || colorCount < 1) {
2880 return NULL;
2881 }
2882 EXPAND_1_COLOR(colorCount);
2883
reed@android.comab840b82009-07-01 17:00:03 +00002884 return SkNEW_ARGS(Radial_Gradient,
2885 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002886}
2887
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002888SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2889 SkScalar startRadius,
2890 const SkPoint& end,
2891 SkScalar endRadius,
2892 const SkColor colors[],
2893 const SkScalar pos[],
2894 int colorCount,
2895 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002896 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002897 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2898 return NULL;
2899 }
2900 EXPAND_1_COLOR(colorCount);
reed@google.comcb7be692012-06-06 20:31:56 +00002901
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002902 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002903 (start, startRadius, end, endRadius, colors, pos,
2904 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002905}
2906
reed@google.comcb7be692012-06-06 20:31:56 +00002907SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
2908 SkScalar startRadius,
2909 const SkPoint& end,
2910 SkScalar endRadius,
2911 const SkColor colors[],
2912 const SkScalar pos[],
2913 int colorCount,
2914 SkShader::TileMode mode,
2915 SkUnitMapper* mapper) {
2916 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2917 return NULL;
2918 }
2919 if (start == end && startRadius == endRadius) {
2920 return SkNEW(SkEmptyShader);
2921 }
2922
2923 return SkNEW_ARGS(Two_Point_Conical_Gradient,
2924 (start, startRadius, end, endRadius, colors, pos,
2925 colorCount, mode, mapper));
2926}
2927
reed@android.com8a1c16f2008-12-17 15:59:43 +00002928SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2929 const SkColor colors[],
2930 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002931 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002932 if (NULL == colors || count < 1) {
2933 return NULL;
2934 }
2935 EXPAND_1_COLOR(count);
2936
2937 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2938}
2939
caryclark@google.comd26147a2011-12-15 14:16:43 +00002940SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
2941 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Linear_Gradient)
2942 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Radial_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002943 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sweep_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002944 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Radial_Gradient)
scroggo@google.comdb15a422012-06-11 16:51:45 +00002945 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Conical_Gradient)
caryclark@google.comd26147a2011-12-15 14:16:43 +00002946SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END