SkRadialGradient.cpp revision 2011fe9cdfa63b83489a146cea6a724cede352c8
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 0 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 49// GCC doesn't like using static functions as template arguments. So force these to be non-static. 50inline SkFixed mirror_tileproc_nonstatic(SkFixed x) { 51 return mirror_tileproc(x); 52} 53 54inline SkFixed repeat_tileproc_nonstatic(SkFixed x) { 55 return repeat_tileproc(x); 56} 57 58void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, 59 SkMatrix* matrix) { 60 SkScalar inv = SkScalarInvert(radius); 61 62 matrix->setTranslate(-center.fX, -center.fY); 63 matrix->postScale(inv, inv); 64} 65 66typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx, 67 SkScalar sfy, SkScalar sdy, 68 uint16_t* dstC, const uint16_t* cache, 69 int toggle, int count); 70 71void 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; 76 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. 83 */ 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 90 if (dy == 0) { 91 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 92 fy *= fy; 93 do { 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)); 97 fx += dx; 98 *dstC++ = cache[toggle + 99 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; 100 toggle = next_dither_toggle16(toggle); 101 } while (--count != 0); 102 } else { 103 do { 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)); 108 fx += dx; 109 fy += dy; 110 *dstC++ = cache[toggle + 111 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; 112 toggle = next_dither_toggle16(toggle); 113 } while (--count != 0); 114 } 115} 116 117template <SkFixed (*TileProc)(SkFixed)> 118void 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) { 121 do { 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); 127 fx += dx; 128 fy += dy; 129 } while (--count != 0); 130} 131 132void 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); 136} 137 138void 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); 142} 143 144} // namespace 145 146///////////////////////////////////////////////////////////////////// 147 148SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, 149 const Descriptor& desc, const SkMatrix* localMatrix) 150 : SkGradientShaderBase(desc, localMatrix), 151 fCenter(center), 152 fRadius(radius) 153{ 154 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE 155 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE); 156 157 rad_to_unit_matrix(center, radius, &fPtsToUnit); 158} 159 160size_t SkRadialGradient::contextSize() const { 161 return sizeof(RadialGradientContext); 162} 163 164SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const { 165 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec)); 166} 167 168SkRadialGradient::RadialGradientContext::RadialGradientContext( 169 const SkRadialGradient& shader, const ContextRec& rec) 170 : INHERITED(shader, rec) {} 171 172void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam, 173 int count) { 174 SkASSERT(count > 0); 175 176 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader); 177 178 uint16_t* SK_RESTRICT dstC = dstCParam; 179 180 SkPoint srcPt; 181 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 182 TileProc proc = radialGradient.fTileProc; 183 const uint16_t* SK_RESTRICT cache = fCache->getCache16(); 184 int toggle = init_dither_toggle16(x, y); 185 186 if (fDstToIndexClass != kPerspective_MatrixClass) { 187 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 188 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 189 190 SkScalar sdx = fDstToIndex.getScaleX(); 191 SkScalar sdy = fDstToIndex.getSkewY(); 192 193 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 194 SkFixed storage[2]; 195 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 196 &storage[0], &storage[1]); 197 sdx = SkFixedToScalar(storage[0]); 198 sdy = SkFixedToScalar(storage[1]); 199 } else { 200 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 201 } 202 203 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat; 204 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { 205 shadeProc = shadeSpan16_radial_clamp; 206 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { 207 shadeProc = shadeSpan16_radial_mirror; 208 } else { 209 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); 210 } 211 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, 212 cache, toggle, count); 213 } else { // perspective case 214 SkScalar dstX = SkIntToScalar(x); 215 SkScalar dstY = SkIntToScalar(y); 216 do { 217 dstProc(fDstToIndex, dstX, dstY, &srcPt); 218 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 219 SkASSERT(fi <= 0xFFFF); 220 221 int index = fi >> (16 - kCache16Bits); 222 *dstC++ = cache[toggle + index]; 223 toggle = next_dither_toggle16(toggle); 224 225 dstX += SK_Scalar1; 226 } while (--count != 0); 227 } 228} 229 230SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap, 231 SkMatrix* matrix, SkShader::TileMode* xy) const { 232 if (bitmap) { 233 this->getGradientTableBitmap(bitmap); 234 } 235 if (matrix) { 236 matrix->setScale(SkIntToScalar(kCache32Count), 237 SkIntToScalar(kCache32Count)); 238 matrix->preConcat(fPtsToUnit); 239 } 240 if (xy) { 241 xy[0] = fTileMode; 242 xy[1] = kClamp_TileMode; 243 } 244 return kRadial_BitmapType; 245} 246 247SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { 248 if (info) { 249 commonAsAGradient(info); 250 info->fPoint[0] = fCenter; 251 info->fRadius[0] = fRadius; 252 } 253 return kRadial_GradientType; 254} 255 256SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer) 257 : INHERITED(buffer), 258 fCenter(buffer.readPoint()), 259 fRadius(buffer.readScalar()) { 260} 261 262void SkRadialGradient::flatten(SkWriteBuffer& buffer) const { 263 this->INHERITED::flatten(buffer); 264 buffer.writePoint(fCenter); 265 buffer.writeScalar(fRadius); 266} 267 268namespace { 269 270inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) { 271 // fast, overly-conservative test: checks unit square instead 272 // of unit circle 273 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) || 274 (fx <= -SK_FixedHalf && dx <= 0); 275 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) || 276 (fy <= -SK_FixedHalf && dy <= 0); 277 278 return xClamped || yClamped; 279} 280 281// Return true if (fx * fy) is always inside the unit circle 282// SkPin32 is expensive, but so are all the SkFixedMul in this test, 283// so it shouldn't be run if count is small. 284inline bool no_need_for_radial_pin(int fx, int dx, 285 int fy, int dy, int count) { 286 SkASSERT(count > 0); 287 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 288 return false; 289 } 290 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) { 291 return false; 292 } 293 fx += (count - 1) * dx; 294 fy += (count - 1) * dy; 295 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 296 return false; 297 } 298 return fx*fx + fy*fy <= 0x7FFF*0x7FFF; 299} 300 301#define UNPINNED_RADIAL_STEP \ 302 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \ 303 *dstC++ = cache[toggle + \ 304 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \ 305 toggle = next_dither_toggle(toggle); \ 306 fx += dx; \ 307 fy += dy; 308 309typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx, 310 SkScalar sfy, SkScalar sdy, 311 SkPMColor* dstC, const SkPMColor* cache, 312 int count, int toggle); 313 314// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT 315void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx, 316 SkScalar sfy, SkScalar sdy, 317 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 318 int count, int toggle) { 319 // Floating point seems to be slower than fixed point, 320 // even when we have float hardware. 321 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table; 322 SkFixed fx = SkScalarToFixed(sfx) >> 1; 323 SkFixed dx = SkScalarToFixed(sdx) >> 1; 324 SkFixed fy = SkScalarToFixed(sfy) >> 1; 325 SkFixed dy = SkScalarToFixed(sdy) >> 1; 326 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) { 327 unsigned fi = SkGradientShaderBase::kCache32Count - 1; 328 sk_memset32_dither(dstC, 329 cache[toggle + fi], 330 cache[next_dither_toggle(toggle) + fi], 331 count); 332 } else if ((count > 4) && 333 no_need_for_radial_pin(fx, dx, fy, dy, count)) { 334 unsigned fi; 335 // 4x unroll appears to be no faster than 2x unroll on Linux 336 while (count > 1) { 337 UNPINNED_RADIAL_STEP; 338 UNPINNED_RADIAL_STEP; 339 count -= 2; 340 } 341 if (count) { 342 UNPINNED_RADIAL_STEP; 343 } 344 } else { 345 // Specializing for dy == 0 gains us 25% on Skia benchmarks 346 if (dy == 0) { 347 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 348 yy *= yy; 349 do { 350 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 351 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS); 352 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 353 *dstC++ = cache[toggle + (sqrt_table[fi] >> 354 SkGradientShaderBase::kSqrt32Shift)]; 355 toggle = next_dither_toggle(toggle); 356 fx += dx; 357 } while (--count != 0); 358 } else { 359 do { 360 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 361 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 362 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); 363 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 364 *dstC++ = cache[toggle + (sqrt_table[fi] >> 365 SkGradientShaderBase::kSqrt32Shift)]; 366 toggle = next_dither_toggle(toggle); 367 fx += dx; 368 fy += dy; 369 } while (--count != 0); 370 } 371 } 372} 373 374// Unrolling this loop doesn't seem to help (when float); we're stalling to 375// get the results of the sqrt (?), and don't have enough extra registers to 376// have many in flight. 377template <SkFixed (*TileProc)(SkFixed)> 378void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 379 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 380 int count, int toggle) { 381 do { 382 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy)); 383 const unsigned fi = TileProc(dist); 384 SkASSERT(fi <= 0xFFFF); 385 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; 386 toggle = next_dither_toggle(toggle); 387 fx += dx; 388 fy += dy; 389 } while (--count != 0); 390} 391 392void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 393 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 394 int count, int toggle) { 395 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); 396} 397 398void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 399 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 400 int count, int toggle) { 401 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); 402} 403 404} // namespace 405 406void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y, 407 SkPMColor* SK_RESTRICT dstC, int count) { 408 SkASSERT(count > 0); 409 410 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader); 411 412 SkPoint srcPt; 413 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 414 TileProc proc = radialGradient.fTileProc; 415 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); 416 int toggle = init_dither_toggle(x, y); 417 418 if (fDstToIndexClass != kPerspective_MatrixClass) { 419 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 420 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 421 SkScalar sdx = fDstToIndex.getScaleX(); 422 SkScalar sdy = fDstToIndex.getSkewY(); 423 424 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 425 SkFixed storage[2]; 426 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 427 &storage[0], &storage[1]); 428 sdx = SkFixedToScalar(storage[0]); 429 sdy = SkFixedToScalar(storage[1]); 430 } else { 431 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 432 } 433 434 RadialShadeProc shadeProc = shadeSpan_radial_repeat; 435 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { 436 shadeProc = shadeSpan_radial_clamp; 437 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { 438 shadeProc = shadeSpan_radial_mirror; 439 } else { 440 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); 441 } 442 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle); 443 } else { // perspective case 444 SkScalar dstX = SkIntToScalar(x); 445 SkScalar dstY = SkIntToScalar(y); 446 do { 447 dstProc(fDstToIndex, dstX, dstY, &srcPt); 448 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 449 SkASSERT(fi <= 0xFFFF); 450 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift]; 451 dstX += SK_Scalar1; 452 } while (--count != 0); 453 } 454} 455 456///////////////////////////////////////////////////////////////////// 457 458#if SK_SUPPORT_GPU 459 460#include "GrTBackendEffectFactory.h" 461#include "SkGr.h" 462 463class GrGLRadialGradient : public GrGLGradientEffect { 464public: 465 466 GrGLRadialGradient(const GrBackendEffectFactory& factory, 467 const GrDrawEffect&) : INHERITED (factory) { } 468 virtual ~GrGLRadialGradient() { } 469 470 virtual void emitCode(GrGLShaderBuilder*, 471 const GrDrawEffect&, 472 EffectKey, 473 const char* outputColor, 474 const char* inputColor, 475 const TransformedCoordsArray&, 476 const TextureSamplerArray&) SK_OVERRIDE; 477 478 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 479 return GenBaseGradientKey(drawEffect); 480 } 481 482private: 483 484 typedef GrGLGradientEffect INHERITED; 485 486}; 487 488///////////////////////////////////////////////////////////////////// 489 490class GrRadialGradient : public GrGradientEffect { 491public: 492 static GrEffectRef* Create(GrContext* ctx, 493 const SkRadialGradient& shader, 494 const SkMatrix& matrix, 495 SkShader::TileMode tm) { 496 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm))); 497 return CreateEffectRef(effect); 498 } 499 500 virtual ~GrRadialGradient() { } 501 502 static const char* Name() { return "Radial Gradient"; } 503 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 504 return GrTBackendEffectFactory<GrRadialGradient>::getInstance(); 505 } 506 507 typedef GrGLRadialGradient GLEffect; 508 509private: 510 GrRadialGradient(GrContext* ctx, 511 const SkRadialGradient& shader, 512 const SkMatrix& matrix, 513 SkShader::TileMode tm) 514 : INHERITED(ctx, shader, matrix, tm) { 515 } 516 517 GR_DECLARE_EFFECT_TEST; 518 519 typedef GrGradientEffect INHERITED; 520}; 521 522///////////////////////////////////////////////////////////////////// 523 524GR_DEFINE_EFFECT_TEST(GrRadialGradient); 525 526GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random, 527 GrContext* context, 528 const GrDrawTargetCaps&, 529 GrTexture**) { 530 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()}; 531 SkScalar radius = random->nextUScalar1(); 532 533 SkColor colors[kMaxRandomGradientColors]; 534 SkScalar stopsArray[kMaxRandomGradientColors]; 535 SkScalar* stops = stopsArray; 536 SkShader::TileMode tm; 537 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 538 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius, 539 colors, stops, colorCount, 540 tm)); 541 SkPaint paint; 542 GrColor grColor; 543 GrEffectRef* effect; 544 shader->asNewEffect(context, paint, NULL, &grColor, &effect); 545 return effect; 546} 547 548///////////////////////////////////////////////////////////////////// 549 550void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder, 551 const GrDrawEffect&, 552 EffectKey key, 553 const char* outputColor, 554 const char* inputColor, 555 const TransformedCoordsArray& coords, 556 const TextureSamplerArray& samplers) { 557 this->emitUniforms(builder, key); 558 SkString t("length("); 559 t.append(builder->ensureFSCoords2D(coords, 0)); 560 t.append(")"); 561 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers); 562} 563 564///////////////////////////////////////////////////////////////////// 565 566bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint, 567 const SkMatrix* localMatrix, GrColor* grColor, 568 GrEffectRef** grEffect) const { 569 SkASSERT(NULL != context); 570 571 SkMatrix matrix; 572 if (!this->getLocalMatrix().invert(&matrix)) { 573 return false; 574 } 575 if (localMatrix) { 576 SkMatrix inv; 577 if (!localMatrix->invert(&inv)) { 578 return false; 579 } 580 matrix.postConcat(inv); 581 } 582 matrix.postConcat(fPtsToUnit); 583 584 *grColor = SkColor2GrColorJustAlpha(paint.getColor()); 585 *grEffect = GrRadialGradient::Create(context, *this, matrix, fTileMode); 586 587 return true; 588} 589 590#else 591 592bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint, 593 const SkMatrix* localMatrix, GrColor* grColor, 594 GrEffect** grEffect) const { 595 SkDEBUGFAIL("Should not call in GPU-less build"); 596 return false; 597} 598 599#endif 600 601#ifndef SK_IGNORE_TO_STRING 602void SkRadialGradient::toString(SkString* str) const { 603 str->append("SkRadialGradient: ("); 604 605 str->append("center: ("); 606 str->appendScalar(fCenter.fX); 607 str->append(", "); 608 str->appendScalar(fCenter.fY); 609 str->append(") radius: "); 610 str->appendScalar(fRadius); 611 str->append(" "); 612 613 this->INHERITED::toString(str); 614 615 str->append(")"); 616} 617#endif 618