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 "SkTwoPointRadialGradient.h" 10 11 /* Two-point radial gradients are specified by two circles, each with a center 12 point and radius. The gradient can be considered to be a series of 13 concentric circles, with the color interpolated from the start circle 14 (at t=0) to the end circle (at t=1). 15 16 For each point (x, y) in the span, we want to find the 17 interpolated circle that intersects that point. The center 18 of the desired circle (Cx, Cy) falls at some distance t 19 along the line segment between the start point (Sx, Sy) and 20 end point (Ex, Ey): 21 22 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1) 23 Cy = (1 - t) * Sy + t * Ey 24 25 The radius of the desired circle (r) is also a linear interpolation t 26 between the start and end radii (Sr and Er): 27 28 r = (1 - t) * Sr + t * Er 29 30 But 31 32 (x - Cx)^2 + (y - Cy)^2 = r^2 33 34 so 35 36 (x - ((1 - t) * Sx + t * Ex))^2 37 + (y - ((1 - t) * Sy + t * Ey))^2 38 = ((1 - t) * Sr + t * Er)^2 39 40 Solving for t yields 41 42 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2 43 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t 44 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0 45 46 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy 47 48 [Dx^2 + Dy^2 - Dr^2)] * t^2 49 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t 50 + [dx^2 + dy^2 - Sr^2] = 0 51 52 A quadratic in t. The two roots of the quadratic reflect the two 53 possible circles on which the point may fall. Solving for t yields 54 the gradient value to use. 55 56 If a<0, the start circle is entirely contained in the 57 end circle, and one of the roots will be <0 or >1 (off the line 58 segment). If a>0, the start circle falls at least partially 59 outside the end circle (or vice versa), and the gradient 60 defines a "tube" where a point may be on one circle (on the 61 inside of the tube) or the other (outside of the tube). We choose 62 one arbitrarily. 63 64 In order to keep the math to within the limits of fixed point, 65 we divide the entire quadratic by Dr^2, and replace 66 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving 67 68 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 69 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t 70 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0 71 72 (x' and y' are computed by appending the subtract and scale to the 73 fDstToIndex matrix in the constructor). 74 75 Since the 'A' component of the quadratic is independent of x' and y', it 76 is precomputed in the constructor. Since the 'B' component is linear in 77 x' and y', if x and y are linear in the span, 'B' can be computed 78 incrementally with a simple delta (db below). If it is not (e.g., 79 a perspective projection), it must be computed in the loop. 80 81 */ 82 83 namespace { 84 85 inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, 86 SkScalar sr2d2, SkScalar foura, 87 SkScalar oneOverTwoA, bool posRoot) { 88 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; 89 if (0 == foura) { 90 return SkScalarToFixed(SkScalarDiv(-c, b)); 91 } 92 93 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); 94 if (discrim < 0) { 95 discrim = -discrim; 96 } 97 SkScalar rootDiscrim = SkScalarSqrt(discrim); 98 SkScalar result; 99 if (posRoot) { 100 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); 101 } else { 102 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); 103 } 104 return SkScalarToFixed(result); 105 } 106 107 typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx, 108 SkScalar fy, SkScalar dy, 109 SkScalar b, SkScalar db, 110 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 111 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 112 int count); 113 114 void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx, 115 SkScalar fy, SkScalar dy, 116 SkScalar b, SkScalar db, 117 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 118 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 119 int count) { 120 for (; count > 0; --count) { 121 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 122 fOneOverTwoA, posRoot); 123 SkFixed index = SkClampMax(t, 0xFFFF); 124 SkASSERT(index <= 0xFFFF); 125 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 126 fx += dx; 127 fy += dy; 128 b += db; 129 } 130 } 131 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx, 132 SkScalar fy, SkScalar dy, 133 SkScalar b, SkScalar db, 134 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 135 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 136 int count) { 137 for (; count > 0; --count) { 138 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 139 fOneOverTwoA, posRoot); 140 SkFixed index = mirror_tileproc(t); 141 SkASSERT(index <= 0xFFFF); 142 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 143 fx += dx; 144 fy += dy; 145 b += db; 146 } 147 } 148 149 void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx, 150 SkScalar fy, SkScalar dy, 151 SkScalar b, SkScalar db, 152 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 153 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 154 int count) { 155 for (; count > 0; --count) { 156 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 157 fOneOverTwoA, posRoot); 158 SkFixed index = repeat_tileproc(t); 159 SkASSERT(index <= 0xFFFF); 160 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 161 fx += dx; 162 fy += dy; 163 b += db; 164 } 165 } 166 } 167 168 ///////////////////////////////////////////////////////////////////// 169 170 SkTwoPointRadialGradient::SkTwoPointRadialGradient( 171 const SkPoint& start, SkScalar startRadius, 172 const SkPoint& end, SkScalar endRadius, 173 const Descriptor& desc) 174 : SkGradientShaderBase(desc), 175 fCenter1(start), 176 fCenter2(end), 177 fRadius1(startRadius), 178 fRadius2(endRadius) { 179 init(); 180 } 181 182 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap( 183 SkBitmap* bitmap, 184 SkMatrix* matrix, 185 SkShader::TileMode* xy) const { 186 if (bitmap) { 187 this->getGradientTableBitmap(bitmap); 188 } 189 SkScalar diffL = 0; // just to avoid gcc warning 190 if (matrix) { 191 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) + 192 SkScalarSquare(fDiff.fY)); 193 } 194 if (matrix) { 195 if (diffL) { 196 SkScalar invDiffL = SkScalarInvert(diffL); 197 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY), 198 SkScalarMul(invDiffL, fDiff.fX)); 199 } else { 200 matrix->reset(); 201 } 202 matrix->preConcat(fPtsToUnit); 203 } 204 if (xy) { 205 xy[0] = fTileMode; 206 xy[1] = kClamp_TileMode; 207 } 208 return kTwoPointRadial_BitmapType; 209 } 210 211 SkShader::GradientType SkTwoPointRadialGradient::asAGradient( 212 SkShader::GradientInfo* info) const { 213 if (info) { 214 commonAsAGradient(info); 215 info->fPoint[0] = fCenter1; 216 info->fPoint[1] = fCenter2; 217 info->fRadius[0] = fRadius1; 218 info->fRadius[1] = fRadius2; 219 } 220 return kRadial2_GradientType; 221 } 222 223 void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam, 224 int count) { 225 SkASSERT(count > 0); 226 227 SkPMColor* SK_RESTRICT dstC = dstCParam; 228 229 // Zero difference between radii: fill with transparent black. 230 if (fDiffRadius == 0) { 231 sk_bzero(dstC, count * sizeof(*dstC)); 232 return; 233 } 234 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 235 TileProc proc = fTileProc; 236 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 237 238 SkScalar foura = fA * 4; 239 bool posRoot = fDiffRadius < 0; 240 if (fDstToIndexClass != kPerspective_MatrixClass) { 241 SkPoint srcPt; 242 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 243 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 244 SkScalar dx, fx = srcPt.fX; 245 SkScalar dy, fy = srcPt.fY; 246 247 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 248 SkFixed fixedX, fixedY; 249 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); 250 dx = SkFixedToScalar(fixedX); 251 dy = SkFixedToScalar(fixedY); 252 } else { 253 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 254 dx = fDstToIndex.getScaleX(); 255 dy = fDstToIndex.getSkewY(); 256 } 257 SkScalar b = (SkScalarMul(fDiff.fX, fx) + 258 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; 259 SkScalar db = (SkScalarMul(fDiff.fX, dx) + 260 SkScalarMul(fDiff.fY, dy)) * 2; 261 262 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat; 263 if (SkShader::kClamp_TileMode == fTileMode) { 264 shadeProc = shadeSpan_twopoint_clamp; 265 } else if (SkShader::kMirror_TileMode == fTileMode) { 266 shadeProc = shadeSpan_twopoint_mirror; 267 } else { 268 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 269 } 270 (*shadeProc)(fx, dx, fy, dy, b, db, 271 fSr2D2, foura, fOneOverTwoA, posRoot, 272 dstC, cache, count); 273 } else { // perspective case 274 SkScalar dstX = SkIntToScalar(x); 275 SkScalar dstY = SkIntToScalar(y); 276 for (; count > 0; --count) { 277 SkPoint srcPt; 278 dstProc(fDstToIndex, dstX, dstY, &srcPt); 279 SkScalar fx = srcPt.fX; 280 SkScalar fy = srcPt.fY; 281 SkScalar b = (SkScalarMul(fDiff.fX, fx) + 282 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; 283 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 284 fOneOverTwoA, posRoot); 285 SkFixed index = proc(t); 286 SkASSERT(index <= 0xFFFF); 287 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 288 dstX += SK_Scalar1; 289 } 290 } 291 } 292 293 bool SkTwoPointRadialGradient::setContext( const SkBitmap& device, 294 const SkPaint& paint, 295 const SkMatrix& matrix){ 296 // For now, we might have divided by zero, so detect that 297 if (0 == fDiffRadius) { 298 return false; 299 } 300 301 if (!this->INHERITED::setContext(device, paint, matrix)) { 302 return false; 303 } 304 305 // we don't have a span16 proc 306 fFlags &= ~kHasSpan16_Flag; 307 return true; 308 } 309 310 #ifdef SK_DEVELOPER 311 void SkTwoPointRadialGradient::toString(SkString* str) const { 312 str->append("SkTwoPointRadialGradient: ("); 313 314 str->append("center1: ("); 315 str->appendScalar(fCenter1.fX); 316 str->append(", "); 317 str->appendScalar(fCenter1.fY); 318 str->append(") radius1: "); 319 str->appendScalar(fRadius1); 320 str->append(" "); 321 322 str->append("center2: ("); 323 str->appendScalar(fCenter2.fX); 324 str->append(", "); 325 str->appendScalar(fCenter2.fY); 326 str->append(") radius2: "); 327 str->appendScalar(fRadius2); 328 str->append(" "); 329 330 this->INHERITED::toString(str); 331 332 str->append(")"); 333 } 334 #endif 335 336 SkTwoPointRadialGradient::SkTwoPointRadialGradient( 337 SkFlattenableReadBuffer& buffer) 338 : INHERITED(buffer), 339 fCenter1(buffer.readPoint()), 340 fCenter2(buffer.readPoint()), 341 fRadius1(buffer.readScalar()), 342 fRadius2(buffer.readScalar()) { 343 init(); 344 }; 345 346 void SkTwoPointRadialGradient::flatten( 347 SkFlattenableWriteBuffer& buffer) const { 348 this->INHERITED::flatten(buffer); 349 buffer.writePoint(fCenter1); 350 buffer.writePoint(fCenter2); 351 buffer.writeScalar(fRadius1); 352 buffer.writeScalar(fRadius2); 353 } 354 355 void SkTwoPointRadialGradient::init() { 356 fDiff = fCenter1 - fCenter2; 357 fDiffRadius = fRadius2 - fRadius1; 358 // hack to avoid zero-divide for now 359 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0; 360 fDiff.fX = SkScalarMul(fDiff.fX, inv); 361 fDiff.fY = SkScalarMul(fDiff.fY, inv); 362 fStartRadius = SkScalarMul(fRadius1, inv); 363 fSr2D2 = SkScalarSquare(fStartRadius); 364 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; 365 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; 366 367 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); 368 fPtsToUnit.postScale(inv, inv); 369 } 370 371 ///////////////////////////////////////////////////////////////////// 372 373 #if SK_SUPPORT_GPU 374 375 #include "GrTBackendEffectFactory.h" 376 377 // For brevity 378 typedef GrGLUniformManager::UniformHandle UniformHandle; 379 static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle; 380 381 class GrGLRadial2Gradient : public GrGLGradientEffect { 382 383 public: 384 385 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&); 386 virtual ~GrGLRadial2Gradient() { } 387 388 virtual void emitCode(GrGLShaderBuilder*, 389 const GrDrawEffect&, 390 EffectKey, 391 const char* outputColor, 392 const char* inputColor, 393 const TextureSamplerArray&) SK_OVERRIDE; 394 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 395 396 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 397 398 protected: 399 400 UniformHandle fVSParamUni; 401 UniformHandle fFSParamUni; 402 403 const char* fVSVaryingName; 404 const char* fFSVaryingName; 405 406 bool fIsDegenerate; 407 408 // @{ 409 /// Values last uploaded as uniforms 410 411 SkScalar fCachedCenter; 412 SkScalar fCachedRadius; 413 bool fCachedPosRoot; 414 415 // @} 416 417 private: 418 419 typedef GrGLGradientEffect INHERITED; 420 421 }; 422 423 ///////////////////////////////////////////////////////////////////// 424 425 class GrRadial2Gradient : public GrGradientEffect { 426 public: 427 static GrEffectRef* Create(GrContext* ctx, 428 const SkTwoPointRadialGradient& shader, 429 const SkMatrix& matrix, 430 SkShader::TileMode tm) { 431 AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm))); 432 return CreateEffectRef(effect); 433 } 434 435 virtual ~GrRadial2Gradient() { } 436 437 static const char* Name() { return "Two-Point Radial Gradient"; } 438 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 439 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance(); 440 } 441 442 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. 443 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; } 444 SkScalar center() const { return fCenterX1; } 445 SkScalar radius() const { return fRadius0; } 446 bool isPosRoot() const { return SkToBool(fPosRoot); } 447 448 typedef GrGLRadial2Gradient GLEffect; 449 450 private: 451 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 452 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase); 453 return (INHERITED::onIsEqual(sBase) && 454 this->fCenterX1 == s.fCenterX1 && 455 this->fRadius0 == s.fRadius0 && 456 this->fPosRoot == s.fPosRoot); 457 } 458 459 GrRadial2Gradient(GrContext* ctx, 460 const SkTwoPointRadialGradient& shader, 461 const SkMatrix& matrix, 462 SkShader::TileMode tm) 463 : INHERITED(ctx, shader, matrix, tm) 464 , fCenterX1(shader.getCenterX1()) 465 , fRadius0(shader.getStartRadius()) 466 , fPosRoot(shader.getDiffRadius() < 0) { } 467 468 GR_DECLARE_EFFECT_TEST; 469 470 // @{ 471 // Cache of values - these can change arbitrarily, EXCEPT 472 // we shouldn't change between degenerate and non-degenerate?! 473 474 SkScalar fCenterX1; 475 SkScalar fRadius0; 476 SkBool8 fPosRoot; 477 478 // @} 479 480 typedef GrGradientEffect INHERITED; 481 }; 482 483 ///////////////////////////////////////////////////////////////////// 484 485 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient); 486 487 GrEffectRef* GrRadial2Gradient::TestCreate(SkMWCRandom* random, 488 GrContext* context, 489 const GrDrawTargetCaps&, 490 GrTexture**) { 491 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 492 SkScalar radius1 = random->nextUScalar1(); 493 SkPoint center2; 494 SkScalar radius2; 495 do { 496 center2.set(random->nextUScalar1(), random->nextUScalar1()); 497 radius2 = random->nextUScalar1 (); 498 // There is a bug in two point radial gradients with identical radii 499 } while (radius1 == radius2); 500 501 SkColor colors[kMaxRandomGradientColors]; 502 SkScalar stopsArray[kMaxRandomGradientColors]; 503 SkScalar* stops = stopsArray; 504 SkShader::TileMode tm; 505 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 506 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1, 507 center2, radius2, 508 colors, stops, colorCount, 509 tm)); 510 SkPaint paint; 511 return shader->asNewEffect(context, paint); 512 } 513 514 ///////////////////////////////////////////////////////////////////// 515 516 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory, 517 const GrDrawEffect& drawEffect) 518 : INHERITED(factory) 519 , fVSParamUni(kInvalidUniformHandle) 520 , fFSParamUni(kInvalidUniformHandle) 521 , fVSVaryingName(NULL) 522 , fFSVaryingName(NULL) 523 , fCachedCenter(SK_ScalarMax) 524 , fCachedRadius(-SK_ScalarMax) 525 , fCachedPosRoot(0) { 526 527 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>(); 528 fIsDegenerate = data.isDegenerate(); 529 } 530 531 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder, 532 const GrDrawEffect& drawEffect, 533 EffectKey key, 534 const char* outputColor, 535 const char* inputColor, 536 const TextureSamplerArray& samplers) { 537 538 this->emitYCoordUniform(builder); 539 const char* fsCoords; 540 const char* vsCoordsVarying; 541 GrSLType coordsVaryingType; 542 this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType); 543 544 // 2 copies of uniform array, 1 for each of vertex & fragment shader, 545 // to work around Xoom bug. Doesn't seem to cause performance decrease 546 // in test apps, but need to keep an eye on it. 547 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType, 548 kFloat_GrSLType, "Radial2VSParams", 6); 549 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType, 550 kFloat_GrSLType, "Radial2FSParams", 6); 551 552 // For radial gradients without perspective we can pass the linear 553 // part of the quadratic as a varying. 554 if (kVec2f_GrSLType == coordsVaryingType) { 555 builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", &fVSVaryingName, &fFSVaryingName); 556 } 557 558 // VS 559 { 560 SkString p2; 561 SkString p3; 562 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2); 563 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3); 564 565 // For radial gradients without perspective we can pass the linear 566 // part of the quadratic as a varying. 567 if (kVec2f_GrSLType == coordsVaryingType) { 568 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3]) 569 builder->vsCodeAppendf("\t%s = 2.0 *(%s * %s.x - %s);\n", 570 fVSVaryingName, p2.c_str(), 571 vsCoordsVarying, p3.c_str()); 572 } 573 } 574 575 // FS 576 { 577 SkString cName("c"); 578 SkString ac4Name("ac4"); 579 SkString rootName("root"); 580 SkString t; 581 SkString p0; 582 SkString p1; 583 SkString p2; 584 SkString p3; 585 SkString p4; 586 SkString p5; 587 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0); 588 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1); 589 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2); 590 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3); 591 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4); 592 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5); 593 594 // If we we're able to interpolate the linear component, 595 // bVar is the varying; otherwise compute it 596 SkString bVar; 597 if (kVec2f_GrSLType == coordsVaryingType) { 598 bVar = fFSVaryingName; 599 } else { 600 bVar = "b"; 601 builder->fsCodeAppendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n", 602 bVar.c_str(), p2.c_str(), fsCoords, p3.c_str()); 603 } 604 605 // c = (x^2)+(y^2) - params[4] 606 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", 607 cName.c_str(), 608 fsCoords, 609 fsCoords, 610 p4.c_str()); 611 612 // If we aren't degenerate, emit some extra code, and accept a slightly 613 // more complex coord. 614 if (!fIsDegenerate) { 615 616 // ac4 = 4.0 * params[0] * c 617 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n", 618 ac4Name.c_str(), p0.c_str(), 619 cName.c_str()); 620 621 // root = sqrt(b^2-4ac) 622 // (abs to avoid exception due to fp precision) 623 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", 624 rootName.c_str(), bVar.c_str(), bVar.c_str(), 625 ac4Name.c_str()); 626 627 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] 628 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), 629 rootName.c_str(), p1.c_str()); 630 } else { 631 // t is: -c/b 632 t.printf("-%s / %s", cName.c_str(), bVar.c_str()); 633 } 634 635 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]); 636 } 637 } 638 639 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, 640 const GrDrawEffect& drawEffect) { 641 INHERITED::setData(uman, drawEffect); 642 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>(); 643 GrAssert(data.isDegenerate() == fIsDegenerate); 644 SkScalar centerX1 = data.center(); 645 SkScalar radius0 = data.radius(); 646 if (fCachedCenter != centerX1 || 647 fCachedRadius != radius0 || 648 fCachedPosRoot != data.isPosRoot()) { 649 650 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1; 651 652 // When we're in the degenerate (linear) case, the second 653 // value will be INF but the program doesn't read it. (We 654 // use the same 6 uniforms even though we don't need them 655 // all in the linear case just to keep the code complexity 656 // down). 657 float values[6] = { 658 SkScalarToFloat(a), 659 1 / (2.f * SkScalarToFloat(a)), 660 SkScalarToFloat(centerX1), 661 SkScalarToFloat(radius0), 662 SkScalarToFloat(SkScalarMul(radius0, radius0)), 663 data.isPosRoot() ? 1.f : -1.f 664 }; 665 666 uman.set1fv(fVSParamUni, 0, 6, values); 667 uman.set1fv(fFSParamUni, 0, 6, values); 668 fCachedCenter = centerX1; 669 fCachedRadius = radius0; 670 fCachedPosRoot = data.isPosRoot(); 671 } 672 } 673 674 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect, 675 const GrGLCaps&) { 676 enum { 677 kIsDegenerate = 1 << kMatrixKeyBitCnt, 678 }; 679 680 EffectKey key = GenMatrixKey(drawEffect); 681 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) { 682 key |= kIsDegenerate; 683 } 684 return key; 685 } 686 687 ///////////////////////////////////////////////////////////////////// 688 689 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const { 690 SkASSERT(NULL != context); 691 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis. 692 SkMatrix matrix; 693 if (!this->getLocalMatrix().invert(&matrix)) { 694 return NULL; 695 } 696 matrix.postConcat(fPtsToUnit); 697 698 SkScalar diffLen = fDiff.length(); 699 if (0 != diffLen) { 700 SkScalar invDiffLen = SkScalarInvert(diffLen); 701 SkMatrix rot; 702 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY), 703 SkScalarMul(invDiffLen, fDiff.fX)); 704 matrix.postConcat(rot); 705 } 706 707 return GrRadial2Gradient::Create(context, *this, matrix, fTileMode); 708 } 709 710 #else 711 712 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const { 713 SkDEBUGFAIL("Should not call in GPU-less build"); 714 return NULL; 715 } 716 717 #endif 718