blob: 41b97cc4c3d7a8f58e925c372ea5f6709ebf9664 [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@android.com8a1c16f2008-12-17 15:59:43 +000030///////////////////////////////////////////////////////////////////////////
31
32typedef SkFixed (*TileProc)(SkFixed);
33
reed@android.com41bccf52009-04-03 13:33:51 +000034static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000035 return SkClampMax(x, 0xFFFF);
36}
37
reed@android.com41bccf52009-04-03 13:33:51 +000038static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 return x & 0xFFFF;
40}
41
reed@android.com41bccf52009-04-03 13:33:51 +000042static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000043 int s = x << 15 >> 31;
44 return (x ^ s) & 0xFFFF;
45}
46
47static const TileProc gTileProcs[] = {
48 clamp_tileproc,
49 repeat_tileproc,
50 mirror_tileproc
51};
52
53//////////////////////////////////////////////////////////////////////////////
54
reed@android.com200645d2009-12-14 16:41:57 +000055static inline int repeat_bits(int x, const int bits) {
56 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000057}
58
reed@android.com200645d2009-12-14 16:41:57 +000059static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000061 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000063 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000064#else
reed@android.com200645d2009-12-14 16:41:57 +000065 int s = x << (31 - bits) >> 31;
66 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000067#endif
68}
69
reed@android.com41bccf52009-04-03 13:33:51 +000070static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 return x & 0xFF;
72}
73
reed@android.com41bccf52009-04-03 13:33:51 +000074static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000075#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000076 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000078 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 return x & 255;
80#else
81 int s = x << 23 >> 31;
82 return (x ^ s) & 0xFF;
83#endif
84}
85
86//////////////////////////////////////////////////////////////////////////////
87
88class Gradient_Shader : public SkShader {
89public:
90 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +000091 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 virtual ~Gradient_Shader();
93
94 // overrides
95 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
96 virtual uint32_t getFlags() { return fFlags; }
97
98protected:
99 Gradient_Shader(SkFlattenableReadBuffer& );
100 SkUnitMapper* fMapper;
101 SkMatrix fPtsToUnit; // set by subclass
102 SkMatrix fDstToIndex;
103 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 TileMode fTileMode;
105 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000106 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 uint8_t fDstToIndexClass;
108 uint8_t fFlags;
109
110 struct Rec {
111 SkFixed fPos; // 0...1
112 uint32_t fScale; // (1 << 24) / range
113 };
114 Rec* fRecs;
115
116 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000117 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000119 kCache16Mask = kCache16Count - 1,
120 kCache16Shift = 16 - kCache16Bits,
121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 kCache32Bits = 8, // pretty much should always be 8
123 kCache32Count = 1 << kCache32Bits
124 };
125 virtual void flatten(SkFlattenableWriteBuffer& );
126 const uint16_t* getCache16();
127 const SkPMColor* getCache32();
128
reed@google.comdc731fd2010-12-23 15:19:47 +0000129 SkMallocPixelRef* fCache32PixelRef;
130
131 void commonAsABitmap(SkBitmap*);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000132 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134private:
135 enum {
136 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
137
reed@android.com1c12abe2009-07-02 15:01:02 +0000138 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 };
140 SkColor fStorage[(kStorageSize + 3) >> 2];
141 SkColor* fOrigColors;
142
143 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
144 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
145
146 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
148
reed@android.com512a8762009-12-14 15:25:36 +0000149 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000150 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
151 U8CPU alpha);
reed@android.com512a8762009-12-14 15:25:36 +0000152
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 typedef SkShader INHERITED;
154};
155
reed@android.com41bccf52009-04-03 13:33:51 +0000156static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 SkASSERT(x >= 0 && x <= SK_Scalar1);
158
159#ifdef SK_SCALAR_IS_FLOAT
160 return (unsigned)(x * 0xFFFF);
161#else
162 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
163#endif
164}
165
reed@android.com41bccf52009-04-03 13:33:51 +0000166Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
167 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 SkASSERT(colorCount > 1);
169
170 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
171
172 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000173 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
176 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
177 fTileMode = mode;
178 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000179
reed@android.com41bccf52009-04-03 13:33:51 +0000180 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000181 fCache32 = NULL;
182 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
reed@android.com41bccf52009-04-03 13:33:51 +0000184 /* Note: we let the caller skip the first and/or last position.
185 i.e. pos[0] = 0.3, pos[1] = 0.7
186 In these cases, we insert dummy entries to ensure that the final data
187 will be bracketed by [0, 1].
188 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
189
190 Thus colorCount (the caller's value, and fColorCount (our value) may
191 differ by up to 2. In the above example:
192 colorCount = 2
193 fColorCount = 4
194 */
195 fColorCount = colorCount;
196 // check if we need to add in dummy start and/or end position/colors
197 bool dummyFirst = false;
198 bool dummyLast = false;
199 if (pos) {
200 dummyFirst = pos[0] != 0;
201 dummyLast = pos[colorCount - 1] != SK_Scalar1;
202 fColorCount += dummyFirst + dummyLast;
203 }
204
205 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000206 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000207 fOrigColors = reinterpret_cast<SkColor*>(
208 sk_malloc_throw(size * fColorCount));
209 }
210 else {
211 fOrigColors = fStorage;
212 }
213
214 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 {
reed@android.com41bccf52009-04-03 13:33:51 +0000216 SkColor* origColors = fOrigColors;
217 if (dummyFirst) {
218 *origColors++ = colors[0];
219 }
220 memcpy(origColors, colors, colorCount * sizeof(SkColor));
221 if (dummyLast) {
222 origColors += colorCount;
223 *origColors = colors[colorCount - 1];
224 }
225 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226
reed@android.com1c12abe2009-07-02 15:01:02 +0000227 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000228 if (fColorCount > 2) {
229 Rec* recs = fRecs;
230 recs->fPos = 0;
231 // recs->fScale = 0; // unused;
232 recs += 1;
233 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 /* We need to convert the user's array of relative positions into
235 fixed-point positions and scale factors. We need these results
236 to be strictly monotonic (no two values equal or out of order).
237 Hence this complex loop that just jams a zero for the scale
238 value if it sees a segment out of order, and it assures that
239 we start at 0 and end at 1.0
240 */
241 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000242 int startIndex = dummyFirst ? 0 : 1;
243 int count = colorCount + dummyLast;
244 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 // force the last value to be 1.0
246 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000247 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000249 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 }
reed@android.com41bccf52009-04-03 13:33:51 +0000252 // pin curr withing range
253 if (curr < 0) {
254 curr = 0;
255 } else if (curr > SK_Fixed1) {
256 curr = SK_Fixed1;
257 }
258 recs->fPos = curr;
259 if (curr > prev) {
260 recs->fScale = (1 << 24) / (curr - prev);
261 } else {
262 recs->fScale = 0; // ignore this segment
263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 // get ready for the next value
265 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000266 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 }
reed@android.com41bccf52009-04-03 13:33:51 +0000268 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 SkFixed dp = SK_Fixed1 / (colorCount - 1);
270 SkFixed p = dp;
271 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000272 for (int i = 1; i < colorCount; i++) {
273 recs->fPos = p;
274 recs->fScale = scale;
275 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 p += dp;
277 }
278 }
279 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000280 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281}
282
283Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000284 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fCacheAlpha = 256;
286
287 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
288
289 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000290 fCache32 = NULL;
291 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292
reed@android.com41bccf52009-04-03 13:33:51 +0000293 int colorCount = fColorCount = buffer.readU32();
294 if (colorCount > kColorStorageCount) {
295 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
296 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
297 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301
302 fTileMode = (TileMode)buffer.readU8();
303 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000304 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 if (colorCount > 2) {
306 Rec* recs = fRecs;
307 recs[0].fPos = 0;
308 for (int i = 1; i < colorCount; i++) {
309 recs[i].fPos = buffer.readS32();
310 recs[i].fScale = buffer.readU32();
311 }
312 }
313 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000314 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315}
316
reed@android.com41bccf52009-04-03 13:33:51 +0000317Gradient_Shader::~Gradient_Shader() {
318 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000320 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000321 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000322 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000324 }
reed@google.com82065d62011-02-07 15:30:46 +0000325 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326}
327
reed@android.com41bccf52009-04-03 13:33:51 +0000328void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 this->INHERITED::flatten(buffer);
330 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000331 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
333 buffer.write8(fTileMode);
334 if (fColorCount > 2) {
335 Rec* recs = fRecs;
336 for (int i = 1; i < fColorCount; i++) {
337 buffer.write32(recs[i].fPos);
338 buffer.write32(recs[i].fScale);
339 }
340 }
341 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
342}
343
344bool Gradient_Shader::setContext(const SkBitmap& device,
345 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000346 const SkMatrix& matrix) {
347 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350
351 const SkMatrix& inverse = this->getTotalInverse();
352
353 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
354 return false;
355 }
356
357 fDstToIndexProc = fDstToIndex.getMapXYProc();
358 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
359
360 // now convert our colors in to PMColors
361 unsigned paintAlpha = this->getPaintAlpha();
362 unsigned colorAlpha = 0xFF;
363
reed@android.com3d06a8c2009-07-07 18:19:59 +0000364 // FIXME: record colorAlpha in constructor, since this is not affected
365 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000366 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 SkColor src = fOrigColors[i];
368 unsigned sa = SkColorGetA(src);
369 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 }
371
372 fFlags = this->INHERITED::getFlags();
373 if ((colorAlpha & paintAlpha) == 0xFF) {
374 fFlags |= kOpaqueAlpha_Flag;
375 }
376 // we can do span16 as long as our individual colors are opaque,
377 // regardless of the paint's alpha
378 if (0xFF == colorAlpha) {
379 fFlags |= kHasSpan16_Flag;
380 }
381
382 // if the new alpha differs from the previous time we were called, inval our cache
383 // this will trigger the cache to be rebuilt.
384 // we don't care about the first time, since the cache ptrs will already be NULL
385 if (fCacheAlpha != paintAlpha) {
386 fCache16 = NULL; // inval the cache
387 fCache32 = NULL; // inval the cache
388 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000389 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000390 if (fCache32PixelRef) {
391 fCache32PixelRef->notifyPixelsChanged();
392 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 }
394 return true;
395}
396
reed@android.com41bccf52009-04-03 13:33:51 +0000397static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398 SkASSERT(a == SkToU8(a));
399 SkASSERT(b == SkToU8(b));
400 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 return a + ((b - a) * scale >> 8);
402}
403
reed@android.com41bccf52009-04-03 13:33:51 +0000404static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
405 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406#if 0
407 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
408 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
409 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
410 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
411
412 return SkPackARGB32(a, r, g, b);
413#else
414 int otherBlend = 256 - blend;
415
416#if 0
417 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
418 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
419 SkASSERT((t0 & t1) == 0);
420 return t0 | t1;
421#else
422 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
423 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
424#endif
425
426#endif
427}
428
429#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
430
reed@android.com41bccf52009-04-03 13:33:51 +0000431/** We take the original colors, not our premultiplied PMColors, since we can
432 build a 16bit table as long as the original colors are opaque, even if the
433 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434*/
reed@android.com512a8762009-12-14 15:25:36 +0000435void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
436 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 SkASSERT(count > 1);
438 SkASSERT(SkColorGetA(c0) == 0xFF);
439 SkASSERT(SkColorGetA(c1) == 0xFF);
440
441 SkFixed r = SkColorGetR(c0);
442 SkFixed g = SkColorGetG(c0);
443 SkFixed b = SkColorGetB(c0);
444
445 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
446 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
447 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
448
449 r = SkIntToFixed(r) + 0x8000;
450 g = SkIntToFixed(g) + 0x8000;
451 b = SkIntToFixed(b) + 0x8000;
452
453 do {
454 unsigned rr = r >> 16;
455 unsigned gg = g >> 16;
456 unsigned bb = b >> 16;
457 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000458 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 cache += 1;
460 r += dr;
461 g += dg;
462 b += db;
463 } while (--count != 0);
464}
465
reed@google.com55b8e8c2011-01-13 16:22:35 +0000466/*
467 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
468 * semantics of how we 2x2 dither 32->16
469 */
470static inline U8CPU dither_fixed_to_8(SkFixed n) {
471 n >>= 8;
472 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
473}
474
475/*
476 * For dithering with premultiply, we want to ceiling the alpha component,
477 * to ensure that it is always >= any color component.
478 */
479static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
480 n >>= 8;
481 return ((n << 1) - (n | (n >> 8))) >> 8;
482}
483
484void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
485 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 SkASSERT(count > 1);
487
reed@android.com1c12abe2009-07-02 15:01:02 +0000488 // need to apply paintAlpha to our two endpoints
489 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
490 SkFixed da;
491 {
492 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
493 da = SkIntToFixed(tmp - a) / (count - 1);
494 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495
reed@android.com1c12abe2009-07-02 15:01:02 +0000496 SkFixed r = SkColorGetR(c0);
497 SkFixed g = SkColorGetG(c0);
498 SkFixed b = SkColorGetB(c0);
499 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
500 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
501 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
503 a = SkIntToFixed(a) + 0x8000;
504 r = SkIntToFixed(r) + 0x8000;
505 g = SkIntToFixed(g) + 0x8000;
506 b = SkIntToFixed(b) + 0x8000;
507
508 do {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000509 cache[0] = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
510 cache[kCache32Count] = SkPreMultiplyARGB(dither_ceil_fixed_to_8(a),
511 dither_fixed_to_8(r),
512 dither_fixed_to_8(g),
513 dither_fixed_to_8(b));
514 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 a += da;
516 r += dr;
517 g += dg;
518 b += db;
519 } while (--count != 0);
520}
521
reed@android.com41bccf52009-04-03 13:33:51 +0000522static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 SkASSERT((unsigned)x <= SK_Fixed1);
524 return x - (x >> 16);
525}
526
reed@android.com200645d2009-12-14 16:41:57 +0000527static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000528 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000529 if (6 == bits) {
530 return (x << 10) | (x << 4) | (x >> 2);
531 }
532 if (8 == bits) {
533 return (x << 8) | x;
534 }
535 sk_throw();
536 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537}
538
reed@android.com41bccf52009-04-03 13:33:51 +0000539const uint16_t* Gradient_Shader::getCache16() {
540 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000541 // double the count for dither entries
542 const int entryCount = kCache16Count * 2;
543 const size_t allocSize = sizeof(uint16_t) * entryCount;
544
reed@android.com3c9b2a42009-08-27 19:28:37 +0000545 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000546 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000547 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000549 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000550 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000551 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 Rec* rec = fRecs;
553 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000554 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000555 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 SkASSERT(nextIndex < kCache16Count);
557
558 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000559 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 prevIndex = nextIndex;
561 }
562 SkASSERT(prevIndex == kCache16Count - 1);
563 }
564
reed@android.com41bccf52009-04-03 13:33:51 +0000565 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000566 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 uint16_t* linear = fCache16; // just computed linear data
568 uint16_t* mapped = fCache16Storage; // storage for mapped data
569 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000570 for (int i = 0; i < kCache16Count; i++) {
571 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000573 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 }
575 sk_free(fCache16);
576 fCache16 = fCache16Storage;
577 }
578 }
579 return fCache16;
580}
581
reed@android.com41bccf52009-04-03 13:33:51 +0000582const SkPMColor* Gradient_Shader::getCache32() {
583 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000584 // double the count for dither entries
585 const int entryCount = kCache32Count * 2;
586 const size_t allocSize = sizeof(SkPMColor) * entryCount;
587
reed@google.comdc731fd2010-12-23 15:19:47 +0000588 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000589 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
590 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000591 }
592 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000593 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000594 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
595 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000596 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 Rec* rec = fRecs;
598 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000599 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
601 SkASSERT(nextIndex < kCache32Count);
602
603 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000604 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
605 fOrigColors[i],
606 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 prevIndex = nextIndex;
608 }
609 SkASSERT(prevIndex == kCache32Count - 1);
610 }
611
reed@android.com41bccf52009-04-03 13:33:51 +0000612 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000613 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000614 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000616 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000618 for (int i = 0; i < kCache32Count; i++) {
619 int index = map->mapUnit16((i << 8) | i) >> 8;
620 mapped[i] = linear[index];
621 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000622 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000623 fCache32PixelRef->unref();
624 fCache32PixelRef = newPR;
625 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 }
627 }
628 return fCache32;
629}
630
reed@google.comdc731fd2010-12-23 15:19:47 +0000631/*
632 * Because our caller might rebuild the same (logically the same) gradient
633 * over and over, we'd like to return exactly the same "bitmap" if possible,
634 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
635 * To do that, we maintain a private cache of built-bitmaps, based on our
636 * colors and positions. Note: we don't try to flatten the fMapper, so if one
637 * is present, we skip the cache for now.
638 */
639void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) {
640 // don't have a way to put the mapper into our cache-key yet
641 if (fMapper) {
642 // force our cahce32pixelref to be built
643 (void)this->getCache32();
644 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
645 bitmap->setPixelRef(fCache32PixelRef);
646 return;
647 }
648
649 // build our key: [numColors + colors[] + {positions[]} ]
650 int count = 1 + fColorCount;
651 if (fColorCount > 2) {
652 count += fColorCount - 1; // fRecs[].fPos
653 }
654
655 SkAutoSTMalloc<16, int32_t> storage(count);
656 int32_t* buffer = storage.get();
657
658 *buffer++ = fColorCount;
659 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
660 buffer += fColorCount;
661 if (fColorCount > 2) {
662 for (int i = 1; i < fColorCount; i++) {
663 *buffer++ = fRecs[i].fPos;
664 }
665 }
666 SkASSERT(buffer - storage.get() == count);
667
668 ///////////////////////////////////
669
670 static SkMutex gMutex;
671 static SkBitmapCache* gCache;
672 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
673 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
674 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000675
reed@google.comdc731fd2010-12-23 15:19:47 +0000676 if (NULL == gCache) {
677 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
678 }
679 size_t size = count * sizeof(int32_t);
680
681 if (!gCache->find(storage.get(), size, bitmap)) {
682 // force our cahce32pixelref to be built
683 (void)this->getCache32();
684 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
685 bitmap->setPixelRef(fCache32PixelRef);
686
687 gCache->add(storage.get(), size, *bitmap);
688 }
689}
690
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000691void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
692 if (info) {
693 if (info->fColorCount >= fColorCount) {
694 if (info->fColors) {
695 memcpy(info->fColors, fOrigColors,
696 fColorCount * sizeof(SkColor));
697 }
698 if (info->fColorOffsets) {
699 if (fColorCount == 2) {
700 info->fColorOffsets[0] = 0;
701 info->fColorOffsets[1] = SK_Scalar1;
702 } else if (fColorCount > 2) {
703 for (int i = 0; i < fColorCount; i++)
704 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
705 }
706 }
707 }
708 info->fColorCount = fColorCount;
709 info->fTileMode = fTileMode;
710 }
711}
712
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713///////////////////////////////////////////////////////////////////////////
714
reed@android.com41bccf52009-04-03 13:33:51 +0000715static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 SkVector vec = pts[1] - pts[0];
717 SkScalar mag = vec.length();
718 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
719
720 vec.scale(inv);
721 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
722 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
723 matrix->postScale(inv, inv);
724}
725
726///////////////////////////////////////////////////////////////////////////////
727
728class Linear_Gradient : public Gradient_Shader {
729public:
730 Linear_Gradient(const SkPoint pts[2],
731 const SkColor colors[], const SkScalar pos[], int colorCount,
732 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000733 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
734 fStart(pts[0]),
735 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 {
737 pts_to_unit_matrix(pts, &fPtsToUnit);
738 }
reed@android.com9b46e772009-06-05 12:24:41 +0000739
reed@android.com5119bdb2009-06-12 21:27:03 +0000740 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
742 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000743 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*,
reed@google.comdc731fd2010-12-23 15:19:47 +0000744 TileMode*, SkScalar* twoPointRadialParams);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000745 virtual GradientType asAGradient(GradientInfo* info) const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746
reed@google.com55b8e8c2011-01-13 16:22:35 +0000747 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 return SkNEW_ARGS(Linear_Gradient, (buffer));
749 }
750
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000751 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
752 this->INHERITED::flatten(buffer);
753 buffer.writeScalar(fStart.fX);
754 buffer.writeScalar(fStart.fY);
755 buffer.writeScalar(fEnd.fX);
756 buffer.writeScalar(fEnd.fY);
757 }
758
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000760 Linear_Gradient(SkFlattenableReadBuffer& buffer)
761 : Gradient_Shader(buffer),
762 fStart(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
763 fEnd(SkPoint::Make(buffer.readScalar(), buffer.readScalar())) {
764 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 virtual Factory getFactory() { return CreateProc; }
766
767private:
768 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000769 const SkPoint fStart;
770 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771};
772
reed@android.com5119bdb2009-06-12 21:27:03 +0000773bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
774 const SkMatrix& matrix) {
775 if (!this->INHERITED::setContext(device, paint, matrix)) {
776 return false;
777 }
778
779 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
780 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000781 fFlags |= SkShader::kConstInY32_Flag;
782 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
783 // only claim this if we do have a 16bit mode (i.e. none of our
784 // colors have alpha), and if we are not dithering (which obviously
785 // is not const in Y).
786 fFlags |= SkShader::kConstInY16_Flag;
787 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000788 }
789 return true;
790}
791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000793static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794{
795 SkASSERT(count > 0);
796 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
797}
798
799void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
800{
801 SkASSERT(count > 0);
802
803 SkPoint srcPt;
804 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
805 TileProc proc = fTileProc;
806 const SkPMColor* cache = this->getCache32();
reed@google.com55b8e8c2011-01-13 16:22:35 +0000807#ifdef USE_DITHER_32BIT_GRADIENT
808 int toggle = ((x ^ y) & 1) << kCache32Bits;
809 const int TOGGLE_MASK = (1 << kCache32Bits);
810#else
811 int toggle = 0;
812 const int TOGGLE_MASK = 0;
813#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814
reed@android.comc552a432009-06-12 20:02:50 +0000815 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000816 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
817 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
819
reed@android.comc552a432009-06-12 20:02:50 +0000820 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 SkFixed dxStorage[1];
822 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
823 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000824 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
826 dx = SkScalarToFixed(fDstToIndex.getScaleX());
827 }
828
reed@android.comc552a432009-06-12 20:02:50 +0000829 if (SkFixedNearlyZero(dx)) {
830 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 unsigned fi = proc(fx);
832 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000833 // TODO: dither version
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000835 } else if (proc == clamp_tileproc) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000836 do {
837 unsigned fi = SkClampMax(fx >> 8, 0xFF);
838 SkASSERT(fi <= 0xFF);
839 fx += dx;
840 *dstC++ = cache[toggle + fi];
841 toggle ^= TOGGLE_MASK;
842 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000843 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 do {
845 unsigned fi = mirror_8bits(fx >> 8);
846 SkASSERT(fi <= 0xFF);
847 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000848 *dstC++ = cache[toggle + fi];
849 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000851 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 SkASSERT(proc == repeat_tileproc);
853 do {
854 unsigned fi = repeat_8bits(fx >> 8);
855 SkASSERT(fi <= 0xFF);
856 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000857 *dstC++ = cache[toggle + fi];
858 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 } while (--count != 0);
860 }
reed@android.comc552a432009-06-12 20:02:50 +0000861 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 SkScalar dstX = SkIntToScalar(x);
863 SkScalar dstY = SkIntToScalar(y);
864 do {
865 dstProc(fDstToIndex, dstX, dstY, &srcPt);
866 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
867 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000868 *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))];
869 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 dstX += SK_Scalar1;
871 } while (--count != 0);
872 }
873}
874
reed@google.com55b8e8c2011-01-13 16:22:35 +0000875SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000876 SkMatrix* matrix,
877 TileMode xy[],
878 SkScalar* twoPointRadialParams) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000880 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 }
882 if (matrix) {
883 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
884 matrix->preConcat(fPtsToUnit);
885 }
886 if (xy) {
887 xy[0] = fTileMode;
888 xy[1] = kClamp_TileMode;
889 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000890 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891}
892
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000893SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
894 if (info) {
895 commonAsAGradient(info);
896 info->fPoint[0] = fStart;
897 info->fPoint[1] = fEnd;
898 }
899 return kLinear_GradientType;
900}
901
reed@android.com3c9b2a42009-08-27 19:28:37 +0000902static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
903 int count) {
904 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 *dst++ = value;
906 count -= 1;
907 SkTSwap(value, other);
908 }
909
910 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000911
reed@android.com3c9b2a42009-08-27 19:28:37 +0000912 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000914 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916
917void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
918{
919 SkASSERT(count > 0);
920
921 SkPoint srcPt;
922 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
923 TileProc proc = fTileProc;
924 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000927 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000928 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
929 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
931
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000932 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 SkFixed dxStorage[1];
934 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
935 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000936 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
938 dx = SkScalarToFixed(fDstToIndex.getScaleX());
939 }
940
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000941 if (SkFixedNearlyZero(dx)) {
942 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +0000943 unsigned fi = proc(fx) >> kCache16Shift;
944 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000946 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 do {
reed@android.com512a8762009-12-14 15:25:36 +0000948 unsigned fi = SkClampMax(fx >> kCache16Shift, kCache16Mask);
949 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 *dstC++ = cache[toggle + fi];
952 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000954 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 do {
reed@android.com200645d2009-12-14 16:41:57 +0000956 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +0000957 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 *dstC++ = cache[toggle + fi];
960 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000962 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 SkASSERT(proc == repeat_tileproc);
964 do {
reed@android.com200645d2009-12-14 16:41:57 +0000965 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +0000966 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 *dstC++ = cache[toggle + fi];
969 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 } while (--count != 0);
971 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000972 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 SkScalar dstX = SkIntToScalar(x);
974 SkScalar dstY = SkIntToScalar(y);
975 do {
976 dstProc(fDstToIndex, dstX, dstY, &srcPt);
977 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
978 SkASSERT(fi <= 0xFFFF);
979
reed@android.com512a8762009-12-14 15:25:36 +0000980 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 *dstC++ = cache[toggle + index];
982 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983
984 dstX += SK_Scalar1;
985 } while (--count != 0);
986 }
987}
988
989///////////////////////////////////////////////////////////////////////////////
990
991#define kSQRT_TABLE_BITS 11
992#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
993
994#include "SkRadialGradient_Table.h"
995
996#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
997
998#include <stdio.h>
999
1000void SkRadialGradient_BuildTable()
1001{
1002 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1003
1004 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1005 SkASSERT(file);
1006 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1007
1008 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
1009 {
1010 if ((i & 15) == 0)
1011 ::fprintf(file, "\t");
1012
1013 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1014
1015 ::fprintf(file, "0x%02X", value);
1016 if (i < kSQRT_TABLE_SIZE-1)
1017 ::fprintf(file, ", ");
1018 if ((i & 15) == 15)
1019 ::fprintf(file, "\n");
1020 }
1021 ::fprintf(file, "};\n");
1022 ::fclose(file);
1023}
1024
1025#endif
1026
1027
1028static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
1029{
1030 SkScalar inv = SkScalarInvert(radius);
1031
1032 matrix->setTranslate(-center.fX, -center.fY);
1033 matrix->postScale(inv, inv);
1034}
1035
1036class Radial_Gradient : public Gradient_Shader {
1037public:
1038 Radial_Gradient(const SkPoint& center, SkScalar radius,
1039 const SkColor colors[], const SkScalar pos[], int colorCount,
1040 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001041 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1042 fCenter(center),
1043 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 {
1045 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1046 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1047
1048 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1049 }
1050 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1051 {
1052 SkASSERT(count > 0);
1053
1054 SkPoint srcPt;
1055 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1056 TileProc proc = fTileProc;
1057 const SkPMColor* cache = this->getCache32();
1058
1059 if (fDstToIndexClass != kPerspective_MatrixClass)
1060 {
reed@google.comdc731fd2010-12-23 15:19:47 +00001061 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1062 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1064 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1065
1066 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1067 {
1068 SkFixed storage[2];
1069 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1070 dx = storage[0];
1071 dy = storage[1];
1072 }
1073 else
1074 {
1075 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1076 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1077 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1078 }
1079
1080 if (proc == clamp_tileproc)
1081 {
1082 const uint8_t* sqrt_table = gSqrt8Table;
1083 fx >>= 1;
1084 dx >>= 1;
1085 fy >>= 1;
1086 dy >>= 1;
1087 do {
1088 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1089 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1090 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1091 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1092 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1093 fx += dx;
1094 fy += dy;
1095 } while (--count != 0);
1096 }
1097 else if (proc == mirror_tileproc)
1098 {
1099 do {
1100 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1101 unsigned fi = mirror_tileproc(dist);
1102 SkASSERT(fi <= 0xFFFF);
1103 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1104 fx += dx;
1105 fy += dy;
1106 } while (--count != 0);
1107 }
1108 else
1109 {
1110 SkASSERT(proc == repeat_tileproc);
1111 do {
1112 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1113 unsigned fi = repeat_tileproc(dist);
1114 SkASSERT(fi <= 0xFFFF);
1115 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1116 fx += dx;
1117 fy += dy;
1118 } while (--count != 0);
1119 }
1120 }
1121 else // perspective case
1122 {
1123 SkScalar dstX = SkIntToScalar(x);
1124 SkScalar dstY = SkIntToScalar(y);
1125 do {
1126 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1127 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1128 SkASSERT(fi <= 0xFFFF);
1129 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1130 dstX += SK_Scalar1;
1131 } while (--count != 0);
1132 }
1133 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001134
1135 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 SkASSERT(count > 0);
1137
1138 SkPoint srcPt;
1139 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1140 TileProc proc = fTileProc;
1141 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143
reed@android.com3c9b2a42009-08-27 19:28:37 +00001144 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001145 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1146 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1148 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1149
reed@android.com3c9b2a42009-08-27 19:28:37 +00001150 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 SkFixed storage[2];
1152 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1153 dx = storage[0];
1154 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001155 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1157 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1158 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1159 }
1160
reed@android.com3c9b2a42009-08-27 19:28:37 +00001161 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 const uint8_t* sqrt_table = gSqrt8Table;
1163
1164 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1165 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1166 precision, but that appears to be visually OK. If we decide this is OK for
1167 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1168 to avoid having to do these extra shifts each time.
1169 */
1170 fx >>= 1;
1171 dx >>= 1;
1172 fy >>= 1;
1173 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001174 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 +00001175 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1176 fy *= fy;
1177 do {
1178 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1179 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1180 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1181 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1183 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001185 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 do {
1187 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1188 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1189 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1190 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1191 fx += dx;
1192 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1194 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 } while (--count != 0);
1196 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001197 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 do {
1199 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1200 unsigned fi = mirror_tileproc(dist);
1201 SkASSERT(fi <= 0xFFFF);
1202 fx += dx;
1203 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1205 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001207 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 SkASSERT(proc == repeat_tileproc);
1209 do {
1210 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1211 unsigned fi = repeat_tileproc(dist);
1212 SkASSERT(fi <= 0xFFFF);
1213 fx += dx;
1214 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1216 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 } while (--count != 0);
1218 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001219 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 SkScalar dstX = SkIntToScalar(x);
1221 SkScalar dstY = SkIntToScalar(y);
1222 do {
1223 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1224 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1225 SkASSERT(fi <= 0xFFFF);
1226
1227 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 *dstC++ = cache[toggle + index];
1229 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230
1231 dstX += SK_Scalar1;
1232 } while (--count != 0);
1233 }
1234 }
1235
reed@google.com55b8e8c2011-01-13 16:22:35 +00001236 virtual BitmapType asABitmap(SkBitmap* bitmap,
1237 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001238 TileMode* xy,
1239 SkScalar* twoPointRadialParams) {
1240 if (bitmap) {
1241 this->commonAsABitmap(bitmap);
1242 }
1243 if (matrix) {
1244 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1245 matrix->preConcat(fPtsToUnit);
1246 }
1247 if (xy) {
1248 xy[0] = fTileMode;
1249 xy[1] = kClamp_TileMode;
1250 }
1251 return kRadial_BitmapType;
1252 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001253 virtual GradientType asAGradient(GradientInfo* info) const {
1254 if (info) {
1255 commonAsAGradient(info);
1256 info->fPoint[0] = fCenter;
1257 info->fRadius[0] = fRadius;
1258 }
1259 return kRadial_GradientType;
1260 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001261
1262 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 return SkNEW_ARGS(Radial_Gradient, (buffer));
1264 }
1265
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001266 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1267 this->INHERITED::flatten(buffer);
1268 buffer.writeScalar(fCenter.fX);
1269 buffer.writeScalar(fCenter.fY);
1270 buffer.writeScalar(fRadius);
1271 }
1272
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001274 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1275 : Gradient_Shader(buffer),
1276 fCenter(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1277 fRadius(buffer.readScalar()) {
1278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 virtual Factory getFactory() { return CreateProc; }
1280
1281private:
1282 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001283 const SkPoint fCenter;
1284 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285};
1286
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001287/* Two-point radial gradients are specified by two circles, each with a center
1288 point and radius. The gradient can be considered to be a series of
1289 concentric circles, with the color interpolated from the start circle
1290 (at t=0) to the end circle (at t=1).
1291
1292 For each point (x, y) in the span, we want to find the
1293 interpolated circle that intersects that point. The center
1294 of the desired circle (Cx, Cy) falls at some distance t
1295 along the line segment between the start point (Sx, Sy) and
1296 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001297
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001298 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1299 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001300
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001301 The radius of the desired circle (r) is also a linear interpolation t
1302 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001303
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001304 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001305
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001306 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001307
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001308 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001309
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001310 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001311
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001312 (x - ((1 - t) * Sx + t * Ex))^2
1313 + (y - ((1 - t) * Sy + t * Ey))^2
1314 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001315
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001316 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001317
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001318 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1319 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1320 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001321
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001322 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1323
1324 [Dx^2 + Dy^2 - Dr^2)] * t^2
1325 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1326 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001327
1328 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001329 possible circles on which the point may fall. Solving for t yields
1330 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001331
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001332 If a<0, the start circle is entirely contained in the
1333 end circle, and one of the roots will be <0 or >1 (off the line
1334 segment). If a>0, the start circle falls at least partially
1335 outside the end circle (or vice versa), and the gradient
1336 defines a "tube" where a point may be on one circle (on the
1337 inside of the tube) or the other (outside of the tube). We choose
1338 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001339
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001340 In order to keep the math to within the limits of fixed point,
1341 we divide the entire quadratic by Dr^2, and replace
1342 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001343
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001344 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1345 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1346 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001347
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001348 (x' and y' are computed by appending the subtract and scale to the
1349 fDstToIndex matrix in the constructor).
1350
1351 Since the 'A' component of the quadratic is independent of x' and y', it
1352 is precomputed in the constructor. Since the 'B' component is linear in
1353 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001354 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001355 a perspective projection), it must be computed in the loop.
1356
1357*/
1358
1359static inline SkFixed two_point_radial(SkFixed b, SkFixed fx, SkFixed fy, SkFixed sr2d2, SkFixed foura, SkFixed oneOverTwoA, bool posRoot) {
1360 SkFixed c = SkFixedSquare(fx) + SkFixedSquare(fy) - sr2d2;
1361 SkFixed discrim = SkFixedSquare(b) - SkFixedMul(foura, c);
1362 if (discrim < 0) {
1363 discrim = -discrim;
1364 }
1365 SkFixed rootDiscrim = SkFixedSqrt(discrim);
1366 if (posRoot) {
1367 return SkFixedMul(-b + rootDiscrim, oneOverTwoA);
1368 } else {
1369 return SkFixedMul(-b - rootDiscrim, oneOverTwoA);
1370 }
1371}
1372
1373class Two_Point_Radial_Gradient : public Gradient_Shader {
1374public:
1375 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1376 const SkPoint& end, SkScalar endRadius,
1377 const SkColor colors[], const SkScalar pos[],
1378 int colorCount, SkShader::TileMode mode,
1379 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001380 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1381 fCenter1(start),
1382 fCenter2(end),
1383 fRadius1(startRadius),
1384 fRadius2(endRadius) {
1385 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001386 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001387
1388 virtual BitmapType asABitmap(SkBitmap* bitmap,
1389 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001390 TileMode* xy,
1391 SkScalar* twoPointRadialParams) {
1392 if (bitmap) {
1393 this->commonAsABitmap(bitmap);
1394 }
1395 SkScalar diffL = 0; // just to avoid gcc warning
1396 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001397 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001398 SkScalarSquare(fDiff.fY));
1399 }
1400 if (matrix) {
1401 SkScalar invDiffL = SkScalarInvert(diffL);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001402 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
reed@google.comdc731fd2010-12-23 15:19:47 +00001403 SkScalarMul(invDiffL, fDiff.fX));
1404 matrix->preConcat(fPtsToUnit);
1405 }
1406 if (xy) {
1407 xy[0] = fTileMode;
1408 xy[1] = kClamp_TileMode;
1409 }
1410 if (NULL != twoPointRadialParams) {
1411 twoPointRadialParams[0] = diffL;
1412 twoPointRadialParams[1] = fStartRadius;
1413 twoPointRadialParams[2] = fDiffRadius;
1414 }
1415 return kTwoPointRadial_BitmapType;
1416 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001417
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001418 virtual GradientType asAGradient(GradientInfo* info) const {
1419 if (info) {
1420 commonAsAGradient(info);
1421 info->fPoint[0] = fCenter1;
1422 info->fPoint[1] = fCenter2;
1423 info->fRadius[0] = fRadius1;
1424 info->fRadius[1] = fRadius2;
1425 }
1426 return kRadial2_GradientType;
1427 }
1428
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001429 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1430 {
1431 SkASSERT(count > 0);
1432
1433 // Zero difference between radii: fill with transparent black.
1434 if (fDiffRadius == 0) {
1435 sk_bzero(dstC, count * sizeof(*dstC));
1436 return;
1437 }
1438 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1439 TileProc proc = fTileProc;
1440 const SkPMColor* cache = this->getCache32();
1441 SkFixed diffx = SkScalarToFixed(fDiff.fX);
1442 SkFixed diffy = SkScalarToFixed(fDiff.fY);
1443 SkFixed foura = SkScalarToFixed(SkScalarMul(fA, 4));
1444 SkFixed startRadius = SkScalarToFixed(fStartRadius);
1445 SkFixed sr2D2 = SkScalarToFixed(fSr2D2);
1446 SkFixed oneOverTwoA = SkScalarToFixed(fOneOverTwoA);
1447 bool posRoot = fDiffRadius < 0;
1448 if (fDstToIndexClass != kPerspective_MatrixClass)
1449 {
1450 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001451 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1452 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001453 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1454 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1455
1456 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1457 {
1458 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &dx, &dy);
1459 }
1460 else
1461 {
1462 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1463 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1464 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1465 }
1466 SkFixed b = (SkFixedMul(diffx, fx) +
1467 SkFixedMul(diffy, fy) - startRadius) << 1;
1468 SkFixed db = (SkFixedMul(diffx, dx) +
1469 SkFixedMul(diffy, dy)) << 1;
1470 if (proc == clamp_tileproc)
1471 {
1472 for (; count > 0; --count) {
1473 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1474 SkFixed index = SkClampMax(t, 0xFFFF);
1475 SkASSERT(index <= 0xFFFF);
1476 *dstC++ = cache[index >> (16 - kCache32Bits)];
1477 fx += dx;
1478 fy += dy;
1479 b += db;
1480 }
1481 }
1482 else if (proc == mirror_tileproc)
1483 {
1484 for (; count > 0; --count) {
1485 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1486 SkFixed index = mirror_tileproc(t);
1487 SkASSERT(index <= 0xFFFF);
1488 *dstC++ = cache[index >> (16 - kCache32Bits)];
1489 fx += dx;
1490 fy += dy;
1491 b += db;
1492 }
1493 }
1494 else
1495 {
1496 SkASSERT(proc == repeat_tileproc);
1497 for (; count > 0; --count) {
1498 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1499 SkFixed index = repeat_tileproc(t);
1500 SkASSERT(index <= 0xFFFF);
1501 *dstC++ = cache[index >> (16 - kCache32Bits)];
1502 fx += dx;
1503 fy += dy;
1504 b += db;
1505 }
1506 }
1507 }
1508 else // perspective case
1509 {
reed@android.com6c59a172009-09-22 20:24:05 +00001510 SkScalar dstX = SkIntToScalar(x);
1511 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001512 for (; count > 0; --count) {
1513 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001514 dstProc(fDstToIndex, dstX, dstY, &srcPt);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001515 SkFixed fx = SkScalarToFixed(srcPt.fX);
1516 SkFixed fy = SkScalarToFixed(srcPt.fY);
1517 SkFixed b = (SkFixedMul(diffx, fx) +
1518 SkFixedMul(diffy, fy) - startRadius) << 1;
1519 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1520 SkFixed index = proc(t);
1521 SkASSERT(index <= 0xFFFF);
1522 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001523 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001524 }
1525 }
1526 }
1527
reed@android.com6c59a172009-09-22 20:24:05 +00001528 virtual bool setContext(const SkBitmap& device,
1529 const SkPaint& paint,
1530 const SkMatrix& matrix) {
1531 if (!this->INHERITED::setContext(device, paint, matrix)) {
1532 return false;
1533 }
1534
1535 // we don't have a span16 proc
1536 fFlags &= ~kHasSpan16_Flag;
1537 return true;
1538 }
1539
reed@google.com55b8e8c2011-01-13 16:22:35 +00001540 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001541 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1542 }
1543
reed@android.combcfc7332009-11-10 16:19:39 +00001544 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1545 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001546 buffer.writeScalar(fCenter1.fX);
1547 buffer.writeScalar(fCenter1.fY);
1548 buffer.writeScalar(fCenter2.fX);
1549 buffer.writeScalar(fCenter2.fY);
1550 buffer.writeScalar(fRadius1);
1551 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001552 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001553
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001554protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001555 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001556 : Gradient_Shader(buffer),
1557 fCenter1(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1558 fCenter2(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1559 fRadius1(buffer.readScalar()),
1560 fRadius2(buffer.readScalar()) {
1561 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001562 };
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001563 virtual Factory getFactory() { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001564
1565private:
1566 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001567 const SkPoint fCenter1;
1568 const SkPoint fCenter2;
1569 const SkScalar fRadius1;
1570 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001571 SkPoint fDiff;
1572 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001573
1574 void init() {
1575 fDiff = fCenter1 - fCenter2;
1576 fDiffRadius = fRadius2 - fRadius1;
1577 SkScalar inv = SkScalarInvert(fDiffRadius);
1578 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1579 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1580 fStartRadius = SkScalarMul(fRadius1, inv);
1581 fSr2D2 = SkScalarSquare(fStartRadius);
1582 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
1583 fOneOverTwoA = SkScalarInvert(fA * 2);
1584
1585 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1586 fPtsToUnit.postScale(inv, inv);
1587 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001588};
1589
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590///////////////////////////////////////////////////////////////////////////////
1591
1592class Sweep_Gradient : public Gradient_Shader {
1593public:
1594 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1595 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001596 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
1597 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598 {
1599 fPtsToUnit.setTranslate(-cx, -cy);
1600 }
1601 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1602 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001603
1604 virtual BitmapType asABitmap(SkBitmap* bitmap,
1605 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001606 TileMode* xy,
1607 SkScalar* twoPointRadialParams) {
1608 if (bitmap) {
1609 this->commonAsABitmap(bitmap);
1610 }
1611 if (matrix) {
1612 *matrix = fPtsToUnit;
1613 }
1614 if (xy) {
1615 xy[0] = fTileMode;
1616 xy[1] = kClamp_TileMode;
1617 }
1618 return kSweep_BitmapType;
1619 }
1620
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001621 virtual GradientType asAGradient(GradientInfo* info) const {
1622 if (info) {
1623 commonAsAGradient(info);
1624 info->fPoint[0] = fCenter;
1625 }
1626 return kSweep_GradientType;
1627 }
1628
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1630 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1631 }
1632
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001633 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1634 this->INHERITED::flatten(buffer);
1635 buffer.writeScalar(fCenter.fX);
1636 buffer.writeScalar(fCenter.fY);
1637 }
1638
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001640 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
1641 : Gradient_Shader(buffer),
1642 fCenter(SkPoint::Make(buffer.readScalar(), buffer.readScalar())) {
1643 }
1644
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645 virtual Factory getFactory() { return CreateProc; }
1646
1647private:
1648 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001649 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650};
1651
1652#ifdef COMPUTE_SWEEP_TABLE
1653#define PI 3.14159265
1654static bool gSweepTableReady;
1655static uint8_t gSweepTable[65];
1656
1657/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1658 We scale the results to [0..32]
1659*/
1660static const uint8_t* build_sweep_table()
1661{
1662 if (!gSweepTableReady)
1663 {
1664 const int N = 65;
1665 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001666
reed@android.com8a1c16f2008-12-17 15:59:43 +00001667 for (int i = 0; i < N; i++)
1668 {
1669 double arg = i / DENOM;
1670 double v = atan(arg);
1671 int iv = (int)round(v * DENOM * 2 / PI);
1672// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1673 printf("%d, ", iv);
1674 gSweepTable[i] = iv;
1675 }
1676 gSweepTableReady = true;
1677 }
1678 return gSweepTable;
1679}
1680#else
1681static const uint8_t gSweepTable[] = {
1682 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1683 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1684 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1685 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1686 32
1687};
1688static const uint8_t* build_sweep_table() { return gSweepTable; }
1689#endif
1690
1691// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1692// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1693// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1694
1695//unsigned div_64(int numer, int denom);
1696static unsigned div_64(int numer, int denom)
1697{
1698 SkASSERT(numer <= denom);
1699 SkASSERT(numer > 0);
1700 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 int nbits = SkCLZ(numer);
1703 int dbits = SkCLZ(denom);
1704 int bits = 6 - nbits + dbits;
1705 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001706
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707 if (bits < 0) // detect underflow
1708 return 0;
1709
1710 denom <<= dbits - 1;
1711 numer <<= nbits - 1;
1712
1713 unsigned result = 0;
1714
1715 // do the first one
1716 if ((numer -= denom) >= 0)
1717 result = 1;
1718 else
1719 numer += denom;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001720
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721 // Now fall into our switch statement if there are more bits to compute
1722 if (bits > 0)
1723 {
1724 // make room for the rest of the answer bits
1725 result <<= bits;
1726 switch (bits) {
1727 case 6:
1728 if ((numer = (numer << 1) - denom) >= 0)
1729 result |= 32;
1730 else
1731 numer += denom;
1732 case 5:
1733 if ((numer = (numer << 1) - denom) >= 0)
1734 result |= 16;
1735 else
1736 numer += denom;
1737 case 4:
1738 if ((numer = (numer << 1) - denom) >= 0)
1739 result |= 8;
1740 else
1741 numer += denom;
1742 case 3:
1743 if ((numer = (numer << 1) - denom) >= 0)
1744 result |= 4;
1745 else
1746 numer += denom;
1747 case 2:
1748 if ((numer = (numer << 1) - denom) >= 0)
1749 result |= 2;
1750 else
1751 numer += denom;
1752 case 1:
1753 default: // not strictly need, but makes GCC make better ARM code
1754 if ((numer = (numer << 1) - denom) >= 0)
1755 result |= 1;
1756 else
1757 numer += denom;
1758 }
1759 }
1760 return result;
1761}
1762
1763// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1764static unsigned atan_0_90(SkFixed y, SkFixed x)
1765{
1766#ifdef SK_DEBUG
1767 {
1768 static bool gOnce;
1769 if (!gOnce)
1770 {
1771 gOnce = true;
1772 SkASSERT(div_64(55, 55) == 64);
1773 SkASSERT(div_64(128, 256) == 32);
1774 SkASSERT(div_64(2326528, 4685824) == 31);
1775 SkASSERT(div_64(753664, 5210112) == 9);
1776 SkASSERT(div_64(229376, 4882432) == 3);
1777 SkASSERT(div_64(2, 64) == 2);
1778 SkASSERT(div_64(1, 64) == 1);
1779 // test that we handle underflow correctly
1780 SkASSERT(div_64(12345, 0x54321234) == 0);
1781 }
1782 }
1783#endif
1784
1785 SkASSERT(y > 0 && x > 0);
1786 const uint8_t* table = build_sweep_table();
1787
1788 unsigned result;
1789 bool swap = (x < y);
1790 if (swap)
1791 {
1792 // first part of the atan(v) = PI/2 - atan(1/v) identity
1793 // since our div_64 and table want v <= 1, where v = y/x
1794 SkTSwap<SkFixed>(x, y);
1795 }
1796
1797 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001798
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799#ifdef SK_DEBUG
1800 {
1801 unsigned result2 = SkDivBits(y, x, 6);
1802 SkASSERT(result2 == result ||
1803 (result == 1 && result2 == 0));
1804 }
1805#endif
1806
1807 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1808 result = table[result];
1809
1810 if (swap)
1811 {
1812 // complete the atan(v) = PI/2 - atan(1/v) identity
1813 result = 64 - result;
1814 // pin to 63
1815 result -= result >> 6;
1816 }
1817
1818 SkASSERT(result <= 63);
1819 return result;
1820}
1821
1822// returns angle in a circle [0..2PI) -> [0..255]
1823static unsigned SkATan2_255(SkFixed y, SkFixed x)
1824{
1825 if (x == 0)
1826 {
1827 if (y == 0)
1828 return 0;
1829 return y < 0 ? 192 : 64;
1830 }
1831 if (y == 0)
1832 return x < 0 ? 128 : 0;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 /* Find the right quadrant for x,y
1835 Since atan_0_90 only handles the first quadrant, we rotate x,y
1836 appropriately before calling it, and then add the right amount
1837 to account for the real quadrant.
1838 quadrant 0 : add 0 | x > 0 && y > 0
1839 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1840 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1841 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001842
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843 map x<0 to (1 << 6)
1844 map y<0 to (3 << 6)
1845 add = map_x ^ map_y
1846 */
1847 int xsign = x >> 31;
1848 int ysign = y >> 31;
1849 int add = ((-xsign) ^ (ysign & 3)) << 6;
1850
1851#ifdef SK_DEBUG
1852 if (0 == add)
1853 SkASSERT(x > 0 && y > 0);
1854 else if (64 == add)
1855 SkASSERT(x < 0 && y > 0);
1856 else if (128 == add)
1857 SkASSERT(x < 0 && y < 0);
1858 else if (192 == add)
1859 SkASSERT(x > 0 && y < 0);
1860 else
1861 SkASSERT(!"bad value for add");
1862#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00001863
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1865 where we need to rotate x,y by 90 or -90
1866 */
1867 x = (x ^ xsign) - xsign;
1868 y = (y ^ ysign) - ysign;
1869 if (add & 64) // quads 1 or 3 need to swap x,y
1870 SkTSwap<SkFixed>(x, y);
1871
1872 unsigned result = add + atan_0_90(y, x);
1873 SkASSERT(result < 256);
1874 return result;
1875}
1876
1877void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1878{
1879 SkMatrix::MapXYProc proc = fDstToIndexProc;
1880 const SkMatrix& matrix = fDstToIndex;
1881 const SkPMColor* cache = this->getCache32();
1882 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001883
reed@android.com8a1c16f2008-12-17 15:59:43 +00001884 if (fDstToIndexClass != kPerspective_MatrixClass)
1885 {
1886 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1887 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1888 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1889 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001890
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1892 {
1893 SkFixed storage[2];
1894 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1895 &storage[0], &storage[1]);
1896 dx = storage[0];
1897 dy = storage[1];
1898 }
1899 else
1900 {
1901 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1902 dx = SkScalarToFixed(matrix.getScaleX());
1903 dy = SkScalarToFixed(matrix.getSkewY());
1904 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001905
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906 for (; count > 0; --count)
1907 {
1908 *dstC++ = cache[SkATan2_255(fy, fx)];
1909 fx += dx;
1910 fy += dy;
1911 }
1912 }
1913 else // perspective case
1914 {
1915 for (int stop = x + count; x < stop; x++)
1916 {
1917 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1918 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001919
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1921 SkScalarToFixed(srcPt.fX));
1922 *dstC++ = cache[index];
1923 }
1924 }
1925}
1926
1927void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1928{
1929 SkMatrix::MapXYProc proc = fDstToIndexProc;
1930 const SkMatrix& matrix = fDstToIndex;
1931 const uint16_t* cache = this->getCache16();
1932 int toggle = ((x ^ y) & 1) << kCache16Bits;
1933 SkPoint srcPt;
1934
1935 if (fDstToIndexClass != kPerspective_MatrixClass)
1936 {
1937 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1938 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1939 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1940 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001941
reed@android.com8a1c16f2008-12-17 15:59:43 +00001942 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1943 {
1944 SkFixed storage[2];
1945 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1946 &storage[0], &storage[1]);
1947 dx = storage[0];
1948 dy = storage[1];
1949 }
1950 else
1951 {
1952 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1953 dx = SkScalarToFixed(matrix.getScaleX());
1954 dy = SkScalarToFixed(matrix.getSkewY());
1955 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001956
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957 for (; count > 0; --count)
1958 {
1959 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1960 *dstC++ = cache[toggle + index];
1961 toggle ^= (1 << kCache16Bits);
1962 fx += dx;
1963 fy += dy;
1964 }
1965 }
1966 else // perspective case
1967 {
1968 for (int stop = x + count; x < stop; x++)
1969 {
1970 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1971 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001972
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1974 SkScalarToFixed(srcPt.fX));
1975 index >>= (8 - kCache16Bits);
1976 *dstC++ = cache[toggle + index];
1977 toggle ^= (1 << kCache16Bits);
1978 }
1979 }
1980}
1981
1982///////////////////////////////////////////////////////////////////////////
1983///////////////////////////////////////////////////////////////////////////
1984
1985// assumes colors is SkColor* and pos is SkScalar*
1986#define EXPAND_1_COLOR(count) \
1987 SkColor tmp[2]; \
1988 do { \
1989 if (1 == count) { \
1990 tmp[0] = tmp[1] = colors[0]; \
1991 colors = tmp; \
1992 pos = NULL; \
1993 count = 2; \
1994 } \
1995 } while (0)
1996
1997SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1998 const SkColor colors[], const SkScalar pos[], int colorCount,
1999 SkShader::TileMode mode, SkUnitMapper* mapper)
2000{
2001 if (NULL == pts || NULL == colors || colorCount < 1) {
2002 return NULL;
2003 }
2004 EXPAND_1_COLOR(colorCount);
2005
reed@android.comab840b82009-07-01 17:00:03 +00002006 return SkNEW_ARGS(Linear_Gradient,
2007 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008}
2009
2010SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
2011 const SkColor colors[], const SkScalar pos[], int colorCount,
2012 SkShader::TileMode mode, SkUnitMapper* mapper)
2013{
2014 if (radius <= 0 || NULL == colors || colorCount < 1) {
2015 return NULL;
2016 }
2017 EXPAND_1_COLOR(colorCount);
2018
reed@android.comab840b82009-07-01 17:00:03 +00002019 return SkNEW_ARGS(Radial_Gradient,
2020 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002021}
2022
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002023SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2024 SkScalar startRadius,
2025 const SkPoint& end,
2026 SkScalar endRadius,
2027 const SkColor colors[],
2028 const SkScalar pos[],
2029 int colorCount,
2030 SkShader::TileMode mode,
2031 SkUnitMapper* mapper)
2032{
2033 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2034 return NULL;
2035 }
2036 EXPAND_1_COLOR(colorCount);
2037
2038 return SkNEW_ARGS(Two_Point_Radial_Gradient,
2039 (start, startRadius, end, endRadius, colors, pos, colorCount, mode, mapper));
2040}
2041
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2043 const SkColor colors[],
2044 const SkScalar pos[],
2045 int count, SkUnitMapper* mapper)
2046{
2047 if (NULL == colors || count < 1) {
2048 return NULL;
2049 }
2050 EXPAND_1_COLOR(count);
2051
2052 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2053}
2054
2055static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
2056 Linear_Gradient::CreateProc);
2057
2058static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
2059 Radial_Gradient::CreateProc);
2060
2061static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
2062 Sweep_Gradient::CreateProc);
2063