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 "SkTwoPointConicalGradient.h" 10 11 static int valid_divide(float numer, float denom, float* ratio) { 12 SkASSERT(ratio); 13 if (0 == denom) { 14 return 0; 15 } 16 *ratio = numer / denom; 17 return 1; 18 } 19 20 // Return the number of distinct real roots, and write them into roots[] in 21 // ascending order 22 static int find_quad_roots(float A, float B, float C, float roots[2]) { 23 SkASSERT(roots); 24 25 if (A == 0) { 26 return valid_divide(-C, B, roots); 27 } 28 29 float R = B*B - 4*A*C; 30 if (R < 0) { 31 return 0; 32 } 33 R = sk_float_sqrt(R); 34 35 #if 1 36 float Q = B; 37 if (Q < 0) { 38 Q -= R; 39 } else { 40 Q += R; 41 } 42 #else 43 // on 10.6 this was much slower than the above branch :( 44 float Q = B + copysignf(R, B); 45 #endif 46 Q *= -0.5f; 47 if (0 == Q) { 48 roots[0] = 0; 49 return 1; 50 } 51 52 float r0 = Q / A; 53 float r1 = C / Q; 54 roots[0] = r0 < r1 ? r0 : r1; 55 roots[1] = r0 > r1 ? r0 : r1; 56 return 2; 57 } 58 59 static float lerp(float x, float dx, float t) { 60 return x + t * dx; 61 } 62 63 static float sqr(float x) { return x * x; } 64 65 void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0, 66 const SkPoint& center1, SkScalar rad1) { 67 fCenterX = SkScalarToFloat(center0.fX); 68 fCenterY = SkScalarToFloat(center0.fY); 69 fDCenterX = SkScalarToFloat(center1.fX) - fCenterX; 70 fDCenterY = SkScalarToFloat(center1.fY) - fCenterY; 71 fRadius = SkScalarToFloat(rad0); 72 fDRadius = SkScalarToFloat(rad1) - fRadius; 73 74 fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius); 75 fRadius2 = sqr(fRadius); 76 fRDR = fRadius * fDRadius; 77 } 78 79 void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) { 80 fRelX = SkScalarToFloat(fx) - fCenterX; 81 fRelY = SkScalarToFloat(fy) - fCenterY; 82 fIncX = SkScalarToFloat(dfx); 83 fIncY = SkScalarToFloat(dfy); 84 fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR); 85 fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY); 86 } 87 88 SkFixed TwoPtRadial::nextT() { 89 float roots[2]; 90 91 float C = sqr(fRelX) + sqr(fRelY) - fRadius2; 92 int countRoots = find_quad_roots(fA, fB, C, roots); 93 94 fRelX += fIncX; 95 fRelY += fIncY; 96 fB += fDB; 97 98 if (0 == countRoots) { 99 return kDontDrawT; 100 } 101 102 // Prefer the bigger t value if both give a radius(t) > 0 103 // find_quad_roots returns the values sorted, so we start with the last 104 float t = roots[countRoots - 1]; 105 float r = lerp(fRadius, fDRadius, t); 106 if (r <= 0) { 107 t = roots[0]; // might be the same as roots[countRoots-1] 108 r = lerp(fRadius, fDRadius, t); 109 if (r <= 0) { 110 return kDontDrawT; 111 } 112 } 113 return SkFloatToFixed(t); 114 } 115 116 typedef void (*TwoPointConicalProc)(TwoPtRadial* rec, SkPMColor* dstC, 117 const SkPMColor* cache, int toggle, int count); 118 119 static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC, 120 const SkPMColor* SK_RESTRICT cache, int toggle, 121 int count) { 122 for (; count > 0; --count) { 123 SkFixed t = rec->nextT(); 124 if (TwoPtRadial::DontDrawT(t)) { 125 *dstC++ = 0; 126 } else { 127 SkFixed index = SkClampMax(t, 0xFFFF); 128 SkASSERT(index <= 0xFFFF); 129 *dstC++ = cache[toggle + 130 (index >> SkGradientShaderBase::kCache32Shift)]; 131 } 132 toggle = next_dither_toggle(toggle); 133 } 134 } 135 136 static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC, 137 const SkPMColor* SK_RESTRICT cache, int toggle, 138 int count) { 139 for (; count > 0; --count) { 140 SkFixed t = rec->nextT(); 141 if (TwoPtRadial::DontDrawT(t)) { 142 *dstC++ = 0; 143 } else { 144 SkFixed index = repeat_tileproc(t); 145 SkASSERT(index <= 0xFFFF); 146 *dstC++ = cache[toggle + 147 (index >> SkGradientShaderBase::kCache32Shift)]; 148 } 149 toggle = next_dither_toggle(toggle); 150 } 151 } 152 153 static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC, 154 const SkPMColor* SK_RESTRICT cache, int toggle, 155 int count) { 156 for (; count > 0; --count) { 157 SkFixed t = rec->nextT(); 158 if (TwoPtRadial::DontDrawT(t)) { 159 *dstC++ = 0; 160 } else { 161 SkFixed index = mirror_tileproc(t); 162 SkASSERT(index <= 0xFFFF); 163 *dstC++ = cache[toggle + 164 (index >> SkGradientShaderBase::kCache32Shift)]; 165 } 166 toggle = next_dither_toggle(toggle); 167 } 168 } 169 170 void SkTwoPointConicalGradient::init() { 171 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2); 172 fPtsToUnit.reset(); 173 } 174 175 ///////////////////////////////////////////////////////////////////// 176 177 SkTwoPointConicalGradient::SkTwoPointConicalGradient( 178 const SkPoint& start, SkScalar startRadius, 179 const SkPoint& end, SkScalar endRadius, 180 const Descriptor& desc) 181 : SkGradientShaderBase(desc), 182 fCenter1(start), 183 fCenter2(end), 184 fRadius1(startRadius), 185 fRadius2(endRadius) { 186 // this is degenerate, and should be caught by our caller 187 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); 188 this->init(); 189 } 190 191 bool SkTwoPointConicalGradient::isOpaque() const { 192 // Because areas outside the cone are left untouched, we cannot treat the 193 // shader as opaque even if the gradient itself is opaque. 194 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380 195 return false; 196 } 197 198 void SkTwoPointConicalGradient::shadeSpan(int x, int y, SkPMColor* dstCParam, 199 int count) { 200 int toggle = init_dither_toggle(x, y); 201 202 SkASSERT(count > 0); 203 204 SkPMColor* SK_RESTRICT dstC = dstCParam; 205 206 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 207 208 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 209 210 TwoPointConicalProc shadeProc = twopoint_repeat; 211 if (SkShader::kClamp_TileMode == fTileMode) { 212 shadeProc = twopoint_clamp; 213 } else if (SkShader::kMirror_TileMode == fTileMode) { 214 shadeProc = twopoint_mirror; 215 } else { 216 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 217 } 218 219 if (fDstToIndexClass != kPerspective_MatrixClass) { 220 SkPoint srcPt; 221 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 222 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 223 SkScalar dx, fx = srcPt.fX; 224 SkScalar dy, fy = srcPt.fY; 225 226 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 227 SkFixed fixedX, fixedY; 228 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); 229 dx = SkFixedToScalar(fixedX); 230 dy = SkFixedToScalar(fixedY); 231 } else { 232 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 233 dx = fDstToIndex.getScaleX(); 234 dy = fDstToIndex.getSkewY(); 235 } 236 237 fRec.setup(fx, fy, dx, dy); 238 (*shadeProc)(&fRec, dstC, cache, toggle, count); 239 } else { // perspective case 240 SkScalar dstX = SkIntToScalar(x); 241 SkScalar dstY = SkIntToScalar(y); 242 for (; count > 0; --count) { 243 SkPoint srcPt; 244 dstProc(fDstToIndex, dstX, dstY, &srcPt); 245 dstX += SK_Scalar1; 246 247 fRec.setup(srcPt.fX, srcPt.fY, 0, 0); 248 (*shadeProc)(&fRec, dstC, cache, toggle, 1); 249 toggle = next_dither_toggle(toggle); 250 } 251 } 252 } 253 254 bool SkTwoPointConicalGradient::setContext(const SkBitmap& device, 255 const SkPaint& paint, 256 const SkMatrix& matrix) { 257 if (!this->INHERITED::setContext(device, paint, matrix)) { 258 return false; 259 } 260 261 // we don't have a span16 proc 262 fFlags &= ~kHasSpan16_Flag; 263 264 // in general, we might discard based on computed-radius, so clear 265 // this flag (todo: sometimes we can detect that we never discard...) 266 fFlags &= ~kOpaqueAlpha_Flag; 267 268 return true; 269 } 270 271 SkShader::BitmapType SkTwoPointConicalGradient::asABitmap( 272 SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const { 273 SkPoint diff = fCenter2 - fCenter1; 274 SkScalar diffLen = 0; 275 276 if (bitmap) { 277 this->getGradientTableBitmap(bitmap); 278 } 279 if (matrix) { 280 diffLen = diff.length(); 281 } 282 if (matrix) { 283 if (diffLen) { 284 SkScalar invDiffLen = SkScalarInvert(diffLen); 285 // rotate to align circle centers with the x-axis 286 matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY), 287 SkScalarMul(invDiffLen, diff.fX)); 288 } else { 289 matrix->reset(); 290 } 291 matrix->preTranslate(-fCenter1.fX, -fCenter1.fY); 292 } 293 if (xy) { 294 xy[0] = fTileMode; 295 xy[1] = kClamp_TileMode; 296 } 297 return kTwoPointConical_BitmapType; 298 } 299 300 SkShader::GradientType SkTwoPointConicalGradient::asAGradient( 301 GradientInfo* info) const { 302 if (info) { 303 commonAsAGradient(info); 304 info->fPoint[0] = fCenter1; 305 info->fPoint[1] = fCenter2; 306 info->fRadius[0] = fRadius1; 307 info->fRadius[1] = fRadius2; 308 } 309 return kConical_GradientType; 310 } 311 312 SkTwoPointConicalGradient::SkTwoPointConicalGradient( 313 SkFlattenableReadBuffer& buffer) 314 : INHERITED(buffer), 315 fCenter1(buffer.readPoint()), 316 fCenter2(buffer.readPoint()), 317 fRadius1(buffer.readScalar()), 318 fRadius2(buffer.readScalar()) { 319 this->init(); 320 }; 321 322 void SkTwoPointConicalGradient::flatten( 323 SkFlattenableWriteBuffer& buffer) const { 324 this->INHERITED::flatten(buffer); 325 buffer.writePoint(fCenter1); 326 buffer.writePoint(fCenter2); 327 buffer.writeScalar(fRadius1); 328 buffer.writeScalar(fRadius2); 329 } 330 331 ///////////////////////////////////////////////////////////////////// 332 333 #if SK_SUPPORT_GPU 334 335 #include "GrTBackendEffectFactory.h" 336 337 // For brevity 338 typedef GrGLUniformManager::UniformHandle UniformHandle; 339 static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle; 340 341 class GrGLConical2Gradient : public GrGLGradientEffect { 342 public: 343 344 GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&); 345 virtual ~GrGLConical2Gradient() { } 346 347 virtual void emitCode(GrGLShaderBuilder*, 348 const GrDrawEffect&, 349 EffectKey, 350 const char* outputColor, 351 const char* inputColor, 352 const TextureSamplerArray&) SK_OVERRIDE; 353 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 354 355 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 356 357 protected: 358 359 UniformHandle fVSParamUni; 360 UniformHandle fFSParamUni; 361 362 const char* fVSVaryingName; 363 const char* fFSVaryingName; 364 365 bool fIsDegenerate; 366 367 // @{ 368 /// Values last uploaded as uniforms 369 370 SkScalar fCachedCenter; 371 SkScalar fCachedRadius; 372 SkScalar fCachedDiffRadius; 373 374 // @} 375 376 private: 377 378 typedef GrGLGradientEffect INHERITED; 379 380 }; 381 382 ///////////////////////////////////////////////////////////////////// 383 384 class GrConical2Gradient : public GrGradientEffect { 385 public: 386 387 static GrEffectRef* Create(GrContext* ctx, 388 const SkTwoPointConicalGradient& shader, 389 const SkMatrix& matrix, 390 SkShader::TileMode tm) { 391 AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm))); 392 return CreateEffectRef(effect); 393 } 394 395 virtual ~GrConical2Gradient() { } 396 397 static const char* Name() { return "Two-Point Conical Gradient"; } 398 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 399 return GrTBackendEffectFactory<GrConical2Gradient>::getInstance(); 400 } 401 402 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. 403 bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); } 404 SkScalar center() const { return fCenterX1; } 405 SkScalar diffRadius() const { return fDiffRadius; } 406 SkScalar radius() const { return fRadius0; } 407 408 typedef GrGLConical2Gradient GLEffect; 409 410 private: 411 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 412 const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase); 413 return (INHERITED::onIsEqual(sBase) && 414 this->fCenterX1 == s.fCenterX1 && 415 this->fRadius0 == s.fRadius0 && 416 this->fDiffRadius == s.fDiffRadius); 417 } 418 419 GrConical2Gradient(GrContext* ctx, 420 const SkTwoPointConicalGradient& shader, 421 const SkMatrix& matrix, 422 SkShader::TileMode tm) 423 : INHERITED(ctx, shader, matrix, tm) 424 , fCenterX1(shader.getCenterX1()) 425 , fRadius0(shader.getStartRadius()) 426 , fDiffRadius(shader.getDiffRadius()) { } 427 428 GR_DECLARE_EFFECT_TEST; 429 430 // @{ 431 // Cache of values - these can change arbitrarily, EXCEPT 432 // we shouldn't change between degenerate and non-degenerate?! 433 434 SkScalar fCenterX1; 435 SkScalar fRadius0; 436 SkScalar fDiffRadius; 437 438 // @} 439 440 typedef GrGradientEffect INHERITED; 441 }; 442 443 GR_DEFINE_EFFECT_TEST(GrConical2Gradient); 444 445 GrEffectRef* GrConical2Gradient::TestCreate(SkMWCRandom* random, 446 GrContext* context, 447 const GrDrawTargetCaps&, 448 GrTexture**) { 449 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 450 SkScalar radius1 = random->nextUScalar1(); 451 SkPoint center2; 452 SkScalar radius2; 453 do { 454 center2.set(random->nextUScalar1(), random->nextUScalar1()); 455 radius2 = random->nextUScalar1 (); 456 // If the circles are identical the factory will give us an empty shader. 457 } while (radius1 == radius2 && center1 == center2); 458 459 SkColor colors[kMaxRandomGradientColors]; 460 SkScalar stopsArray[kMaxRandomGradientColors]; 461 SkScalar* stops = stopsArray; 462 SkShader::TileMode tm; 463 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 464 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, 465 center2, radius2, 466 colors, stops, colorCount, 467 tm)); 468 SkPaint paint; 469 return shader->asNewEffect(context, paint); 470 } 471 472 473 ///////////////////////////////////////////////////////////////////// 474 475 GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory, 476 const GrDrawEffect& drawEffect) 477 : INHERITED(factory) 478 , fVSParamUni(kInvalidUniformHandle) 479 , fFSParamUni(kInvalidUniformHandle) 480 , fVSVaryingName(NULL) 481 , fFSVaryingName(NULL) 482 , fCachedCenter(SK_ScalarMax) 483 , fCachedRadius(-SK_ScalarMax) 484 , fCachedDiffRadius(-SK_ScalarMax) { 485 486 const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>(); 487 fIsDegenerate = data.isDegenerate(); 488 } 489 490 void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder, 491 const GrDrawEffect&, 492 EffectKey key, 493 const char* outputColor, 494 const char* inputColor, 495 const TextureSamplerArray& samplers) { 496 const char* fsCoords; 497 const char* vsCoordsVarying; 498 GrSLType coordsVaryingType; 499 this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType); 500 501 this->emitYCoordUniform(builder); 502 // 2 copies of uniform array, 1 for each of vertex & fragment shader, 503 // to work around Xoom bug. Doesn't seem to cause performance decrease 504 // in test apps, but need to keep an eye on it. 505 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType, 506 kFloat_GrSLType, "Conical2VSParams", 6); 507 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType, 508 kFloat_GrSLType, "Conical2FSParams", 6); 509 510 // For radial gradients without perspective we can pass the linear 511 // part of the quadratic as a varying. 512 if (kVec2f_GrSLType == coordsVaryingType) { 513 builder->addVarying(kFloat_GrSLType, "Conical2BCoeff", 514 &fVSVaryingName, &fFSVaryingName); 515 } 516 517 // VS 518 { 519 SkString p2; // distance between centers 520 SkString p3; // start radius 521 SkString p5; // difference in radii (r1 - r0) 522 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2); 523 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3); 524 builder->getUniformVariable(fVSParamUni).appendArrayAccess(5, &p5); 525 526 // For radial gradients without perspective we can pass the linear 527 // part of the quadratic as a varying. 528 if (kVec2f_GrSLType == coordsVaryingType) { 529 // r2Var = -2 * (r2Parm[2] * varCoord.x - r2Param[3] * r2Param[5]) 530 builder->vsCodeAppendf("\t%s = -2.0 * (%s * %s.x + %s * %s);\n", 531 fVSVaryingName, p2.c_str(), 532 vsCoordsVarying, p3.c_str(), p5.c_str()); 533 } 534 } 535 536 // FS 537 { 538 539 SkString cName("c"); 540 SkString ac4Name("ac4"); 541 SkString dName("d"); 542 SkString qName("q"); 543 SkString r0Name("r0"); 544 SkString r1Name("r1"); 545 SkString tName("t"); 546 SkString p0; // 4a 547 SkString p1; // 1/a 548 SkString p2; // distance between centers 549 SkString p3; // start radius 550 SkString p4; // start radius squared 551 SkString p5; // difference in radii (r1 - r0) 552 553 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0); 554 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1); 555 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2); 556 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3); 557 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4); 558 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5); 559 560 // If we we're able to interpolate the linear component, 561 // bVar is the varying; otherwise compute it 562 SkString bVar; 563 if (kVec2f_GrSLType == coordsVaryingType) { 564 bVar = fFSVaryingName; 565 } else { 566 bVar = "b"; 567 builder->fsCodeAppendf("\tfloat %s = -2.0 * (%s * %s.x + %s * %s);\n", 568 bVar.c_str(), p2.c_str(), fsCoords, 569 p3.c_str(), p5.c_str()); 570 } 571 572 // output will default to transparent black (we simply won't write anything 573 // else to it if invalid, instead of discarding or returning prematurely) 574 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); 575 576 // c = (x^2)+(y^2) - params[4] 577 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", cName.c_str(), 578 fsCoords, fsCoords, 579 p4.c_str()); 580 581 // Non-degenerate case (quadratic) 582 if (!fIsDegenerate) { 583 584 // ac4 = params[0] * c 585 builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(), 586 cName.c_str()); 587 588 // d = b^2 - ac4 589 builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(), 590 bVar.c_str(), bVar.c_str(), ac4Name.c_str()); 591 592 // only proceed if discriminant is >= 0 593 builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str()); 594 595 // intermediate value we'll use to compute the roots 596 // q = -0.5 * (b +/- sqrt(d)) 597 builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)" 598 " * sqrt(%s));\n", qName.c_str(), bVar.c_str(), 599 bVar.c_str(), dName.c_str()); 600 601 // compute both roots 602 // r0 = q * params[1] 603 builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(), 604 qName.c_str(), p1.c_str()); 605 // r1 = c / q 606 builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(), 607 cName.c_str(), qName.c_str()); 608 609 // Note: If there are two roots that both generate radius(t) > 0, the 610 // Canvas spec says to choose the larger t. 611 612 // so we'll look at the larger one first: 613 builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), 614 r0Name.c_str(), r1Name.c_str()); 615 616 // if r(t) > 0, then we're done; t will be our x coordinate 617 builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), 618 p5.c_str(), p3.c_str()); 619 620 builder->fsCodeAppend("\t\t"); 621 this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]); 622 623 // otherwise, if r(t) for the larger root was <= 0, try the other root 624 builder->fsCodeAppend("\t\t} else {\n"); 625 builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), 626 r0Name.c_str(), r1Name.c_str()); 627 628 // if r(t) > 0 for the smaller root, then t will be our x coordinate 629 builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n", 630 tName.c_str(), p5.c_str(), p3.c_str()); 631 632 builder->fsCodeAppend("\t\t\t"); 633 this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]); 634 635 // end if (r(t) > 0) for smaller root 636 builder->fsCodeAppend("\t\t\t}\n"); 637 // end if (r(t) > 0), else, for larger root 638 builder->fsCodeAppend("\t\t}\n"); 639 // end if (discriminant >= 0) 640 builder->fsCodeAppend("\t}\n"); 641 } else { 642 643 // linear case: t = -c/b 644 builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), 645 cName.c_str(), bVar.c_str()); 646 647 // if r(t) > 0, then t will be the x coordinate 648 builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), 649 p5.c_str(), p3.c_str()); 650 builder->fsCodeAppend("\t"); 651 this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]); 652 builder->fsCodeAppend("\t}\n"); 653 } 654 } 655 } 656 657 void GrGLConical2Gradient::setData(const GrGLUniformManager& uman, 658 const GrDrawEffect& drawEffect) { 659 INHERITED::setData(uman, drawEffect); 660 const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>(); 661 GrAssert(data.isDegenerate() == fIsDegenerate); 662 SkScalar centerX1 = data.center(); 663 SkScalar radius0 = data.radius(); 664 SkScalar diffRadius = data.diffRadius(); 665 666 if (fCachedCenter != centerX1 || 667 fCachedRadius != radius0 || 668 fCachedDiffRadius != diffRadius) { 669 670 SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius; 671 672 // When we're in the degenerate (linear) case, the second 673 // value will be INF but the program doesn't read it. (We 674 // use the same 6 uniforms even though we don't need them 675 // all in the linear case just to keep the code complexity 676 // down). 677 float values[6] = { 678 SkScalarToFloat(a * 4), 679 1.f / (SkScalarToFloat(a)), 680 SkScalarToFloat(centerX1), 681 SkScalarToFloat(radius0), 682 SkScalarToFloat(SkScalarMul(radius0, radius0)), 683 SkScalarToFloat(diffRadius) 684 }; 685 686 uman.set1fv(fVSParamUni, 0, 6, values); 687 uman.set1fv(fFSParamUni, 0, 6, values); 688 fCachedCenter = centerX1; 689 fCachedRadius = radius0; 690 fCachedDiffRadius = diffRadius; 691 } 692 } 693 694 GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffect, 695 const GrGLCaps&) { 696 enum { 697 kIsDegenerate = 1 << kMatrixKeyBitCnt, 698 }; 699 700 EffectKey key = GenMatrixKey(drawEffect); 701 if (drawEffect.castEffect<GrConical2Gradient>().isDegenerate()) { 702 key |= kIsDegenerate; 703 } 704 return key; 705 } 706 707 ///////////////////////////////////////////////////////////////////// 708 709 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const { 710 SkASSERT(NULL != context); 711 SkASSERT(fPtsToUnit.isIdentity()); 712 // invert the localM, translate to center1, rotate so center2 is on x axis. 713 SkMatrix matrix; 714 if (!this->getLocalMatrix().invert(&matrix)) { 715 return NULL; 716 } 717 matrix.postTranslate(-fCenter1.fX, -fCenter1.fY); 718 719 SkPoint diff = fCenter2 - fCenter1; 720 SkScalar diffLen = diff.length(); 721 if (0 != diffLen) { 722 SkScalar invDiffLen = SkScalarInvert(diffLen); 723 SkMatrix rot; 724 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY), 725 SkScalarMul(invDiffLen, diff.fX)); 726 matrix.postConcat(rot); 727 } 728 729 return GrConical2Gradient::Create(context, *this, matrix, fTileMode); 730 } 731 732 #else 733 734 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&) const { 735 SkDEBUGFAIL("Should not call in GPU-less build"); 736 return NULL; 737 } 738 739 #endif 740 741 #ifdef SK_DEVELOPER 742 void SkTwoPointConicalGradient::toString(SkString* str) const { 743 str->append("SkTwoPointConicalGradient: ("); 744 745 str->append("center1: ("); 746 str->appendScalar(fCenter1.fX); 747 str->append(", "); 748 str->appendScalar(fCenter1.fY); 749 str->append(") radius1: "); 750 str->appendScalar(fRadius1); 751 str->append(" "); 752 753 str->append("center2: ("); 754 str->appendScalar(fCenter2.fX); 755 str->append(", "); 756 str->appendScalar(fCenter2.fY); 757 str->append(") radius2: "); 758 str->appendScalar(fRadius2); 759 str->append(" "); 760 761 this->INHERITED::toString(str); 762 763 str->append(")"); 764 } 765 #endif 766