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