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 83namespace { 84 85inline 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 107typedef 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 114void 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} 131void 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 149void 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 170SkTwoPointRadialGradient::SkTwoPointRadialGradient(const SkPoint& start, SkScalar startRadius, 171 const SkPoint& end, SkScalar endRadius, 172 const Descriptor& desc) 173 : SkGradientShaderBase(desc) 174 , fCenter1(start) 175 , fCenter2(end) 176 , fRadius1(startRadius) 177 , fRadius2(endRadius) 178{ 179 init(); 180} 181 182SkShader::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 211SkShader::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 223size_t SkTwoPointRadialGradient::contextSize() const { 224 return sizeof(TwoPointRadialGradientContext); 225} 226 227SkShader::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 236SkTwoPointRadialGradient::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 244void 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 321void 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#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING 347SkTwoPointRadialGradient::SkTwoPointRadialGradient(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#endif 356 357SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) { 358 DescriptorScope desc; 359 if (!desc.unflatten(buffer)) { 360 return NULL; 361 } 362 const SkPoint c1 = buffer.readPoint(); 363 const SkPoint c2 = buffer.readPoint(); 364 const SkScalar r1 = buffer.readScalar(); 365 const SkScalar r2 = buffer.readScalar(); 366 return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos, 367 desc.fCount, desc.fTileMode, desc.fGradFlags, 368 desc.fLocalMatrix); 369} 370 371void SkTwoPointRadialGradient::flatten( 372 SkWriteBuffer& buffer) const { 373 this->INHERITED::flatten(buffer); 374 buffer.writePoint(fCenter1); 375 buffer.writePoint(fCenter2); 376 buffer.writeScalar(fRadius1); 377 buffer.writeScalar(fRadius2); 378} 379 380void SkTwoPointRadialGradient::init() { 381 fDiff = fCenter1 - fCenter2; 382 fDiffRadius = fRadius2 - fRadius1; 383 // hack to avoid zero-divide for now 384 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0; 385 fDiff.fX = SkScalarMul(fDiff.fX, inv); 386 fDiff.fY = SkScalarMul(fDiff.fY, inv); 387 fStartRadius = SkScalarMul(fRadius1, inv); 388 fSr2D2 = SkScalarSquare(fStartRadius); 389 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; 390 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; 391 392 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); 393 fPtsToUnit.postScale(inv, inv); 394} 395 396///////////////////////////////////////////////////////////////////// 397 398#if SK_SUPPORT_GPU 399 400#include "GrTBackendProcessorFactory.h" 401#include "gl/builders/GrGLProgramBuilder.h" 402#include "SkGr.h" 403 404// For brevity 405typedef GrGLProgramDataManager::UniformHandle UniformHandle; 406 407class GrGLRadial2Gradient : public GrGLGradientEffect { 408 409public: 410 411 GrGLRadial2Gradient(const GrBackendProcessorFactory& factory, const GrProcessor&); 412 virtual ~GrGLRadial2Gradient() { } 413 414 virtual void emitCode(GrGLProgramBuilder*, 415 const GrFragmentProcessor&, 416 const GrProcessorKey&, 417 const char* outputColor, 418 const char* inputColor, 419 const TransformedCoordsArray&, 420 const TextureSamplerArray&) SK_OVERRIDE; 421 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE; 422 423 static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b); 424 425protected: 426 427 UniformHandle fParamUni; 428 429 const char* fVSVaryingName; 430 const char* fFSVaryingName; 431 432 bool fIsDegenerate; 433 434 // @{ 435 /// Values last uploaded as uniforms 436 437 SkScalar fCachedCenter; 438 SkScalar fCachedRadius; 439 bool fCachedPosRoot; 440 441 // @} 442 443private: 444 445 typedef GrGLGradientEffect INHERITED; 446 447}; 448 449///////////////////////////////////////////////////////////////////// 450 451class GrRadial2Gradient : public GrGradientEffect { 452public: 453 static GrFragmentProcessor* Create(GrContext* ctx, 454 const SkTwoPointRadialGradient& shader, 455 const SkMatrix& matrix, 456 SkShader::TileMode tm) { 457 return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)); 458 } 459 460 virtual ~GrRadial2Gradient() { } 461 462 static const char* Name() { return "Two-Point Radial Gradient"; } 463 virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE { 464 return GrTBackendFragmentProcessorFactory<GrRadial2Gradient>::getInstance(); 465 } 466 467 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. 468 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; } 469 SkScalar center() const { return fCenterX1; } 470 SkScalar radius() const { return fRadius0; } 471 bool isPosRoot() const { return SkToBool(fPosRoot); } 472 473 typedef GrGLRadial2Gradient GLProcessor; 474 475private: 476 virtual bool onIsEqual(const GrProcessor& sBase) const SK_OVERRIDE { 477 const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>(); 478 return (INHERITED::onIsEqual(sBase) && 479 this->fCenterX1 == s.fCenterX1 && 480 this->fRadius0 == s.fRadius0 && 481 this->fPosRoot == s.fPosRoot); 482 } 483 484 GrRadial2Gradient(GrContext* ctx, 485 const SkTwoPointRadialGradient& shader, 486 const SkMatrix& matrix, 487 SkShader::TileMode tm) 488 : INHERITED(ctx, shader, matrix, tm) 489 , fCenterX1(shader.getCenterX1()) 490 , fRadius0(shader.getStartRadius()) 491 , fPosRoot(shader.getDiffRadius() < 0) { 492 // We pass the linear part of the quadratic as a varying. 493 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z) 494 fBTransform = this->getCoordTransform(); 495 SkMatrix& bMatrix = *fBTransform.accessMatrix(); 496 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) - 497 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0])); 498 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) - 499 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1])); 500 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) - 501 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2])); 502 this->addCoordTransform(&fBTransform); 503 } 504 505 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 506 507 // @{ 508 // Cache of values - these can change arbitrarily, EXCEPT 509 // we shouldn't change between degenerate and non-degenerate?! 510 511 GrCoordTransform fBTransform; 512 SkScalar fCenterX1; 513 SkScalar fRadius0; 514 SkBool8 fPosRoot; 515 516 // @} 517 518 typedef GrGradientEffect INHERITED; 519}; 520 521///////////////////////////////////////////////////////////////////// 522 523GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient); 524 525GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random, 526 GrContext* context, 527 const GrDrawTargetCaps&, 528 GrTexture**) { 529 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 530 SkScalar radius1 = random->nextUScalar1(); 531 SkPoint center2; 532 SkScalar radius2; 533 do { 534 center2.set(random->nextUScalar1(), random->nextUScalar1()); 535 radius2 = random->nextUScalar1 (); 536 // There is a bug in two point radial gradients with identical radii 537 } while (radius1 == radius2); 538 539 SkColor colors[kMaxRandomGradientColors]; 540 SkScalar stopsArray[kMaxRandomGradientColors]; 541 SkScalar* stops = stopsArray; 542 SkShader::TileMode tm; 543 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 544 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1, 545 center2, radius2, 546 colors, stops, colorCount, 547 tm)); 548 SkPaint paint; 549 GrFragmentProcessor* fp; 550 GrColor paintColor; 551 SkAssertResult(shader->asFragmentProcessor(context, paint, NULL, &paintColor, &fp)); 552 return fp; 553} 554 555///////////////////////////////////////////////////////////////////// 556 557GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendProcessorFactory& factory, 558 const GrProcessor& processor) 559 : INHERITED(factory) 560 , fVSVaryingName(NULL) 561 , fFSVaryingName(NULL) 562 , fCachedCenter(SK_ScalarMax) 563 , fCachedRadius(-SK_ScalarMax) 564 , fCachedPosRoot(0) { 565 566 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>(); 567 fIsDegenerate = data.isDegenerate(); 568} 569 570void GrGLRadial2Gradient::emitCode(GrGLProgramBuilder* builder, 571 const GrFragmentProcessor&, 572 const GrProcessorKey& key, 573 const char* outputColor, 574 const char* inputColor, 575 const TransformedCoordsArray& coords, 576 const TextureSamplerArray& samplers) { 577 uint32_t baseKey = key.get32(0); 578 this->emitUniforms(builder, baseKey); 579 fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility, 580 kFloat_GrSLType, "Radial2FSParams", 6); 581 582 SkString cName("c"); 583 SkString ac4Name("ac4"); 584 SkString rootName("root"); 585 SkString t; 586 SkString p0; 587 SkString p1; 588 SkString p2; 589 SkString p3; 590 SkString p4; 591 SkString p5; 592 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); 593 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); 594 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); 595 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); 596 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); 597 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); 598 599 GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder(); 600 // We interpolate the linear component in coords[1]. 601 SkASSERT(coords[0].getType() == coords[1].getType()); 602 const char* coords2D; 603 SkString bVar; 604 if (kVec3f_GrSLType == coords[0].getType()) { 605 fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", 606 coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); 607 coords2D = "interpolants.xy"; 608 bVar = "interpolants.z"; 609 } else { 610 coords2D = coords[0].c_str(); 611 bVar.printf("%s.x", coords[1].c_str()); 612 } 613 614 // c = (x^2)+(y^2) - params[4] 615 fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", 616 cName.c_str(), coords2D, coords2D, p4.c_str()); 617 618 // If we aren't degenerate, emit some extra code, and accept a slightly 619 // more complex coord. 620 if (!fIsDegenerate) { 621 622 // ac4 = 4.0 * params[0] * c 623 fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n", 624 ac4Name.c_str(), p0.c_str(), 625 cName.c_str()); 626 627 // root = sqrt(b^2-4ac) 628 // (abs to avoid exception due to fp precision) 629 fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", 630 rootName.c_str(), bVar.c_str(), bVar.c_str(), 631 ac4Name.c_str()); 632 633 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] 634 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), 635 rootName.c_str(), p1.c_str()); 636 } else { 637 // t is: -c/b 638 t.printf("-%s / %s", cName.c_str(), bVar.c_str()); 639 } 640 641 this->emitColor(builder, t.c_str(), baseKey, outputColor, inputColor, samplers); 642} 643 644void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman, 645 const GrProcessor& processor) { 646 INHERITED::setData(pdman, processor); 647 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>(); 648 SkASSERT(data.isDegenerate() == fIsDegenerate); 649 SkScalar centerX1 = data.center(); 650 SkScalar radius0 = data.radius(); 651 if (fCachedCenter != centerX1 || 652 fCachedRadius != radius0 || 653 fCachedPosRoot != data.isPosRoot()) { 654 655 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1; 656 657 // When we're in the degenerate (linear) case, the second 658 // value will be INF but the program doesn't read it. (We 659 // use the same 6 uniforms even though we don't need them 660 // all in the linear case just to keep the code complexity 661 // down). 662 float values[6] = { 663 SkScalarToFloat(a), 664 1 / (2.f * SkScalarToFloat(a)), 665 SkScalarToFloat(centerX1), 666 SkScalarToFloat(radius0), 667 SkScalarToFloat(SkScalarMul(radius0, radius0)), 668 data.isPosRoot() ? 1.f : -1.f 669 }; 670 671 pdman.set1fv(fParamUni, 6, values); 672 fCachedCenter = centerX1; 673 fCachedRadius = radius0; 674 fCachedPosRoot = data.isPosRoot(); 675 } 676} 677 678void GrGLRadial2Gradient::GenKey(const GrProcessor& processor, 679 const GrGLCaps&, GrProcessorKeyBuilder* b) { 680 uint32_t* key = b->add32n(2); 681 key[0] = GenBaseGradientKey(processor); 682 key[1] = processor.cast<GrRadial2Gradient>().isDegenerate(); 683} 684 685///////////////////////////////////////////////////////////////////// 686 687bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint, 688 const SkMatrix* localMatrix, GrColor* paintColor, 689 GrFragmentProcessor** fp) const { 690 SkASSERT(context); 691 692 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis. 693 SkMatrix matrix; 694 if (!this->getLocalMatrix().invert(&matrix)) { 695 return false; 696 } 697 if (localMatrix) { 698 SkMatrix inv; 699 if (!localMatrix->invert(&inv)) { 700 return false; 701 } 702 matrix.postConcat(inv); 703 } 704 matrix.postConcat(fPtsToUnit); 705 706 SkScalar diffLen = fDiff.length(); 707 if (0 != diffLen) { 708 SkScalar invDiffLen = SkScalarInvert(diffLen); 709 SkMatrix rot; 710 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY), 711 SkScalarMul(invDiffLen, fDiff.fX)); 712 matrix.postConcat(rot); 713 } 714 715 *paintColor = SkColor2GrColorJustAlpha(paint.getColor()); 716 *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode); 717 718 return true; 719} 720 721#else 722 723bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, 724 GrColor*, GrFragmentProcessor**) const { 725 SkDEBUGFAIL("Should not call in GPU-less build"); 726 return false; 727} 728 729#endif 730