SkRadialGradient.cpp revision ae81d5c4aa1716756b2cfb4c44f27f4dce2716ef
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 SkColor colors[], const SkScalar pos[], int colorCount, 157 SkShader::TileMode mode, SkUnitMapper* mapper) 158 : SkGradientShaderBase(colors, pos, colorCount, mode, mapper), 159 fCenter(center), 160 fRadius(radius) 161{ 162 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE 163 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE); 164 165 rad_to_unit_matrix(center, radius, &fPtsToUnit); 166} 167 168void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam, 169 int count) { 170 SkASSERT(count > 0); 171 172 uint16_t* SK_RESTRICT dstC = dstCParam; 173 174 SkPoint srcPt; 175 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 176 TileProc proc = fTileProc; 177 const uint16_t* SK_RESTRICT cache = this->getCache16(); 178 int toggle = init_dither_toggle16(x, y); 179 180 if (fDstToIndexClass != kPerspective_MatrixClass) { 181 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 182 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 183 184 SkScalar sdx = fDstToIndex.getScaleX(); 185 SkScalar sdy = fDstToIndex.getSkewY(); 186 187 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 188 SkFixed storage[2]; 189 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 190 &storage[0], &storage[1]); 191 sdx = SkFixedToScalar(storage[0]); 192 sdy = SkFixedToScalar(storage[1]); 193 } else { 194 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 195 } 196 197 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat; 198 if (SkShader::kClamp_TileMode == fTileMode) { 199 shadeProc = shadeSpan16_radial_clamp; 200 } else if (SkShader::kMirror_TileMode == fTileMode) { 201 shadeProc = shadeSpan16_radial_mirror; 202 } else { 203 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 204 } 205 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, 206 cache, toggle, count); 207 } else { // perspective case 208 SkScalar dstX = SkIntToScalar(x); 209 SkScalar dstY = SkIntToScalar(y); 210 do { 211 dstProc(fDstToIndex, dstX, dstY, &srcPt); 212 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 213 SkASSERT(fi <= 0xFFFF); 214 215 int index = fi >> (16 - kCache16Bits); 216 *dstC++ = cache[toggle + index]; 217 toggle = next_dither_toggle16(toggle); 218 219 dstX += SK_Scalar1; 220 } while (--count != 0); 221 } 222} 223 224SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap, 225 SkMatrix* matrix, SkShader::TileMode* xy) const { 226 if (bitmap) { 227 this->getGradientTableBitmap(bitmap); 228 } 229 if (matrix) { 230 matrix->setScale(SkIntToScalar(kCache32Count), 231 SkIntToScalar(kCache32Count)); 232 matrix->preConcat(fPtsToUnit); 233 } 234 if (xy) { 235 xy[0] = fTileMode; 236 xy[1] = kClamp_TileMode; 237 } 238 return kRadial_BitmapType; 239} 240 241SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { 242 if (info) { 243 commonAsAGradient(info); 244 info->fPoint[0] = fCenter; 245 info->fRadius[0] = fRadius; 246 } 247 return kRadial_GradientType; 248} 249 250SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer) 251 : INHERITED(buffer), 252 fCenter(buffer.readPoint()), 253 fRadius(buffer.readScalar()) { 254} 255 256void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const { 257 this->INHERITED::flatten(buffer); 258 buffer.writePoint(fCenter); 259 buffer.writeScalar(fRadius); 260} 261 262namespace { 263 264inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) { 265 // fast, overly-conservative test: checks unit square instead 266 // of unit circle 267 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) || 268 (fx <= -SK_FixedHalf && dx <= 0); 269 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) || 270 (fy <= -SK_FixedHalf && dy <= 0); 271 272 return xClamped || yClamped; 273} 274 275// Return true if (fx * fy) is always inside the unit circle 276// SkPin32 is expensive, but so are all the SkFixedMul in this test, 277// so it shouldn't be run if count is small. 278inline bool no_need_for_radial_pin(int fx, int dx, 279 int fy, int dy, int count) { 280 SkASSERT(count > 0); 281 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 282 return false; 283 } 284 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) { 285 return false; 286 } 287 fx += (count - 1) * dx; 288 fy += (count - 1) * dy; 289 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 290 return false; 291 } 292 return fx*fx + fy*fy <= 0x7FFF*0x7FFF; 293} 294 295#define UNPINNED_RADIAL_STEP \ 296 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \ 297 *dstC++ = cache[toggle + \ 298 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \ 299 toggle = next_dither_toggle(toggle); \ 300 fx += dx; \ 301 fy += dy; 302 303typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx, 304 SkScalar sfy, SkScalar sdy, 305 SkPMColor* dstC, const SkPMColor* cache, 306 int count, int toggle); 307 308// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT 309void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx, 310 SkScalar sfy, SkScalar sdy, 311 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 312 int count, int toggle) { 313 // Floating point seems to be slower than fixed point, 314 // even when we have float hardware. 315 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table; 316 SkFixed fx = SkScalarToFixed(sfx) >> 1; 317 SkFixed dx = SkScalarToFixed(sdx) >> 1; 318 SkFixed fy = SkScalarToFixed(sfy) >> 1; 319 SkFixed dy = SkScalarToFixed(sdy) >> 1; 320 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) { 321 unsigned fi = SkGradientShaderBase::kCache32Count - 1; 322 sk_memset32_dither(dstC, 323 cache[toggle + fi], 324 cache[next_dither_toggle(toggle) + fi], 325 count); 326 } else if ((count > 4) && 327 no_need_for_radial_pin(fx, dx, fy, dy, count)) { 328 unsigned fi; 329 // 4x unroll appears to be no faster than 2x unroll on Linux 330 while (count > 1) { 331 UNPINNED_RADIAL_STEP; 332 UNPINNED_RADIAL_STEP; 333 count -= 2; 334 } 335 if (count) { 336 UNPINNED_RADIAL_STEP; 337 } 338 } else { 339 // Specializing for dy == 0 gains us 25% on Skia benchmarks 340 if (dy == 0) { 341 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 342 yy *= yy; 343 do { 344 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 345 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS); 346 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 347 *dstC++ = cache[toggle + (sqrt_table[fi] >> 348 SkGradientShaderBase::kSqrt32Shift)]; 349 toggle = next_dither_toggle(toggle); 350 fx += dx; 351 } while (--count != 0); 352 } else { 353 do { 354 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 355 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 356 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); 357 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 358 *dstC++ = cache[toggle + (sqrt_table[fi] >> 359 SkGradientShaderBase::kSqrt32Shift)]; 360 toggle = next_dither_toggle(toggle); 361 fx += dx; 362 fy += dy; 363 } while (--count != 0); 364 } 365 } 366} 367 368// Unrolling this loop doesn't seem to help (when float); we're stalling to 369// get the results of the sqrt (?), and don't have enough extra registers to 370// have many in flight. 371void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx, 372 SkScalar sfy, SkScalar sdy, 373 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 374 int count, int toggle) { 375 do { 376#ifdef SK_SCALAR_IS_FLOAT 377 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy); 378 SkFixed dist = SkFloatToFixed(fdist); 379#else 380 SkFixed magnitudeSquared = SkFixedSquare(sfx) + 381 SkFixedSquare(sfy); 382 if (magnitudeSquared < 0) // Overflow. 383 magnitudeSquared = SK_FixedMax; 384 SkFixed dist = SkFixedSqrt(magnitudeSquared); 385#endif 386 unsigned fi = mirror_tileproc(dist); 387 SkASSERT(fi <= 0xFFFF); 388 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; 389 toggle = next_dither_toggle(toggle); 390 sfx += sdx; 391 sfy += sdy; 392 } while (--count != 0); 393} 394 395void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx, 396 SkScalar sfy, SkScalar sdy, 397 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 398 int count, int toggle) { 399 SkFixed fx = SkScalarToFixed(sfx); 400 SkFixed dx = SkScalarToFixed(sdx); 401 SkFixed fy = SkScalarToFixed(sfy); 402 SkFixed dy = SkScalarToFixed(sdy); 403 do { 404 SkFixed magnitudeSquared = SkFixedSquare(fx) + 405 SkFixedSquare(fy); 406 if (magnitudeSquared < 0) // Overflow. 407 magnitudeSquared = SK_FixedMax; 408 SkFixed dist = SkFixedSqrt(magnitudeSquared); 409 unsigned fi = repeat_tileproc(dist); 410 SkASSERT(fi <= 0xFFFF); 411 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; 412 toggle = next_dither_toggle(toggle); 413 fx += dx; 414 fy += dy; 415 } while (--count != 0); 416} 417} 418 419void SkRadialGradient::shadeSpan(int x, int y, 420 SkPMColor* SK_RESTRICT dstC, int count) { 421 SkASSERT(count > 0); 422 423 SkPoint srcPt; 424 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 425 TileProc proc = fTileProc; 426 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 427 int toggle = init_dither_toggle(x, y); 428 429 if (fDstToIndexClass != kPerspective_MatrixClass) { 430 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 431 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 432 SkScalar sdx = fDstToIndex.getScaleX(); 433 SkScalar sdy = fDstToIndex.getSkewY(); 434 435 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 436 SkFixed storage[2]; 437 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 438 &storage[0], &storage[1]); 439 sdx = SkFixedToScalar(storage[0]); 440 sdy = SkFixedToScalar(storage[1]); 441 } else { 442 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 443 } 444 445 RadialShadeProc shadeProc = shadeSpan_radial_repeat; 446 if (SkShader::kClamp_TileMode == fTileMode) { 447 shadeProc = shadeSpan_radial_clamp; 448 } else if (SkShader::kMirror_TileMode == fTileMode) { 449 shadeProc = shadeSpan_radial_mirror; 450 } else { 451 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 452 } 453 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle); 454 } else { // perspective case 455 SkScalar dstX = SkIntToScalar(x); 456 SkScalar dstY = SkIntToScalar(y); 457 do { 458 dstProc(fDstToIndex, dstX, dstY, &srcPt); 459 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 460 SkASSERT(fi <= 0xFFFF); 461 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift]; 462 dstX += SK_Scalar1; 463 } while (--count != 0); 464 } 465} 466 467///////////////////////////////////////////////////////////////////// 468 469#if SK_SUPPORT_GPU 470 471#include "GrTBackendEffectFactory.h" 472 473class GrGLRadialGradient : public GrGLGradientEffect { 474public: 475 476 GrGLRadialGradient(const GrBackendEffectFactory& factory, 477 const GrDrawEffect&) : INHERITED (factory) { } 478 virtual ~GrGLRadialGradient() { } 479 480 virtual void emitCode(GrGLShaderBuilder*, 481 const GrDrawEffect&, 482 EffectKey, 483 const char* outputColor, 484 const char* inputColor, 485 const TextureSamplerArray&) SK_OVERRIDE; 486 487 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 488 return GenMatrixKey(drawEffect); 489 } 490 491private: 492 493 typedef GrGLGradientEffect INHERITED; 494 495}; 496 497///////////////////////////////////////////////////////////////////// 498 499class GrRadialGradient : public GrGradientEffect { 500public: 501 static GrEffectRef* Create(GrContext* ctx, 502 const SkRadialGradient& shader, 503 const SkMatrix& matrix, 504 SkShader::TileMode tm) { 505 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm))); 506 return CreateEffectRef(effect); 507 } 508 509 virtual ~GrRadialGradient() { } 510 511 static const char* Name() { return "Radial Gradient"; } 512 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 513 return GrTBackendEffectFactory<GrRadialGradient>::getInstance(); 514 } 515 516 typedef GrGLRadialGradient GLEffect; 517 518private: 519 GrRadialGradient(GrContext* ctx, 520 const SkRadialGradient& shader, 521 const SkMatrix& matrix, 522 SkShader::TileMode tm) 523 : INHERITED(ctx, shader, matrix, tm) { 524 } 525 526 GR_DECLARE_EFFECT_TEST; 527 528 typedef GrGradientEffect INHERITED; 529}; 530 531///////////////////////////////////////////////////////////////////// 532 533GR_DEFINE_EFFECT_TEST(GrRadialGradient); 534 535GrEffectRef* GrRadialGradient::TestCreate(SkMWCRandom* random, 536 GrContext* context, 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