1 2/* 3 * Copyright 2012 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9#include "SkRadialGradient.h" 10#include "SkRadialGradient_Table.h" 11 12#define kSQRT_TABLE_BITS 11 13#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS) 14 15#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG) 16 17#include <stdio.h> 18 19void SkRadialGradient_BuildTable() { 20 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table 21 22 FILE* file = ::fopen("SkRadialGradient_Table.h", "w"); 23 SkASSERT(file); 24 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n"); 25 26 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) { 27 if ((i & 15) == 0) { 28 ::fprintf(file, "\t"); 29 } 30 31 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8); 32 33 ::fprintf(file, "0x%02X", value); 34 if (i < kSQRT_TABLE_SIZE-1) { 35 ::fprintf(file, ", "); 36 } 37 if ((i & 15) == 15) { 38 ::fprintf(file, "\n"); 39 } 40 } 41 ::fprintf(file, "};\n"); 42 ::fclose(file); 43} 44 45#endif 46 47namespace { 48 49void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, 50 SkMatrix* matrix) { 51 SkScalar inv = SkScalarInvert(radius); 52 53 matrix->setTranslate(-center.fX, -center.fY); 54 matrix->postScale(inv, inv); 55} 56 57typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx, 58 SkScalar sfy, SkScalar sdy, 59 uint16_t* dstC, const uint16_t* cache, 60 int toggle, int count); 61 62void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx, 63 SkScalar sfy, SkScalar sdy, 64 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, 65 int toggle, int count) { 66 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table; 67 68 /* knock these down so we can pin against +- 0x7FFF, which is an 69 immediate load, rather than 0xFFFF which is slower. This is a 70 compromise, since it reduces our precision, but that appears 71 to be visually OK. If we decide this is OK for all of our cases, 72 we could (it seems) put this scale-down into fDstToIndex, 73 to avoid having to do these extra shifts each time. 74 */ 75 SkFixed fx = SkScalarToFixed(sfx) >> 1; 76 SkFixed dx = SkScalarToFixed(sdx) >> 1; 77 SkFixed fy = SkScalarToFixed(sfy) >> 1; 78 SkFixed dy = SkScalarToFixed(sdy) >> 1; 79 // might perform this check for the other modes, 80 // but the win will be a smaller % of the total 81 if (dy == 0) { 82 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 83 fy *= fy; 84 do { 85 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 86 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS); 87 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 88 fx += dx; 89 *dstC++ = cache[toggle + 90 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; 91 toggle = next_dither_toggle16(toggle); 92 } while (--count != 0); 93 } else { 94 do { 95 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 96 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 97 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); 98 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 99 fx += dx; 100 fy += dy; 101 *dstC++ = cache[toggle + 102 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; 103 toggle = next_dither_toggle16(toggle); 104 } while (--count != 0); 105 } 106} 107 108void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx, 109 SkScalar sfy, SkScalar sdy, 110 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, 111 int toggle, int count) { 112 do { 113#ifdef SK_SCALAR_IS_FLOAT 114 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy); 115 SkFixed dist = SkFloatToFixed(fdist); 116#else 117 SkFixed magnitudeSquared = SkFixedSquare(sfx) + 118 SkFixedSquare(sfy); 119 if (magnitudeSquared < 0) // Overflow. 120 magnitudeSquared = SK_FixedMax; 121 SkFixed dist = SkFixedSqrt(magnitudeSquared); 122#endif 123 unsigned fi = mirror_tileproc(dist); 124 SkASSERT(fi <= 0xFFFF); 125 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)]; 126 toggle = next_dither_toggle16(toggle); 127 sfx += sdx; 128 sfy += sdy; 129 } while (--count != 0); 130} 131 132void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx, 133 SkScalar sfy, SkScalar sdy, 134 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, 135 int toggle, int count) { 136 SkFixed fx = SkScalarToFixed(sfx); 137 SkFixed dx = SkScalarToFixed(sdx); 138 SkFixed fy = SkScalarToFixed(sfy); 139 SkFixed dy = SkScalarToFixed(sdy); 140 do { 141 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy)); 142 unsigned fi = repeat_tileproc(dist); 143 SkASSERT(fi <= 0xFFFF); 144 fx += dx; 145 fy += dy; 146 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)]; 147 toggle = next_dither_toggle16(toggle); 148 } while (--count != 0); 149} 150 151} 152 153///////////////////////////////////////////////////////////////////// 154 155SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, 156 const Descriptor& desc) 157 : SkGradientShaderBase(desc), 158 fCenter(center), 159 fRadius(radius) 160{ 161 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE 162 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE); 163 164 rad_to_unit_matrix(center, radius, &fPtsToUnit); 165} 166 167void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam, 168 int count) { 169 SkASSERT(count > 0); 170 171 uint16_t* SK_RESTRICT dstC = dstCParam; 172 173 SkPoint srcPt; 174 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 175 TileProc proc = fTileProc; 176 const uint16_t* SK_RESTRICT cache = this->getCache16(); 177 int toggle = init_dither_toggle16(x, y); 178 179 if (fDstToIndexClass != kPerspective_MatrixClass) { 180 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 181 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 182 183 SkScalar sdx = fDstToIndex.getScaleX(); 184 SkScalar sdy = fDstToIndex.getSkewY(); 185 186 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 187 SkFixed storage[2]; 188 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 189 &storage[0], &storage[1]); 190 sdx = SkFixedToScalar(storage[0]); 191 sdy = SkFixedToScalar(storage[1]); 192 } else { 193 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 194 } 195 196 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat; 197 if (SkShader::kClamp_TileMode == fTileMode) { 198 shadeProc = shadeSpan16_radial_clamp; 199 } else if (SkShader::kMirror_TileMode == fTileMode) { 200 shadeProc = shadeSpan16_radial_mirror; 201 } else { 202 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 203 } 204 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, 205 cache, toggle, count); 206 } else { // perspective case 207 SkScalar dstX = SkIntToScalar(x); 208 SkScalar dstY = SkIntToScalar(y); 209 do { 210 dstProc(fDstToIndex, dstX, dstY, &srcPt); 211 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 212 SkASSERT(fi <= 0xFFFF); 213 214 int index = fi >> (16 - kCache16Bits); 215 *dstC++ = cache[toggle + index]; 216 toggle = next_dither_toggle16(toggle); 217 218 dstX += SK_Scalar1; 219 } while (--count != 0); 220 } 221} 222 223SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap, 224 SkMatrix* matrix, SkShader::TileMode* xy) const { 225 if (bitmap) { 226 this->getGradientTableBitmap(bitmap); 227 } 228 if (matrix) { 229 matrix->setScale(SkIntToScalar(kCache32Count), 230 SkIntToScalar(kCache32Count)); 231 matrix->preConcat(fPtsToUnit); 232 } 233 if (xy) { 234 xy[0] = fTileMode; 235 xy[1] = kClamp_TileMode; 236 } 237 return kRadial_BitmapType; 238} 239 240SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { 241 if (info) { 242 commonAsAGradient(info); 243 info->fPoint[0] = fCenter; 244 info->fRadius[0] = fRadius; 245 } 246 return kRadial_GradientType; 247} 248 249SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer) 250 : INHERITED(buffer), 251 fCenter(buffer.readPoint()), 252 fRadius(buffer.readScalar()) { 253} 254 255void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const { 256 this->INHERITED::flatten(buffer); 257 buffer.writePoint(fCenter); 258 buffer.writeScalar(fRadius); 259} 260 261namespace { 262 263inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) { 264 // fast, overly-conservative test: checks unit square instead 265 // of unit circle 266 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) || 267 (fx <= -SK_FixedHalf && dx <= 0); 268 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) || 269 (fy <= -SK_FixedHalf && dy <= 0); 270 271 return xClamped || yClamped; 272} 273 274// Return true if (fx * fy) is always inside the unit circle 275// SkPin32 is expensive, but so are all the SkFixedMul in this test, 276// so it shouldn't be run if count is small. 277inline bool no_need_for_radial_pin(int fx, int dx, 278 int fy, int dy, int count) { 279 SkASSERT(count > 0); 280 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 281 return false; 282 } 283 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) { 284 return false; 285 } 286 fx += (count - 1) * dx; 287 fy += (count - 1) * dy; 288 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 289 return false; 290 } 291 return fx*fx + fy*fy <= 0x7FFF*0x7FFF; 292} 293 294#define UNPINNED_RADIAL_STEP \ 295 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \ 296 *dstC++ = cache[toggle + \ 297 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \ 298 toggle = next_dither_toggle(toggle); \ 299 fx += dx; \ 300 fy += dy; 301 302typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx, 303 SkScalar sfy, SkScalar sdy, 304 SkPMColor* dstC, const SkPMColor* cache, 305 int count, int toggle); 306 307// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT 308void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx, 309 SkScalar sfy, SkScalar sdy, 310 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 311 int count, int toggle) { 312 // Floating point seems to be slower than fixed point, 313 // even when we have float hardware. 314 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table; 315 SkFixed fx = SkScalarToFixed(sfx) >> 1; 316 SkFixed dx = SkScalarToFixed(sdx) >> 1; 317 SkFixed fy = SkScalarToFixed(sfy) >> 1; 318 SkFixed dy = SkScalarToFixed(sdy) >> 1; 319 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) { 320 unsigned fi = SkGradientShaderBase::kCache32Count - 1; 321 sk_memset32_dither(dstC, 322 cache[toggle + fi], 323 cache[next_dither_toggle(toggle) + fi], 324 count); 325 } else if ((count > 4) && 326 no_need_for_radial_pin(fx, dx, fy, dy, count)) { 327 unsigned fi; 328 // 4x unroll appears to be no faster than 2x unroll on Linux 329 while (count > 1) { 330 UNPINNED_RADIAL_STEP; 331 UNPINNED_RADIAL_STEP; 332 count -= 2; 333 } 334 if (count) { 335 UNPINNED_RADIAL_STEP; 336 } 337 } else { 338 // Specializing for dy == 0 gains us 25% on Skia benchmarks 339 if (dy == 0) { 340 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 341 yy *= yy; 342 do { 343 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 344 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS); 345 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 346 *dstC++ = cache[toggle + (sqrt_table[fi] >> 347 SkGradientShaderBase::kSqrt32Shift)]; 348 toggle = next_dither_toggle(toggle); 349 fx += dx; 350 } while (--count != 0); 351 } else { 352 do { 353 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 354 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 355 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); 356 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 357 *dstC++ = cache[toggle + (sqrt_table[fi] >> 358 SkGradientShaderBase::kSqrt32Shift)]; 359 toggle = next_dither_toggle(toggle); 360 fx += dx; 361 fy += dy; 362 } while (--count != 0); 363 } 364 } 365} 366 367// Unrolling this loop doesn't seem to help (when float); we're stalling to 368// get the results of the sqrt (?), and don't have enough extra registers to 369// have many in flight. 370void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx, 371 SkScalar sfy, SkScalar sdy, 372 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 373 int count, int toggle) { 374 do { 375#ifdef SK_SCALAR_IS_FLOAT 376 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy); 377 SkFixed dist = SkFloatToFixed(fdist); 378#else 379 SkFixed magnitudeSquared = SkFixedSquare(sfx) + 380 SkFixedSquare(sfy); 381 if (magnitudeSquared < 0) // Overflow. 382 magnitudeSquared = SK_FixedMax; 383 SkFixed dist = SkFixedSqrt(magnitudeSquared); 384#endif 385 unsigned fi = mirror_tileproc(dist); 386 SkASSERT(fi <= 0xFFFF); 387 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; 388 toggle = next_dither_toggle(toggle); 389 sfx += sdx; 390 sfy += sdy; 391 } while (--count != 0); 392} 393 394void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx, 395 SkScalar sfy, SkScalar sdy, 396 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 397 int count, int toggle) { 398 SkFixed fx = SkScalarToFixed(sfx); 399 SkFixed dx = SkScalarToFixed(sdx); 400 SkFixed fy = SkScalarToFixed(sfy); 401 SkFixed dy = SkScalarToFixed(sdy); 402 do { 403 SkFixed magnitudeSquared = SkFixedSquare(fx) + 404 SkFixedSquare(fy); 405 if (magnitudeSquared < 0) // Overflow. 406 magnitudeSquared = SK_FixedMax; 407 SkFixed dist = SkFixedSqrt(magnitudeSquared); 408 unsigned fi = repeat_tileproc(dist); 409 SkASSERT(fi <= 0xFFFF); 410 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; 411 toggle = next_dither_toggle(toggle); 412 fx += dx; 413 fy += dy; 414 } while (--count != 0); 415} 416} 417 418void SkRadialGradient::shadeSpan(int x, int y, 419 SkPMColor* SK_RESTRICT dstC, int count) { 420 SkASSERT(count > 0); 421 422 SkPoint srcPt; 423 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 424 TileProc proc = fTileProc; 425 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 426 int toggle = init_dither_toggle(x, y); 427 428 if (fDstToIndexClass != kPerspective_MatrixClass) { 429 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 430 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 431 SkScalar sdx = fDstToIndex.getScaleX(); 432 SkScalar sdy = fDstToIndex.getSkewY(); 433 434 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 435 SkFixed storage[2]; 436 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 437 &storage[0], &storage[1]); 438 sdx = SkFixedToScalar(storage[0]); 439 sdy = SkFixedToScalar(storage[1]); 440 } else { 441 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 442 } 443 444 RadialShadeProc shadeProc = shadeSpan_radial_repeat; 445 if (SkShader::kClamp_TileMode == fTileMode) { 446 shadeProc = shadeSpan_radial_clamp; 447 } else if (SkShader::kMirror_TileMode == fTileMode) { 448 shadeProc = shadeSpan_radial_mirror; 449 } else { 450 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 451 } 452 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle); 453 } else { // perspective case 454 SkScalar dstX = SkIntToScalar(x); 455 SkScalar dstY = SkIntToScalar(y); 456 do { 457 dstProc(fDstToIndex, dstX, dstY, &srcPt); 458 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 459 SkASSERT(fi <= 0xFFFF); 460 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift]; 461 dstX += SK_Scalar1; 462 } while (--count != 0); 463 } 464} 465 466///////////////////////////////////////////////////////////////////// 467 468#if SK_SUPPORT_GPU 469 470#include "GrTBackendEffectFactory.h" 471 472class GrGLRadialGradient : public GrGLGradientEffect { 473public: 474 475 GrGLRadialGradient(const GrBackendEffectFactory& factory, 476 const GrDrawEffect&) : INHERITED (factory) { } 477 virtual ~GrGLRadialGradient() { } 478 479 virtual void emitCode(GrGLShaderBuilder*, 480 const GrDrawEffect&, 481 EffectKey, 482 const char* outputColor, 483 const char* inputColor, 484 const TextureSamplerArray&) SK_OVERRIDE; 485 486 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 487 return GenMatrixKey(drawEffect); 488 } 489 490private: 491 492 typedef GrGLGradientEffect INHERITED; 493 494}; 495 496///////////////////////////////////////////////////////////////////// 497 498class GrRadialGradient : public GrGradientEffect { 499public: 500 static GrEffectRef* Create(GrContext* ctx, 501 const SkRadialGradient& shader, 502 const SkMatrix& matrix, 503 SkShader::TileMode tm) { 504 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm))); 505 return CreateEffectRef(effect); 506 } 507 508 virtual ~GrRadialGradient() { } 509 510 static const char* Name() { return "Radial Gradient"; } 511 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 512 return GrTBackendEffectFactory<GrRadialGradient>::getInstance(); 513 } 514 515 typedef GrGLRadialGradient GLEffect; 516 517private: 518 GrRadialGradient(GrContext* ctx, 519 const SkRadialGradient& shader, 520 const SkMatrix& matrix, 521 SkShader::TileMode tm) 522 : INHERITED(ctx, shader, matrix, tm) { 523 } 524 525 GR_DECLARE_EFFECT_TEST; 526 527 typedef GrGradientEffect INHERITED; 528}; 529 530///////////////////////////////////////////////////////////////////// 531 532GR_DEFINE_EFFECT_TEST(GrRadialGradient); 533 534GrEffectRef* GrRadialGradient::TestCreate(SkMWCRandom* random, 535 GrContext* context, 536 const GrDrawTargetCaps&, 537 GrTexture**) { 538 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()}; 539 SkScalar radius = random->nextUScalar1(); 540 541 SkColor colors[kMaxRandomGradientColors]; 542 SkScalar stopsArray[kMaxRandomGradientColors]; 543 SkScalar* stops = stopsArray; 544 SkShader::TileMode tm; 545 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 546 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius, 547 colors, stops, colorCount, 548 tm)); 549 SkPaint paint; 550 return shader->asNewEffect(context, paint); 551} 552 553///////////////////////////////////////////////////////////////////// 554 555void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder, 556 const GrDrawEffect&, 557 EffectKey key, 558 const char* outputColor, 559 const char* inputColor, 560 const TextureSamplerArray& samplers) { 561 this->emitYCoordUniform(builder); 562 const char* coords; 563 this->setupMatrix(builder, key, &coords); 564 SkString t("length("); 565 t.append(coords); 566 t.append(")"); 567 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]); 568} 569 570///////////////////////////////////////////////////////////////////// 571 572GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const { 573 SkASSERT(NULL != context); 574 575 SkMatrix matrix; 576 if (!this->getLocalMatrix().invert(&matrix)) { 577 return NULL; 578 } 579 matrix.postConcat(fPtsToUnit); 580 return GrRadialGradient::Create(context, *this, matrix, fTileMode); 581} 582 583#else 584 585GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const { 586 SkDEBUGFAIL("Should not call in GPU-less build"); 587 return NULL; 588} 589 590#endif 591 592#ifdef SK_DEVELOPER 593void SkRadialGradient::toString(SkString* str) const { 594 str->append("SkRadialGradient: ("); 595 596 str->append("center: ("); 597 str->appendScalar(fCenter.fX); 598 str->append(", "); 599 str->appendScalar(fCenter.fY); 600 str->append(") radius: "); 601 str->appendScalar(fRadius); 602 str->append(" "); 603 604 this->INHERITED::toString(str); 605 606 str->append(")"); 607} 608#endif 609