blob: 026b149463069d2ac1fae155e2e7a2deb6cca6f9 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/effects/SkGradientShader.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
reed@google.com55b8e8c2011-01-13 16:22:35 +00005** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.com55b8e8c2011-01-13 16:22:35 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.com55b8e8c2011-01-13 16:22:35 +000011** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** limitations under the License.
16*/
17
18#include "SkGradientShader.h"
19#include "SkColorPriv.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000020#include "SkMallocPixelRef.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkUnitMapper.h"
22#include "SkUtils.h"
reed@google.comdc731fd2010-12-23 15:19:47 +000023#include "SkTemplates.h"
24#include "SkBitmapCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025
reed@google.com9c7443d2011-01-17 18:46:37 +000026#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
27 #define USE_DITHER_32BIT_GRADIENT
28#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +000029
reed@google.com13659f12011-04-18 19:59:38 +000030#define SK_ENABLE_FAST_LINEAR_GRADIENTS
reed@google.com17705072011-04-18 12:43:32 +000031
32#ifdef SK_ENABLE_FAST_LINEAR_GRADIENTS
reed@google.com5eb158d2011-04-15 15:50:34 +000033static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
34 int count) {
35 if (count > 0) {
36 if (v0 == v1) {
37 sk_memset32(dst, v0, count);
38 } else {
39 int pairs = count >> 1;
40 for (int i = 0; i < pairs; i++) {
41 *dst++ = v0;
42 *dst++ = v1;
43 }
44 if (count & 1) {
45 *dst = v0;
46 }
47 }
48 }
49}
reed@google.com17705072011-04-18 12:43:32 +000050#endif
reed@google.com5eb158d2011-04-15 15:50:34 +000051
reed@google.com61eb0402011-04-15 12:11:12 +000052///////////////////////////////////////////////////////////////////////////////
tomhudson@google.com9ce767c2011-04-25 20:49:39 +000053// Can't use a two-argument function with side effects like this in a
54// constructor's initializer's argument list because the order of
55// evaluations in that context is undefined (and backwards on linux/gcc).
56static SkPoint unflatten_point(SkReader32& buffer) {
57 SkPoint retval;
58 retval.fX = buffer.readScalar();
59 retval.fY = buffer.readScalar();
60 return retval;
61}
62
63///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000064
65typedef SkFixed (*TileProc)(SkFixed);
66
reed@android.com41bccf52009-04-03 13:33:51 +000067static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 return SkClampMax(x, 0xFFFF);
69}
70
reed@android.com41bccf52009-04-03 13:33:51 +000071static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 return x & 0xFFFF;
73}
74
reed@android.com41bccf52009-04-03 13:33:51 +000075static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 int s = x << 15 >> 31;
77 return (x ^ s) & 0xFFFF;
78}
79
80static const TileProc gTileProcs[] = {
81 clamp_tileproc,
82 repeat_tileproc,
83 mirror_tileproc
84};
85
reed@google.com61eb0402011-04-15 12:11:12 +000086///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000087
reed@android.com200645d2009-12-14 16:41:57 +000088static inline int repeat_bits(int x, const int bits) {
89 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000090}
91
reed@android.com200645d2009-12-14 16:41:57 +000092static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000094 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000096 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000097#else
reed@android.com200645d2009-12-14 16:41:57 +000098 int s = x << (31 - bits) >> 31;
99 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100#endif
101}
102
reed@android.com41bccf52009-04-03 13:33:51 +0000103static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 return x & 0xFF;
105}
106
reed@android.com41bccf52009-04-03 13:33:51 +0000107static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +0000109 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +0000111 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 return x & 255;
113#else
114 int s = x << 23 >> 31;
115 return (x ^ s) & 0xFF;
116#endif
117}
118
reed@google.com61eb0402011-04-15 12:11:12 +0000119///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
121class Gradient_Shader : public SkShader {
122public:
123 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000124 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 virtual ~Gradient_Shader();
126
127 // overrides
128 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
129 virtual uint32_t getFlags() { return fFlags; }
130
131protected:
132 Gradient_Shader(SkFlattenableReadBuffer& );
133 SkUnitMapper* fMapper;
134 SkMatrix fPtsToUnit; // set by subclass
135 SkMatrix fDstToIndex;
136 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 TileMode fTileMode;
138 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000139 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 uint8_t fDstToIndexClass;
141 uint8_t fFlags;
142
143 struct Rec {
144 SkFixed fPos; // 0...1
145 uint32_t fScale; // (1 << 24) / range
146 };
147 Rec* fRecs;
148
149 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000150 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000152 kCache16Mask = kCache16Count - 1,
153 kCache16Shift = 16 - kCache16Bits,
154
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 kCache32Bits = 8, // pretty much should always be 8
156 kCache32Count = 1 << kCache32Bits
157 };
158 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000159 const uint16_t* getCache16() const;
160 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161
reed@google.com7c2f27d2011-03-07 19:29:00 +0000162 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000163 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000164
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165private:
166 enum {
167 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
168
reed@android.com1c12abe2009-07-02 15:01:02 +0000169 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 };
171 SkColor fStorage[(kStorageSize + 3) >> 2];
172 SkColor* fOrigColors;
173
reed@google.com7c2f27d2011-03-07 19:29:00 +0000174 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
175 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
reed@google.com7c2f27d2011-03-07 19:29:00 +0000177 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
178 mutable SkMallocPixelRef* fCache32PixelRef;
reed@google.com95eed982011-07-05 17:01:56 +0000179 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 +0000180
reed@android.com512a8762009-12-14 15:25:36 +0000181 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000182 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
183 U8CPU alpha);
reed@google.com95eed982011-07-05 17:01:56 +0000184 void setCacheAlpha(U8CPU alpha) const;
reed@android.com512a8762009-12-14 15:25:36 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 typedef SkShader INHERITED;
187};
188
reed@android.com41bccf52009-04-03 13:33:51 +0000189static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 SkASSERT(x >= 0 && x <= SK_Scalar1);
191
192#ifdef SK_SCALAR_IS_FLOAT
193 return (unsigned)(x * 0xFFFF);
194#else
195 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
196#endif
197}
198
reed@android.com41bccf52009-04-03 13:33:51 +0000199Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
200 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 SkASSERT(colorCount > 1);
202
203 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
204
205 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000206 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
209 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
210 fTileMode = mode;
211 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000212
reed@android.com41bccf52009-04-03 13:33:51 +0000213 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000214 fCache32 = NULL;
215 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216
reed@android.com41bccf52009-04-03 13:33:51 +0000217 /* Note: we let the caller skip the first and/or last position.
218 i.e. pos[0] = 0.3, pos[1] = 0.7
219 In these cases, we insert dummy entries to ensure that the final data
220 will be bracketed by [0, 1].
221 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
222
223 Thus colorCount (the caller's value, and fColorCount (our value) may
224 differ by up to 2. In the above example:
225 colorCount = 2
226 fColorCount = 4
227 */
228 fColorCount = colorCount;
229 // check if we need to add in dummy start and/or end position/colors
230 bool dummyFirst = false;
231 bool dummyLast = false;
232 if (pos) {
233 dummyFirst = pos[0] != 0;
234 dummyLast = pos[colorCount - 1] != SK_Scalar1;
235 fColorCount += dummyFirst + dummyLast;
236 }
237
238 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000239 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000240 fOrigColors = reinterpret_cast<SkColor*>(
241 sk_malloc_throw(size * fColorCount));
242 }
243 else {
244 fOrigColors = fStorage;
245 }
246
247 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 {
reed@android.com41bccf52009-04-03 13:33:51 +0000249 SkColor* origColors = fOrigColors;
250 if (dummyFirst) {
251 *origColors++ = colors[0];
252 }
253 memcpy(origColors, colors, colorCount * sizeof(SkColor));
254 if (dummyLast) {
255 origColors += colorCount;
256 *origColors = colors[colorCount - 1];
257 }
258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259
reed@android.com1c12abe2009-07-02 15:01:02 +0000260 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000261 if (fColorCount > 2) {
262 Rec* recs = fRecs;
263 recs->fPos = 0;
264 // recs->fScale = 0; // unused;
265 recs += 1;
266 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 /* We need to convert the user's array of relative positions into
268 fixed-point positions and scale factors. We need these results
269 to be strictly monotonic (no two values equal or out of order).
270 Hence this complex loop that just jams a zero for the scale
271 value if it sees a segment out of order, and it assures that
272 we start at 0 and end at 1.0
273 */
274 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000275 int startIndex = dummyFirst ? 0 : 1;
276 int count = colorCount + dummyLast;
277 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 // force the last value to be 1.0
279 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000280 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000282 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 }
reed@android.com41bccf52009-04-03 13:33:51 +0000285 // pin curr withing range
286 if (curr < 0) {
287 curr = 0;
288 } else if (curr > SK_Fixed1) {
289 curr = SK_Fixed1;
290 }
291 recs->fPos = curr;
292 if (curr > prev) {
293 recs->fScale = (1 << 24) / (curr - prev);
294 } else {
295 recs->fScale = 0; // ignore this segment
296 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 // get ready for the next value
298 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000299 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 }
reed@android.com41bccf52009-04-03 13:33:51 +0000301 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 SkFixed dp = SK_Fixed1 / (colorCount - 1);
303 SkFixed p = dp;
304 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000305 for (int i = 1; i < colorCount; i++) {
306 recs->fPos = p;
307 recs->fScale = scale;
308 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 p += dp;
310 }
311 }
312 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000313 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314}
315
316Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000317 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 fCacheAlpha = 256;
319
320 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
321
322 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000323 fCache32 = NULL;
324 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325
reed@android.com41bccf52009-04-03 13:33:51 +0000326 int colorCount = fColorCount = buffer.readU32();
327 if (colorCount > kColorStorageCount) {
328 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
329 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
330 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000332 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334
335 fTileMode = (TileMode)buffer.readU8();
336 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000337 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 if (colorCount > 2) {
339 Rec* recs = fRecs;
340 recs[0].fPos = 0;
341 for (int i = 1; i < colorCount; i++) {
342 recs[i].fPos = buffer.readS32();
343 recs[i].fScale = buffer.readU32();
344 }
345 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000346 SkReadMatrix(&buffer, &fPtsToUnit);
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000347 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348}
349
reed@android.com41bccf52009-04-03 13:33:51 +0000350Gradient_Shader::~Gradient_Shader() {
351 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000353 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000354 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000355 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000357 }
reed@google.com82065d62011-02-07 15:30:46 +0000358 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359}
360
reed@android.com41bccf52009-04-03 13:33:51 +0000361void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 this->INHERITED::flatten(buffer);
363 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000364 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
366 buffer.write8(fTileMode);
367 if (fColorCount > 2) {
368 Rec* recs = fRecs;
369 for (int i = 1; i < fColorCount; i++) {
370 buffer.write32(recs[i].fPos);
371 buffer.write32(recs[i].fScale);
372 }
373 }
reed@google.comf2eb5ab2011-05-10 22:56:42 +0000374 SkWriteMatrix(&buffer, fPtsToUnit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375}
376
377bool Gradient_Shader::setContext(const SkBitmap& device,
378 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000379 const SkMatrix& matrix) {
380 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000382 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383
384 const SkMatrix& inverse = this->getTotalInverse();
385
386 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
387 return false;
388 }
389
390 fDstToIndexProc = fDstToIndex.getMapXYProc();
391 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
392
393 // now convert our colors in to PMColors
394 unsigned paintAlpha = this->getPaintAlpha();
395 unsigned colorAlpha = 0xFF;
396
reed@android.com3d06a8c2009-07-07 18:19:59 +0000397 // FIXME: record colorAlpha in constructor, since this is not affected
398 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000399 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 SkColor src = fOrigColors[i];
401 unsigned sa = SkColorGetA(src);
402 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 }
404
405 fFlags = this->INHERITED::getFlags();
406 if ((colorAlpha & paintAlpha) == 0xFF) {
407 fFlags |= kOpaqueAlpha_Flag;
408 }
409 // we can do span16 as long as our individual colors are opaque,
410 // regardless of the paint's alpha
411 if (0xFF == colorAlpha) {
412 fFlags |= kHasSpan16_Flag;
413 }
414
reed@google.com95eed982011-07-05 17:01:56 +0000415 this->setCacheAlpha(paintAlpha);
416 return true;
417}
418
419void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 // if the new alpha differs from the previous time we were called, inval our cache
421 // this will trigger the cache to be rebuilt.
422 // we don't care about the first time, since the cache ptrs will already be NULL
reed@google.com95eed982011-07-05 17:01:56 +0000423 if (fCacheAlpha != alpha) {
424 fCache16 = NULL; // inval the cache
425 fCache32 = NULL; // inval the cache
426 fCacheAlpha = alpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000427 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000428 if (fCache32PixelRef) {
429 fCache32PixelRef->notifyPixelsChanged();
430 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432}
433
reed@android.com41bccf52009-04-03 13:33:51 +0000434static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 SkASSERT(a == SkToU8(a));
436 SkASSERT(b == SkToU8(b));
437 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 return a + ((b - a) * scale >> 8);
439}
440
reed@android.com41bccf52009-04-03 13:33:51 +0000441static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
442 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443#if 0
444 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
445 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
446 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
447 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
448
449 return SkPackARGB32(a, r, g, b);
450#else
451 int otherBlend = 256 - blend;
452
453#if 0
454 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
455 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
456 SkASSERT((t0 & t1) == 0);
457 return t0 | t1;
458#else
459 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
460 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
461#endif
462
463#endif
464}
465
466#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
467
reed@android.com41bccf52009-04-03 13:33:51 +0000468/** We take the original colors, not our premultiplied PMColors, since we can
469 build a 16bit table as long as the original colors are opaque, even if the
470 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471*/
reed@android.com512a8762009-12-14 15:25:36 +0000472void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
473 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 SkASSERT(count > 1);
475 SkASSERT(SkColorGetA(c0) == 0xFF);
476 SkASSERT(SkColorGetA(c1) == 0xFF);
477
478 SkFixed r = SkColorGetR(c0);
479 SkFixed g = SkColorGetG(c0);
480 SkFixed b = SkColorGetB(c0);
481
482 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
483 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
484 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
485
486 r = SkIntToFixed(r) + 0x8000;
487 g = SkIntToFixed(g) + 0x8000;
488 b = SkIntToFixed(b) + 0x8000;
489
490 do {
491 unsigned rr = r >> 16;
492 unsigned gg = g >> 16;
493 unsigned bb = b >> 16;
494 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000495 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 cache += 1;
497 r += dr;
498 g += dg;
499 b += db;
500 } while (--count != 0);
501}
502
reed@google.com55b8e8c2011-01-13 16:22:35 +0000503/*
504 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
505 * semantics of how we 2x2 dither 32->16
506 */
507static inline U8CPU dither_fixed_to_8(SkFixed n) {
508 n >>= 8;
509 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
510}
511
512/*
513 * For dithering with premultiply, we want to ceiling the alpha component,
514 * to ensure that it is always >= any color component.
515 */
516static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
517 n >>= 8;
518 return ((n << 1) - (n | (n >> 8))) >> 8;
519}
520
521void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
522 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 SkASSERT(count > 1);
524
reed@android.com1c12abe2009-07-02 15:01:02 +0000525 // need to apply paintAlpha to our two endpoints
526 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
527 SkFixed da;
528 {
529 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
530 da = SkIntToFixed(tmp - a) / (count - 1);
531 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532
reed@android.com1c12abe2009-07-02 15:01:02 +0000533 SkFixed r = SkColorGetR(c0);
534 SkFixed g = SkColorGetG(c0);
535 SkFixed b = SkColorGetB(c0);
536 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
537 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
538 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539
540 a = SkIntToFixed(a) + 0x8000;
541 r = SkIntToFixed(r) + 0x8000;
542 g = SkIntToFixed(g) + 0x8000;
543 b = SkIntToFixed(b) + 0x8000;
544
545 do {
reed@google.com0b8b3bb2011-06-30 21:06:22 +0000546 cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
547 cache[kCache32Count] = SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
548 dither_fixed_to_8(r),
549 dither_fixed_to_8(g),
550 dither_fixed_to_8(b));
reed@google.com55b8e8c2011-01-13 16:22:35 +0000551 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 a += da;
553 r += dr;
554 g += dg;
555 b += db;
556 } while (--count != 0);
557}
558
reed@android.com41bccf52009-04-03 13:33:51 +0000559static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 SkASSERT((unsigned)x <= SK_Fixed1);
561 return x - (x >> 16);
562}
563
reed@android.com200645d2009-12-14 16:41:57 +0000564static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000565 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000566 if (6 == bits) {
567 return (x << 10) | (x << 4) | (x >> 2);
568 }
569 if (8 == bits) {
570 return (x << 8) | x;
571 }
572 sk_throw();
573 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574}
575
reed@google.com7c2f27d2011-03-07 19:29:00 +0000576const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000577 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000578 // double the count for dither entries
579 const int entryCount = kCache16Count * 2;
580 const size_t allocSize = sizeof(uint16_t) * entryCount;
581
reed@android.com3c9b2a42009-08-27 19:28:37 +0000582 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000583 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000586 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000587 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000588 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 Rec* rec = fRecs;
590 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000591 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000592 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 SkASSERT(nextIndex < kCache16Count);
594
595 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000596 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 prevIndex = nextIndex;
598 }
599 SkASSERT(prevIndex == kCache16Count - 1);
600 }
601
reed@android.com41bccf52009-04-03 13:33:51 +0000602 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000603 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 uint16_t* linear = fCache16; // just computed linear data
605 uint16_t* mapped = fCache16Storage; // storage for mapped data
606 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000607 for (int i = 0; i < kCache16Count; i++) {
608 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000610 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 }
612 sk_free(fCache16);
613 fCache16 = fCache16Storage;
614 }
615 }
616 return fCache16;
617}
618
reed@google.com7c2f27d2011-03-07 19:29:00 +0000619const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000620 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000621 // double the count for dither entries
622 const int entryCount = kCache32Count * 2;
623 const size_t allocSize = sizeof(SkPMColor) * entryCount;
624
reed@google.comdc731fd2010-12-23 15:19:47 +0000625 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000626 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
627 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000628 }
629 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000630 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000631 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
632 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000633 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 Rec* rec = fRecs;
635 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000636 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
638 SkASSERT(nextIndex < kCache32Count);
639
640 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000641 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
642 fOrigColors[i],
643 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 prevIndex = nextIndex;
645 }
646 SkASSERT(prevIndex == kCache32Count - 1);
647 }
648
reed@android.com41bccf52009-04-03 13:33:51 +0000649 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000650 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000651 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000653 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000655 for (int i = 0; i < kCache32Count; i++) {
656 int index = map->mapUnit16((i << 8) | i) >> 8;
657 mapped[i] = linear[index];
658 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000659 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000660 fCache32PixelRef->unref();
661 fCache32PixelRef = newPR;
662 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 }
664 }
665 return fCache32;
666}
667
reed@google.comdc731fd2010-12-23 15:19:47 +0000668/*
669 * Because our caller might rebuild the same (logically the same) gradient
670 * over and over, we'd like to return exactly the same "bitmap" if possible,
671 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
672 * To do that, we maintain a private cache of built-bitmaps, based on our
673 * colors and positions. Note: we don't try to flatten the fMapper, so if one
674 * is present, we skip the cache for now.
675 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000676void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.com95eed982011-07-05 17:01:56 +0000677 // our caller assumes no external alpha, so we ensure that our cache is
678 // built with 0xFF
679 this->setCacheAlpha(0xFF);
680
reed@google.comdc731fd2010-12-23 15:19:47 +0000681 // don't have a way to put the mapper into our cache-key yet
682 if (fMapper) {
683 // force our cahce32pixelref to be built
684 (void)this->getCache32();
685 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
686 bitmap->setPixelRef(fCache32PixelRef);
687 return;
688 }
689
690 // build our key: [numColors + colors[] + {positions[]} ]
691 int count = 1 + fColorCount;
692 if (fColorCount > 2) {
693 count += fColorCount - 1; // fRecs[].fPos
694 }
695
696 SkAutoSTMalloc<16, int32_t> storage(count);
697 int32_t* buffer = storage.get();
698
699 *buffer++ = fColorCount;
700 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
701 buffer += fColorCount;
702 if (fColorCount > 2) {
703 for (int i = 1; i < fColorCount; i++) {
704 *buffer++ = fRecs[i].fPos;
705 }
706 }
707 SkASSERT(buffer - storage.get() == count);
708
709 ///////////////////////////////////
710
711 static SkMutex gMutex;
712 static SkBitmapCache* gCache;
713 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
714 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
715 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000716
reed@google.comdc731fd2010-12-23 15:19:47 +0000717 if (NULL == gCache) {
718 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
719 }
720 size_t size = count * sizeof(int32_t);
721
722 if (!gCache->find(storage.get(), size, bitmap)) {
723 // force our cahce32pixelref to be built
724 (void)this->getCache32();
725 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
726 bitmap->setPixelRef(fCache32PixelRef);
727
728 gCache->add(storage.get(), size, *bitmap);
729 }
730}
731
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000732void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
733 if (info) {
734 if (info->fColorCount >= fColorCount) {
735 if (info->fColors) {
736 memcpy(info->fColors, fOrigColors,
737 fColorCount * sizeof(SkColor));
738 }
739 if (info->fColorOffsets) {
740 if (fColorCount == 2) {
741 info->fColorOffsets[0] = 0;
742 info->fColorOffsets[1] = SK_Scalar1;
743 } else if (fColorCount > 2) {
744 for (int i = 0; i < fColorCount; i++)
745 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
746 }
747 }
748 }
749 info->fColorCount = fColorCount;
750 info->fTileMode = fTileMode;
751 }
752}
753
reed@google.com61eb0402011-04-15 12:11:12 +0000754///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755
reed@android.com41bccf52009-04-03 13:33:51 +0000756static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 SkVector vec = pts[1] - pts[0];
758 SkScalar mag = vec.length();
759 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
760
761 vec.scale(inv);
762 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
763 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
764 matrix->postScale(inv, inv);
765}
766
767///////////////////////////////////////////////////////////////////////////////
768
769class Linear_Gradient : public Gradient_Shader {
770public:
771 Linear_Gradient(const SkPoint pts[2],
772 const SkColor colors[], const SkScalar pos[], int colorCount,
773 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000774 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
775 fStart(pts[0]),
776 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 {
778 pts_to_unit_matrix(pts, &fPtsToUnit);
779 }
reed@android.com9b46e772009-06-05 12:24:41 +0000780
reed@android.com5119bdb2009-06-12 21:27:03 +0000781 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
783 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000784 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*,
reed@google.com7c2f27d2011-03-07 19:29:00 +0000785 TileMode*, SkScalar* twoPointRadialParams) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000786 virtual GradientType asAGradient(GradientInfo* info) const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787
reed@google.com55b8e8c2011-01-13 16:22:35 +0000788 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 return SkNEW_ARGS(Linear_Gradient, (buffer));
790 }
791
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000792 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
793 this->INHERITED::flatten(buffer);
794 buffer.writeScalar(fStart.fX);
795 buffer.writeScalar(fStart.fY);
796 buffer.writeScalar(fEnd.fX);
797 buffer.writeScalar(fEnd.fY);
798 }
799
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000801 Linear_Gradient(SkFlattenableReadBuffer& buffer)
802 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +0000803 fStart(unflatten_point(buffer)),
804 fEnd(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000805 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 virtual Factory getFactory() { return CreateProc; }
807
808private:
809 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000810 const SkPoint fStart;
811 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812};
813
reed@android.com5119bdb2009-06-12 21:27:03 +0000814bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
815 const SkMatrix& matrix) {
816 if (!this->INHERITED::setContext(device, paint, matrix)) {
817 return false;
818 }
819
820 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
821 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000822 fFlags |= SkShader::kConstInY32_Flag;
823 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
824 // only claim this if we do have a 16bit mode (i.e. none of our
825 // colors have alpha), and if we are not dithering (which obviously
826 // is not const in Y).
827 fFlags |= SkShader::kConstInY16_Flag;
828 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000829 }
830 return true;
831}
832
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@google.com61eb0402011-04-15 12:11:12 +0000834static inline bool no_need_for_clamp(int fx, int dx, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 SkASSERT(count > 0);
836 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
837}
838
reed@google.com5eb158d2011-04-15 15:50:34 +0000839#include "SkClampRange.h"
840
841#define NO_CHECK_ITER \
reed@google.com13659f12011-04-18 19:59:38 +0000842 do { \
843 unsigned fi = fx >> 8; \
reed@google.com5eb158d2011-04-15 15:50:34 +0000844 SkASSERT(fi <= 0xFF); \
845 fx += dx; \
846 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +0000847 toggle ^= TOGGLE_MASK; \
848 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +0000849
850
reed@google.com61eb0402011-04-15 12:11:12 +0000851void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 SkASSERT(count > 0);
853
854 SkPoint srcPt;
855 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
856 TileProc proc = fTileProc;
857 const SkPMColor* cache = this->getCache32();
reed@google.com55b8e8c2011-01-13 16:22:35 +0000858#ifdef USE_DITHER_32BIT_GRADIENT
859 int toggle = ((x ^ y) & 1) << kCache32Bits;
860 const int TOGGLE_MASK = (1 << kCache32Bits);
861#else
862 int toggle = 0;
863 const int TOGGLE_MASK = 0;
864#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865
reed@android.comc552a432009-06-12 20:02:50 +0000866 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000867 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
868 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
870
reed@android.comc552a432009-06-12 20:02:50 +0000871 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 SkFixed dxStorage[1];
873 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
874 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000875 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
877 dx = SkScalarToFixed(fDstToIndex.getScaleX());
878 }
879
reed@android.comc552a432009-06-12 20:02:50 +0000880 if (SkFixedNearlyZero(dx)) {
881 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 unsigned fi = proc(fx);
883 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000884 // TODO: dither version
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000886 } else if (proc == clamp_tileproc) {
reed@google.com17705072011-04-18 12:43:32 +0000887#ifdef SK_ENABLE_FAST_LINEAR_GRADIENTS
reed@google.com5eb158d2011-04-15 15:50:34 +0000888 SkClampRange range;
889 range.init(fx, dx, count, 0, 0xFF);
890
891 if ((count = range.fCount0) > 0) {
892 sk_memset32_dither(dstC,
893 cache[toggle + range.fV0],
894 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
895 count);
896 dstC += count;
897 }
898 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +0000899 int unroll = count >> 3;
900 fx = range.fFx1;
901 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000902 NO_CHECK_ITER; NO_CHECK_ITER;
903 NO_CHECK_ITER; NO_CHECK_ITER;
904 NO_CHECK_ITER; NO_CHECK_ITER;
905 NO_CHECK_ITER; NO_CHECK_ITER;
906 }
907 if ((count &= 7) > 0) {
908 do {
909 NO_CHECK_ITER;
910 } while (--count != 0);
911 }
912 }
913 if ((count = range.fCount2) > 0) {
914 sk_memset32_dither(dstC,
915 cache[toggle + range.fV1],
916 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
917 count);
918 }
reed@google.com17705072011-04-18 12:43:32 +0000919#else
920 do {
921 unsigned fi = SkClampMax(fx >> 8, 0xFF);
922 SkASSERT(fi <= 0xFF);
923 fx += dx;
924 *dstC++ = cache[toggle + fi];
925 toggle ^= TOGGLE_MASK;
926 } while (--count != 0);
reed@google.com5eb158d2011-04-15 15:50:34 +0000927#endif
reed@android.comc552a432009-06-12 20:02:50 +0000928 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 do {
930 unsigned fi = mirror_8bits(fx >> 8);
931 SkASSERT(fi <= 0xFF);
932 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000933 *dstC++ = cache[toggle + fi];
934 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000936 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 SkASSERT(proc == repeat_tileproc);
938 do {
939 unsigned fi = repeat_8bits(fx >> 8);
940 SkASSERT(fi <= 0xFF);
941 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000942 *dstC++ = cache[toggle + fi];
943 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 } while (--count != 0);
945 }
reed@android.comc552a432009-06-12 20:02:50 +0000946 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 SkScalar dstX = SkIntToScalar(x);
948 SkScalar dstY = SkIntToScalar(y);
949 do {
950 dstProc(fDstToIndex, dstX, dstY, &srcPt);
951 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
952 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000953 *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))];
954 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 dstX += SK_Scalar1;
956 } while (--count != 0);
957 }
958}
959
reed@google.com55b8e8c2011-01-13 16:22:35 +0000960SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000961 SkMatrix* matrix,
962 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +0000963 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000965 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 }
967 if (matrix) {
968 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
969 matrix->preConcat(fPtsToUnit);
970 }
971 if (xy) {
972 xy[0] = fTileMode;
973 xy[1] = kClamp_TileMode;
974 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000975 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976}
977
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000978SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
979 if (info) {
980 commonAsAGradient(info);
981 info->fPoint[0] = fStart;
982 info->fPoint[1] = fEnd;
983 }
984 return kLinear_GradientType;
985}
986
reed@android.com3c9b2a42009-08-27 19:28:37 +0000987static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
988 int count) {
989 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 *dst++ = value;
991 count -= 1;
992 SkTSwap(value, other);
993 }
994
995 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000996
reed@android.com3c9b2a42009-08-27 19:28:37 +0000997 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000999 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001
reed@google.com5eb158d2011-04-15 15:50:34 +00001002#define NO_CHECK_ITER_16 \
reed@google.com13659f12011-04-18 19:59:38 +00001003 do { \
1004 unsigned fi = fx >> kCache16Shift; \
reed@google.com5eb158d2011-04-15 15:50:34 +00001005 SkASSERT(fi <= kCache16Mask); \
1006 fx += dx; \
1007 *dstC++ = cache[toggle + fi]; \
reed@google.com13659f12011-04-18 19:59:38 +00001008 toggle ^= TOGGLE_MASK; \
1009 } while (0)
reed@google.com5eb158d2011-04-15 15:50:34 +00001010
1011
reed@google.com61eb0402011-04-15 12:11:12 +00001012void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 SkASSERT(count > 0);
1014
1015 SkPoint srcPt;
1016 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1017 TileProc proc = fTileProc;
1018 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@google.com5eb158d2011-04-15 15:50:34 +00001020 const int TOGGLE_MASK = (1 << kCache32Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001022 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001023 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1024 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1026
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001027 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 SkFixed dxStorage[1];
1029 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1030 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001031 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1033 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1034 }
1035
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001036 if (SkFixedNearlyZero(dx)) {
1037 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +00001038 unsigned fi = proc(fx) >> kCache16Shift;
1039 SkASSERT(fi <= kCache16Mask);
reed@google.com5eb158d2011-04-15 15:50:34 +00001040 dither_memset16(dstC, cache[toggle + fi],
1041 cache[(toggle ^ TOGGLE_MASK) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001042 } else if (proc == clamp_tileproc) {
reed@google.com17705072011-04-18 12:43:32 +00001043#ifdef SK_ENABLE_FAST_LINEAR_GRADIENTS
reed@google.com5eb158d2011-04-15 15:50:34 +00001044 SkClampRange range;
1045 range.init(fx, dx, count, 0, kCache16Mask);
1046
1047 if ((count = range.fCount0) > 0) {
1048 dither_memset16(dstC,
1049 cache[toggle + range.fV0],
1050 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
1051 count);
1052 dstC += count;
1053 }
1054 if ((count = range.fCount1) > 0) {
reed@google.com13659f12011-04-18 19:59:38 +00001055 int unroll = count >> 3;
1056 fx = range.fFx1;
1057 for (int i = 0; i < unroll; i++) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001058 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1059 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1060 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1061 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1062 }
1063 if ((count &= 7) > 0) {
1064 do {
1065 NO_CHECK_ITER_16;
1066 } while (--count != 0);
1067 }
1068 }
1069 if ((count = range.fCount2) > 0) {
1070 dither_memset16(dstC,
1071 cache[toggle + range.fV1],
1072 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
1073 count);
1074 }
reed@google.com17705072011-04-18 12:43:32 +00001075#else
1076 do {
1077 unsigned fi = SkClampMax(fx >> kCache16Shift, kCache16Mask);
1078 SkASSERT(fi <= kCache16Mask);
1079 fx += dx;
1080 *dstC++ = cache[toggle + fi];
1081 toggle ^= TOGGLE_MASK;
1082 } while (--count != 0);
reed@google.com5eb158d2011-04-15 15:50:34 +00001083#endif
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001084 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 do {
reed@android.com200645d2009-12-14 16:41:57 +00001086 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001087 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001090 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001092 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 SkASSERT(proc == repeat_tileproc);
1094 do {
reed@android.com200645d2009-12-14 16:41:57 +00001095 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001096 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001099 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 } while (--count != 0);
1101 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001102 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 SkScalar dstX = SkIntToScalar(x);
1104 SkScalar dstY = SkIntToScalar(y);
1105 do {
1106 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1107 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1108 SkASSERT(fi <= 0xFFFF);
1109
reed@android.com512a8762009-12-14 15:25:36 +00001110 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 *dstC++ = cache[toggle + index];
reed@google.com5eb158d2011-04-15 15:50:34 +00001112 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113
1114 dstX += SK_Scalar1;
1115 } while (--count != 0);
1116 }
1117}
1118
1119///////////////////////////////////////////////////////////////////////////////
1120
1121#define kSQRT_TABLE_BITS 11
1122#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1123
1124#include "SkRadialGradient_Table.h"
1125
1126#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1127
1128#include <stdio.h>
1129
reed@google.com61eb0402011-04-15 12:11:12 +00001130void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1132
1133 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1134 SkASSERT(file);
1135 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1136
reed@google.com61eb0402011-04-15 12:11:12 +00001137 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1138 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001140 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141
1142 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1143
1144 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001145 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001147 }
1148 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001150 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 }
1152 ::fprintf(file, "};\n");
1153 ::fclose(file);
1154}
1155
1156#endif
1157
1158
reed@google.com61eb0402011-04-15 12:11:12 +00001159static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1160 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 SkScalar inv = SkScalarInvert(radius);
1162
1163 matrix->setTranslate(-center.fX, -center.fY);
1164 matrix->postScale(inv, inv);
1165}
1166
1167class Radial_Gradient : public Gradient_Shader {
1168public:
1169 Radial_Gradient(const SkPoint& center, SkScalar radius,
1170 const SkColor colors[], const SkScalar pos[], int colorCount,
1171 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001172 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1173 fCenter(center),
1174 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 {
1176 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1177 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1178
1179 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1180 }
reed@google.com61eb0402011-04-15 12:11:12 +00001181
1182 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183 SkASSERT(count > 0);
1184
1185 SkPoint srcPt;
1186 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1187 TileProc proc = fTileProc;
1188 const SkPMColor* cache = this->getCache32();
1189
reed@google.com61eb0402011-04-15 12:11:12 +00001190 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001191 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1192 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1194 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1195
reed@google.com61eb0402011-04-15 12:11:12 +00001196 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 SkFixed storage[2];
1198 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1199 dx = storage[0];
1200 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00001201 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1203 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1204 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1205 }
1206
reed@google.com61eb0402011-04-15 12:11:12 +00001207 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 const uint8_t* sqrt_table = gSqrt8Table;
1209 fx >>= 1;
1210 dx >>= 1;
1211 fy >>= 1;
1212 dy >>= 1;
1213 do {
1214 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1215 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1216 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1217 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1218 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1219 fx += dx;
1220 fy += dy;
1221 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001222 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001224 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1225 if (magnitudeSquared < 0) // Overflow.
1226 magnitudeSquared = SK_FixedMax;
1227 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 unsigned fi = mirror_tileproc(dist);
1229 SkASSERT(fi <= 0xFFFF);
1230 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1231 fx += dx;
1232 fy += dy;
1233 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001234 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 SkASSERT(proc == repeat_tileproc);
1236 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001237 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1238 if (magnitudeSquared < 0) // Overflow.
1239 magnitudeSquared = SK_FixedMax;
1240 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 unsigned fi = repeat_tileproc(dist);
1242 SkASSERT(fi <= 0xFFFF);
1243 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1244 fx += dx;
1245 fy += dy;
1246 } while (--count != 0);
1247 }
reed@google.com61eb0402011-04-15 12:11:12 +00001248 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 SkScalar dstX = SkIntToScalar(x);
1250 SkScalar dstY = SkIntToScalar(y);
1251 do {
1252 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1253 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1254 SkASSERT(fi <= 0xFFFF);
1255 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1256 dstX += SK_Scalar1;
1257 } while (--count != 0);
1258 }
1259 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001260
1261 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 SkASSERT(count > 0);
1263
1264 SkPoint srcPt;
1265 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1266 TileProc proc = fTileProc;
1267 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269
reed@android.com3c9b2a42009-08-27 19:28:37 +00001270 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001271 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1272 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1274 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1275
reed@android.com3c9b2a42009-08-27 19:28:37 +00001276 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277 SkFixed storage[2];
1278 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1279 dx = storage[0];
1280 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001281 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1283 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1284 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1285 }
1286
reed@android.com3c9b2a42009-08-27 19:28:37 +00001287 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 const uint8_t* sqrt_table = gSqrt8Table;
1289
1290 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1291 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1292 precision, but that appears to be visually OK. If we decide this is OK for
1293 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1294 to avoid having to do these extra shifts each time.
1295 */
1296 fx >>= 1;
1297 dx >>= 1;
1298 fy >>= 1;
1299 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001300 if (dy == 0) { // might perform this check for the other modes, but the win will be a smaller % of the total
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1302 fy *= fy;
1303 do {
1304 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1305 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1306 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1307 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1309 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001311 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 do {
1313 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1314 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1315 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1316 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1317 fx += dx;
1318 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1320 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 } while (--count != 0);
1322 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001323 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 do {
1325 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1326 unsigned fi = mirror_tileproc(dist);
1327 SkASSERT(fi <= 0xFFFF);
1328 fx += dx;
1329 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1331 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001333 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 SkASSERT(proc == repeat_tileproc);
1335 do {
1336 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1337 unsigned fi = repeat_tileproc(dist);
1338 SkASSERT(fi <= 0xFFFF);
1339 fx += dx;
1340 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1342 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 } while (--count != 0);
1344 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001345 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 SkScalar dstX = SkIntToScalar(x);
1347 SkScalar dstY = SkIntToScalar(y);
1348 do {
1349 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1350 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1351 SkASSERT(fi <= 0xFFFF);
1352
1353 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 *dstC++ = cache[toggle + index];
1355 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356
1357 dstX += SK_Scalar1;
1358 } while (--count != 0);
1359 }
1360 }
1361
reed@google.com55b8e8c2011-01-13 16:22:35 +00001362 virtual BitmapType asABitmap(SkBitmap* bitmap,
1363 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001364 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001365 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001366 if (bitmap) {
1367 this->commonAsABitmap(bitmap);
1368 }
1369 if (matrix) {
1370 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1371 matrix->preConcat(fPtsToUnit);
1372 }
1373 if (xy) {
1374 xy[0] = fTileMode;
1375 xy[1] = kClamp_TileMode;
1376 }
1377 return kRadial_BitmapType;
1378 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001379 virtual GradientType asAGradient(GradientInfo* info) const {
1380 if (info) {
1381 commonAsAGradient(info);
1382 info->fPoint[0] = fCenter;
1383 info->fRadius[0] = fRadius;
1384 }
1385 return kRadial_GradientType;
1386 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001387
1388 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 return SkNEW_ARGS(Radial_Gradient, (buffer));
1390 }
1391
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001392 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1393 this->INHERITED::flatten(buffer);
1394 buffer.writeScalar(fCenter.fX);
1395 buffer.writeScalar(fCenter.fY);
1396 buffer.writeScalar(fRadius);
1397 }
1398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001400 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1401 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001402 fCenter(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001403 fRadius(buffer.readScalar()) {
1404 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 virtual Factory getFactory() { return CreateProc; }
1406
1407private:
1408 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001409 const SkPoint fCenter;
1410 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411};
1412
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001413/* Two-point radial gradients are specified by two circles, each with a center
1414 point and radius. The gradient can be considered to be a series of
1415 concentric circles, with the color interpolated from the start circle
1416 (at t=0) to the end circle (at t=1).
1417
1418 For each point (x, y) in the span, we want to find the
1419 interpolated circle that intersects that point. The center
1420 of the desired circle (Cx, Cy) falls at some distance t
1421 along the line segment between the start point (Sx, Sy) and
1422 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001423
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001424 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1425 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001426
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001427 The radius of the desired circle (r) is also a linear interpolation t
1428 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001429
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001430 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001431
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001432 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001433
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001434 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001435
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001436 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001437
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001438 (x - ((1 - t) * Sx + t * Ex))^2
1439 + (y - ((1 - t) * Sy + t * Ey))^2
1440 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001441
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001442 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001443
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001444 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1445 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1446 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001447
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001448 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1449
1450 [Dx^2 + Dy^2 - Dr^2)] * t^2
1451 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1452 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001453
1454 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001455 possible circles on which the point may fall. Solving for t yields
1456 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001457
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001458 If a<0, the start circle is entirely contained in the
1459 end circle, and one of the roots will be <0 or >1 (off the line
1460 segment). If a>0, the start circle falls at least partially
1461 outside the end circle (or vice versa), and the gradient
1462 defines a "tube" where a point may be on one circle (on the
1463 inside of the tube) or the other (outside of the tube). We choose
1464 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001465
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001466 In order to keep the math to within the limits of fixed point,
1467 we divide the entire quadratic by Dr^2, and replace
1468 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001469
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001470 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1471 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1472 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001473
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001474 (x' and y' are computed by appending the subtract and scale to the
1475 fDstToIndex matrix in the constructor).
1476
1477 Since the 'A' component of the quadratic is independent of x' and y', it
1478 is precomputed in the constructor. Since the 'B' component is linear in
1479 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001480 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001481 a perspective projection), it must be computed in the loop.
1482
1483*/
1484
reed@google.come61414c2011-04-15 18:14:16 +00001485#ifdef SK_USE_SLOW_2POINT_RADIAL_GRADIENT
1486static inline SkFixed two_point_radial(SkFixed b, SkFixed fx, SkFixed fy, SkFixed sr2d2, SkFixed foura, SkFixed oneOverTwoA, bool posRoot) {
1487 SkFixed c = SkFixedSquare(fx) + SkFixedSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001488 if (0 == foura) {
1489 return SkFixedDiv(-c, b);
1490 }
reed@google.come61414c2011-04-15 18:14:16 +00001491 SkFixed discrim = SkFixedSquare(b) - SkFixedMul(foura, c);
1492 if (discrim < 0) {
1493 discrim = -discrim;
1494 }
1495 SkFixed rootDiscrim = SkFixedSqrt(discrim);
1496 if (posRoot) {
1497 return SkFixedMul(-b + rootDiscrim, oneOverTwoA);
1498 } else {
1499 return SkFixedMul(-b - rootDiscrim, oneOverTwoA);
1500 }
1501}
1502#else
reed@google.com84e9c082011-04-13 17:44:24 +00001503static inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1504 SkScalar sr2d2, SkScalar foura,
1505 SkScalar oneOverTwoA, bool posRoot) {
1506 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
reed@google.com8871a842011-06-23 15:07:04 +00001507 if (0 == foura) {
1508 return SkScalarToFixed(SkScalarDiv(-c, b));
1509 }
1510
reed@google.com84e9c082011-04-13 17:44:24 +00001511 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001512 if (discrim < 0) {
1513 discrim = -discrim;
1514 }
reed@google.com84e9c082011-04-13 17:44:24 +00001515 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1516 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001517 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001518 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001519 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001520 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001521 }
reed@google.com84e9c082011-04-13 17:44:24 +00001522 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001523}
reed@google.come61414c2011-04-15 18:14:16 +00001524#endif
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001525
1526class Two_Point_Radial_Gradient : public Gradient_Shader {
1527public:
1528 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1529 const SkPoint& end, SkScalar endRadius,
1530 const SkColor colors[], const SkScalar pos[],
1531 int colorCount, SkShader::TileMode mode,
1532 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001533 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1534 fCenter1(start),
1535 fCenter2(end),
1536 fRadius1(startRadius),
1537 fRadius2(endRadius) {
1538 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001539 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001540
1541 virtual BitmapType asABitmap(SkBitmap* bitmap,
1542 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001543 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001544 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001545 if (bitmap) {
1546 this->commonAsABitmap(bitmap);
1547 }
1548 SkScalar diffL = 0; // just to avoid gcc warning
1549 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001550 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001551 SkScalarSquare(fDiff.fY));
1552 }
1553 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001554 if (diffL) {
1555 SkScalar invDiffL = SkScalarInvert(diffL);
1556 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1557 SkScalarMul(invDiffL, fDiff.fX));
1558 } else {
1559 matrix->reset();
1560 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001561 matrix->preConcat(fPtsToUnit);
1562 }
1563 if (xy) {
1564 xy[0] = fTileMode;
1565 xy[1] = kClamp_TileMode;
1566 }
1567 if (NULL != twoPointRadialParams) {
1568 twoPointRadialParams[0] = diffL;
1569 twoPointRadialParams[1] = fStartRadius;
1570 twoPointRadialParams[2] = fDiffRadius;
1571 }
1572 return kTwoPointRadial_BitmapType;
1573 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001574
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001575 virtual GradientType asAGradient(GradientInfo* info) const {
1576 if (info) {
1577 commonAsAGradient(info);
1578 info->fPoint[0] = fCenter1;
1579 info->fPoint[1] = fCenter2;
1580 info->fRadius[0] = fRadius1;
1581 info->fRadius[1] = fRadius2;
1582 }
1583 return kRadial2_GradientType;
1584 }
1585
reed@google.come61414c2011-04-15 18:14:16 +00001586#ifdef SK_USE_SLOW_2POINT_RADIAL_GRADIENT
1587 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1588 {
1589 SkASSERT(count > 0);
1590
1591 // Zero difference between radii: fill with transparent black.
1592 if (fDiffRadius == 0) {
1593 sk_bzero(dstC, count * sizeof(*dstC));
1594 return;
1595 }
1596 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1597 TileProc proc = fTileProc;
1598 const SkPMColor* cache = this->getCache32();
1599 SkFixed diffx = SkScalarToFixed(fDiff.fX);
1600 SkFixed diffy = SkScalarToFixed(fDiff.fY);
1601 SkFixed foura = SkScalarToFixed(SkScalarMul(fA, 4));
1602 SkFixed startRadius = SkScalarToFixed(fStartRadius);
1603 SkFixed sr2D2 = SkScalarToFixed(fSr2D2);
1604 SkFixed oneOverTwoA = SkScalarToFixed(fOneOverTwoA);
1605 bool posRoot = fDiffRadius < 0;
1606 if (fDstToIndexClass != kPerspective_MatrixClass)
1607 {
1608 SkPoint srcPt;
1609 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1610 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1611 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1612 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1613
1614 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1615 {
1616 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &dx, &dy);
1617 }
1618 else
1619 {
1620 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1621 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1622 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1623 }
1624 SkFixed b = (SkFixedMul(diffx, fx) +
1625 SkFixedMul(diffy, fy) - startRadius) << 1;
1626 SkFixed db = (SkFixedMul(diffx, dx) +
1627 SkFixedMul(diffy, dy)) << 1;
1628 if (proc == clamp_tileproc)
1629 {
1630 for (; count > 0; --count) {
1631 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1632 SkFixed index = SkClampMax(t, 0xFFFF);
1633 SkASSERT(index <= 0xFFFF);
1634 *dstC++ = cache[index >> (16 - kCache32Bits)];
1635 fx += dx;
1636 fy += dy;
1637 b += db;
1638 }
1639 }
1640 else if (proc == mirror_tileproc)
1641 {
1642 for (; count > 0; --count) {
1643 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1644 SkFixed index = mirror_tileproc(t);
1645 SkASSERT(index <= 0xFFFF);
1646 *dstC++ = cache[index >> (16 - kCache32Bits)];
1647 fx += dx;
1648 fy += dy;
1649 b += db;
1650 }
1651 }
1652 else
1653 {
1654 SkASSERT(proc == repeat_tileproc);
1655 for (; count > 0; --count) {
1656 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1657 SkFixed index = repeat_tileproc(t);
1658 SkASSERT(index <= 0xFFFF);
1659 *dstC++ = cache[index >> (16 - kCache32Bits)];
1660 fx += dx;
1661 fy += dy;
1662 b += db;
1663 }
1664 }
1665 }
1666 else // perspective case
1667 {
1668 SkScalar dstX = SkIntToScalar(x);
1669 SkScalar dstY = SkIntToScalar(y);
1670 for (; count > 0; --count) {
1671 SkPoint srcPt;
1672 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1673 SkFixed fx = SkScalarToFixed(srcPt.fX);
1674 SkFixed fy = SkScalarToFixed(srcPt.fY);
1675 SkFixed b = (SkFixedMul(diffx, fx) +
1676 SkFixedMul(diffy, fy) - startRadius) << 1;
1677 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1678 SkFixed index = proc(t);
1679 SkASSERT(index <= 0xFFFF);
1680 *dstC++ = cache[index >> (16 - kCache32Bits)];
1681 dstX += SK_Scalar1;
1682 }
1683 }
1684 }
1685#else
reed@google.com61eb0402011-04-15 12:11:12 +00001686 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001687 SkASSERT(count > 0);
1688
1689 // Zero difference between radii: fill with transparent black.
1690 if (fDiffRadius == 0) {
1691 sk_bzero(dstC, count * sizeof(*dstC));
1692 return;
1693 }
1694 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1695 TileProc proc = fTileProc;
1696 const SkPMColor* cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001697
1698 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001699 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001700 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001701 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001702 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1703 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001704 SkScalar dx, fx = srcPt.fX;
1705 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001706
reed@google.com61eb0402011-04-15 12:11:12 +00001707 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001708 SkFixed fixedX, fixedY;
1709 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1710 dx = SkFixedToScalar(fixedX);
1711 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001712 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001713 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001714 dx = fDstToIndex.getScaleX();
1715 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001716 }
reed@google.com84e9c082011-04-13 17:44:24 +00001717 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1718 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1719 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1720 SkScalarMul(fDiff.fY, dy)) * 2;
reed@google.com61eb0402011-04-15 12:11:12 +00001721 if (proc == clamp_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001722 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001723 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001724 SkFixed index = SkClampMax(t, 0xFFFF);
1725 SkASSERT(index <= 0xFFFF);
1726 *dstC++ = cache[index >> (16 - kCache32Bits)];
1727 fx += dx;
1728 fy += dy;
1729 b += db;
1730 }
reed@google.com61eb0402011-04-15 12:11:12 +00001731 } else if (proc == mirror_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001732 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001733 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001734 SkFixed index = mirror_tileproc(t);
1735 SkASSERT(index <= 0xFFFF);
1736 *dstC++ = cache[index >> (16 - kCache32Bits)];
1737 fx += dx;
1738 fy += dy;
1739 b += db;
1740 }
reed@google.com61eb0402011-04-15 12:11:12 +00001741 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001742 SkASSERT(proc == repeat_tileproc);
1743 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001744 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001745 SkFixed index = repeat_tileproc(t);
1746 SkASSERT(index <= 0xFFFF);
1747 *dstC++ = cache[index >> (16 - kCache32Bits)];
1748 fx += dx;
1749 fy += dy;
1750 b += db;
1751 }
1752 }
reed@google.com61eb0402011-04-15 12:11:12 +00001753 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001754 SkScalar dstX = SkIntToScalar(x);
1755 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001756 for (; count > 0; --count) {
1757 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001758 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001759 SkScalar fx = srcPt.fX;
1760 SkScalar fy = srcPt.fY;
1761 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1762 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1763 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001764 SkFixed index = proc(t);
1765 SkASSERT(index <= 0xFFFF);
1766 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001767 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001768 }
1769 }
1770 }
reed@google.come61414c2011-04-15 18:14:16 +00001771#endif
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001772
reed@android.com6c59a172009-09-22 20:24:05 +00001773 virtual bool setContext(const SkBitmap& device,
1774 const SkPaint& paint,
1775 const SkMatrix& matrix) {
1776 if (!this->INHERITED::setContext(device, paint, matrix)) {
1777 return false;
1778 }
1779
1780 // we don't have a span16 proc
1781 fFlags &= ~kHasSpan16_Flag;
1782 return true;
1783 }
1784
reed@google.com55b8e8c2011-01-13 16:22:35 +00001785 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001786 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1787 }
1788
reed@android.combcfc7332009-11-10 16:19:39 +00001789 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1790 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001791 buffer.writeScalar(fCenter1.fX);
1792 buffer.writeScalar(fCenter1.fY);
1793 buffer.writeScalar(fCenter2.fX);
1794 buffer.writeScalar(fCenter2.fY);
1795 buffer.writeScalar(fRadius1);
1796 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001797 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001798
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001799protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001800 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001801 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001802 fCenter1(unflatten_point(buffer)),
1803 fCenter2(unflatten_point(buffer)),
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001804 fRadius1(buffer.readScalar()),
1805 fRadius2(buffer.readScalar()) {
1806 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001807 };
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001808 virtual Factory getFactory() { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001809
1810private:
1811 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001812 const SkPoint fCenter1;
1813 const SkPoint fCenter2;
1814 const SkScalar fRadius1;
1815 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001816 SkPoint fDiff;
1817 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001818
1819 void init() {
1820 fDiff = fCenter1 - fCenter2;
1821 fDiffRadius = fRadius2 - fRadius1;
1822 SkScalar inv = SkScalarInvert(fDiffRadius);
1823 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1824 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1825 fStartRadius = SkScalarMul(fRadius1, inv);
1826 fSr2D2 = SkScalarSquare(fStartRadius);
1827 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
reed@google.com21031e92011-06-27 18:38:27 +00001828 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001829
1830 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1831 fPtsToUnit.postScale(inv, inv);
1832 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001833};
1834
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835///////////////////////////////////////////////////////////////////////////////
1836
1837class Sweep_Gradient : public Gradient_Shader {
1838public:
1839 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1840 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001841 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
1842 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843 {
1844 fPtsToUnit.setTranslate(-cx, -cy);
1845 }
1846 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1847 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001848
1849 virtual BitmapType asABitmap(SkBitmap* bitmap,
1850 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001851 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001852 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001853 if (bitmap) {
1854 this->commonAsABitmap(bitmap);
1855 }
1856 if (matrix) {
1857 *matrix = fPtsToUnit;
1858 }
1859 if (xy) {
1860 xy[0] = fTileMode;
1861 xy[1] = kClamp_TileMode;
1862 }
1863 return kSweep_BitmapType;
1864 }
1865
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001866 virtual GradientType asAGradient(GradientInfo* info) const {
1867 if (info) {
1868 commonAsAGradient(info);
1869 info->fPoint[0] = fCenter;
1870 }
1871 return kSweep_GradientType;
1872 }
1873
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1875 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1876 }
1877
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001878 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1879 this->INHERITED::flatten(buffer);
1880 buffer.writeScalar(fCenter.fX);
1881 buffer.writeScalar(fCenter.fY);
1882 }
1883
reed@android.com8a1c16f2008-12-17 15:59:43 +00001884protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001885 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
1886 : Gradient_Shader(buffer),
tomhudson@google.com9ce767c2011-04-25 20:49:39 +00001887 fCenter(unflatten_point(buffer)) {
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001888 }
1889
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 virtual Factory getFactory() { return CreateProc; }
1891
1892private:
1893 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001894 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895};
1896
1897#ifdef COMPUTE_SWEEP_TABLE
1898#define PI 3.14159265
1899static bool gSweepTableReady;
1900static uint8_t gSweepTable[65];
1901
1902/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1903 We scale the results to [0..32]
1904*/
reed@google.com61eb0402011-04-15 12:11:12 +00001905static const uint8_t* build_sweep_table() {
1906 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907 const int N = 65;
1908 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001909
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910 for (int i = 0; i < N; i++)
1911 {
1912 double arg = i / DENOM;
1913 double v = atan(arg);
1914 int iv = (int)round(v * DENOM * 2 / PI);
1915// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1916 printf("%d, ", iv);
1917 gSweepTable[i] = iv;
1918 }
1919 gSweepTableReady = true;
1920 }
1921 return gSweepTable;
1922}
1923#else
1924static const uint8_t gSweepTable[] = {
1925 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1926 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1927 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1928 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1929 32
1930};
1931static const uint8_t* build_sweep_table() { return gSweepTable; }
1932#endif
1933
1934// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1935// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1936// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1937
1938//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00001939static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940 SkASSERT(numer <= denom);
1941 SkASSERT(numer > 0);
1942 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001943
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 int nbits = SkCLZ(numer);
1945 int dbits = SkCLZ(denom);
1946 int bits = 6 - nbits + dbits;
1947 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001948
reed@google.com61eb0402011-04-15 12:11:12 +00001949 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00001950 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001951 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952
1953 denom <<= dbits - 1;
1954 numer <<= nbits - 1;
1955
1956 unsigned result = 0;
1957
1958 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00001959 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001960 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00001961 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00001963 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001964
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00001966 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967 // make room for the rest of the answer bits
1968 result <<= bits;
1969 switch (bits) {
1970 case 6:
1971 if ((numer = (numer << 1) - denom) >= 0)
1972 result |= 32;
1973 else
1974 numer += denom;
1975 case 5:
1976 if ((numer = (numer << 1) - denom) >= 0)
1977 result |= 16;
1978 else
1979 numer += denom;
1980 case 4:
1981 if ((numer = (numer << 1) - denom) >= 0)
1982 result |= 8;
1983 else
1984 numer += denom;
1985 case 3:
1986 if ((numer = (numer << 1) - denom) >= 0)
1987 result |= 4;
1988 else
1989 numer += denom;
1990 case 2:
1991 if ((numer = (numer << 1) - denom) >= 0)
1992 result |= 2;
1993 else
1994 numer += denom;
1995 case 1:
1996 default: // not strictly need, but makes GCC make better ARM code
1997 if ((numer = (numer << 1) - denom) >= 0)
1998 result |= 1;
1999 else
2000 numer += denom;
2001 }
2002 }
2003 return result;
2004}
2005
2006// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00002007static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008#ifdef SK_DEBUG
2009 {
2010 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00002011 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 gOnce = true;
2013 SkASSERT(div_64(55, 55) == 64);
2014 SkASSERT(div_64(128, 256) == 32);
2015 SkASSERT(div_64(2326528, 4685824) == 31);
2016 SkASSERT(div_64(753664, 5210112) == 9);
2017 SkASSERT(div_64(229376, 4882432) == 3);
2018 SkASSERT(div_64(2, 64) == 2);
2019 SkASSERT(div_64(1, 64) == 1);
2020 // test that we handle underflow correctly
2021 SkASSERT(div_64(12345, 0x54321234) == 0);
2022 }
2023 }
2024#endif
2025
2026 SkASSERT(y > 0 && x > 0);
2027 const uint8_t* table = build_sweep_table();
2028
2029 unsigned result;
2030 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00002031 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 // first part of the atan(v) = PI/2 - atan(1/v) identity
2033 // since our div_64 and table want v <= 1, where v = y/x
2034 SkTSwap<SkFixed>(x, y);
2035 }
2036
2037 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002038
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039#ifdef SK_DEBUG
2040 {
2041 unsigned result2 = SkDivBits(y, x, 6);
2042 SkASSERT(result2 == result ||
2043 (result == 1 && result2 == 0));
2044 }
2045#endif
2046
2047 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2048 result = table[result];
2049
reed@google.com61eb0402011-04-15 12:11:12 +00002050 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051 // complete the atan(v) = PI/2 - atan(1/v) identity
2052 result = 64 - result;
2053 // pin to 63
2054 result -= result >> 6;
2055 }
2056
2057 SkASSERT(result <= 63);
2058 return result;
2059}
2060
2061// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com61eb0402011-04-15 12:11:12 +00002062static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2063 if (x == 0) {
2064 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002065 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002066 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067 return y < 0 ? 192 : 64;
2068 }
reed@google.com61eb0402011-04-15 12:11:12 +00002069 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002071 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002072
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 /* Find the right quadrant for x,y
2074 Since atan_0_90 only handles the first quadrant, we rotate x,y
2075 appropriately before calling it, and then add the right amount
2076 to account for the real quadrant.
2077 quadrant 0 : add 0 | x > 0 && y > 0
2078 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2079 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2080 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002081
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082 map x<0 to (1 << 6)
2083 map y<0 to (3 << 6)
2084 add = map_x ^ map_y
2085 */
2086 int xsign = x >> 31;
2087 int ysign = y >> 31;
2088 int add = ((-xsign) ^ (ysign & 3)) << 6;
2089
2090#ifdef SK_DEBUG
2091 if (0 == add)
2092 SkASSERT(x > 0 && y > 0);
2093 else if (64 == add)
2094 SkASSERT(x < 0 && y > 0);
2095 else if (128 == add)
2096 SkASSERT(x < 0 && y < 0);
2097 else if (192 == add)
2098 SkASSERT(x > 0 && y < 0);
2099 else
2100 SkASSERT(!"bad value for add");
2101#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002102
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2104 where we need to rotate x,y by 90 or -90
2105 */
2106 x = (x ^ xsign) - xsign;
2107 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002108 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111
2112 unsigned result = add + atan_0_90(y, x);
2113 SkASSERT(result < 256);
2114 return result;
2115}
2116
reed@google.com61eb0402011-04-15 12:11:12 +00002117void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118 SkMatrix::MapXYProc proc = fDstToIndexProc;
2119 const SkMatrix& matrix = fDstToIndex;
2120 const SkPMColor* cache = this->getCache32();
2121 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002122
reed@google.com61eb0402011-04-15 12:11:12 +00002123 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2125 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2126 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
2127 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002128
reed@google.com61eb0402011-04-15 12:11:12 +00002129 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130 SkFixed storage[2];
2131 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2132 &storage[0], &storage[1]);
2133 dx = storage[0];
2134 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00002135 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
2137 dx = SkScalarToFixed(matrix.getScaleX());
2138 dy = SkScalarToFixed(matrix.getSkewY());
2139 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002140
reed@google.com61eb0402011-04-15 12:11:12 +00002141 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 *dstC++ = cache[SkATan2_255(fy, fx)];
2143 fx += dx;
2144 fy += dy;
2145 }
reed@google.com61eb0402011-04-15 12:11:12 +00002146 } else { // perspective case
2147 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2149 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002150
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
2152 SkScalarToFixed(srcPt.fX));
2153 *dstC++ = cache[index];
2154 }
2155 }
2156}
2157
reed@google.com61eb0402011-04-15 12:11:12 +00002158void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 SkMatrix::MapXYProc proc = fDstToIndexProc;
2160 const SkMatrix& matrix = fDstToIndex;
2161 const uint16_t* cache = this->getCache16();
2162 int toggle = ((x ^ y) & 1) << kCache16Bits;
2163 SkPoint srcPt;
2164
reed@google.com61eb0402011-04-15 12:11:12 +00002165 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2167 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2168 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
2169 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002170
reed@google.com61eb0402011-04-15 12:11:12 +00002171 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 SkFixed storage[2];
2173 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2174 &storage[0], &storage[1]);
2175 dx = storage[0];
2176 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00002177 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
2179 dx = SkScalarToFixed(matrix.getScaleX());
2180 dy = SkScalarToFixed(matrix.getSkewY());
2181 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002182
reed@google.com61eb0402011-04-15 12:11:12 +00002183 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2185 *dstC++ = cache[toggle + index];
2186 toggle ^= (1 << kCache16Bits);
2187 fx += dx;
2188 fy += dy;
2189 }
reed@google.com61eb0402011-04-15 12:11:12 +00002190 } else { // perspective case
2191 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2193 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002194
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
2196 SkScalarToFixed(srcPt.fX));
2197 index >>= (8 - kCache16Bits);
2198 *dstC++ = cache[toggle + index];
2199 toggle ^= (1 << kCache16Bits);
2200 }
2201 }
2202}
2203
reed@google.com61eb0402011-04-15 12:11:12 +00002204///////////////////////////////////////////////////////////////////////////////
2205///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206
2207// assumes colors is SkColor* and pos is SkScalar*
2208#define EXPAND_1_COLOR(count) \
2209 SkColor tmp[2]; \
2210 do { \
2211 if (1 == count) { \
2212 tmp[0] = tmp[1] = colors[0]; \
2213 colors = tmp; \
2214 pos = NULL; \
2215 count = 2; \
2216 } \
2217 } while (0)
2218
reed@google.com61eb0402011-04-15 12:11:12 +00002219SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2220 const SkColor colors[],
2221 const SkScalar pos[], int colorCount,
2222 SkShader::TileMode mode,
2223 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224 if (NULL == pts || NULL == colors || colorCount < 1) {
2225 return NULL;
2226 }
2227 EXPAND_1_COLOR(colorCount);
2228
reed@android.comab840b82009-07-01 17:00:03 +00002229 return SkNEW_ARGS(Linear_Gradient,
2230 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231}
2232
reed@google.com61eb0402011-04-15 12:11:12 +00002233SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2234 const SkColor colors[],
2235 const SkScalar pos[], int colorCount,
2236 SkShader::TileMode mode,
2237 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238 if (radius <= 0 || NULL == colors || colorCount < 1) {
2239 return NULL;
2240 }
2241 EXPAND_1_COLOR(colorCount);
2242
reed@android.comab840b82009-07-01 17:00:03 +00002243 return SkNEW_ARGS(Radial_Gradient,
2244 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245}
2246
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002247SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2248 SkScalar startRadius,
2249 const SkPoint& end,
2250 SkScalar endRadius,
2251 const SkColor colors[],
2252 const SkScalar pos[],
2253 int colorCount,
2254 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002255 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002256 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2257 return NULL;
2258 }
2259 EXPAND_1_COLOR(colorCount);
2260
2261 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002262 (start, startRadius, end, endRadius, colors, pos,
2263 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002264}
2265
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2267 const SkColor colors[],
2268 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002269 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 if (NULL == colors || count < 1) {
2271 return NULL;
2272 }
2273 EXPAND_1_COLOR(count);
2274
2275 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2276}
2277
2278static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
2279 Linear_Gradient::CreateProc);
2280
2281static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
2282 Radial_Gradient::CreateProc);
2283
2284static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
2285 Sweep_Gradient::CreateProc);
tomhudson@google.com938d6042011-04-27 13:57:03 +00002286
2287static SkFlattenable::Registrar
2288 gTwoPointRadialGradientReg("Two_Point_Radial_Gradient",
2289 Two_Point_Radial_Gradient::CreateProc);