SkRadialGradient.cpp revision 4eaf9cef5a76098f78efac30beb966ac833d32c2
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#include "SkNx.h" 12 13#define kSQRT_TABLE_BITS 11 14#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS) 15 16SK_COMPILE_ASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, SqrtTableSizesMatch); 17 18#if 0 19 20#include <stdio.h> 21 22void SkRadialGradient_BuildTable() { 23 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table 24 25 FILE* file = ::fopen("SkRadialGradient_Table.h", "w"); 26 SkASSERT(file); 27 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n"); 28 29 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) { 30 if ((i & 15) == 0) { 31 ::fprintf(file, "\t"); 32 } 33 34 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8); 35 36 ::fprintf(file, "0x%02X", value); 37 if (i < kSQRT_TABLE_SIZE-1) { 38 ::fprintf(file, ", "); 39 } 40 if ((i & 15) == 15) { 41 ::fprintf(file, "\n"); 42 } 43 } 44 ::fprintf(file, "};\n"); 45 ::fclose(file); 46} 47 48#endif 49 50namespace { 51 52// GCC doesn't like using static functions as template arguments. So force these to be non-static. 53inline SkFixed mirror_tileproc_nonstatic(SkFixed x) { 54 return mirror_tileproc(x); 55} 56 57inline SkFixed repeat_tileproc_nonstatic(SkFixed x) { 58 return repeat_tileproc(x); 59} 60 61SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) { 62 SkScalar inv = SkScalarInvert(radius); 63 64 SkMatrix matrix; 65 matrix.setTranslate(-center.fX, -center.fY); 66 matrix.postScale(inv, inv); 67 return matrix; 68} 69 70typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx, 71 SkScalar sfy, SkScalar sdy, 72 uint16_t* dstC, const uint16_t* cache, 73 int toggle, int count); 74 75void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx, 76 SkScalar sfy, SkScalar sdy, 77 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, 78 int toggle, int count) { 79 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table; 80 81 /* knock these down so we can pin against +- 0x7FFF, which is an 82 immediate load, rather than 0xFFFF which is slower. This is a 83 compromise, since it reduces our precision, but that appears 84 to be visually OK. If we decide this is OK for all of our cases, 85 we could (it seems) put this scale-down into fDstToIndex, 86 to avoid having to do these extra shifts each time. 87 */ 88 SkFixed fx = SkScalarToFixed(sfx) >> 1; 89 SkFixed dx = SkScalarToFixed(sdx) >> 1; 90 SkFixed fy = SkScalarToFixed(sfy) >> 1; 91 SkFixed dy = SkScalarToFixed(sdy) >> 1; 92 // might perform this check for the other modes, 93 // but the win will be a smaller % of the total 94 if (dy == 0) { 95 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 96 fy *= fy; 97 do { 98 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 99 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS); 100 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 101 fx += dx; 102 *dstC++ = cache[toggle + 103 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; 104 toggle = next_dither_toggle16(toggle); 105 } while (--count != 0); 106 } else { 107 do { 108 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 109 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 110 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); 111 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 112 fx += dx; 113 fy += dy; 114 *dstC++ = cache[toggle + 115 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; 116 toggle = next_dither_toggle16(toggle); 117 } while (--count != 0); 118 } 119} 120 121template <SkFixed (*TileProc)(SkFixed)> 122void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 123 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, 124 int toggle, int count) { 125 do { 126 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy)); 127 const unsigned fi = TileProc(dist); 128 SkASSERT(fi <= 0xFFFF); 129 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)]; 130 toggle = next_dither_toggle16(toggle); 131 fx += dx; 132 fy += dy; 133 } while (--count != 0); 134} 135 136void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 137 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, 138 int toggle, int count) { 139 shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count); 140} 141 142void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 143 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, 144 int toggle, int count) { 145 shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count); 146} 147 148} // namespace 149 150///////////////////////////////////////////////////////////////////// 151 152SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc) 153 : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius)) 154 , fCenter(center) 155 , fRadius(radius) { 156} 157 158size_t SkRadialGradient::contextSize() const { 159 return sizeof(RadialGradientContext); 160} 161 162SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const { 163 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec)); 164} 165 166SkRadialGradient::RadialGradientContext::RadialGradientContext( 167 const SkRadialGradient& shader, const ContextRec& rec) 168 : INHERITED(shader, rec) {} 169 170void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam, 171 int count) { 172 SkASSERT(count > 0); 173 174 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader); 175 176 uint16_t* SK_RESTRICT dstC = dstCParam; 177 178 SkPoint srcPt; 179 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 180 TileProc proc = radialGradient.fTileProc; 181 const uint16_t* SK_RESTRICT cache = fCache->getCache16(); 182 int toggle = init_dither_toggle16(x, y); 183 184 if (fDstToIndexClass != kPerspective_MatrixClass) { 185 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 186 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 187 188 SkScalar sdx = fDstToIndex.getScaleX(); 189 SkScalar sdy = fDstToIndex.getSkewY(); 190 191 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 192 SkFixed storage[2]; 193 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 194 &storage[0], &storage[1]); 195 sdx = SkFixedToScalar(storage[0]); 196 sdy = SkFixedToScalar(storage[1]); 197 } else { 198 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 199 } 200 201 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat; 202 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { 203 shadeProc = shadeSpan16_radial_clamp; 204 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { 205 shadeProc = shadeSpan16_radial_mirror; 206 } else { 207 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); 208 } 209 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, 210 cache, toggle, count); 211 } else { // perspective case 212 SkScalar dstX = SkIntToScalar(x); 213 SkScalar dstY = SkIntToScalar(y); 214 do { 215 dstProc(fDstToIndex, dstX, dstY, &srcPt); 216 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 217 SkASSERT(fi <= 0xFFFF); 218 219 int index = fi >> (16 - kCache16Bits); 220 *dstC++ = cache[toggle + index]; 221 toggle = next_dither_toggle16(toggle); 222 223 dstX += SK_Scalar1; 224 } while (--count != 0); 225 } 226} 227 228SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap, 229 SkMatrix* matrix, SkShader::TileMode* xy) const { 230 if (bitmap) { 231 this->getGradientTableBitmap(bitmap); 232 } 233 if (matrix) { 234 matrix->setScale(SkIntToScalar(kCache32Count), 235 SkIntToScalar(kCache32Count)); 236 matrix->preConcat(fPtsToUnit); 237 } 238 if (xy) { 239 xy[0] = fTileMode; 240 xy[1] = kClamp_TileMode; 241 } 242 return kRadial_BitmapType; 243} 244 245SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { 246 if (info) { 247 commonAsAGradient(info); 248 info->fPoint[0] = fCenter; 249 info->fRadius[0] = fRadius; 250 } 251 return kRadial_GradientType; 252} 253 254SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) { 255 DescriptorScope desc; 256 if (!desc.unflatten(buffer)) { 257 return NULL; 258 } 259 const SkPoint center = buffer.readPoint(); 260 const SkScalar radius = buffer.readScalar(); 261 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount, 262 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix); 263} 264 265void SkRadialGradient::flatten(SkWriteBuffer& buffer) const { 266 this->INHERITED::flatten(buffer); 267 buffer.writePoint(fCenter); 268 buffer.writeScalar(fRadius); 269} 270 271namespace { 272 273inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) { 274 // fast, overly-conservative test: checks unit square instead of unit circle 275 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) || (fx <= -SK_FixedHalf && dx <= 0); 276 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) || (fy <= -SK_FixedHalf && dy <= 0); 277 return xClamped || yClamped; 278} 279 280inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) { 281 // fast, overly-conservative test: checks unit square instead of unit circle 282 bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0); 283 bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0); 284 return xClamped || yClamped; 285} 286 287// Return true if (fx * fy) is always inside the unit circle 288// SkPin32 is expensive, but so are all the SkFixedMul in this test, 289// so it shouldn't be run if count is small. 290inline bool no_need_for_radial_pin(int fx, int dx, 291 int fy, int dy, int count) { 292 SkASSERT(count > 0); 293 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 294 return false; 295 } 296 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) { 297 return false; 298 } 299 fx += (count - 1) * dx; 300 fy += (count - 1) * dy; 301 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) { 302 return false; 303 } 304 return fx*fx + fy*fy <= 0x7FFF*0x7FFF; 305} 306 307#define UNPINNED_RADIAL_STEP \ 308 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \ 309 *dstC++ = cache[toggle + \ 310 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \ 311 toggle = next_dither_toggle(toggle); \ 312 fx += dx; \ 313 fy += dy; 314 315typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx, 316 SkScalar sfy, SkScalar sdy, 317 SkPMColor* dstC, const SkPMColor* cache, 318 int count, int toggle); 319 320// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT 321void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx, 322 SkScalar sfy, SkScalar sdy, 323 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 324 int count, int toggle) { 325 // Floating point seems to be slower than fixed point, 326 // even when we have float hardware. 327 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table; 328 SkFixed fx = SkScalarToFixed(sfx) >> 1; 329 SkFixed dx = SkScalarToFixed(sdx) >> 1; 330 SkFixed fy = SkScalarToFixed(sfy) >> 1; 331 SkFixed dy = SkScalarToFixed(sdy) >> 1; 332 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) { 333 unsigned fi = SkGradientShaderBase::kCache32Count - 1; 334 sk_memset32_dither(dstC, 335 cache[toggle + fi], 336 cache[next_dither_toggle(toggle) + fi], 337 count); 338 } else if ((count > 4) && 339 no_need_for_radial_pin(fx, dx, fy, dy, count)) { 340 unsigned fi; 341 // 4x unroll appears to be no faster than 2x unroll on Linux 342 while (count > 1) { 343 UNPINNED_RADIAL_STEP; 344 UNPINNED_RADIAL_STEP; 345 count -= 2; 346 } 347 if (count) { 348 UNPINNED_RADIAL_STEP; 349 } 350 } else { 351 // Specializing for dy == 0 gains us 25% on Skia benchmarks 352 if (dy == 0) { 353 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 354 yy *= yy; 355 do { 356 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 357 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS); 358 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 359 *dstC++ = cache[toggle + (sqrt_table[fi] >> 360 SkGradientShaderBase::kSqrt32Shift)]; 361 toggle = next_dither_toggle(toggle); 362 fx += dx; 363 } while (--count != 0); 364 } else { 365 do { 366 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1); 367 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1); 368 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); 369 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); 370 *dstC++ = cache[toggle + (sqrt_table[fi] >> 371 SkGradientShaderBase::kSqrt32Shift)]; 372 toggle = next_dither_toggle(toggle); 373 fx += dx; 374 fy += dy; 375 } while (--count != 0); 376 } 377 } 378} 379 380static inline Sk4f fast_sqrt(const Sk4f& R) { 381 // R * R.rsqrt0() is much faster, but it's non-monotonic, which isn't so pretty for gradients. 382 return R * R.rsqrt1(); 383} 384 385static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) { 386 return a * a + b * b; 387} 388 389void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy, 390 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 391 int count, int toggle) { 392 if (radial_completely_pinned(sfx, sdx, sfy, sdy)) { 393 unsigned fi = SkGradientShaderBase::kCache32Count - 1; 394 sk_memset32_dither(dstC, 395 cache[toggle + fi], 396 cache[next_dither_toggle(toggle) + fi], 397 count); 398 } else { 399 const Sk4f max(255); 400 const float scale = 255; 401 sfx *= scale; 402 sfy *= scale; 403 sdx *= scale; 404 sdy *= scale; 405 const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx); 406 const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy); 407 const Sk4f dx4(sdx * 4); 408 const Sk4f dy4(sdy * 4); 409 410 Sk4f tmpxy = fx4 * dx4 + fy4 * dy4; 411 Sk4f tmpdxdy = sum_squares(dx4, dy4); 412 Sk4f R = sum_squares(fx4, fy4); 413 Sk4f dR = tmpxy + tmpxy + tmpdxdy; 414 const Sk4f ddR = tmpdxdy + tmpdxdy; 415 416 for (int i = 0; i < (count >> 2); ++i) { 417 Sk4f dist = Sk4f::Min(fast_sqrt(R), max); 418 R += dR; 419 dR += ddR; 420 421 int fi[4]; 422 dist.castTrunc().store(fi); 423 424 for (int i = 0; i < 4; i++) { 425 *dstC++ = cache[toggle + fi[i]]; 426 toggle = next_dither_toggle(toggle); 427 } 428 } 429 count &= 3; 430 if (count) { 431 Sk4f dist = Sk4f::Min(fast_sqrt(R), max); 432 433 int fi[4]; 434 dist.castTrunc().store(fi); 435 for (int i = 0; i < count; i++) { 436 *dstC++ = cache[toggle + fi[i]]; 437 toggle = next_dither_toggle(toggle); 438 } 439 } 440 } 441} 442 443// Unrolling this loop doesn't seem to help (when float); we're stalling to 444// get the results of the sqrt (?), and don't have enough extra registers to 445// have many in flight. 446template <SkFixed (*TileProc)(SkFixed)> 447void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 448 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 449 int count, int toggle) { 450 do { 451 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy)); 452 const unsigned fi = TileProc(dist); 453 SkASSERT(fi <= 0xFFFF); 454 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; 455 toggle = next_dither_toggle(toggle); 456 fx += dx; 457 fy += dy; 458 } while (--count != 0); 459} 460 461void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 462 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 463 int count, int toggle) { 464 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); 465} 466 467void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 468 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 469 int count, int toggle) { 470 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); 471} 472 473} // namespace 474 475void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y, 476 SkPMColor* SK_RESTRICT dstC, int count) { 477#ifdef SK_SUPPORT_LEGACY_RADIAL_GRADIENT_SQRT 478 const bool use_new_proc = false; 479#else 480 const bool use_new_proc = true; 481#endif 482 SkASSERT(count > 0); 483 484 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader); 485 486 SkPoint srcPt; 487 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 488 TileProc proc = radialGradient.fTileProc; 489 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); 490 int toggle = init_dither_toggle(x, y); 491 492 if (fDstToIndexClass != kPerspective_MatrixClass) { 493 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 494 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 495 SkScalar sdx = fDstToIndex.getScaleX(); 496 SkScalar sdy = fDstToIndex.getSkewY(); 497 498 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 499 SkFixed storage[2]; 500 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), 501 &storage[0], &storage[1]); 502 sdx = SkFixedToScalar(storage[0]); 503 sdy = SkFixedToScalar(storage[1]); 504 } else { 505 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 506 } 507 508 RadialShadeProc shadeProc = shadeSpan_radial_repeat; 509 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { 510 shadeProc = use_new_proc ? shadeSpan_radial_clamp2 : shadeSpan_radial_clamp; 511 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { 512 shadeProc = shadeSpan_radial_mirror; 513 } else { 514 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); 515 } 516 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle); 517 } else { // perspective case 518 SkScalar dstX = SkIntToScalar(x); 519 SkScalar dstY = SkIntToScalar(y); 520 do { 521 dstProc(fDstToIndex, dstX, dstY, &srcPt); 522 unsigned fi = proc(SkScalarToFixed(srcPt.length())); 523 SkASSERT(fi <= 0xFFFF); 524 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift]; 525 dstX += SK_Scalar1; 526 } while (--count != 0); 527 } 528} 529 530///////////////////////////////////////////////////////////////////// 531 532#if SK_SUPPORT_GPU 533 534#include "SkGr.h" 535#include "gl/builders/GrGLProgramBuilder.h" 536 537class GrGLRadialGradient : public GrGLGradientEffect { 538public: 539 540 GrGLRadialGradient(const GrProcessor&) {} 541 virtual ~GrGLRadialGradient() { } 542 543 virtual void emitCode(GrGLFPBuilder*, 544 const GrFragmentProcessor&, 545 const char* outputColor, 546 const char* inputColor, 547 const TransformedCoordsArray&, 548 const TextureSamplerArray&) override; 549 550 static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) { 551 b->add32(GenBaseGradientKey(processor)); 552 } 553 554private: 555 556 typedef GrGLGradientEffect INHERITED; 557 558}; 559 560///////////////////////////////////////////////////////////////////// 561 562class GrRadialGradient : public GrGradientEffect { 563public: 564 static GrFragmentProcessor* Create(GrContext* ctx, 565 const SkRadialGradient& shader, 566 const SkMatrix& matrix, 567 SkShader::TileMode tm) { 568 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)); 569 } 570 571 virtual ~GrRadialGradient() { } 572 573 const char* name() const override { return "Radial Gradient"; } 574 575 virtual void getGLProcessorKey(const GrGLSLCaps& caps, 576 GrProcessorKeyBuilder* b) const override { 577 GrGLRadialGradient::GenKey(*this, caps, b); 578 } 579 580 GrGLFragmentProcessor* createGLInstance() const override { 581 return SkNEW_ARGS(GrGLRadialGradient, (*this)); 582 } 583 584private: 585 GrRadialGradient(GrContext* ctx, 586 const SkRadialGradient& shader, 587 const SkMatrix& matrix, 588 SkShader::TileMode tm) 589 : INHERITED(ctx, shader, matrix, tm) { 590 this->initClassID<GrRadialGradient>(); 591 } 592 593 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 594 595 typedef GrGradientEffect INHERITED; 596}; 597 598///////////////////////////////////////////////////////////////////// 599 600GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient); 601 602GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random, 603 GrContext* context, 604 const GrDrawTargetCaps&, 605 GrTexture**) { 606 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()}; 607 SkScalar radius = random->nextUScalar1(); 608 609 SkColor colors[kMaxRandomGradientColors]; 610 SkScalar stopsArray[kMaxRandomGradientColors]; 611 SkScalar* stops = stopsArray; 612 SkShader::TileMode tm; 613 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 614 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius, 615 colors, stops, colorCount, 616 tm)); 617 SkPaint paint; 618 GrColor paintColor; 619 GrFragmentProcessor* fp; 620 SkAssertResult(shader->asFragmentProcessor(context, paint, 621 GrTest::TestMatrix(random), NULL, 622 &paintColor, &fp)); 623 return fp; 624} 625 626///////////////////////////////////////////////////////////////////// 627 628void GrGLRadialGradient::emitCode(GrGLFPBuilder* builder, 629 const GrFragmentProcessor& fp, 630 const char* outputColor, 631 const char* inputColor, 632 const TransformedCoordsArray& coords, 633 const TextureSamplerArray& samplers) { 634 const GrRadialGradient& ge = fp.cast<GrRadialGradient>(); 635 this->emitUniforms(builder, ge); 636 SkString t("length("); 637 t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0)); 638 t.append(")"); 639 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers); 640} 641 642///////////////////////////////////////////////////////////////////// 643 644bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint, 645 const SkMatrix& viewM, 646 const SkMatrix* localMatrix, GrColor* paintColor, 647 GrFragmentProcessor** fp) const { 648 SkASSERT(context); 649 650 SkMatrix matrix; 651 if (!this->getLocalMatrix().invert(&matrix)) { 652 return false; 653 } 654 if (localMatrix) { 655 SkMatrix inv; 656 if (!localMatrix->invert(&inv)) { 657 return false; 658 } 659 matrix.postConcat(inv); 660 } 661 matrix.postConcat(fPtsToUnit); 662 663 *paintColor = SkColor2GrColorJustAlpha(paint.getColor()); 664 *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode); 665 666 return true; 667} 668 669#else 670 671bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&, 672 const SkMatrix*, GrColor*, 673 GrFragmentProcessor**) const { 674 SkDEBUGFAIL("Should not call in GPU-less build"); 675 return false; 676} 677 678#endif 679 680#ifndef SK_IGNORE_TO_STRING 681void SkRadialGradient::toString(SkString* str) const { 682 str->append("SkRadialGradient: ("); 683 684 str->append("center: ("); 685 str->appendScalar(fCenter.fX); 686 str->append(", "); 687 str->appendScalar(fCenter.fY); 688 str->append(") radius: "); 689 str->appendScalar(fRadius); 690 str->append(" "); 691 692 this->INHERITED::toString(str); 693 694 str->append(")"); 695} 696#endif 697