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, const SkMatrix* localMatrix) 174 : SkGradientShaderBase(desc, localMatrix), 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 size_t SkTwoPointRadialGradient::contextSize() const { 224 return sizeof(TwoPointRadialGradientContext); 225 } 226 227 SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& rec, 228 void* storage) const { 229 // For now, we might have divided by zero, so detect that. 230 if (0 == fDiffRadius) { 231 return NULL; 232 } 233 return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this, rec)); 234 } 235 236 SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext( 237 const SkTwoPointRadialGradient& shader, const ContextRec& rec) 238 : INHERITED(shader, rec) 239 { 240 // we don't have a span16 proc 241 fFlags &= ~kHasSpan16_Flag; 242 } 243 244 void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan( 245 int x, int y, SkPMColor* dstCParam, int count) { 246 SkASSERT(count > 0); 247 248 const SkTwoPointRadialGradient& twoPointRadialGradient = 249 static_cast<const SkTwoPointRadialGradient&>(fShader); 250 251 SkPMColor* SK_RESTRICT dstC = dstCParam; 252 253 // Zero difference between radii: fill with transparent black. 254 if (twoPointRadialGradient.fDiffRadius == 0) { 255 sk_bzero(dstC, count * sizeof(*dstC)); 256 return; 257 } 258 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 259 TileProc proc = twoPointRadialGradient.fTileProc; 260 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); 261 262 SkScalar foura = twoPointRadialGradient.fA * 4; 263 bool posRoot = twoPointRadialGradient.fDiffRadius < 0; 264 if (fDstToIndexClass != kPerspective_MatrixClass) { 265 SkPoint srcPt; 266 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 267 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 268 SkScalar dx, fx = srcPt.fX; 269 SkScalar dy, fy = srcPt.fY; 270 271 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 272 SkFixed fixedX, fixedY; 273 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); 274 dx = SkFixedToScalar(fixedX); 275 dy = SkFixedToScalar(fixedY); 276 } else { 277 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 278 dx = fDstToIndex.getScaleX(); 279 dy = fDstToIndex.getSkewY(); 280 } 281 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) + 282 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) - 283 twoPointRadialGradient.fStartRadius) * 2; 284 SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) + 285 SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2; 286 287 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat; 288 if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) { 289 shadeProc = shadeSpan_twopoint_clamp; 290 } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) { 291 shadeProc = shadeSpan_twopoint_mirror; 292 } else { 293 SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode); 294 } 295 (*shadeProc)(fx, dx, fy, dy, b, db, 296 twoPointRadialGradient.fSr2D2, foura, 297 twoPointRadialGradient.fOneOverTwoA, posRoot, 298 dstC, cache, count); 299 } else { // perspective case 300 SkScalar dstX = SkIntToScalar(x); 301 SkScalar dstY = SkIntToScalar(y); 302 for (; count > 0; --count) { 303 SkPoint srcPt; 304 dstProc(fDstToIndex, dstX, dstY, &srcPt); 305 SkScalar fx = srcPt.fX; 306 SkScalar fy = srcPt.fY; 307 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) + 308 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) - 309 twoPointRadialGradient.fStartRadius) * 2; 310 SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura, 311 twoPointRadialGradient.fOneOverTwoA, posRoot); 312 SkFixed index = proc(t); 313 SkASSERT(index <= 0xFFFF); 314 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 315 dstX += SK_Scalar1; 316 } 317 } 318 } 319 320 #ifndef SK_IGNORE_TO_STRING 321 void SkTwoPointRadialGradient::toString(SkString* str) const { 322 str->append("SkTwoPointRadialGradient: ("); 323 324 str->append("center1: ("); 325 str->appendScalar(fCenter1.fX); 326 str->append(", "); 327 str->appendScalar(fCenter1.fY); 328 str->append(") radius1: "); 329 str->appendScalar(fRadius1); 330 str->append(" "); 331 332 str->append("center2: ("); 333 str->appendScalar(fCenter2.fX); 334 str->append(", "); 335 str->appendScalar(fCenter2.fY); 336 str->append(") radius2: "); 337 str->appendScalar(fRadius2); 338 str->append(" "); 339 340 this->INHERITED::toString(str); 341 342 str->append(")"); 343 } 344 #endif 345 346 SkTwoPointRadialGradient::SkTwoPointRadialGradient( 347 SkReadBuffer& buffer) 348 : INHERITED(buffer), 349 fCenter1(buffer.readPoint()), 350 fCenter2(buffer.readPoint()), 351 fRadius1(buffer.readScalar()), 352 fRadius2(buffer.readScalar()) { 353 init(); 354 }; 355 356 void SkTwoPointRadialGradient::flatten( 357 SkWriteBuffer& buffer) const { 358 this->INHERITED::flatten(buffer); 359 buffer.writePoint(fCenter1); 360 buffer.writePoint(fCenter2); 361 buffer.writeScalar(fRadius1); 362 buffer.writeScalar(fRadius2); 363 } 364 365 void SkTwoPointRadialGradient::init() { 366 fDiff = fCenter1 - fCenter2; 367 fDiffRadius = fRadius2 - fRadius1; 368 // hack to avoid zero-divide for now 369 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0; 370 fDiff.fX = SkScalarMul(fDiff.fX, inv); 371 fDiff.fY = SkScalarMul(fDiff.fY, inv); 372 fStartRadius = SkScalarMul(fRadius1, inv); 373 fSr2D2 = SkScalarSquare(fStartRadius); 374 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; 375 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; 376 377 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); 378 fPtsToUnit.postScale(inv, inv); 379 } 380 381 ///////////////////////////////////////////////////////////////////// 382 383 #if SK_SUPPORT_GPU 384 385 #include "GrTBackendEffectFactory.h" 386 #include "SkGr.h" 387 388 // For brevity 389 typedef GrGLUniformManager::UniformHandle UniformHandle; 390 391 class GrGLRadial2Gradient : public GrGLGradientEffect { 392 393 public: 394 395 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&); 396 virtual ~GrGLRadial2Gradient() { } 397 398 virtual void emitCode(GrGLShaderBuilder*, 399 const GrDrawEffect&, 400 EffectKey, 401 const char* outputColor, 402 const char* inputColor, 403 const TransformedCoordsArray&, 404 const TextureSamplerArray&) SK_OVERRIDE; 405 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 406 407 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 408 409 protected: 410 411 UniformHandle fParamUni; 412 413 const char* fVSVaryingName; 414 const char* fFSVaryingName; 415 416 bool fIsDegenerate; 417 418 // @{ 419 /// Values last uploaded as uniforms 420 421 SkScalar fCachedCenter; 422 SkScalar fCachedRadius; 423 bool fCachedPosRoot; 424 425 // @} 426 427 private: 428 429 typedef GrGLGradientEffect INHERITED; 430 431 }; 432 433 ///////////////////////////////////////////////////////////////////// 434 435 class GrRadial2Gradient : public GrGradientEffect { 436 public: 437 static GrEffectRef* Create(GrContext* ctx, 438 const SkTwoPointRadialGradient& shader, 439 const SkMatrix& matrix, 440 SkShader::TileMode tm) { 441 AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm))); 442 return CreateEffectRef(effect); 443 } 444 445 virtual ~GrRadial2Gradient() { } 446 447 static const char* Name() { return "Two-Point Radial Gradient"; } 448 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 449 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance(); 450 } 451 452 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. 453 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; } 454 SkScalar center() const { return fCenterX1; } 455 SkScalar radius() const { return fRadius0; } 456 bool isPosRoot() const { return SkToBool(fPosRoot); } 457 458 typedef GrGLRadial2Gradient GLEffect; 459 460 private: 461 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 462 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase); 463 return (INHERITED::onIsEqual(sBase) && 464 this->fCenterX1 == s.fCenterX1 && 465 this->fRadius0 == s.fRadius0 && 466 this->fPosRoot == s.fPosRoot); 467 } 468 469 GrRadial2Gradient(GrContext* ctx, 470 const SkTwoPointRadialGradient& shader, 471 const SkMatrix& matrix, 472 SkShader::TileMode tm) 473 : INHERITED(ctx, shader, matrix, tm) 474 , fCenterX1(shader.getCenterX1()) 475 , fRadius0(shader.getStartRadius()) 476 , fPosRoot(shader.getDiffRadius() < 0) { 477 // We pass the linear part of the quadratic as a varying. 478 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z) 479 fBTransform = this->getCoordTransform(); 480 SkMatrix& bMatrix = *fBTransform.accessMatrix(); 481 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) - 482 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0])); 483 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) - 484 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1])); 485 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) - 486 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2])); 487 this->addCoordTransform(&fBTransform); 488 } 489 490 GR_DECLARE_EFFECT_TEST; 491 492 // @{ 493 // Cache of values - these can change arbitrarily, EXCEPT 494 // we shouldn't change between degenerate and non-degenerate?! 495 496 GrCoordTransform fBTransform; 497 SkScalar fCenterX1; 498 SkScalar fRadius0; 499 SkBool8 fPosRoot; 500 501 // @} 502 503 typedef GrGradientEffect INHERITED; 504 }; 505 506 ///////////////////////////////////////////////////////////////////// 507 508 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient); 509 510 GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random, 511 GrContext* context, 512 const GrDrawTargetCaps&, 513 GrTexture**) { 514 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 515 SkScalar radius1 = random->nextUScalar1(); 516 SkPoint center2; 517 SkScalar radius2; 518 do { 519 center2.set(random->nextUScalar1(), random->nextUScalar1()); 520 radius2 = random->nextUScalar1 (); 521 // There is a bug in two point radial gradients with identical radii 522 } while (radius1 == radius2); 523 524 SkColor colors[kMaxRandomGradientColors]; 525 SkScalar stopsArray[kMaxRandomGradientColors]; 526 SkScalar* stops = stopsArray; 527 SkShader::TileMode tm; 528 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 529 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1, 530 center2, radius2, 531 colors, stops, colorCount, 532 tm)); 533 SkPaint paint; 534 GrEffectRef* effect; 535 GrColor grColor; 536 shader->asNewEffect(context, paint, NULL, &grColor, &effect); 537 return effect; 538 } 539 540 ///////////////////////////////////////////////////////////////////// 541 542 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory, 543 const GrDrawEffect& drawEffect) 544 : INHERITED(factory) 545 , fVSVaryingName(NULL) 546 , fFSVaryingName(NULL) 547 , fCachedCenter(SK_ScalarMax) 548 , fCachedRadius(-SK_ScalarMax) 549 , fCachedPosRoot(0) { 550 551 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>(); 552 fIsDegenerate = data.isDegenerate(); 553 } 554 555 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder, 556 const GrDrawEffect& drawEffect, 557 EffectKey key, 558 const char* outputColor, 559 const char* inputColor, 560 const TransformedCoordsArray& coords, 561 const TextureSamplerArray& samplers) { 562 563 this->emitUniforms(builder, key); 564 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, 565 kFloat_GrSLType, "Radial2FSParams", 6); 566 567 SkString cName("c"); 568 SkString ac4Name("ac4"); 569 SkString rootName("root"); 570 SkString t; 571 SkString p0; 572 SkString p1; 573 SkString p2; 574 SkString p3; 575 SkString p4; 576 SkString p5; 577 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); 578 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); 579 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); 580 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); 581 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); 582 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); 583 584 // We interpolate the linear component in coords[1]. 585 SkASSERT(coords[0].type() == coords[1].type()); 586 const char* coords2D; 587 SkString bVar; 588 if (kVec3f_GrSLType == coords[0].type()) { 589 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", 590 coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); 591 coords2D = "interpolants.xy"; 592 bVar = "interpolants.z"; 593 } else { 594 coords2D = coords[0].c_str(); 595 bVar.printf("%s.x", coords[1].c_str()); 596 } 597 598 // c = (x^2)+(y^2) - params[4] 599 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", 600 cName.c_str(), coords2D, coords2D, p4.c_str()); 601 602 // If we aren't degenerate, emit some extra code, and accept a slightly 603 // more complex coord. 604 if (!fIsDegenerate) { 605 606 // ac4 = 4.0 * params[0] * c 607 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n", 608 ac4Name.c_str(), p0.c_str(), 609 cName.c_str()); 610 611 // root = sqrt(b^2-4ac) 612 // (abs to avoid exception due to fp precision) 613 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", 614 rootName.c_str(), bVar.c_str(), bVar.c_str(), 615 ac4Name.c_str()); 616 617 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] 618 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), 619 rootName.c_str(), p1.c_str()); 620 } else { 621 // t is: -c/b 622 t.printf("-%s / %s", cName.c_str(), bVar.c_str()); 623 } 624 625 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers); 626 } 627 628 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, 629 const GrDrawEffect& drawEffect) { 630 INHERITED::setData(uman, drawEffect); 631 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>(); 632 SkASSERT(data.isDegenerate() == fIsDegenerate); 633 SkScalar centerX1 = data.center(); 634 SkScalar radius0 = data.radius(); 635 if (fCachedCenter != centerX1 || 636 fCachedRadius != radius0 || 637 fCachedPosRoot != data.isPosRoot()) { 638 639 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1; 640 641 // When we're in the degenerate (linear) case, the second 642 // value will be INF but the program doesn't read it. (We 643 // use the same 6 uniforms even though we don't need them 644 // all in the linear case just to keep the code complexity 645 // down). 646 float values[6] = { 647 SkScalarToFloat(a), 648 1 / (2.f * SkScalarToFloat(a)), 649 SkScalarToFloat(centerX1), 650 SkScalarToFloat(radius0), 651 SkScalarToFloat(SkScalarMul(radius0, radius0)), 652 data.isPosRoot() ? 1.f : -1.f 653 }; 654 655 uman.set1fv(fParamUni, 6, values); 656 fCachedCenter = centerX1; 657 fCachedRadius = radius0; 658 fCachedPosRoot = data.isPosRoot(); 659 } 660 } 661 662 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect, 663 const GrGLCaps&) { 664 enum { 665 kIsDegenerate = 1 << kBaseKeyBitCnt, 666 }; 667 668 EffectKey key = GenBaseGradientKey(drawEffect); 669 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) { 670 key |= kIsDegenerate; 671 } 672 return key; 673 } 674 675 ///////////////////////////////////////////////////////////////////// 676 677 bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint, 678 const SkMatrix* localMatrix, GrColor* grColor, 679 GrEffectRef** grEffect) const { 680 SkASSERT(NULL != context); 681 682 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis. 683 SkMatrix matrix; 684 if (!this->getLocalMatrix().invert(&matrix)) { 685 return false; 686 } 687 if (localMatrix) { 688 SkMatrix inv; 689 if (!localMatrix->invert(&inv)) { 690 return false; 691 } 692 matrix.postConcat(inv); 693 } 694 matrix.postConcat(fPtsToUnit); 695 696 SkScalar diffLen = fDiff.length(); 697 if (0 != diffLen) { 698 SkScalar invDiffLen = SkScalarInvert(diffLen); 699 SkMatrix rot; 700 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY), 701 SkScalarMul(invDiffLen, fDiff.fX)); 702 matrix.postConcat(rot); 703 } 704 705 *grColor = SkColor2GrColorJustAlpha(paint.getColor()); 706 *grEffect = GrRadial2Gradient::Create(context, *this, matrix, fTileMode); 707 708 return true; 709 } 710 711 #else 712 713 bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint, 714 const SkMatrix* localMatrix, GrColor* grColor, 715 GrEffectRef** grEffect) const { 716 SkDEBUGFAIL("Should not call in GPU-less build"); 717 return false; 718 } 719 720 #endif 721