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