3 * Copyright 2012 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
9 #include "SkRadialGradient.h"
10 #include "SkRadialGradient_Table.h"
12 #define kSQRT_TABLE_BITS 11
13 #define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
19 void SkRadialGradient_BuildTable() {
20 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
22 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
24 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
26 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
28 ::fprintf(file, "\t");
31 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
33 ::fprintf(file, "0x%02X", value);
34 if (i < kSQRT_TABLE_SIZE-1) {
35 ::fprintf(file, ", ");
38 ::fprintf(file, "\n");
41 ::fprintf(file, "};\n");
49 // GCC doesn't like using static functions as template arguments. So force these to be non-static.
50 inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
51 return mirror_tileproc(x);
54 inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
55 return repeat_tileproc(x);
58 void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
60 SkScalar inv = SkScalarInvert(radius);
62 matrix->setTranslate(-center.fX, -center.fY);
63 matrix->postScale(inv, inv);
66 typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
67 SkScalar sfy, SkScalar sdy,
68 uint16_t* dstC, const uint16_t* cache,
69 int toggle, int count);
71 void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
72 SkScalar sfy, SkScalar sdy,
73 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
74 int toggle, int count) {
75 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
77 /* knock these down so we can pin against +- 0x7FFF, which is an
78 immediate load, rather than 0xFFFF which is slower. This is a
79 compromise, since it reduces our precision, but that appears
80 to be visually OK. If we decide this is OK for all of our cases,
81 we could (it seems) put this scale-down into fDstToIndex,
82 to avoid having to do these extra shifts each time.
84 SkFixed fx = SkScalarToFixed(sfx) >> 1;
85 SkFixed dx = SkScalarToFixed(sdx) >> 1;
86 SkFixed fy = SkScalarToFixed(sfy) >> 1;
87 SkFixed dy = SkScalarToFixed(sdy) >> 1;
88 // might perform this check for the other modes,
89 // but the win will be a smaller % of the total
91 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
94 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
95 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
96 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
98 *dstC++ = cache[toggle +
99 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
100 toggle = next_dither_toggle16(toggle);
101 } while (--count != 0);
104 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
105 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
106 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
107 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
110 *dstC++ = cache[toggle +
111 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
112 toggle = next_dither_toggle16(toggle);
113 } while (--count != 0);
117 template <SkFixed (*TileProc)(SkFixed)>
118 void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
119 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
120 int toggle, int count) {
122 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
123 const unsigned fi = TileProc(dist);
124 SkASSERT(fi <= 0xFFFF);
125 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
126 toggle = next_dither_toggle16(toggle);
129 } while (--count != 0);
132 void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
133 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
134 int toggle, int count) {
135 shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
138 void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
139 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
140 int toggle, int count) {
141 shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
146 /////////////////////////////////////////////////////////////////////
148 SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
149 : SkGradientShaderBase(desc)
153 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
154 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
156 rad_to_unit_matrix(center, radius, &fPtsToUnit);
159 size_t SkRadialGradient::contextSize() const {
160 return sizeof(RadialGradientContext);
163 SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
164 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
167 SkRadialGradient::RadialGradientContext::RadialGradientContext(
168 const SkRadialGradient& shader, const ContextRec& rec)
169 : INHERITED(shader, rec) {}
171 void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
175 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
177 uint16_t* SK_RESTRICT dstC = dstCParam;
180 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
181 TileProc proc = radialGradient.fTileProc;
182 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
183 int toggle = init_dither_toggle16(x, y);
185 if (fDstToIndexClass != kPerspective_MatrixClass) {
186 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
187 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
189 SkScalar sdx = fDstToIndex.getScaleX();
190 SkScalar sdy = fDstToIndex.getSkewY();
192 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
194 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
195 &storage[0], &storage[1]);
196 sdx = SkFixedToScalar(storage[0]);
197 sdy = SkFixedToScalar(storage[1]);
199 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
202 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
203 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
204 shadeProc = shadeSpan16_radial_clamp;
205 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
206 shadeProc = shadeSpan16_radial_mirror;
208 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
210 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
211 cache, toggle, count);
212 } else { // perspective case
213 SkScalar dstX = SkIntToScalar(x);
214 SkScalar dstY = SkIntToScalar(y);
216 dstProc(fDstToIndex, dstX, dstY, &srcPt);
217 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
218 SkASSERT(fi <= 0xFFFF);
220 int index = fi >> (16 - kCache16Bits);
221 *dstC++ = cache[toggle + index];
222 toggle = next_dither_toggle16(toggle);
225 } while (--count != 0);
229 SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
230 SkMatrix* matrix, SkShader::TileMode* xy) const {
232 this->getGradientTableBitmap(bitmap);
235 matrix->setScale(SkIntToScalar(kCache32Count),
236 SkIntToScalar(kCache32Count));
237 matrix->preConcat(fPtsToUnit);
241 xy[1] = kClamp_TileMode;
243 return kRadial_BitmapType;
246 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
248 commonAsAGradient(info);
249 info->fPoint[0] = fCenter;
250 info->fRadius[0] = fRadius;
252 return kRadial_GradientType;
255 SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
257 fCenter(buffer.readPoint()),
258 fRadius(buffer.readScalar()) {
261 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
262 this->INHERITED::flatten(buffer);
263 buffer.writePoint(fCenter);
264 buffer.writeScalar(fRadius);
269 inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
270 // fast, overly-conservative test: checks unit square instead
272 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
273 (fx <= -SK_FixedHalf && dx <= 0);
274 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
275 (fy <= -SK_FixedHalf && dy <= 0);
277 return xClamped || yClamped;
280 // Return true if (fx * fy) is always inside the unit circle
281 // SkPin32 is expensive, but so are all the SkFixedMul in this test,
282 // so it shouldn't be run if count is small.
283 inline bool no_need_for_radial_pin(int fx, int dx,
284 int fy, int dy, int count) {
286 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
289 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
292 fx += (count - 1) * dx;
293 fy += (count - 1) * dy;
294 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
297 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
300 #define UNPINNED_RADIAL_STEP \
301 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
302 *dstC++ = cache[toggle + \
303 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
304 toggle = next_dither_toggle(toggle); \
308 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
309 SkScalar sfy, SkScalar sdy,
310 SkPMColor* dstC, const SkPMColor* cache,
311 int count, int toggle);
313 // On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
314 void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
315 SkScalar sfy, SkScalar sdy,
316 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
317 int count, int toggle) {
318 // Floating point seems to be slower than fixed point,
319 // even when we have float hardware.
320 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
321 SkFixed fx = SkScalarToFixed(sfx) >> 1;
322 SkFixed dx = SkScalarToFixed(sdx) >> 1;
323 SkFixed fy = SkScalarToFixed(sfy) >> 1;
324 SkFixed dy = SkScalarToFixed(sdy) >> 1;
325 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
326 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
327 sk_memset32_dither(dstC,
329 cache[next_dither_toggle(toggle) + fi],
331 } else if ((count > 4) &&
332 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
334 // 4x unroll appears to be no faster than 2x unroll on Linux
336 UNPINNED_RADIAL_STEP;
337 UNPINNED_RADIAL_STEP;
341 UNPINNED_RADIAL_STEP;
344 // Specializing for dy == 0 gains us 25% on Skia benchmarks
346 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
349 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
350 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
351 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
352 *dstC++ = cache[toggle + (sqrt_table[fi] >>
353 SkGradientShaderBase::kSqrt32Shift)];
354 toggle = next_dither_toggle(toggle);
356 } while (--count != 0);
359 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
360 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
361 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
362 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
363 *dstC++ = cache[toggle + (sqrt_table[fi] >>
364 SkGradientShaderBase::kSqrt32Shift)];
365 toggle = next_dither_toggle(toggle);
368 } while (--count != 0);
373 // Unrolling this loop doesn't seem to help (when float); we're stalling to
374 // get the results of the sqrt (?), and don't have enough extra registers to
375 // have many in flight.
376 template <SkFixed (*TileProc)(SkFixed)>
377 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
378 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
379 int count, int toggle) {
381 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
382 const unsigned fi = TileProc(dist);
383 SkASSERT(fi <= 0xFFFF);
384 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
385 toggle = next_dither_toggle(toggle);
388 } while (--count != 0);
391 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
392 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
393 int count, int toggle) {
394 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
397 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
398 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
399 int count, int toggle) {
400 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
405 void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
406 SkPMColor* SK_RESTRICT dstC, int count) {
409 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
412 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
413 TileProc proc = radialGradient.fTileProc;
414 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
415 int toggle = init_dither_toggle(x, y);
417 if (fDstToIndexClass != kPerspective_MatrixClass) {
418 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
419 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
420 SkScalar sdx = fDstToIndex.getScaleX();
421 SkScalar sdy = fDstToIndex.getSkewY();
423 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
425 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
426 &storage[0], &storage[1]);
427 sdx = SkFixedToScalar(storage[0]);
428 sdy = SkFixedToScalar(storage[1]);
430 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
433 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
434 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
435 shadeProc = shadeSpan_radial_clamp;
436 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
437 shadeProc = shadeSpan_radial_mirror;
439 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
441 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
442 } else { // perspective case
443 SkScalar dstX = SkIntToScalar(x);
444 SkScalar dstY = SkIntToScalar(y);
446 dstProc(fDstToIndex, dstX, dstY, &srcPt);
447 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
448 SkASSERT(fi <= 0xFFFF);
449 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
451 } while (--count != 0);
455 /////////////////////////////////////////////////////////////////////
459 #include "GrTBackendEffectFactory.h"
460 #include "gl/GrGLShaderBuilder.h"
463 class GrGLRadialGradient : public GrGLGradientEffect {
466 GrGLRadialGradient(const GrBackendEffectFactory& factory,
467 const GrDrawEffect&) : INHERITED (factory) { }
468 virtual ~GrGLRadialGradient() { }
470 virtual void emitCode(GrGLShaderBuilder*,
473 const char* outputColor,
474 const char* inputColor,
475 const TransformedCoordsArray&,
476 const TextureSamplerArray&) SK_OVERRIDE;
478 static void GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&, GrEffectKeyBuilder* b) {
479 b->add32(GenBaseGradientKey(drawEffect));
484 typedef GrGLGradientEffect INHERITED;
488 /////////////////////////////////////////////////////////////////////
490 class GrRadialGradient : public GrGradientEffect {
492 static GrEffect* Create(GrContext* ctx,
493 const SkRadialGradient& shader,
494 const SkMatrix& matrix,
495 SkShader::TileMode tm) {
496 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
499 virtual ~GrRadialGradient() { }
501 static const char* Name() { return "Radial Gradient"; }
502 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
503 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
506 typedef GrGLRadialGradient GLEffect;
509 GrRadialGradient(GrContext* ctx,
510 const SkRadialGradient& shader,
511 const SkMatrix& matrix,
512 SkShader::TileMode tm)
513 : INHERITED(ctx, shader, matrix, tm) {
516 GR_DECLARE_EFFECT_TEST;
518 typedef GrGradientEffect INHERITED;
521 /////////////////////////////////////////////////////////////////////
523 GR_DEFINE_EFFECT_TEST(GrRadialGradient);
525 GrEffect* GrRadialGradient::TestCreate(SkRandom* random,
527 const GrDrawTargetCaps&,
529 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
530 SkScalar radius = random->nextUScalar1();
532 SkColor colors[kMaxRandomGradientColors];
533 SkScalar stopsArray[kMaxRandomGradientColors];
534 SkScalar* stops = stopsArray;
535 SkShader::TileMode tm;
536 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
537 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
538 colors, stops, colorCount,
543 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
547 /////////////////////////////////////////////////////////////////////
549 void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
551 const GrEffectKey& key,
552 const char* outputColor,
553 const char* inputColor,
554 const TransformedCoordsArray& coords,
555 const TextureSamplerArray& samplers) {
556 uint32_t baseKey = key.get32(0);
557 this->emitUniforms(builder, baseKey);
558 SkString t("length(");
559 t.append(builder->ensureFSCoords2D(coords, 0));
561 this->emitColor(builder, t.c_str(), baseKey, outputColor, inputColor, samplers);
564 /////////////////////////////////////////////////////////////////////
566 bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
567 const SkMatrix* localMatrix, GrColor* paintColor,
568 GrEffect** effect) const {
569 SkASSERT(NULL != context);
572 if (!this->getLocalMatrix().invert(&matrix)) {
577 if (!localMatrix->invert(&inv)) {
580 matrix.postConcat(inv);
582 matrix.postConcat(fPtsToUnit);
584 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
585 *effect = GrRadialGradient::Create(context, *this, matrix, fTileMode);
592 bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
593 const SkMatrix* localMatrix, GrColor* paintColor,
594 GrEffect** effect) const {
595 SkDEBUGFAIL("Should not call in GPU-less build");
601 #ifndef SK_IGNORE_TO_STRING
602 void SkRadialGradient::toString(SkString* str) const {
603 str->append("SkRadialGradient: (");
605 str->append("center: (");
606 str->appendScalar(fCenter.fX);
608 str->appendScalar(fCenter.fY);
609 str->append(") radius: ");
610 str->appendScalar(fRadius);
613 this->INHERITED::toString(str);