blob: 84d7440a3d2c1281d7fedb7db131dbf38663e52c [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.com5eb158d2011-04-15 15:50:34 +000030static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
31 int count) {
32 if (count > 0) {
33 if (v0 == v1) {
34 sk_memset32(dst, v0, count);
35 } else {
36 int pairs = count >> 1;
37 for (int i = 0; i < pairs; i++) {
38 *dst++ = v0;
39 *dst++ = v1;
40 }
41 if (count & 1) {
42 *dst = v0;
43 }
44 }
45 }
46}
47
reed@google.com61eb0402011-04-15 12:11:12 +000048///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
50typedef SkFixed (*TileProc)(SkFixed);
51
reed@android.com41bccf52009-04-03 13:33:51 +000052static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 return SkClampMax(x, 0xFFFF);
54}
55
reed@android.com41bccf52009-04-03 13:33:51 +000056static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000057 return x & 0xFFFF;
58}
59
reed@android.com41bccf52009-04-03 13:33:51 +000060static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 int s = x << 15 >> 31;
62 return (x ^ s) & 0xFFFF;
63}
64
65static const TileProc gTileProcs[] = {
66 clamp_tileproc,
67 repeat_tileproc,
68 mirror_tileproc
69};
70
reed@google.com61eb0402011-04-15 12:11:12 +000071///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000072
reed@android.com200645d2009-12-14 16:41:57 +000073static inline int repeat_bits(int x, const int bits) {
74 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000075}
76
reed@android.com200645d2009-12-14 16:41:57 +000077static inline int mirror_bits(int x, const int bits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000078#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com200645d2009-12-14 16:41:57 +000079 if (x & (1 << bits))
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 x = ~x;
reed@android.com200645d2009-12-14 16:41:57 +000081 return x & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000082#else
reed@android.com200645d2009-12-14 16:41:57 +000083 int s = x << (31 - bits) >> 31;
84 return (x ^ s) & ((1 << bits) - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085#endif
86}
87
reed@android.com41bccf52009-04-03 13:33:51 +000088static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 return x & 0xFF;
90}
91
reed@android.com41bccf52009-04-03 13:33:51 +000092static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000094 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000096 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 return x & 255;
98#else
99 int s = x << 23 >> 31;
100 return (x ^ s) & 0xFF;
101#endif
102}
103
reed@google.com61eb0402011-04-15 12:11:12 +0000104///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105
106class Gradient_Shader : public SkShader {
107public:
108 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +0000109 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 virtual ~Gradient_Shader();
111
112 // overrides
113 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
114 virtual uint32_t getFlags() { return fFlags; }
115
116protected:
117 Gradient_Shader(SkFlattenableReadBuffer& );
118 SkUnitMapper* fMapper;
119 SkMatrix fPtsToUnit; // set by subclass
120 SkMatrix fDstToIndex;
121 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 TileMode fTileMode;
123 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000124 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 uint8_t fDstToIndexClass;
126 uint8_t fFlags;
127
128 struct Rec {
129 SkFixed fPos; // 0...1
130 uint32_t fScale; // (1 << 24) / range
131 };
132 Rec* fRecs;
133
134 enum {
reed@android.com512a8762009-12-14 15:25:36 +0000135 kCache16Bits = 8, // seems like enough for visual accuracy
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 kCache16Count = 1 << kCache16Bits,
reed@android.com512a8762009-12-14 15:25:36 +0000137 kCache16Mask = kCache16Count - 1,
138 kCache16Shift = 16 - kCache16Bits,
139
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 kCache32Bits = 8, // pretty much should always be 8
141 kCache32Count = 1 << kCache32Bits
142 };
143 virtual void flatten(SkFlattenableWriteBuffer& );
reed@google.com7c2f27d2011-03-07 19:29:00 +0000144 const uint16_t* getCache16() const;
145 const SkPMColor* getCache32() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146
reed@google.com7c2f27d2011-03-07 19:29:00 +0000147 void commonAsABitmap(SkBitmap*) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000148 void commonAsAGradient(GradientInfo*) const;
reed@android.com9b46e772009-06-05 12:24:41 +0000149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150private:
151 enum {
152 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
153
reed@android.com1c12abe2009-07-02 15:01:02 +0000154 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 };
156 SkColor fStorage[(kStorageSize + 3) >> 2];
157 SkColor* fOrigColors;
158
reed@google.com7c2f27d2011-03-07 19:29:00 +0000159 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
160 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161
reed@google.com7c2f27d2011-03-07 19:29:00 +0000162 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
163 mutable SkMallocPixelRef* fCache32PixelRef;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
165
reed@android.com512a8762009-12-14 15:25:36 +0000166 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000167 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
168 U8CPU alpha);
reed@android.com512a8762009-12-14 15:25:36 +0000169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 typedef SkShader INHERITED;
171};
172
reed@android.com41bccf52009-04-03 13:33:51 +0000173static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 SkASSERT(x >= 0 && x <= SK_Scalar1);
175
176#ifdef SK_SCALAR_IS_FLOAT
177 return (unsigned)(x * 0xFFFF);
178#else
179 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
180#endif
181}
182
reed@android.com41bccf52009-04-03 13:33:51 +0000183Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
184 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 SkASSERT(colorCount > 1);
186
187 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
188
189 fMapper = mapper;
reed@google.com82065d62011-02-07 15:30:46 +0000190 SkSafeRef(mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
193 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
194 fTileMode = mode;
195 fTileProc = gTileProcs[mode];
reed@google.com55b8e8c2011-01-13 16:22:35 +0000196
reed@android.com41bccf52009-04-03 13:33:51 +0000197 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000198 fCache32 = NULL;
199 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200
reed@android.com41bccf52009-04-03 13:33:51 +0000201 /* Note: we let the caller skip the first and/or last position.
202 i.e. pos[0] = 0.3, pos[1] = 0.7
203 In these cases, we insert dummy entries to ensure that the final data
204 will be bracketed by [0, 1].
205 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
206
207 Thus colorCount (the caller's value, and fColorCount (our value) may
208 differ by up to 2. In the above example:
209 colorCount = 2
210 fColorCount = 4
211 */
212 fColorCount = colorCount;
213 // check if we need to add in dummy start and/or end position/colors
214 bool dummyFirst = false;
215 bool dummyLast = false;
216 if (pos) {
217 dummyFirst = pos[0] != 0;
218 dummyLast = pos[colorCount - 1] != SK_Scalar1;
219 fColorCount += dummyFirst + dummyLast;
220 }
221
222 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000223 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000224 fOrigColors = reinterpret_cast<SkColor*>(
225 sk_malloc_throw(size * fColorCount));
226 }
227 else {
228 fOrigColors = fStorage;
229 }
230
231 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 {
reed@android.com41bccf52009-04-03 13:33:51 +0000233 SkColor* origColors = fOrigColors;
234 if (dummyFirst) {
235 *origColors++ = colors[0];
236 }
237 memcpy(origColors, colors, colorCount * sizeof(SkColor));
238 if (dummyLast) {
239 origColors += colorCount;
240 *origColors = colors[colorCount - 1];
241 }
242 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
reed@android.com1c12abe2009-07-02 15:01:02 +0000244 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000245 if (fColorCount > 2) {
246 Rec* recs = fRecs;
247 recs->fPos = 0;
248 // recs->fScale = 0; // unused;
249 recs += 1;
250 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 /* We need to convert the user's array of relative positions into
252 fixed-point positions and scale factors. We need these results
253 to be strictly monotonic (no two values equal or out of order).
254 Hence this complex loop that just jams a zero for the scale
255 value if it sees a segment out of order, and it assures that
256 we start at 0 and end at 1.0
257 */
258 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000259 int startIndex = dummyFirst ? 0 : 1;
260 int count = colorCount + dummyLast;
261 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 // force the last value to be 1.0
263 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000264 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000266 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 }
reed@android.com41bccf52009-04-03 13:33:51 +0000269 // pin curr withing range
270 if (curr < 0) {
271 curr = 0;
272 } else if (curr > SK_Fixed1) {
273 curr = SK_Fixed1;
274 }
275 recs->fPos = curr;
276 if (curr > prev) {
277 recs->fScale = (1 << 24) / (curr - prev);
278 } else {
279 recs->fScale = 0; // ignore this segment
280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 // get ready for the next value
282 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000283 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 }
reed@android.com41bccf52009-04-03 13:33:51 +0000285 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 SkFixed dp = SK_Fixed1 / (colorCount - 1);
287 SkFixed p = dp;
288 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000289 for (int i = 1; i < colorCount; i++) {
290 recs->fPos = p;
291 recs->fScale = scale;
292 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 p += dp;
294 }
295 }
296 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000297 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298}
299
300Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000301 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 fCacheAlpha = 256;
303
304 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
305
306 fCache16 = fCache16Storage = NULL;
reed@google.comdc731fd2010-12-23 15:19:47 +0000307 fCache32 = NULL;
308 fCache32PixelRef = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309
reed@android.com41bccf52009-04-03 13:33:51 +0000310 int colorCount = fColorCount = buffer.readU32();
311 if (colorCount > kColorStorageCount) {
312 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
313 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
314 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318
319 fTileMode = (TileMode)buffer.readU8();
320 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000321 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 if (colorCount > 2) {
323 Rec* recs = fRecs;
324 recs[0].fPos = 0;
325 for (int i = 1; i < colorCount; i++) {
326 recs[i].fPos = buffer.readS32();
327 recs[i].fScale = buffer.readU32();
328 }
329 }
330 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000331 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332}
333
reed@android.com41bccf52009-04-03 13:33:51 +0000334Gradient_Shader::~Gradient_Shader() {
335 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000337 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000338 SkSafeUnref(fCache32PixelRef);
reed@android.com41bccf52009-04-03 13:33:51 +0000339 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000341 }
reed@google.com82065d62011-02-07 15:30:46 +0000342 SkSafeUnref(fMapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343}
344
reed@android.com41bccf52009-04-03 13:33:51 +0000345void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 this->INHERITED::flatten(buffer);
347 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000348 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
350 buffer.write8(fTileMode);
351 if (fColorCount > 2) {
352 Rec* recs = fRecs;
353 for (int i = 1; i < fColorCount; i++) {
354 buffer.write32(recs[i].fPos);
355 buffer.write32(recs[i].fScale);
356 }
357 }
358 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
359}
360
361bool Gradient_Shader::setContext(const SkBitmap& device,
362 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000363 const SkMatrix& matrix) {
364 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000366 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367
368 const SkMatrix& inverse = this->getTotalInverse();
369
370 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
371 return false;
372 }
373
374 fDstToIndexProc = fDstToIndex.getMapXYProc();
375 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
376
377 // now convert our colors in to PMColors
378 unsigned paintAlpha = this->getPaintAlpha();
379 unsigned colorAlpha = 0xFF;
380
reed@android.com3d06a8c2009-07-07 18:19:59 +0000381 // FIXME: record colorAlpha in constructor, since this is not affected
382 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000383 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 SkColor src = fOrigColors[i];
385 unsigned sa = SkColorGetA(src);
386 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 }
388
389 fFlags = this->INHERITED::getFlags();
390 if ((colorAlpha & paintAlpha) == 0xFF) {
391 fFlags |= kOpaqueAlpha_Flag;
392 }
393 // we can do span16 as long as our individual colors are opaque,
394 // regardless of the paint's alpha
395 if (0xFF == colorAlpha) {
396 fFlags |= kHasSpan16_Flag;
397 }
398
399 // if the new alpha differs from the previous time we were called, inval our cache
400 // this will trigger the cache to be rebuilt.
401 // we don't care about the first time, since the cache ptrs will already be NULL
402 if (fCacheAlpha != paintAlpha) {
403 fCache16 = NULL; // inval the cache
404 fCache32 = NULL; // inval the cache
405 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000406 // inform our subclasses
reed@google.comdc731fd2010-12-23 15:19:47 +0000407 if (fCache32PixelRef) {
408 fCache32PixelRef->notifyPixelsChanged();
409 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410 }
411 return true;
412}
413
reed@android.com41bccf52009-04-03 13:33:51 +0000414static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 SkASSERT(a == SkToU8(a));
416 SkASSERT(b == SkToU8(b));
417 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418 return a + ((b - a) * scale >> 8);
419}
420
reed@android.com41bccf52009-04-03 13:33:51 +0000421static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
422 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423#if 0
424 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
425 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
426 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
427 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
428
429 return SkPackARGB32(a, r, g, b);
430#else
431 int otherBlend = 256 - blend;
432
433#if 0
434 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
435 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
436 SkASSERT((t0 & t1) == 0);
437 return t0 | t1;
438#else
439 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
440 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
441#endif
442
443#endif
444}
445
446#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
447
reed@android.com41bccf52009-04-03 13:33:51 +0000448/** We take the original colors, not our premultiplied PMColors, since we can
449 build a 16bit table as long as the original colors are opaque, even if the
450 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451*/
reed@android.com512a8762009-12-14 15:25:36 +0000452void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
453 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 SkASSERT(count > 1);
455 SkASSERT(SkColorGetA(c0) == 0xFF);
456 SkASSERT(SkColorGetA(c1) == 0xFF);
457
458 SkFixed r = SkColorGetR(c0);
459 SkFixed g = SkColorGetG(c0);
460 SkFixed b = SkColorGetB(c0);
461
462 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
463 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
464 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
465
466 r = SkIntToFixed(r) + 0x8000;
467 g = SkIntToFixed(g) + 0x8000;
468 b = SkIntToFixed(b) + 0x8000;
469
470 do {
471 unsigned rr = r >> 16;
472 unsigned gg = g >> 16;
473 unsigned bb = b >> 16;
474 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
reed@android.com512a8762009-12-14 15:25:36 +0000475 cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 cache += 1;
477 r += dr;
478 g += dg;
479 b += db;
480 } while (--count != 0);
481}
482
reed@google.com55b8e8c2011-01-13 16:22:35 +0000483/*
484 * 2x2 dither a fixed-point color component (8.16) down to 8, matching the
485 * semantics of how we 2x2 dither 32->16
486 */
487static inline U8CPU dither_fixed_to_8(SkFixed n) {
488 n >>= 8;
489 return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
490}
491
492/*
493 * For dithering with premultiply, we want to ceiling the alpha component,
494 * to ensure that it is always >= any color component.
495 */
496static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
497 n >>= 8;
498 return ((n << 1) - (n | (n >> 8))) >> 8;
499}
500
501void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
502 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 SkASSERT(count > 1);
504
reed@android.com1c12abe2009-07-02 15:01:02 +0000505 // need to apply paintAlpha to our two endpoints
506 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
507 SkFixed da;
508 {
509 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
510 da = SkIntToFixed(tmp - a) / (count - 1);
511 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512
reed@android.com1c12abe2009-07-02 15:01:02 +0000513 SkFixed r = SkColorGetR(c0);
514 SkFixed g = SkColorGetG(c0);
515 SkFixed b = SkColorGetB(c0);
516 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
517 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
518 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519
520 a = SkIntToFixed(a) + 0x8000;
521 r = SkIntToFixed(r) + 0x8000;
522 g = SkIntToFixed(g) + 0x8000;
523 b = SkIntToFixed(b) + 0x8000;
524
525 do {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000526 cache[0] = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
527 cache[kCache32Count] = SkPreMultiplyARGB(dither_ceil_fixed_to_8(a),
528 dither_fixed_to_8(r),
529 dither_fixed_to_8(g),
530 dither_fixed_to_8(b));
531 cache += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 a += da;
533 r += dr;
534 g += dg;
535 b += db;
536 } while (--count != 0);
537}
538
reed@android.com41bccf52009-04-03 13:33:51 +0000539static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 SkASSERT((unsigned)x <= SK_Fixed1);
541 return x - (x >> 16);
542}
543
reed@android.com200645d2009-12-14 16:41:57 +0000544static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
reed@android.come191b162009-12-18 21:33:39 +0000545 SkASSERT(x < (1U << bits));
reed@android.com200645d2009-12-14 16:41:57 +0000546 if (6 == bits) {
547 return (x << 10) | (x << 4) | (x >> 2);
548 }
549 if (8 == bits) {
550 return (x << 8) | x;
551 }
552 sk_throw();
553 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554}
555
reed@google.com7c2f27d2011-03-07 19:29:00 +0000556const uint16_t* Gradient_Shader::getCache16() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000557 if (fCache16 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000558 // double the count for dither entries
559 const int entryCount = kCache16Count * 2;
560 const size_t allocSize = sizeof(uint16_t) * entryCount;
561
reed@android.com3c9b2a42009-08-27 19:28:37 +0000562 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@google.com55b8e8c2011-01-13 16:22:35 +0000563 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000564 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000566 if (fColorCount == 2) {
reed@android.com512a8762009-12-14 15:25:36 +0000567 Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000568 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569 Rec* rec = fRecs;
570 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000571 for (int i = 1; i < fColorCount; i++) {
reed@android.com512a8762009-12-14 15:25:36 +0000572 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 SkASSERT(nextIndex < kCache16Count);
574
575 if (nextIndex > prevIndex)
reed@android.com512a8762009-12-14 15:25:36 +0000576 Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 prevIndex = nextIndex;
578 }
579 SkASSERT(prevIndex == kCache16Count - 1);
580 }
581
reed@android.com41bccf52009-04-03 13:33:51 +0000582 if (fMapper) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000583 fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 uint16_t* linear = fCache16; // just computed linear data
585 uint16_t* mapped = fCache16Storage; // storage for mapped data
586 SkUnitMapper* map = fMapper;
reed@android.com200645d2009-12-14 16:41:57 +0000587 for (int i = 0; i < kCache16Count; i++) {
588 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 mapped[i] = linear[index];
reed@android.com200645d2009-12-14 16:41:57 +0000590 mapped[i + kCache16Count] = linear[index + kCache16Count];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 }
592 sk_free(fCache16);
593 fCache16 = fCache16Storage;
594 }
595 }
596 return fCache16;
597}
598
reed@google.com7c2f27d2011-03-07 19:29:00 +0000599const SkPMColor* Gradient_Shader::getCache32() const {
reed@android.com41bccf52009-04-03 13:33:51 +0000600 if (fCache32 == NULL) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000601 // double the count for dither entries
602 const int entryCount = kCache32Count * 2;
603 const size_t allocSize = sizeof(SkPMColor) * entryCount;
604
reed@google.comdc731fd2010-12-23 15:19:47 +0000605 if (NULL == fCache32PixelRef) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000606 fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
607 (NULL, allocSize, NULL));
reed@google.comdc731fd2010-12-23 15:19:47 +0000608 }
609 fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
reed@android.com41bccf52009-04-03 13:33:51 +0000610 if (fColorCount == 2) {
reed@google.com55b8e8c2011-01-13 16:22:35 +0000611 Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
612 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000613 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 Rec* rec = fRecs;
615 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000616 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
618 SkASSERT(nextIndex < kCache32Count);
619
620 if (nextIndex > prevIndex)
reed@google.com55b8e8c2011-01-13 16:22:35 +0000621 Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
622 fOrigColors[i],
623 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 prevIndex = nextIndex;
625 }
626 SkASSERT(prevIndex == kCache32Count - 1);
627 }
628
reed@android.com41bccf52009-04-03 13:33:51 +0000629 if (fMapper) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000630 SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
reed@google.com55b8e8c2011-01-13 16:22:35 +0000631 (NULL, allocSize, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 SkPMColor* linear = fCache32; // just computed linear data
reed@google.comdc731fd2010-12-23 15:19:47 +0000633 SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 SkUnitMapper* map = fMapper;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000635 for (int i = 0; i < kCache32Count; i++) {
636 int index = map->mapUnit16((i << 8) | i) >> 8;
637 mapped[i] = linear[index];
638 mapped[i + kCache32Count] = linear[index + kCache32Count];
reed@android.com41bccf52009-04-03 13:33:51 +0000639 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000640 fCache32PixelRef->unref();
641 fCache32PixelRef = newPR;
642 fCache32 = (SkPMColor*)newPR->getAddr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 }
644 }
645 return fCache32;
646}
647
reed@google.comdc731fd2010-12-23 15:19:47 +0000648/*
649 * Because our caller might rebuild the same (logically the same) gradient
650 * over and over, we'd like to return exactly the same "bitmap" if possible,
651 * allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
652 * To do that, we maintain a private cache of built-bitmaps, based on our
653 * colors and positions. Note: we don't try to flatten the fMapper, so if one
654 * is present, we skip the cache for now.
655 */
reed@google.com7c2f27d2011-03-07 19:29:00 +0000656void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
reed@google.comdc731fd2010-12-23 15:19:47 +0000657 // don't have a way to put the mapper into our cache-key yet
658 if (fMapper) {
659 // force our cahce32pixelref to be built
660 (void)this->getCache32();
661 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
662 bitmap->setPixelRef(fCache32PixelRef);
663 return;
664 }
665
666 // build our key: [numColors + colors[] + {positions[]} ]
667 int count = 1 + fColorCount;
668 if (fColorCount > 2) {
669 count += fColorCount - 1; // fRecs[].fPos
670 }
671
672 SkAutoSTMalloc<16, int32_t> storage(count);
673 int32_t* buffer = storage.get();
674
675 *buffer++ = fColorCount;
676 memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
677 buffer += fColorCount;
678 if (fColorCount > 2) {
679 for (int i = 1; i < fColorCount; i++) {
680 *buffer++ = fRecs[i].fPos;
681 }
682 }
683 SkASSERT(buffer - storage.get() == count);
684
685 ///////////////////////////////////
686
687 static SkMutex gMutex;
688 static SkBitmapCache* gCache;
689 // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
690 static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
691 SkAutoMutexAcquire ama(gMutex);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000692
reed@google.comdc731fd2010-12-23 15:19:47 +0000693 if (NULL == gCache) {
694 gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
695 }
696 size_t size = count * sizeof(int32_t);
697
698 if (!gCache->find(storage.get(), size, bitmap)) {
699 // force our cahce32pixelref to be built
700 (void)this->getCache32();
701 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
702 bitmap->setPixelRef(fCache32PixelRef);
703
704 gCache->add(storage.get(), size, *bitmap);
705 }
706}
707
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000708void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
709 if (info) {
710 if (info->fColorCount >= fColorCount) {
711 if (info->fColors) {
712 memcpy(info->fColors, fOrigColors,
713 fColorCount * sizeof(SkColor));
714 }
715 if (info->fColorOffsets) {
716 if (fColorCount == 2) {
717 info->fColorOffsets[0] = 0;
718 info->fColorOffsets[1] = SK_Scalar1;
719 } else if (fColorCount > 2) {
720 for (int i = 0; i < fColorCount; i++)
721 info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
722 }
723 }
724 }
725 info->fColorCount = fColorCount;
726 info->fTileMode = fTileMode;
727 }
728}
729
reed@google.com61eb0402011-04-15 12:11:12 +0000730///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731
reed@android.com41bccf52009-04-03 13:33:51 +0000732static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 SkVector vec = pts[1] - pts[0];
734 SkScalar mag = vec.length();
735 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
736
737 vec.scale(inv);
738 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
739 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
740 matrix->postScale(inv, inv);
741}
742
743///////////////////////////////////////////////////////////////////////////////
744
745class Linear_Gradient : public Gradient_Shader {
746public:
747 Linear_Gradient(const SkPoint pts[2],
748 const SkColor colors[], const SkScalar pos[], int colorCount,
749 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000750 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
751 fStart(pts[0]),
752 fEnd(pts[1])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 {
754 pts_to_unit_matrix(pts, &fPtsToUnit);
755 }
reed@android.com9b46e772009-06-05 12:24:41 +0000756
reed@android.com5119bdb2009-06-12 21:27:03 +0000757 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
759 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000760 virtual BitmapType asABitmap(SkBitmap*, SkMatrix*,
reed@google.com7c2f27d2011-03-07 19:29:00 +0000761 TileMode*, SkScalar* twoPointRadialParams) const;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000762 virtual GradientType asAGradient(GradientInfo* info) const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763
reed@google.com55b8e8c2011-01-13 16:22:35 +0000764 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 return SkNEW_ARGS(Linear_Gradient, (buffer));
766 }
767
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000768 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
769 this->INHERITED::flatten(buffer);
770 buffer.writeScalar(fStart.fX);
771 buffer.writeScalar(fStart.fY);
772 buffer.writeScalar(fEnd.fX);
773 buffer.writeScalar(fEnd.fY);
774 }
775
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000777 Linear_Gradient(SkFlattenableReadBuffer& buffer)
778 : Gradient_Shader(buffer),
779 fStart(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
780 fEnd(SkPoint::Make(buffer.readScalar(), buffer.readScalar())) {
781 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 virtual Factory getFactory() { return CreateProc; }
783
784private:
785 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000786 const SkPoint fStart;
787 const SkPoint fEnd;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788};
789
reed@android.com5119bdb2009-06-12 21:27:03 +0000790bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
791 const SkMatrix& matrix) {
792 if (!this->INHERITED::setContext(device, paint, matrix)) {
793 return false;
794 }
795
796 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
797 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000798 fFlags |= SkShader::kConstInY32_Flag;
799 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
800 // only claim this if we do have a 16bit mode (i.e. none of our
801 // colors have alpha), and if we are not dithering (which obviously
802 // is not const in Y).
803 fFlags |= SkShader::kConstInY16_Flag;
804 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000805 }
806 return true;
807}
808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@google.com61eb0402011-04-15 12:11:12 +0000810static inline bool no_need_for_clamp(int fx, int dx, int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 SkASSERT(count > 0);
812 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
813}
814
reed@google.com5eb158d2011-04-15 15:50:34 +0000815#include "SkClampRange.h"
816
817#define NO_CHECK_ITER \
818 fi = fx >> 8; \
819 SkASSERT(fi <= 0xFF); \
820 fx += dx; \
821 *dstC++ = cache[toggle + fi]; \
822 toggle ^= TOGGLE_MASK
823
824
reed@google.com61eb0402011-04-15 12:11:12 +0000825void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 SkASSERT(count > 0);
827
828 SkPoint srcPt;
829 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
830 TileProc proc = fTileProc;
831 const SkPMColor* cache = this->getCache32();
reed@google.com55b8e8c2011-01-13 16:22:35 +0000832#ifdef USE_DITHER_32BIT_GRADIENT
833 int toggle = ((x ^ y) & 1) << kCache32Bits;
834 const int TOGGLE_MASK = (1 << kCache32Bits);
835#else
836 int toggle = 0;
837 const int TOGGLE_MASK = 0;
838#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839
reed@android.comc552a432009-06-12 20:02:50 +0000840 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000841 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
842 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
844
reed@android.comc552a432009-06-12 20:02:50 +0000845 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 SkFixed dxStorage[1];
847 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
848 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000849 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
851 dx = SkScalarToFixed(fDstToIndex.getScaleX());
852 }
853
reed@android.comc552a432009-06-12 20:02:50 +0000854 if (SkFixedNearlyZero(dx)) {
855 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 unsigned fi = proc(fx);
857 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000858 // TODO: dither version
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000860 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +0000861#if 0
reed@google.com55b8e8c2011-01-13 16:22:35 +0000862 do {
863 unsigned fi = SkClampMax(fx >> 8, 0xFF);
864 SkASSERT(fi <= 0xFF);
865 fx += dx;
866 *dstC++ = cache[toggle + fi];
867 toggle ^= TOGGLE_MASK;
868 } while (--count != 0);
reed@google.com5eb158d2011-04-15 15:50:34 +0000869#else
870 SkClampRange range;
871 range.init(fx, dx, count, 0, 0xFF);
872
873 if ((count = range.fCount0) > 0) {
874 sk_memset32_dither(dstC,
875 cache[toggle + range.fV0],
876 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
877 count);
878 dstC += count;
879 }
880 if ((count = range.fCount1) > 0) {
881 unsigned fi;
882 int i, unroll = count >> 3;
883 for (i = 0; i < unroll; i++) {
884 NO_CHECK_ITER; NO_CHECK_ITER;
885 NO_CHECK_ITER; NO_CHECK_ITER;
886 NO_CHECK_ITER; NO_CHECK_ITER;
887 NO_CHECK_ITER; NO_CHECK_ITER;
888 }
889 if ((count &= 7) > 0) {
890 do {
891 NO_CHECK_ITER;
892 } while (--count != 0);
893 }
894 }
895 if ((count = range.fCount2) > 0) {
896 sk_memset32_dither(dstC,
897 cache[toggle + range.fV1],
898 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
899 count);
900 }
901#endif
reed@android.comc552a432009-06-12 20:02:50 +0000902 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 do {
904 unsigned fi = mirror_8bits(fx >> 8);
905 SkASSERT(fi <= 0xFF);
906 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000907 *dstC++ = cache[toggle + fi];
908 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000910 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 SkASSERT(proc == repeat_tileproc);
912 do {
913 unsigned fi = repeat_8bits(fx >> 8);
914 SkASSERT(fi <= 0xFF);
915 fx += dx;
reed@google.com55b8e8c2011-01-13 16:22:35 +0000916 *dstC++ = cache[toggle + fi];
917 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 } while (--count != 0);
919 }
reed@android.comc552a432009-06-12 20:02:50 +0000920 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 SkScalar dstX = SkIntToScalar(x);
922 SkScalar dstY = SkIntToScalar(y);
923 do {
924 dstProc(fDstToIndex, dstX, dstY, &srcPt);
925 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
926 SkASSERT(fi <= 0xFFFF);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000927 *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))];
928 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 dstX += SK_Scalar1;
930 } while (--count != 0);
931 }
932}
933
reed@google.com55b8e8c2011-01-13 16:22:35 +0000934SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
reed@google.comdc731fd2010-12-23 15:19:47 +0000935 SkMatrix* matrix,
936 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +0000937 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 if (bitmap) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000939 this->commonAsABitmap(bitmap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 }
941 if (matrix) {
942 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
943 matrix->preConcat(fPtsToUnit);
944 }
945 if (xy) {
946 xy[0] = fTileMode;
947 xy[1] = kClamp_TileMode;
948 }
reed@google.comdc731fd2010-12-23 15:19:47 +0000949 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950}
951
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000952SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
953 if (info) {
954 commonAsAGradient(info);
955 info->fPoint[0] = fStart;
956 info->fPoint[1] = fEnd;
957 }
958 return kLinear_GradientType;
959}
960
reed@android.com3c9b2a42009-08-27 19:28:37 +0000961static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
962 int count) {
963 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 *dst++ = value;
965 count -= 1;
966 SkTSwap(value, other);
967 }
968
969 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
reed@google.com55b8e8c2011-01-13 16:22:35 +0000970
reed@android.com3c9b2a42009-08-27 19:28:37 +0000971 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000973 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975
reed@google.com5eb158d2011-04-15 15:50:34 +0000976#define NO_CHECK_ITER_16 \
977 fi = fx >> kCache16Shift; \
978 SkASSERT(fi <= kCache16Mask); \
979 fx += dx; \
980 *dstC++ = cache[toggle + fi]; \
981 toggle ^= TOGGLE_MASK
982
983
reed@google.com61eb0402011-04-15 12:11:12 +0000984void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 SkASSERT(count > 0);
986
987 SkPoint srcPt;
988 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
989 TileProc proc = fTileProc;
990 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@google.com5eb158d2011-04-15 15:50:34 +0000992 const int TOGGLE_MASK = (1 << kCache32Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000994 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +0000995 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
996 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
998
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000999 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 SkFixed dxStorage[1];
1001 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
1002 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001003 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1005 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1006 }
1007
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001008 if (SkFixedNearlyZero(dx)) {
1009 // we're a vertical gradient, so no change in a span
reed@android.com512a8762009-12-14 15:25:36 +00001010 unsigned fi = proc(fx) >> kCache16Shift;
1011 SkASSERT(fi <= kCache16Mask);
reed@google.com5eb158d2011-04-15 15:50:34 +00001012 dither_memset16(dstC, cache[toggle + fi],
1013 cache[(toggle ^ TOGGLE_MASK) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001014 } else if (proc == clamp_tileproc) {
reed@google.com5eb158d2011-04-15 15:50:34 +00001015#if 0
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 do {
reed@android.com512a8762009-12-14 15:25:36 +00001017 unsigned fi = SkClampMax(fx >> kCache16Shift, kCache16Mask);
1018 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001021 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 } while (--count != 0);
reed@google.com5eb158d2011-04-15 15:50:34 +00001023#else
1024 SkClampRange range;
1025 range.init(fx, dx, count, 0, kCache16Mask);
1026
1027 if ((count = range.fCount0) > 0) {
1028 dither_memset16(dstC,
1029 cache[toggle + range.fV0],
1030 cache[(toggle ^ TOGGLE_MASK) + range.fV0],
1031 count);
1032 dstC += count;
1033 }
1034 if ((count = range.fCount1) > 0) {
1035 unsigned fi;
1036 int i, unroll = count >> 3;
1037 for (i = 0; i < unroll; i++) {
1038 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1039 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1040 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1041 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
1042 }
1043 if ((count &= 7) > 0) {
1044 do {
1045 NO_CHECK_ITER_16;
1046 } while (--count != 0);
1047 }
1048 }
1049 if ((count = range.fCount2) > 0) {
1050 dither_memset16(dstC,
1051 cache[toggle + range.fV1],
1052 cache[(toggle ^ TOGGLE_MASK) + range.fV1],
1053 count);
1054 }
1055#endif
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001056 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 do {
reed@android.com200645d2009-12-14 16:41:57 +00001058 unsigned fi = mirror_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001059 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001062 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001064 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 SkASSERT(proc == repeat_tileproc);
1066 do {
reed@android.com200645d2009-12-14 16:41:57 +00001067 unsigned fi = repeat_bits(fx >> kCache16Shift, kCache16Bits);
reed@android.com512a8762009-12-14 15:25:36 +00001068 SkASSERT(fi <= kCache16Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 *dstC++ = cache[toggle + fi];
reed@google.com5eb158d2011-04-15 15:50:34 +00001071 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 } while (--count != 0);
1073 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +00001074 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 SkScalar dstX = SkIntToScalar(x);
1076 SkScalar dstY = SkIntToScalar(y);
1077 do {
1078 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1079 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
1080 SkASSERT(fi <= 0xFFFF);
1081
reed@android.com512a8762009-12-14 15:25:36 +00001082 int index = fi >> kCache16Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 *dstC++ = cache[toggle + index];
reed@google.com5eb158d2011-04-15 15:50:34 +00001084 toggle ^= TOGGLE_MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085
1086 dstX += SK_Scalar1;
1087 } while (--count != 0);
1088 }
1089}
1090
1091///////////////////////////////////////////////////////////////////////////////
1092
1093#define kSQRT_TABLE_BITS 11
1094#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
1095
1096#include "SkRadialGradient_Table.h"
1097
1098#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
1099
1100#include <stdio.h>
1101
reed@google.com61eb0402011-04-15 12:11:12 +00001102void SkRadialGradient_BuildTable() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
1104
1105 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
1106 SkASSERT(file);
1107 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
1108
reed@google.com61eb0402011-04-15 12:11:12 +00001109 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
1110 if ((i & 15) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 ::fprintf(file, "\t");
reed@google.com61eb0402011-04-15 12:11:12 +00001112 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113
1114 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
1115
1116 ::fprintf(file, "0x%02X", value);
reed@google.com61eb0402011-04-15 12:11:12 +00001117 if (i < kSQRT_TABLE_SIZE-1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 ::fprintf(file, ", ");
reed@google.com61eb0402011-04-15 12:11:12 +00001119 }
1120 if ((i & 15) == 15) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 ::fprintf(file, "\n");
reed@google.com61eb0402011-04-15 12:11:12 +00001122 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 }
1124 ::fprintf(file, "};\n");
1125 ::fclose(file);
1126}
1127
1128#endif
1129
1130
reed@google.com61eb0402011-04-15 12:11:12 +00001131static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
1132 SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 SkScalar inv = SkScalarInvert(radius);
1134
1135 matrix->setTranslate(-center.fX, -center.fY);
1136 matrix->postScale(inv, inv);
1137}
1138
1139class Radial_Gradient : public Gradient_Shader {
1140public:
1141 Radial_Gradient(const SkPoint& center, SkScalar radius,
1142 const SkColor colors[], const SkScalar pos[], int colorCount,
1143 SkShader::TileMode mode, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001144 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1145 fCenter(center),
1146 fRadius(radius)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 {
1148 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
1149 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
1150
1151 rad_to_unit_matrix(center, radius, &fPtsToUnit);
1152 }
reed@google.com61eb0402011-04-15 12:11:12 +00001153
1154 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 SkASSERT(count > 0);
1156
1157 SkPoint srcPt;
1158 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1159 TileProc proc = fTileProc;
1160 const SkPMColor* cache = this->getCache32();
1161
reed@google.com61eb0402011-04-15 12:11:12 +00001162 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001163 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1164 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1166 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1167
reed@google.com61eb0402011-04-15 12:11:12 +00001168 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 SkFixed storage[2];
1170 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1171 dx = storage[0];
1172 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00001173 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1175 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1176 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1177 }
1178
reed@google.com61eb0402011-04-15 12:11:12 +00001179 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 const uint8_t* sqrt_table = gSqrt8Table;
1181 fx >>= 1;
1182 dx >>= 1;
1183 fy >>= 1;
1184 dy >>= 1;
1185 do {
1186 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1187 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1188 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1189 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1190 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
1191 fx += dx;
1192 fy += dy;
1193 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001194 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001196 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1197 if (magnitudeSquared < 0) // Overflow.
1198 magnitudeSquared = SK_FixedMax;
1199 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 unsigned fi = mirror_tileproc(dist);
1201 SkASSERT(fi <= 0xFFFF);
1202 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1203 fx += dx;
1204 fy += dy;
1205 } while (--count != 0);
reed@google.com61eb0402011-04-15 12:11:12 +00001206 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 SkASSERT(proc == repeat_tileproc);
1208 do {
wjmaclean@chromium.org1fbf7602011-04-13 13:31:06 +00001209 SkFixed magnitudeSquared = SkFixedSquare(fx) + SkFixedSquare(fy);
1210 if (magnitudeSquared < 0) // Overflow.
1211 magnitudeSquared = SK_FixedMax;
1212 SkFixed dist = SkFixedSqrt(magnitudeSquared);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 unsigned fi = repeat_tileproc(dist);
1214 SkASSERT(fi <= 0xFFFF);
1215 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1216 fx += dx;
1217 fy += dy;
1218 } while (--count != 0);
1219 }
reed@google.com61eb0402011-04-15 12:11:12 +00001220 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 SkScalar dstX = SkIntToScalar(x);
1222 SkScalar dstY = SkIntToScalar(y);
1223 do {
1224 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1225 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1226 SkASSERT(fi <= 0xFFFF);
1227 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1228 dstX += SK_Scalar1;
1229 } while (--count != 0);
1230 }
1231 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001232
1233 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234 SkASSERT(count > 0);
1235
1236 SkPoint srcPt;
1237 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1238 TileProc proc = fTileProc;
1239 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241
reed@android.com3c9b2a42009-08-27 19:28:37 +00001242 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@google.comdc731fd2010-12-23 15:19:47 +00001243 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1244 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1246 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1247
reed@android.com3c9b2a42009-08-27 19:28:37 +00001248 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 SkFixed storage[2];
1250 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1251 dx = storage[0];
1252 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001253 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1255 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1256 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1257 }
1258
reed@android.com3c9b2a42009-08-27 19:28:37 +00001259 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 const uint8_t* sqrt_table = gSqrt8Table;
1261
1262 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1263 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1264 precision, but that appears to be visually OK. If we decide this is OK for
1265 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1266 to avoid having to do these extra shifts each time.
1267 */
1268 fx >>= 1;
1269 dx >>= 1;
1270 fy >>= 1;
1271 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001272 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 +00001273 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1274 fy *= fy;
1275 do {
1276 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1277 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1278 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1279 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1281 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001283 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 do {
1285 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1286 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1287 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1288 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1289 fx += dx;
1290 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1292 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 } while (--count != 0);
1294 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001295 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 do {
1297 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1298 unsigned fi = mirror_tileproc(dist);
1299 SkASSERT(fi <= 0xFFFF);
1300 fx += dx;
1301 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1303 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001305 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 SkASSERT(proc == repeat_tileproc);
1307 do {
1308 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1309 unsigned fi = repeat_tileproc(dist);
1310 SkASSERT(fi <= 0xFFFF);
1311 fx += dx;
1312 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1314 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 } while (--count != 0);
1316 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001317 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 SkScalar dstX = SkIntToScalar(x);
1319 SkScalar dstY = SkIntToScalar(y);
1320 do {
1321 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1322 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1323 SkASSERT(fi <= 0xFFFF);
1324
1325 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 *dstC++ = cache[toggle + index];
1327 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328
1329 dstX += SK_Scalar1;
1330 } while (--count != 0);
1331 }
1332 }
1333
reed@google.com55b8e8c2011-01-13 16:22:35 +00001334 virtual BitmapType asABitmap(SkBitmap* bitmap,
1335 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001336 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001337 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001338 if (bitmap) {
1339 this->commonAsABitmap(bitmap);
1340 }
1341 if (matrix) {
1342 matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
1343 matrix->preConcat(fPtsToUnit);
1344 }
1345 if (xy) {
1346 xy[0] = fTileMode;
1347 xy[1] = kClamp_TileMode;
1348 }
1349 return kRadial_BitmapType;
1350 }
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001351 virtual GradientType asAGradient(GradientInfo* info) const {
1352 if (info) {
1353 commonAsAGradient(info);
1354 info->fPoint[0] = fCenter;
1355 info->fRadius[0] = fRadius;
1356 }
1357 return kRadial_GradientType;
1358 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001359
1360 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361 return SkNEW_ARGS(Radial_Gradient, (buffer));
1362 }
1363
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001364 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1365 this->INHERITED::flatten(buffer);
1366 buffer.writeScalar(fCenter.fX);
1367 buffer.writeScalar(fCenter.fY);
1368 buffer.writeScalar(fRadius);
1369 }
1370
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001372 Radial_Gradient(SkFlattenableReadBuffer& buffer)
1373 : Gradient_Shader(buffer),
1374 fCenter(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1375 fRadius(buffer.readScalar()) {
1376 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 virtual Factory getFactory() { return CreateProc; }
1378
1379private:
1380 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001381 const SkPoint fCenter;
1382 const SkScalar fRadius;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383};
1384
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001385/* Two-point radial gradients are specified by two circles, each with a center
1386 point and radius. The gradient can be considered to be a series of
1387 concentric circles, with the color interpolated from the start circle
1388 (at t=0) to the end circle (at t=1).
1389
1390 For each point (x, y) in the span, we want to find the
1391 interpolated circle that intersects that point. The center
1392 of the desired circle (Cx, Cy) falls at some distance t
1393 along the line segment between the start point (Sx, Sy) and
1394 end point (Ex, Ey):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001395
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001396 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1397 Cy = (1 - t) * Sy + t * Ey
reed@google.com55b8e8c2011-01-13 16:22:35 +00001398
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001399 The radius of the desired circle (r) is also a linear interpolation t
1400 between the start and end radii (Sr and Er):
reed@google.com55b8e8c2011-01-13 16:22:35 +00001401
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001402 r = (1 - t) * Sr + t * Er
reed@google.com55b8e8c2011-01-13 16:22:35 +00001403
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001404 But
reed@google.com55b8e8c2011-01-13 16:22:35 +00001405
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001406 (x - Cx)^2 + (y - Cy)^2 = r^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001407
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001408 so
reed@google.com55b8e8c2011-01-13 16:22:35 +00001409
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001410 (x - ((1 - t) * Sx + t * Ex))^2
1411 + (y - ((1 - t) * Sy + t * Ey))^2
1412 = ((1 - t) * Sr + t * Er)^2
reed@google.com55b8e8c2011-01-13 16:22:35 +00001413
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001414 Solving for t yields
reed@google.com55b8e8c2011-01-13 16:22:35 +00001415
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001416 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1417 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1418 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001419
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001420 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1421
1422 [Dx^2 + Dy^2 - Dr^2)] * t^2
1423 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1424 + [dx^2 + dy^2 - Sr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001425
1426 A quadratic in t. The two roots of the quadratic reflect the two
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001427 possible circles on which the point may fall. Solving for t yields
1428 the gradient value to use.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001429
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001430 If a<0, the start circle is entirely contained in the
1431 end circle, and one of the roots will be <0 or >1 (off the line
1432 segment). If a>0, the start circle falls at least partially
1433 outside the end circle (or vice versa), and the gradient
1434 defines a "tube" where a point may be on one circle (on the
1435 inside of the tube) or the other (outside of the tube). We choose
1436 one arbitrarily.
reed@google.com55b8e8c2011-01-13 16:22:35 +00001437
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001438 In order to keep the math to within the limits of fixed point,
1439 we divide the entire quadratic by Dr^2, and replace
1440 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
reed@google.com55b8e8c2011-01-13 16:22:35 +00001441
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001442 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1443 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1444 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00001445
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001446 (x' and y' are computed by appending the subtract and scale to the
1447 fDstToIndex matrix in the constructor).
1448
1449 Since the 'A' component of the quadratic is independent of x' and y', it
1450 is precomputed in the constructor. Since the 'B' component is linear in
1451 x' and y', if x and y are linear in the span, 'B' can be computed
reed@google.com55b8e8c2011-01-13 16:22:35 +00001452 incrementally with a simple delta (db below). If it is not (e.g.,
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001453 a perspective projection), it must be computed in the loop.
1454
1455*/
1456
reed@google.come61414c2011-04-15 18:14:16 +00001457#ifdef SK_USE_SLOW_2POINT_RADIAL_GRADIENT
1458static inline SkFixed two_point_radial(SkFixed b, SkFixed fx, SkFixed fy, SkFixed sr2d2, SkFixed foura, SkFixed oneOverTwoA, bool posRoot) {
1459 SkFixed c = SkFixedSquare(fx) + SkFixedSquare(fy) - sr2d2;
1460 SkFixed discrim = SkFixedSquare(b) - SkFixedMul(foura, c);
1461 if (discrim < 0) {
1462 discrim = -discrim;
1463 }
1464 SkFixed rootDiscrim = SkFixedSqrt(discrim);
1465 if (posRoot) {
1466 return SkFixedMul(-b + rootDiscrim, oneOverTwoA);
1467 } else {
1468 return SkFixedMul(-b - rootDiscrim, oneOverTwoA);
1469 }
1470}
1471#else
reed@google.com84e9c082011-04-13 17:44:24 +00001472static inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
1473 SkScalar sr2d2, SkScalar foura,
1474 SkScalar oneOverTwoA, bool posRoot) {
1475 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
1476 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001477 if (discrim < 0) {
1478 discrim = -discrim;
1479 }
reed@google.com84e9c082011-04-13 17:44:24 +00001480 SkScalar rootDiscrim = SkScalarSqrt(discrim);
1481 SkScalar result;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001482 if (posRoot) {
reed@google.com84e9c082011-04-13 17:44:24 +00001483 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001484 } else {
reed@google.com84e9c082011-04-13 17:44:24 +00001485 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001486 }
reed@google.com84e9c082011-04-13 17:44:24 +00001487 return SkScalarToFixed(result);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001488}
reed@google.come61414c2011-04-15 18:14:16 +00001489#endif
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001490
1491class Two_Point_Radial_Gradient : public Gradient_Shader {
1492public:
1493 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1494 const SkPoint& end, SkScalar endRadius,
1495 const SkColor colors[], const SkScalar pos[],
1496 int colorCount, SkShader::TileMode mode,
1497 SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001498 : Gradient_Shader(colors, pos, colorCount, mode, mapper),
1499 fCenter1(start),
1500 fCenter2(end),
1501 fRadius1(startRadius),
1502 fRadius2(endRadius) {
1503 init();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001504 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001505
1506 virtual BitmapType asABitmap(SkBitmap* bitmap,
1507 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001508 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001509 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001510 if (bitmap) {
1511 this->commonAsABitmap(bitmap);
1512 }
1513 SkScalar diffL = 0; // just to avoid gcc warning
1514 if (matrix || twoPointRadialParams) {
reed@google.com55b8e8c2011-01-13 16:22:35 +00001515 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
reed@google.comdc731fd2010-12-23 15:19:47 +00001516 SkScalarSquare(fDiff.fY));
1517 }
1518 if (matrix) {
bsalomon@google.comdc66c422011-04-06 19:35:37 +00001519 if (diffL) {
1520 SkScalar invDiffL = SkScalarInvert(diffL);
1521 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
1522 SkScalarMul(invDiffL, fDiff.fX));
1523 } else {
1524 matrix->reset();
1525 }
reed@google.comdc731fd2010-12-23 15:19:47 +00001526 matrix->preConcat(fPtsToUnit);
1527 }
1528 if (xy) {
1529 xy[0] = fTileMode;
1530 xy[1] = kClamp_TileMode;
1531 }
1532 if (NULL != twoPointRadialParams) {
1533 twoPointRadialParams[0] = diffL;
1534 twoPointRadialParams[1] = fStartRadius;
1535 twoPointRadialParams[2] = fDiffRadius;
1536 }
1537 return kTwoPointRadial_BitmapType;
1538 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001539
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001540 virtual GradientType asAGradient(GradientInfo* info) const {
1541 if (info) {
1542 commonAsAGradient(info);
1543 info->fPoint[0] = fCenter1;
1544 info->fPoint[1] = fCenter2;
1545 info->fRadius[0] = fRadius1;
1546 info->fRadius[1] = fRadius2;
1547 }
1548 return kRadial2_GradientType;
1549 }
1550
reed@google.come61414c2011-04-15 18:14:16 +00001551#ifdef SK_USE_SLOW_2POINT_RADIAL_GRADIENT
1552 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1553 {
1554 SkASSERT(count > 0);
1555
1556 // Zero difference between radii: fill with transparent black.
1557 if (fDiffRadius == 0) {
1558 sk_bzero(dstC, count * sizeof(*dstC));
1559 return;
1560 }
1561 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1562 TileProc proc = fTileProc;
1563 const SkPMColor* cache = this->getCache32();
1564 SkFixed diffx = SkScalarToFixed(fDiff.fX);
1565 SkFixed diffy = SkScalarToFixed(fDiff.fY);
1566 SkFixed foura = SkScalarToFixed(SkScalarMul(fA, 4));
1567 SkFixed startRadius = SkScalarToFixed(fStartRadius);
1568 SkFixed sr2D2 = SkScalarToFixed(fSr2D2);
1569 SkFixed oneOverTwoA = SkScalarToFixed(fOneOverTwoA);
1570 bool posRoot = fDiffRadius < 0;
1571 if (fDstToIndexClass != kPerspective_MatrixClass)
1572 {
1573 SkPoint srcPt;
1574 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1575 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1576 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1577 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1578
1579 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1580 {
1581 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &dx, &dy);
1582 }
1583 else
1584 {
1585 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1586 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1587 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1588 }
1589 SkFixed b = (SkFixedMul(diffx, fx) +
1590 SkFixedMul(diffy, fy) - startRadius) << 1;
1591 SkFixed db = (SkFixedMul(diffx, dx) +
1592 SkFixedMul(diffy, dy)) << 1;
1593 if (proc == clamp_tileproc)
1594 {
1595 for (; count > 0; --count) {
1596 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1597 SkFixed index = SkClampMax(t, 0xFFFF);
1598 SkASSERT(index <= 0xFFFF);
1599 *dstC++ = cache[index >> (16 - kCache32Bits)];
1600 fx += dx;
1601 fy += dy;
1602 b += db;
1603 }
1604 }
1605 else if (proc == mirror_tileproc)
1606 {
1607 for (; count > 0; --count) {
1608 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1609 SkFixed index = mirror_tileproc(t);
1610 SkASSERT(index <= 0xFFFF);
1611 *dstC++ = cache[index >> (16 - kCache32Bits)];
1612 fx += dx;
1613 fy += dy;
1614 b += db;
1615 }
1616 }
1617 else
1618 {
1619 SkASSERT(proc == repeat_tileproc);
1620 for (; count > 0; --count) {
1621 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1622 SkFixed index = repeat_tileproc(t);
1623 SkASSERT(index <= 0xFFFF);
1624 *dstC++ = cache[index >> (16 - kCache32Bits)];
1625 fx += dx;
1626 fy += dy;
1627 b += db;
1628 }
1629 }
1630 }
1631 else // perspective case
1632 {
1633 SkScalar dstX = SkIntToScalar(x);
1634 SkScalar dstY = SkIntToScalar(y);
1635 for (; count > 0; --count) {
1636 SkPoint srcPt;
1637 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1638 SkFixed fx = SkScalarToFixed(srcPt.fX);
1639 SkFixed fy = SkScalarToFixed(srcPt.fY);
1640 SkFixed b = (SkFixedMul(diffx, fx) +
1641 SkFixedMul(diffy, fy) - startRadius) << 1;
1642 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1643 SkFixed index = proc(t);
1644 SkASSERT(index <= 0xFFFF);
1645 *dstC++ = cache[index >> (16 - kCache32Bits)];
1646 dstX += SK_Scalar1;
1647 }
1648 }
1649 }
1650#else
reed@google.com61eb0402011-04-15 12:11:12 +00001651 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001652 SkASSERT(count > 0);
1653
1654 // Zero difference between radii: fill with transparent black.
1655 if (fDiffRadius == 0) {
1656 sk_bzero(dstC, count * sizeof(*dstC));
1657 return;
1658 }
1659 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1660 TileProc proc = fTileProc;
1661 const SkPMColor* cache = this->getCache32();
reed@google.com84e9c082011-04-13 17:44:24 +00001662
1663 SkScalar foura = fA * 4;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001664 bool posRoot = fDiffRadius < 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001665 if (fDstToIndexClass != kPerspective_MatrixClass) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001666 SkPoint srcPt;
reed@google.comdc731fd2010-12-23 15:19:47 +00001667 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
1668 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001669 SkScalar dx, fx = srcPt.fX;
1670 SkScalar dy, fy = srcPt.fY;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001671
reed@google.com61eb0402011-04-15 12:11:12 +00001672 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@google.com84e9c082011-04-13 17:44:24 +00001673 SkFixed fixedX, fixedY;
1674 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
1675 dx = SkFixedToScalar(fixedX);
1676 dy = SkFixedToScalar(fixedY);
reed@google.com61eb0402011-04-15 12:11:12 +00001677 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001678 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
reed@google.com84e9c082011-04-13 17:44:24 +00001679 dx = fDstToIndex.getScaleX();
1680 dy = fDstToIndex.getSkewY();
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001681 }
reed@google.com84e9c082011-04-13 17:44:24 +00001682 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1683 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1684 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
1685 SkScalarMul(fDiff.fY, dy)) * 2;
reed@google.com61eb0402011-04-15 12:11:12 +00001686 if (proc == clamp_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001687 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001688 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001689 SkFixed index = SkClampMax(t, 0xFFFF);
1690 SkASSERT(index <= 0xFFFF);
1691 *dstC++ = cache[index >> (16 - kCache32Bits)];
1692 fx += dx;
1693 fy += dy;
1694 b += db;
1695 }
reed@google.com61eb0402011-04-15 12:11:12 +00001696 } else if (proc == mirror_tileproc) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001697 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001698 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001699 SkFixed index = mirror_tileproc(t);
1700 SkASSERT(index <= 0xFFFF);
1701 *dstC++ = cache[index >> (16 - kCache32Bits)];
1702 fx += dx;
1703 fy += dy;
1704 b += db;
1705 }
reed@google.com61eb0402011-04-15 12:11:12 +00001706 } else {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001707 SkASSERT(proc == repeat_tileproc);
1708 for (; count > 0; --count) {
reed@google.com84e9c082011-04-13 17:44:24 +00001709 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001710 SkFixed index = repeat_tileproc(t);
1711 SkASSERT(index <= 0xFFFF);
1712 *dstC++ = cache[index >> (16 - kCache32Bits)];
1713 fx += dx;
1714 fy += dy;
1715 b += db;
1716 }
1717 }
reed@google.com61eb0402011-04-15 12:11:12 +00001718 } else { // perspective case
reed@android.com6c59a172009-09-22 20:24:05 +00001719 SkScalar dstX = SkIntToScalar(x);
1720 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001721 for (; count > 0; --count) {
1722 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001723 dstProc(fDstToIndex, dstX, dstY, &srcPt);
reed@google.com84e9c082011-04-13 17:44:24 +00001724 SkScalar fx = srcPt.fX;
1725 SkScalar fy = srcPt.fY;
1726 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
1727 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
1728 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, posRoot);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001729 SkFixed index = proc(t);
1730 SkASSERT(index <= 0xFFFF);
1731 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001732 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001733 }
1734 }
1735 }
reed@google.come61414c2011-04-15 18:14:16 +00001736#endif
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001737
reed@android.com6c59a172009-09-22 20:24:05 +00001738 virtual bool setContext(const SkBitmap& device,
1739 const SkPaint& paint,
1740 const SkMatrix& matrix) {
1741 if (!this->INHERITED::setContext(device, paint, matrix)) {
1742 return false;
1743 }
1744
1745 // we don't have a span16 proc
1746 fFlags &= ~kHasSpan16_Flag;
1747 return true;
1748 }
1749
reed@google.com55b8e8c2011-01-13 16:22:35 +00001750 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001751 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1752 }
1753
reed@android.combcfc7332009-11-10 16:19:39 +00001754 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1755 this->INHERITED::flatten(buffer);
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001756 buffer.writeScalar(fCenter1.fX);
1757 buffer.writeScalar(fCenter1.fY);
1758 buffer.writeScalar(fCenter2.fX);
1759 buffer.writeScalar(fCenter2.fY);
1760 buffer.writeScalar(fRadius1);
1761 buffer.writeScalar(fRadius2);
reed@android.combcfc7332009-11-10 16:19:39 +00001762 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001763
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001764protected:
reed@android.combcfc7332009-11-10 16:19:39 +00001765 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001766 : Gradient_Shader(buffer),
1767 fCenter1(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1768 fCenter2(SkPoint::Make(buffer.readScalar(), buffer.readScalar())),
1769 fRadius1(buffer.readScalar()),
1770 fRadius2(buffer.readScalar()) {
1771 init();
reed@android.combcfc7332009-11-10 16:19:39 +00001772 };
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001773 virtual Factory getFactory() { return CreateProc; }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001774
1775private:
1776 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001777 const SkPoint fCenter1;
1778 const SkPoint fCenter2;
1779 const SkScalar fRadius1;
1780 const SkScalar fRadius2;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001781 SkPoint fDiff;
1782 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001783
1784 void init() {
1785 fDiff = fCenter1 - fCenter2;
1786 fDiffRadius = fRadius2 - fRadius1;
1787 SkScalar inv = SkScalarInvert(fDiffRadius);
1788 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1789 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1790 fStartRadius = SkScalarMul(fRadius1, inv);
1791 fSr2D2 = SkScalarSquare(fStartRadius);
1792 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
1793 fOneOverTwoA = SkScalarInvert(fA * 2);
1794
1795 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
1796 fPtsToUnit.postScale(inv, inv);
1797 }
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001798};
1799
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800///////////////////////////////////////////////////////////////////////////////
1801
1802class Sweep_Gradient : public Gradient_Shader {
1803public:
1804 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1805 const SkScalar pos[], int count, SkUnitMapper* mapper)
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001806 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
1807 fCenter(SkPoint::Make(cx, cy))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 {
1809 fPtsToUnit.setTranslate(-cx, -cy);
1810 }
1811 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1812 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001813
1814 virtual BitmapType asABitmap(SkBitmap* bitmap,
1815 SkMatrix* matrix,
reed@google.comdc731fd2010-12-23 15:19:47 +00001816 TileMode* xy,
reed@google.com7c2f27d2011-03-07 19:29:00 +00001817 SkScalar* twoPointRadialParams) const {
reed@google.comdc731fd2010-12-23 15:19:47 +00001818 if (bitmap) {
1819 this->commonAsABitmap(bitmap);
1820 }
1821 if (matrix) {
1822 *matrix = fPtsToUnit;
1823 }
1824 if (xy) {
1825 xy[0] = fTileMode;
1826 xy[1] = kClamp_TileMode;
1827 }
1828 return kSweep_BitmapType;
1829 }
1830
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001831 virtual GradientType asAGradient(GradientInfo* info) const {
1832 if (info) {
1833 commonAsAGradient(info);
1834 info->fPoint[0] = fCenter;
1835 }
1836 return kSweep_GradientType;
1837 }
1838
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1840 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1841 }
1842
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001843 virtual void flatten(SkFlattenableWriteBuffer& buffer) {
1844 this->INHERITED::flatten(buffer);
1845 buffer.writeScalar(fCenter.fX);
1846 buffer.writeScalar(fCenter.fY);
1847 }
1848
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849protected:
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001850 Sweep_Gradient(SkFlattenableReadBuffer& buffer)
1851 : Gradient_Shader(buffer),
1852 fCenter(SkPoint::Make(buffer.readScalar(), buffer.readScalar())) {
1853 }
1854
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855 virtual Factory getFactory() { return CreateProc; }
1856
1857private:
1858 typedef Gradient_Shader INHERITED;
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +00001859 const SkPoint fCenter;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001860};
1861
1862#ifdef COMPUTE_SWEEP_TABLE
1863#define PI 3.14159265
1864static bool gSweepTableReady;
1865static uint8_t gSweepTable[65];
1866
1867/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1868 We scale the results to [0..32]
1869*/
reed@google.com61eb0402011-04-15 12:11:12 +00001870static const uint8_t* build_sweep_table() {
1871 if (!gSweepTableReady) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872 const int N = 65;
1873 const double DENOM = N - 1;
reed@google.com55b8e8c2011-01-13 16:22:35 +00001874
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875 for (int i = 0; i < N; i++)
1876 {
1877 double arg = i / DENOM;
1878 double v = atan(arg);
1879 int iv = (int)round(v * DENOM * 2 / PI);
1880// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1881 printf("%d, ", iv);
1882 gSweepTable[i] = iv;
1883 }
1884 gSweepTableReady = true;
1885 }
1886 return gSweepTable;
1887}
1888#else
1889static const uint8_t gSweepTable[] = {
1890 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1891 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1892 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1893 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1894 32
1895};
1896static const uint8_t* build_sweep_table() { return gSweepTable; }
1897#endif
1898
1899// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1900// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1901// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1902
1903//unsigned div_64(int numer, int denom);
reed@google.com61eb0402011-04-15 12:11:12 +00001904static unsigned div_64(int numer, int denom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905 SkASSERT(numer <= denom);
1906 SkASSERT(numer > 0);
1907 SkASSERT(denom > 0);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001908
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 int nbits = SkCLZ(numer);
1910 int dbits = SkCLZ(denom);
1911 int bits = 6 - nbits + dbits;
1912 SkASSERT(bits <= 6);
reed@google.com55b8e8c2011-01-13 16:22:35 +00001913
reed@google.com61eb0402011-04-15 12:11:12 +00001914 if (bits < 0) { // detect underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00001916 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917
1918 denom <<= dbits - 1;
1919 numer <<= nbits - 1;
1920
1921 unsigned result = 0;
1922
1923 // do the first one
reed@google.com61eb0402011-04-15 12:11:12 +00001924 if ((numer -= denom) >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 result = 1;
reed@google.com61eb0402011-04-15 12:11:12 +00001926 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 numer += denom;
reed@google.com61eb0402011-04-15 12:11:12 +00001928 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00001929
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930 // Now fall into our switch statement if there are more bits to compute
reed@google.com61eb0402011-04-15 12:11:12 +00001931 if (bits > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932 // make room for the rest of the answer bits
1933 result <<= bits;
1934 switch (bits) {
1935 case 6:
1936 if ((numer = (numer << 1) - denom) >= 0)
1937 result |= 32;
1938 else
1939 numer += denom;
1940 case 5:
1941 if ((numer = (numer << 1) - denom) >= 0)
1942 result |= 16;
1943 else
1944 numer += denom;
1945 case 4:
1946 if ((numer = (numer << 1) - denom) >= 0)
1947 result |= 8;
1948 else
1949 numer += denom;
1950 case 3:
1951 if ((numer = (numer << 1) - denom) >= 0)
1952 result |= 4;
1953 else
1954 numer += denom;
1955 case 2:
1956 if ((numer = (numer << 1) - denom) >= 0)
1957 result |= 2;
1958 else
1959 numer += denom;
1960 case 1:
1961 default: // not strictly need, but makes GCC make better ARM code
1962 if ((numer = (numer << 1) - denom) >= 0)
1963 result |= 1;
1964 else
1965 numer += denom;
1966 }
1967 }
1968 return result;
1969}
1970
1971// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
reed@google.com61eb0402011-04-15 12:11:12 +00001972static unsigned atan_0_90(SkFixed y, SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973#ifdef SK_DEBUG
1974 {
1975 static bool gOnce;
reed@google.com61eb0402011-04-15 12:11:12 +00001976 if (!gOnce) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977 gOnce = true;
1978 SkASSERT(div_64(55, 55) == 64);
1979 SkASSERT(div_64(128, 256) == 32);
1980 SkASSERT(div_64(2326528, 4685824) == 31);
1981 SkASSERT(div_64(753664, 5210112) == 9);
1982 SkASSERT(div_64(229376, 4882432) == 3);
1983 SkASSERT(div_64(2, 64) == 2);
1984 SkASSERT(div_64(1, 64) == 1);
1985 // test that we handle underflow correctly
1986 SkASSERT(div_64(12345, 0x54321234) == 0);
1987 }
1988 }
1989#endif
1990
1991 SkASSERT(y > 0 && x > 0);
1992 const uint8_t* table = build_sweep_table();
1993
1994 unsigned result;
1995 bool swap = (x < y);
reed@google.com61eb0402011-04-15 12:11:12 +00001996 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997 // first part of the atan(v) = PI/2 - atan(1/v) identity
1998 // since our div_64 and table want v <= 1, where v = y/x
1999 SkTSwap<SkFixed>(x, y);
2000 }
2001
2002 result = div_64(y, x);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002003
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004#ifdef SK_DEBUG
2005 {
2006 unsigned result2 = SkDivBits(y, x, 6);
2007 SkASSERT(result2 == result ||
2008 (result == 1 && result2 == 0));
2009 }
2010#endif
2011
2012 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
2013 result = table[result];
2014
reed@google.com61eb0402011-04-15 12:11:12 +00002015 if (swap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 // complete the atan(v) = PI/2 - atan(1/v) identity
2017 result = 64 - result;
2018 // pin to 63
2019 result -= result >> 6;
2020 }
2021
2022 SkASSERT(result <= 63);
2023 return result;
2024}
2025
2026// returns angle in a circle [0..2PI) -> [0..255]
reed@google.com61eb0402011-04-15 12:11:12 +00002027static unsigned SkATan2_255(SkFixed y, SkFixed x) {
2028 if (x == 0) {
2029 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030 return 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002031 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 return y < 0 ? 192 : 64;
2033 }
reed@google.com61eb0402011-04-15 12:11:12 +00002034 if (y == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 return x < 0 ? 128 : 0;
reed@google.com61eb0402011-04-15 12:11:12 +00002036 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002037
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038 /* Find the right quadrant for x,y
2039 Since atan_0_90 only handles the first quadrant, we rotate x,y
2040 appropriately before calling it, and then add the right amount
2041 to account for the real quadrant.
2042 quadrant 0 : add 0 | x > 0 && y > 0
2043 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
2044 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
2045 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
reed@google.com55b8e8c2011-01-13 16:22:35 +00002046
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047 map x<0 to (1 << 6)
2048 map y<0 to (3 << 6)
2049 add = map_x ^ map_y
2050 */
2051 int xsign = x >> 31;
2052 int ysign = y >> 31;
2053 int add = ((-xsign) ^ (ysign & 3)) << 6;
2054
2055#ifdef SK_DEBUG
2056 if (0 == add)
2057 SkASSERT(x > 0 && y > 0);
2058 else if (64 == add)
2059 SkASSERT(x < 0 && y > 0);
2060 else if (128 == add)
2061 SkASSERT(x < 0 && y < 0);
2062 else if (192 == add)
2063 SkASSERT(x > 0 && y < 0);
2064 else
2065 SkASSERT(!"bad value for add");
2066#endif
reed@google.com55b8e8c2011-01-13 16:22:35 +00002067
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
2069 where we need to rotate x,y by 90 or -90
2070 */
2071 x = (x ^ xsign) - xsign;
2072 y = (y ^ ysign) - ysign;
reed@google.com61eb0402011-04-15 12:11:12 +00002073 if (add & 64) { // quads 1 or 3 need to swap x,y
reed@android.com8a1c16f2008-12-17 15:59:43 +00002074 SkTSwap<SkFixed>(x, y);
reed@google.com61eb0402011-04-15 12:11:12 +00002075 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076
2077 unsigned result = add + atan_0_90(y, x);
2078 SkASSERT(result < 256);
2079 return result;
2080}
2081
reed@google.com61eb0402011-04-15 12:11:12 +00002082void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083 SkMatrix::MapXYProc proc = fDstToIndexProc;
2084 const SkMatrix& matrix = fDstToIndex;
2085 const SkPMColor* cache = this->getCache32();
2086 SkPoint srcPt;
reed@google.com55b8e8c2011-01-13 16:22:35 +00002087
reed@google.com61eb0402011-04-15 12:11:12 +00002088 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2090 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2091 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
2092 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002093
reed@google.com61eb0402011-04-15 12:11:12 +00002094 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 SkFixed storage[2];
2096 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2097 &storage[0], &storage[1]);
2098 dx = storage[0];
2099 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00002100 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
2102 dx = SkScalarToFixed(matrix.getScaleX());
2103 dy = SkScalarToFixed(matrix.getSkewY());
2104 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002105
reed@google.com61eb0402011-04-15 12:11:12 +00002106 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107 *dstC++ = cache[SkATan2_255(fy, fx)];
2108 fx += dx;
2109 fy += dy;
2110 }
reed@google.com61eb0402011-04-15 12:11:12 +00002111 } else { // perspective case
2112 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002113 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2114 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002115
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
2117 SkScalarToFixed(srcPt.fX));
2118 *dstC++ = cache[index];
2119 }
2120 }
2121}
2122
reed@google.com61eb0402011-04-15 12:11:12 +00002123void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 SkMatrix::MapXYProc proc = fDstToIndexProc;
2125 const SkMatrix& matrix = fDstToIndex;
2126 const uint16_t* cache = this->getCache16();
2127 int toggle = ((x ^ y) & 1) << kCache16Bits;
2128 SkPoint srcPt;
2129
reed@google.com61eb0402011-04-15 12:11:12 +00002130 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2132 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
2133 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
2134 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002135
reed@google.com61eb0402011-04-15 12:11:12 +00002136 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137 SkFixed storage[2];
2138 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
2139 &storage[0], &storage[1]);
2140 dx = storage[0];
2141 dy = storage[1];
reed@google.com61eb0402011-04-15 12:11:12 +00002142 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
2144 dx = SkScalarToFixed(matrix.getScaleX());
2145 dy = SkScalarToFixed(matrix.getSkewY());
2146 }
reed@google.com55b8e8c2011-01-13 16:22:35 +00002147
reed@google.com61eb0402011-04-15 12:11:12 +00002148 for (; count > 0; --count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
2150 *dstC++ = cache[toggle + index];
2151 toggle ^= (1 << kCache16Bits);
2152 fx += dx;
2153 fy += dy;
2154 }
reed@google.com61eb0402011-04-15 12:11:12 +00002155 } else { // perspective case
2156 for (int stop = x + count; x < stop; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
2158 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com55b8e8c2011-01-13 16:22:35 +00002159
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
2161 SkScalarToFixed(srcPt.fX));
2162 index >>= (8 - kCache16Bits);
2163 *dstC++ = cache[toggle + index];
2164 toggle ^= (1 << kCache16Bits);
2165 }
2166 }
2167}
2168
reed@google.com61eb0402011-04-15 12:11:12 +00002169///////////////////////////////////////////////////////////////////////////////
2170///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171
2172// assumes colors is SkColor* and pos is SkScalar*
2173#define EXPAND_1_COLOR(count) \
2174 SkColor tmp[2]; \
2175 do { \
2176 if (1 == count) { \
2177 tmp[0] = tmp[1] = colors[0]; \
2178 colors = tmp; \
2179 pos = NULL; \
2180 count = 2; \
2181 } \
2182 } while (0)
2183
reed@google.com61eb0402011-04-15 12:11:12 +00002184SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
2185 const SkColor colors[],
2186 const SkScalar pos[], int colorCount,
2187 SkShader::TileMode mode,
2188 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 if (NULL == pts || NULL == colors || colorCount < 1) {
2190 return NULL;
2191 }
2192 EXPAND_1_COLOR(colorCount);
2193
reed@android.comab840b82009-07-01 17:00:03 +00002194 return SkNEW_ARGS(Linear_Gradient,
2195 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196}
2197
reed@google.com61eb0402011-04-15 12:11:12 +00002198SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
2199 const SkColor colors[],
2200 const SkScalar pos[], int colorCount,
2201 SkShader::TileMode mode,
2202 SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 if (radius <= 0 || NULL == colors || colorCount < 1) {
2204 return NULL;
2205 }
2206 EXPAND_1_COLOR(colorCount);
2207
reed@android.comab840b82009-07-01 17:00:03 +00002208 return SkNEW_ARGS(Radial_Gradient,
2209 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210}
2211
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002212SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
2213 SkScalar startRadius,
2214 const SkPoint& end,
2215 SkScalar endRadius,
2216 const SkColor colors[],
2217 const SkScalar pos[],
2218 int colorCount,
2219 SkShader::TileMode mode,
reed@google.com61eb0402011-04-15 12:11:12 +00002220 SkUnitMapper* mapper) {
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002221 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
2222 return NULL;
2223 }
2224 EXPAND_1_COLOR(colorCount);
2225
2226 return SkNEW_ARGS(Two_Point_Radial_Gradient,
reed@google.com61eb0402011-04-15 12:11:12 +00002227 (start, startRadius, end, endRadius, colors, pos,
2228 colorCount, mode, mapper));
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00002229}
2230
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
2232 const SkColor colors[],
2233 const SkScalar pos[],
reed@google.com61eb0402011-04-15 12:11:12 +00002234 int count, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 if (NULL == colors || count < 1) {
2236 return NULL;
2237 }
2238 EXPAND_1_COLOR(count);
2239
2240 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
2241}
2242
2243static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
2244 Linear_Gradient::CreateProc);
2245
2246static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
2247 Radial_Gradient::CreateProc);
2248
2249static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
2250 Sweep_Gradient::CreateProc);
2251