1 2/* 3 * Copyright 2014 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_gpu.h" 10 11#include "SkTwoPointConicalGradient.h" 12 13#if SK_SUPPORT_GPU 14#include "GrTBackendEffectFactory.h" 15// For brevity 16typedef GrGLUniformManager::UniformHandle UniformHandle; 17 18static const SkScalar kErrorTol = 0.00001f; 19static const SkScalar kEdgeErrorTol = 5.f * kErrorTol; 20 21/** 22 * We have three general cases for 2pt conical gradients. First we always assume that 23 * the start radius <= end radius. Our first case (kInside_) is when the start circle 24 * is completely enclosed by the end circle. The second case (kOutside_) is the case 25 * when the start circle is either completely outside the end circle or the circles 26 * overlap. The final case (kEdge_) is when the start circle is inside the end one, 27 * but the two are just barely touching at 1 point along their edges. 28 */ 29enum ConicalType { 30 kInside_ConicalType, 31 kOutside_ConicalType, 32 kEdge_ConicalType, 33}; 34 35////////////////////////////////////////////////////////////////////////////// 36 37static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader, 38 SkMatrix* invLMatrix) { 39 // Inverse of the current local matrix is passed in then, 40 // translate to center1, rotate so center2 is on x axis. 41 const SkPoint& center1 = shader.getStartCenter(); 42 const SkPoint& center2 = shader.getEndCenter(); 43 44 invLMatrix->postTranslate(-center1.fX, -center1.fY); 45 46 SkPoint diff = center2 - center1; 47 SkScalar diffLen = diff.length(); 48 if (0 != diffLen) { 49 SkScalar invDiffLen = SkScalarInvert(diffLen); 50 SkMatrix rot; 51 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY), 52 SkScalarMul(invDiffLen, diff.fX)); 53 invLMatrix->postConcat(rot); 54 } 55} 56 57class GLEdge2PtConicalEffect; 58 59class Edge2PtConicalEffect : public GrGradientEffect { 60public: 61 62 static GrEffectRef* Create(GrContext* ctx, 63 const SkTwoPointConicalGradient& shader, 64 const SkMatrix& matrix, 65 SkShader::TileMode tm) { 66 AutoEffectUnref effect(SkNEW_ARGS(Edge2PtConicalEffect, (ctx, shader, matrix, tm))); 67 return CreateEffectRef(effect); 68 } 69 70 virtual ~Edge2PtConicalEffect() {} 71 72 static const char* Name() { return "Two-Point Conical Gradient Edge Touching"; } 73 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 74 75 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. 76 SkScalar center() const { return fCenterX1; } 77 SkScalar diffRadius() const { return fDiffRadius; } 78 SkScalar radius() const { return fRadius0; } 79 80 typedef GLEdge2PtConicalEffect GLEffect; 81 82private: 83 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 84 const Edge2PtConicalEffect& s = CastEffect<Edge2PtConicalEffect>(sBase); 85 return (INHERITED::onIsEqual(sBase) && 86 this->fCenterX1 == s.fCenterX1 && 87 this->fRadius0 == s.fRadius0 && 88 this->fDiffRadius == s.fDiffRadius); 89 } 90 91 Edge2PtConicalEffect(GrContext* ctx, 92 const SkTwoPointConicalGradient& shader, 93 const SkMatrix& matrix, 94 SkShader::TileMode tm) 95 : INHERITED(ctx, shader, matrix, tm), 96 fCenterX1(shader.getCenterX1()), 97 fRadius0(shader.getStartRadius()), 98 fDiffRadius(shader.getDiffRadius()){ 99 // We should only be calling this shader if we are degenerate case with touching circles 100 // When deciding if we are in edge case, we scaled by the end radius for cases when the 101 // start radius was close to zero, otherwise we scaled by the start radius 102 SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - SkScalarAbs(fCenterX1)) < 103 kEdgeErrorTol * (fRadius0 < kErrorTol ? shader.getEndRadius() : fRadius0)); 104 105 // We pass the linear part of the quadratic as a varying. 106 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) 107 fBTransform = this->getCoordTransform(); 108 SkMatrix& bMatrix = *fBTransform.accessMatrix(); 109 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius); 110 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) + 111 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0])); 112 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) + 113 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1])); 114 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) + 115 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2])); 116 this->addCoordTransform(&fBTransform); 117 } 118 119 GR_DECLARE_EFFECT_TEST; 120 121 // @{ 122 // Cache of values - these can change arbitrarily, EXCEPT 123 // we shouldn't change between degenerate and non-degenerate?! 124 125 GrCoordTransform fBTransform; 126 SkScalar fCenterX1; 127 SkScalar fRadius0; 128 SkScalar fDiffRadius; 129 130 // @} 131 132 typedef GrGradientEffect INHERITED; 133}; 134 135class GLEdge2PtConicalEffect : public GrGLGradientEffect { 136public: 137 GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); 138 virtual ~GLEdge2PtConicalEffect() { } 139 140 virtual void emitCode(GrGLShaderBuilder*, 141 const GrDrawEffect&, 142 EffectKey, 143 const char* outputColor, 144 const char* inputColor, 145 const TransformedCoordsArray&, 146 const TextureSamplerArray&) SK_OVERRIDE; 147 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 148 149 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 150 151protected: 152 UniformHandle fParamUni; 153 154 const char* fVSVaryingName; 155 const char* fFSVaryingName; 156 157 // @{ 158 /// Values last uploaded as uniforms 159 160 SkScalar fCachedRadius; 161 SkScalar fCachedDiffRadius; 162 163 // @} 164 165private: 166 typedef GrGLGradientEffect INHERITED; 167 168}; 169 170const GrBackendEffectFactory& Edge2PtConicalEffect::getFactory() const { 171 return GrTBackendEffectFactory<Edge2PtConicalEffect>::getInstance(); 172} 173 174GR_DEFINE_EFFECT_TEST(Edge2PtConicalEffect); 175 176GrEffectRef* Edge2PtConicalEffect::TestCreate(SkRandom* random, 177 GrContext* context, 178 const GrDrawTargetCaps&, 179 GrTexture**) { 180 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 181 SkScalar radius1 = random->nextUScalar1(); 182 SkPoint center2; 183 SkScalar radius2; 184 do { 185 center2.set(random->nextUScalar1(), random->nextUScalar1()); 186 // If the circles are identical the factory will give us an empty shader. 187 // This will happen if we pick identical centers 188 } while (center1 == center2); 189 190 // Below makes sure that circle one is contained within circle two 191 // and both circles are touching on an edge 192 SkPoint diff = center2 - center1; 193 SkScalar diffLen = diff.length(); 194 radius2 = radius1 + diffLen; 195 196 SkColor colors[kMaxRandomGradientColors]; 197 SkScalar stopsArray[kMaxRandomGradientColors]; 198 SkScalar* stops = stopsArray; 199 SkShader::TileMode tm; 200 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 201 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, 202 center2, radius2, 203 colors, stops, colorCount, 204 tm)); 205 SkPaint paint; 206 GrEffectRef* effect; 207 GrColor grColor; 208 shader->asNewEffect(context, paint, NULL, &grColor, &effect); 209 return effect; 210} 211 212GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory, 213 const GrDrawEffect& drawEffect) 214 : INHERITED(factory) 215 , fVSVaryingName(NULL) 216 , fFSVaryingName(NULL) 217 , fCachedRadius(-SK_ScalarMax) 218 , fCachedDiffRadius(-SK_ScalarMax) {} 219 220void GLEdge2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, 221 const GrDrawEffect&, 222 EffectKey key, 223 const char* outputColor, 224 const char* inputColor, 225 const TransformedCoordsArray& coords, 226 const TextureSamplerArray& samplers) { 227 this->emitUniforms(builder, key); 228 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, 229 kFloat_GrSLType, "Conical2FSParams", 3); 230 231 SkString cName("c"); 232 SkString tName("t"); 233 SkString p0; // start radius 234 SkString p1; // start radius squared 235 SkString p2; // difference in radii (r1 - r0) 236 237 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); 238 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); 239 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); 240 241 // We interpolate the linear component in coords[1]. 242 SkASSERT(coords[0].type() == coords[1].type()); 243 const char* coords2D; 244 SkString bVar; 245 if (kVec3f_GrSLType == coords[0].type()) { 246 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n", 247 coords[0].c_str(), coords[0].c_str(), coords[1].c_str(), coords[1].c_str()); 248 coords2D = "interpolants.xy"; 249 bVar = "interpolants.z"; 250 } else { 251 coords2D = coords[0].c_str(); 252 bVar.printf("%s.x", coords[1].c_str()); 253 } 254 255 // output will default to transparent black (we simply won't write anything 256 // else to it if invalid, instead of discarding or returning prematurely) 257 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); 258 259 // c = (x^2)+(y^2) - params[1] 260 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", 261 cName.c_str(), coords2D, coords2D, p1.c_str()); 262 263 // linear case: t = -c/b 264 builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), 265 cName.c_str(), bVar.c_str()); 266 267 // if r(t) > 0, then t will be the x coordinate 268 builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), 269 p2.c_str(), p0.c_str()); 270 builder->fsCodeAppend("\t"); 271 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 272 builder->fsCodeAppend("\t}\n"); 273} 274 275void GLEdge2PtConicalEffect::setData(const GrGLUniformManager& uman, 276 const GrDrawEffect& drawEffect) { 277 INHERITED::setData(uman, drawEffect); 278 const Edge2PtConicalEffect& data = drawEffect.castEffect<Edge2PtConicalEffect>(); 279 SkScalar radius0 = data.radius(); 280 SkScalar diffRadius = data.diffRadius(); 281 282 if (fCachedRadius != radius0 || 283 fCachedDiffRadius != diffRadius) { 284 285 float values[3] = { 286 SkScalarToFloat(radius0), 287 SkScalarToFloat(SkScalarMul(radius0, radius0)), 288 SkScalarToFloat(diffRadius) 289 }; 290 291 uman.set1fv(fParamUni, 3, values); 292 fCachedRadius = radius0; 293 fCachedDiffRadius = diffRadius; 294 } 295} 296 297GrGLEffect::EffectKey GLEdge2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, 298 const GrGLCaps&) { 299 return GenBaseGradientKey(drawEffect); 300} 301 302////////////////////////////////////////////////////////////////////////////// 303// Focal Conical Gradients 304////////////////////////////////////////////////////////////////////////////// 305 306static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader, 307 SkMatrix* invLMatrix, SkScalar* focalX) { 308 // Inverse of the current local matrix is passed in then, 309 // translate, scale, and rotate such that endCircle is unit circle on x-axis, 310 // and focal point is at the origin. 311 ConicalType conicalType; 312 const SkPoint& focal = shader.getStartCenter(); 313 const SkPoint& centerEnd = shader.getEndCenter(); 314 SkScalar radius = shader.getEndRadius(); 315 SkScalar invRadius = 1.f / radius; 316 317 SkMatrix matrix; 318 319 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY); 320 matrix.postScale(invRadius, invRadius); 321 322 SkPoint focalTrans; 323 matrix.mapPoints(&focalTrans, &focal, 1); 324 *focalX = focalTrans.length(); 325 326 if (0.f != *focalX) { 327 SkScalar invFocalX = SkScalarInvert(*focalX); 328 SkMatrix rot; 329 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY), 330 SkScalarMul(invFocalX, focalTrans.fX)); 331 matrix.postConcat(rot); 332 } 333 334 matrix.postTranslate(-(*focalX), 0.f); 335 336 // If the focal point is touching the edge of the circle it will 337 // cause a degenerate case that must be handled separately 338 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the 339 // stability trade off versus the linear approx used in the Edge Shader 340 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) { 341 return kEdge_ConicalType; 342 } 343 344 // Scale factor 1 / (1 - focalX * focalX) 345 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX); 346 SkScalar s = SkScalarDiv(1.f, oneMinusF2); 347 348 349 if (s >= 0.f) { 350 conicalType = kInside_ConicalType; 351 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2)); 352 } else { 353 conicalType = kOutside_ConicalType; 354 matrix.postScale(s, s); 355 } 356 357 invLMatrix->postConcat(matrix); 358 359 return conicalType; 360} 361 362////////////////////////////////////////////////////////////////////////////// 363 364class GLFocalOutside2PtConicalEffect; 365 366class FocalOutside2PtConicalEffect : public GrGradientEffect { 367public: 368 369 static GrEffectRef* Create(GrContext* ctx, 370 const SkTwoPointConicalGradient& shader, 371 const SkMatrix& matrix, 372 SkShader::TileMode tm, 373 SkScalar focalX) { 374 AutoEffectUnref effect(SkNEW_ARGS(FocalOutside2PtConicalEffect, (ctx, shader, matrix, tm, focalX))); 375 return CreateEffectRef(effect); 376 } 377 378 virtual ~FocalOutside2PtConicalEffect() { } 379 380 static const char* Name() { return "Two-Point Conical Gradient Focal Outside"; } 381 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 382 383 bool isFlipped() const { return fIsFlipped; } 384 SkScalar focal() const { return fFocalX; } 385 386 typedef GLFocalOutside2PtConicalEffect GLEffect; 387 388private: 389 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 390 const FocalOutside2PtConicalEffect& s = CastEffect<FocalOutside2PtConicalEffect>(sBase); 391 return (INHERITED::onIsEqual(sBase) && 392 this->fFocalX == s.fFocalX && 393 this->fIsFlipped == s.fIsFlipped); 394 } 395 396 FocalOutside2PtConicalEffect(GrContext* ctx, 397 const SkTwoPointConicalGradient& shader, 398 const SkMatrix& matrix, 399 SkShader::TileMode tm, 400 SkScalar focalX) 401 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX), fIsFlipped(shader.isFlippedGrad()) {} 402 403 GR_DECLARE_EFFECT_TEST; 404 405 SkScalar fFocalX; 406 bool fIsFlipped; 407 408 typedef GrGradientEffect INHERITED; 409}; 410 411class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect { 412public: 413 GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); 414 virtual ~GLFocalOutside2PtConicalEffect() { } 415 416 virtual void emitCode(GrGLShaderBuilder*, 417 const GrDrawEffect&, 418 EffectKey, 419 const char* outputColor, 420 const char* inputColor, 421 const TransformedCoordsArray&, 422 const TextureSamplerArray&) SK_OVERRIDE; 423 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 424 425 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 426 427protected: 428 UniformHandle fParamUni; 429 430 const char* fVSVaryingName; 431 const char* fFSVaryingName; 432 433 bool fIsFlipped; 434 435 // @{ 436 /// Values last uploaded as uniforms 437 438 SkScalar fCachedFocal; 439 440 // @} 441 442private: 443 typedef GrGLGradientEffect INHERITED; 444 445}; 446 447const GrBackendEffectFactory& FocalOutside2PtConicalEffect::getFactory() const { 448 return GrTBackendEffectFactory<FocalOutside2PtConicalEffect>::getInstance(); 449} 450 451GR_DEFINE_EFFECT_TEST(FocalOutside2PtConicalEffect); 452 453GrEffectRef* FocalOutside2PtConicalEffect::TestCreate(SkRandom* random, 454 GrContext* context, 455 const GrDrawTargetCaps&, 456 GrTexture**) { 457 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 458 SkScalar radius1 = 0.f; 459 SkPoint center2; 460 SkScalar radius2; 461 do { 462 center2.set(random->nextUScalar1(), random->nextUScalar1()); 463 // Need to make sure the centers are not the same or else focal point will be inside 464 } while (center1 == center2); 465 SkPoint diff = center2 - center1; 466 SkScalar diffLen = diff.length(); 467 // Below makes sure that the focal point is not contained within circle two 468 radius2 = random->nextRangeF(0.f, diffLen); 469 470 SkColor colors[kMaxRandomGradientColors]; 471 SkScalar stopsArray[kMaxRandomGradientColors]; 472 SkScalar* stops = stopsArray; 473 SkShader::TileMode tm; 474 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 475 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, 476 center2, radius2, 477 colors, stops, colorCount, 478 tm)); 479 SkPaint paint; 480 GrEffectRef* effect; 481 GrColor grColor; 482 shader->asNewEffect(context, paint, NULL, &grColor, &effect); 483 return effect; 484} 485 486GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, 487 const GrDrawEffect& drawEffect) 488 : INHERITED(factory) 489 , fVSVaryingName(NULL) 490 , fFSVaryingName(NULL) 491 , fCachedFocal(SK_ScalarMax) { 492 const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>(); 493 fIsFlipped = data.isFlipped(); 494} 495 496void GLFocalOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, 497 const GrDrawEffect&, 498 EffectKey key, 499 const char* outputColor, 500 const char* inputColor, 501 const TransformedCoordsArray& coords, 502 const TextureSamplerArray& samplers) { 503 this->emitUniforms(builder, key); 504 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, 505 kFloat_GrSLType, "Conical2FSParams", 2); 506 SkString tName("t"); 507 SkString p0; // focalX 508 SkString p1; // 1 - focalX * focalX 509 510 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); 511 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); 512 513 // if we have a vec3 from being in perspective, convert it to a vec2 first 514 SkString coords2DString = builder->ensureFSCoords2D(coords, 0); 515 const char* coords2D = coords2DString.c_str(); 516 517 // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2) 518 519 // output will default to transparent black (we simply won't write anything 520 // else to it if invalid, instead of discarding or returning prematurely) 521 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); 522 523 builder->fsCodeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D); 524 builder->fsCodeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D); 525 builder->fsCodeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str()); 526 527 // Must check to see if we flipped the circle order (to make sure start radius < end radius) 528 // If so we must also flip sign on sqrt 529 if (!fIsFlipped) { 530 builder->fsCodeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(), 531 coords2D, p0.c_str()); 532 } else { 533 builder->fsCodeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(), 534 coords2D, p0.c_str()); 535 } 536 537 builder->fsCodeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str()); 538 builder->fsCodeAppend("\t\t"); 539 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 540 builder->fsCodeAppend("\t}\n"); 541} 542 543void GLFocalOutside2PtConicalEffect::setData(const GrGLUniformManager& uman, 544 const GrDrawEffect& drawEffect) { 545 INHERITED::setData(uman, drawEffect); 546 const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>(); 547 SkASSERT(data.isFlipped() == fIsFlipped); 548 SkScalar focal = data.focal(); 549 550 if (fCachedFocal != focal) { 551 SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal); 552 553 float values[2] = { 554 SkScalarToFloat(focal), 555 SkScalarToFloat(oneMinus2F), 556 }; 557 558 uman.set1fv(fParamUni, 2, values); 559 fCachedFocal = focal; 560 } 561} 562 563GrGLEffect::EffectKey GLFocalOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, 564 const GrGLCaps&) { 565 enum { 566 kIsFlipped = 1 << kBaseKeyBitCnt, 567 }; 568 569 EffectKey key = GenBaseGradientKey(drawEffect); 570 571 if (drawEffect.castEffect<FocalOutside2PtConicalEffect>().isFlipped()) { 572 key |= kIsFlipped; 573 } 574 return key; 575} 576 577////////////////////////////////////////////////////////////////////////////// 578 579class GLFocalInside2PtConicalEffect; 580 581class FocalInside2PtConicalEffect : public GrGradientEffect { 582public: 583 584 static GrEffectRef* Create(GrContext* ctx, 585 const SkTwoPointConicalGradient& shader, 586 const SkMatrix& matrix, 587 SkShader::TileMode tm, 588 SkScalar focalX) { 589 AutoEffectUnref effect(SkNEW_ARGS(FocalInside2PtConicalEffect, (ctx, shader, matrix, tm, focalX))); 590 return CreateEffectRef(effect); 591 } 592 593 virtual ~FocalInside2PtConicalEffect() {} 594 595 static const char* Name() { return "Two-Point Conical Gradient Focal Inside"; } 596 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 597 598 SkScalar focal() const { return fFocalX; } 599 600 typedef GLFocalInside2PtConicalEffect GLEffect; 601 602private: 603 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 604 const FocalInside2PtConicalEffect& s = CastEffect<FocalInside2PtConicalEffect>(sBase); 605 return (INHERITED::onIsEqual(sBase) && 606 this->fFocalX == s.fFocalX); 607 } 608 609 FocalInside2PtConicalEffect(GrContext* ctx, 610 const SkTwoPointConicalGradient& shader, 611 const SkMatrix& matrix, 612 SkShader::TileMode tm, 613 SkScalar focalX) 614 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {} 615 616 GR_DECLARE_EFFECT_TEST; 617 618 SkScalar fFocalX; 619 620 typedef GrGradientEffect INHERITED; 621}; 622 623class GLFocalInside2PtConicalEffect : public GrGLGradientEffect { 624public: 625 GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); 626 virtual ~GLFocalInside2PtConicalEffect() {} 627 628 virtual void emitCode(GrGLShaderBuilder*, 629 const GrDrawEffect&, 630 EffectKey, 631 const char* outputColor, 632 const char* inputColor, 633 const TransformedCoordsArray&, 634 const TextureSamplerArray&) SK_OVERRIDE; 635 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 636 637 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 638 639protected: 640 UniformHandle fFocalUni; 641 642 const char* fVSVaryingName; 643 const char* fFSVaryingName; 644 645 // @{ 646 /// Values last uploaded as uniforms 647 648 SkScalar fCachedFocal; 649 650 // @} 651 652private: 653 typedef GrGLGradientEffect INHERITED; 654 655}; 656 657const GrBackendEffectFactory& FocalInside2PtConicalEffect::getFactory() const { 658 return GrTBackendEffectFactory<FocalInside2PtConicalEffect>::getInstance(); 659} 660 661GR_DEFINE_EFFECT_TEST(FocalInside2PtConicalEffect); 662 663GrEffectRef* FocalInside2PtConicalEffect::TestCreate(SkRandom* random, 664 GrContext* context, 665 const GrDrawTargetCaps&, 666 GrTexture**) { 667 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 668 SkScalar radius1 = 0.f; 669 SkPoint center2; 670 SkScalar radius2; 671 do { 672 center2.set(random->nextUScalar1(), random->nextUScalar1()); 673 // Below makes sure radius2 is larger enouch such that the focal point 674 // is inside the end circle 675 SkScalar increase = random->nextUScalar1(); 676 SkPoint diff = center2 - center1; 677 SkScalar diffLen = diff.length(); 678 radius2 = diffLen + increase; 679 // If the circles are identical the factory will give us an empty shader. 680 } while (radius1 == radius2 && center1 == center2); 681 682 SkColor colors[kMaxRandomGradientColors]; 683 SkScalar stopsArray[kMaxRandomGradientColors]; 684 SkScalar* stops = stopsArray; 685 SkShader::TileMode tm; 686 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 687 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, 688 center2, radius2, 689 colors, stops, colorCount, 690 tm)); 691 SkPaint paint; 692 GrColor grColor; 693 GrEffectRef* grEffect; 694 shader->asNewEffect(context, paint, NULL, &grColor, &grEffect); 695 return grEffect; 696} 697 698GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, 699 const GrDrawEffect& drawEffect) 700 : INHERITED(factory) 701 , fVSVaryingName(NULL) 702 , fFSVaryingName(NULL) 703 , fCachedFocal(SK_ScalarMax) {} 704 705void GLFocalInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, 706 const GrDrawEffect&, 707 EffectKey key, 708 const char* outputColor, 709 const char* inputColor, 710 const TransformedCoordsArray& coords, 711 const TextureSamplerArray& samplers) { 712 this->emitUniforms(builder, key); 713 fFocalUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 714 kFloat_GrSLType, "Conical2FSParams"); 715 SkString tName("t"); 716 717 // this is the distance along x-axis from the end center to focal point in 718 // transformed coordinates 719 GrGLShaderVar focal = builder->getUniformVariable(fFocalUni); 720 721 // if we have a vec3 from being in perspective, convert it to a vec2 first 722 SkString coords2DString = builder->ensureFSCoords2D(coords, 0); 723 const char* coords2D = coords2DString.c_str(); 724 725 // t = p.x * focalX + length(p) 726 builder->fsCodeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(), 727 coords2D, focal.c_str(), coords2D); 728 729 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 730} 731 732void GLFocalInside2PtConicalEffect::setData(const GrGLUniformManager& uman, 733 const GrDrawEffect& drawEffect) { 734 INHERITED::setData(uman, drawEffect); 735 const FocalInside2PtConicalEffect& data = drawEffect.castEffect<FocalInside2PtConicalEffect>(); 736 SkScalar focal = data.focal(); 737 738 if (fCachedFocal != focal) { 739 uman.set1f(fFocalUni, SkScalarToFloat(focal)); 740 fCachedFocal = focal; 741 } 742} 743 744GrGLEffect::EffectKey GLFocalInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, 745 const GrGLCaps&) { 746 return GenBaseGradientKey(drawEffect); 747} 748 749////////////////////////////////////////////////////////////////////////////// 750// Circle Conical Gradients 751////////////////////////////////////////////////////////////////////////////// 752 753struct CircleConicalInfo { 754 SkPoint fCenterEnd; 755 SkScalar fA; 756 SkScalar fB; 757 SkScalar fC; 758}; 759 760// Returns focal distance along x-axis in transformed coords 761static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader, 762 SkMatrix* invLMatrix, CircleConicalInfo* info) { 763 // Inverse of the current local matrix is passed in then, 764 // translate and scale such that start circle is on the origin and has radius 1 765 const SkPoint& centerStart = shader.getStartCenter(); 766 const SkPoint& centerEnd = shader.getEndCenter(); 767 SkScalar radiusStart = shader.getStartRadius(); 768 SkScalar radiusEnd = shader.getEndRadius(); 769 770 SkMatrix matrix; 771 772 matrix.setTranslate(-centerStart.fX, -centerStart.fY); 773 774 SkScalar invStartRad = 1.f / radiusStart; 775 matrix.postScale(invStartRad, invStartRad); 776 777 radiusEnd /= radiusStart; 778 779 SkPoint centerEndTrans; 780 matrix.mapPoints(¢erEndTrans, ¢erEnd, 1); 781 782 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY 783 - radiusEnd * radiusEnd + 2 * radiusEnd - 1; 784 785 // Check to see if start circle is inside end circle with edges touching. 786 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting 787 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing 788 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is 789 // still accurate. 790 if (SkScalarAbs(A) < kEdgeErrorTol) { 791 return kEdge_ConicalType; 792 } 793 794 SkScalar C = 1.f / A; 795 SkScalar B = (radiusEnd - 1.f) * C; 796 797 matrix.postScale(C, C); 798 799 invLMatrix->postConcat(matrix); 800 801 info->fCenterEnd = centerEndTrans; 802 info->fA = A; 803 info->fB = B; 804 info->fC = C; 805 806 // if A ends up being negative, the start circle is contained completely inside the end cirlce 807 if (A < 0.f) { 808 return kInside_ConicalType; 809 } 810 return kOutside_ConicalType; 811} 812 813class GLCircleInside2PtConicalEffect; 814 815class CircleInside2PtConicalEffect : public GrGradientEffect { 816public: 817 818 static GrEffectRef* Create(GrContext* ctx, 819 const SkTwoPointConicalGradient& shader, 820 const SkMatrix& matrix, 821 SkShader::TileMode tm, 822 const CircleConicalInfo& info) { 823 AutoEffectUnref effect(SkNEW_ARGS(CircleInside2PtConicalEffect, (ctx, shader, matrix, tm, info))); 824 return CreateEffectRef(effect); 825 } 826 827 virtual ~CircleInside2PtConicalEffect() {} 828 829 static const char* Name() { return "Two-Point Conical Gradient Inside"; } 830 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 831 832 SkScalar centerX() const { return fInfo.fCenterEnd.fX; } 833 SkScalar centerY() const { return fInfo.fCenterEnd.fY; } 834 SkScalar A() const { return fInfo.fA; } 835 SkScalar B() const { return fInfo.fB; } 836 SkScalar C() const { return fInfo.fC; } 837 838 typedef GLCircleInside2PtConicalEffect GLEffect; 839 840private: 841 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 842 const CircleInside2PtConicalEffect& s = CastEffect<CircleInside2PtConicalEffect>(sBase); 843 return (INHERITED::onIsEqual(sBase) && 844 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && 845 this->fInfo.fA == s.fInfo.fA && 846 this->fInfo.fB == s.fInfo.fB && 847 this->fInfo.fC == s.fInfo.fC); 848 } 849 850 CircleInside2PtConicalEffect(GrContext* ctx, 851 const SkTwoPointConicalGradient& shader, 852 const SkMatrix& matrix, 853 SkShader::TileMode tm, 854 const CircleConicalInfo& info) 855 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {} 856 857 GR_DECLARE_EFFECT_TEST; 858 859 const CircleConicalInfo fInfo; 860 861 typedef GrGradientEffect INHERITED; 862}; 863 864class GLCircleInside2PtConicalEffect : public GrGLGradientEffect { 865public: 866 GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); 867 virtual ~GLCircleInside2PtConicalEffect() {} 868 869 virtual void emitCode(GrGLShaderBuilder*, 870 const GrDrawEffect&, 871 EffectKey, 872 const char* outputColor, 873 const char* inputColor, 874 const TransformedCoordsArray&, 875 const TextureSamplerArray&) SK_OVERRIDE; 876 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 877 878 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 879 880protected: 881 UniformHandle fCenterUni; 882 UniformHandle fParamUni; 883 884 const char* fVSVaryingName; 885 const char* fFSVaryingName; 886 887 // @{ 888 /// Values last uploaded as uniforms 889 890 SkScalar fCachedCenterX; 891 SkScalar fCachedCenterY; 892 SkScalar fCachedA; 893 SkScalar fCachedB; 894 SkScalar fCachedC; 895 896 // @} 897 898private: 899 typedef GrGLGradientEffect INHERITED; 900 901}; 902 903const GrBackendEffectFactory& CircleInside2PtConicalEffect::getFactory() const { 904 return GrTBackendEffectFactory<CircleInside2PtConicalEffect>::getInstance(); 905} 906 907GR_DEFINE_EFFECT_TEST(CircleInside2PtConicalEffect); 908 909GrEffectRef* CircleInside2PtConicalEffect::TestCreate(SkRandom* random, 910 GrContext* context, 911 const GrDrawTargetCaps&, 912 GrTexture**) { 913 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 914 SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0 915 SkPoint center2; 916 SkScalar radius2; 917 do { 918 center2.set(random->nextUScalar1(), random->nextUScalar1()); 919 // Below makes sure that circle one is contained within circle two 920 SkScalar increase = random->nextUScalar1(); 921 SkPoint diff = center2 - center1; 922 SkScalar diffLen = diff.length(); 923 radius2 = radius1 + diffLen + increase; 924 // If the circles are identical the factory will give us an empty shader. 925 } while (radius1 == radius2 && center1 == center2); 926 927 SkColor colors[kMaxRandomGradientColors]; 928 SkScalar stopsArray[kMaxRandomGradientColors]; 929 SkScalar* stops = stopsArray; 930 SkShader::TileMode tm; 931 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 932 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, 933 center2, radius2, 934 colors, stops, colorCount, 935 tm)); 936 SkPaint paint; 937 GrColor grColor; 938 GrEffectRef* grEffect; 939 shader->asNewEffect(context, paint, NULL, &grColor, &grEffect); 940 return grEffect; 941} 942 943GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, 944 const GrDrawEffect& drawEffect) 945 : INHERITED(factory) 946 , fVSVaryingName(NULL) 947 , fFSVaryingName(NULL) 948 , fCachedCenterX(SK_ScalarMax) 949 , fCachedCenterY(SK_ScalarMax) 950 , fCachedA(SK_ScalarMax) 951 , fCachedB(SK_ScalarMax) 952 , fCachedC(SK_ScalarMax) {} 953 954void GLCircleInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, 955 const GrDrawEffect&, 956 EffectKey key, 957 const char* outputColor, 958 const char* inputColor, 959 const TransformedCoordsArray& coords, 960 const TextureSamplerArray& samplers) { 961 this->emitUniforms(builder, key); 962 fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 963 kVec2f_GrSLType, "Conical2FSCenter"); 964 fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 965 kVec3f_GrSLType, "Conical2FSParams"); 966 SkString tName("t"); 967 968 GrGLShaderVar center = builder->getUniformVariable(fCenterUni); 969 // params.x = A 970 // params.y = B 971 // params.z = C 972 GrGLShaderVar params = builder->getUniformVariable(fParamUni); 973 974 // if we have a vec3 from being in perspective, convert it to a vec2 first 975 SkString coords2DString = builder->ensureFSCoords2D(coords, 0); 976 const char* coords2D = coords2DString.c_str(); 977 978 // p = coords2D 979 // e = center end 980 // r = radius end 981 // A = dot(e, e) - r^2 + 2 * r - 1 982 // B = (r -1) / A 983 // C = 1 / A 984 // d = dot(e, p) + B 985 // t = d +/- sqrt(d^2 - A * dot(p, p) + C) 986 builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); 987 builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str()); 988 builder->fsCodeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n", 989 tName.c_str(), params.c_str(), params.c_str()); 990 991 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 992} 993 994void GLCircleInside2PtConicalEffect::setData(const GrGLUniformManager& uman, 995 const GrDrawEffect& drawEffect) { 996 INHERITED::setData(uman, drawEffect); 997 const CircleInside2PtConicalEffect& data = drawEffect.castEffect<CircleInside2PtConicalEffect>(); 998 SkScalar centerX = data.centerX(); 999 SkScalar centerY = data.centerY(); 1000 SkScalar A = data.A(); 1001 SkScalar B = data.B(); 1002 SkScalar C = data.C(); 1003 1004 if (fCachedCenterX != centerX || fCachedCenterY != centerY || 1005 fCachedA != A || fCachedB != B || fCachedC != C) { 1006 1007 uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); 1008 uman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C)); 1009 1010 fCachedCenterX = centerX; 1011 fCachedCenterY = centerY; 1012 fCachedA = A; 1013 fCachedB = B; 1014 fCachedC = C; 1015 } 1016} 1017 1018GrGLEffect::EffectKey GLCircleInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, 1019 const GrGLCaps&) { 1020 EffectKey key = GenBaseGradientKey(drawEffect); 1021 return key; 1022} 1023 1024////////////////////////////////////////////////////////////////////////////// 1025 1026class GLCircleOutside2PtConicalEffect; 1027 1028class CircleOutside2PtConicalEffect : public GrGradientEffect { 1029public: 1030 1031 static GrEffectRef* Create(GrContext* ctx, 1032 const SkTwoPointConicalGradient& shader, 1033 const SkMatrix& matrix, 1034 SkShader::TileMode tm, 1035 const CircleConicalInfo& info) { 1036 AutoEffectUnref effect(SkNEW_ARGS(CircleOutside2PtConicalEffect, (ctx, shader, matrix, tm, info))); 1037 return CreateEffectRef(effect); 1038 } 1039 1040 virtual ~CircleOutside2PtConicalEffect() {} 1041 1042 static const char* Name() { return "Two-Point Conical Gradient Outside"; } 1043 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 1044 1045 SkScalar centerX() const { return fInfo.fCenterEnd.fX; } 1046 SkScalar centerY() const { return fInfo.fCenterEnd.fY; } 1047 SkScalar A() const { return fInfo.fA; } 1048 SkScalar B() const { return fInfo.fB; } 1049 SkScalar C() const { return fInfo.fC; } 1050 SkScalar tLimit() const { return fTLimit; } 1051 bool isFlipped() const { return fIsFlipped; } 1052 1053 typedef GLCircleOutside2PtConicalEffect GLEffect; 1054 1055private: 1056 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 1057 const CircleOutside2PtConicalEffect& s = CastEffect<CircleOutside2PtConicalEffect>(sBase); 1058 return (INHERITED::onIsEqual(sBase) && 1059 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && 1060 this->fInfo.fA == s.fInfo.fA && 1061 this->fInfo.fB == s.fInfo.fB && 1062 this->fInfo.fC == s.fInfo.fC && 1063 this->fTLimit == s.fTLimit && 1064 this->fIsFlipped == s.fIsFlipped); 1065 } 1066 1067 CircleOutside2PtConicalEffect(GrContext* ctx, 1068 const SkTwoPointConicalGradient& shader, 1069 const SkMatrix& matrix, 1070 SkShader::TileMode tm, 1071 const CircleConicalInfo& info) 1072 : INHERITED(ctx, shader, matrix, tm), fInfo(info) { 1073 if (shader.getStartRadius() != shader.getEndRadius()) { 1074 fTLimit = SkScalarDiv(shader.getStartRadius(), (shader.getStartRadius() - shader.getEndRadius())); 1075 } else { 1076 fTLimit = SK_ScalarMin; 1077 } 1078 1079 fIsFlipped = shader.isFlippedGrad(); 1080 } 1081 1082 GR_DECLARE_EFFECT_TEST; 1083 1084 const CircleConicalInfo fInfo; 1085 SkScalar fTLimit; 1086 bool fIsFlipped; 1087 1088 typedef GrGradientEffect INHERITED; 1089}; 1090 1091class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect { 1092public: 1093 GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); 1094 virtual ~GLCircleOutside2PtConicalEffect() {} 1095 1096 virtual void emitCode(GrGLShaderBuilder*, 1097 const GrDrawEffect&, 1098 EffectKey, 1099 const char* outputColor, 1100 const char* inputColor, 1101 const TransformedCoordsArray&, 1102 const TextureSamplerArray&) SK_OVERRIDE; 1103 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 1104 1105 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 1106 1107protected: 1108 UniformHandle fCenterUni; 1109 UniformHandle fParamUni; 1110 1111 const char* fVSVaryingName; 1112 const char* fFSVaryingName; 1113 1114 bool fIsFlipped; 1115 1116 // @{ 1117 /// Values last uploaded as uniforms 1118 1119 SkScalar fCachedCenterX; 1120 SkScalar fCachedCenterY; 1121 SkScalar fCachedA; 1122 SkScalar fCachedB; 1123 SkScalar fCachedC; 1124 SkScalar fCachedTLimit; 1125 1126 // @} 1127 1128private: 1129 typedef GrGLGradientEffect INHERITED; 1130 1131}; 1132 1133const GrBackendEffectFactory& CircleOutside2PtConicalEffect::getFactory() const { 1134 return GrTBackendEffectFactory<CircleOutside2PtConicalEffect>::getInstance(); 1135} 1136 1137GR_DEFINE_EFFECT_TEST(CircleOutside2PtConicalEffect); 1138 1139GrEffectRef* CircleOutside2PtConicalEffect::TestCreate(SkRandom* random, 1140 GrContext* context, 1141 const GrDrawTargetCaps&, 1142 GrTexture**) { 1143 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 1144 SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0 1145 SkPoint center2; 1146 SkScalar radius2; 1147 SkScalar diffLen; 1148 do { 1149 center2.set(random->nextUScalar1(), random->nextUScalar1()); 1150 // If the circles share a center than we can't be in the outside case 1151 } while (center1 == center2); 1152 SkPoint diff = center2 - center1; 1153 diffLen = diff.length(); 1154 // Below makes sure that circle one is not contained within circle two 1155 // and have radius2 >= radius to match sorting on cpu side 1156 radius2 = radius1 + random->nextRangeF(0.f, diffLen); 1157 1158 SkColor colors[kMaxRandomGradientColors]; 1159 SkScalar stopsArray[kMaxRandomGradientColors]; 1160 SkScalar* stops = stopsArray; 1161 SkShader::TileMode tm; 1162 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 1163 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, 1164 center2, radius2, 1165 colors, stops, colorCount, 1166 tm)); 1167 SkPaint paint; 1168 GrColor grColor; 1169 GrEffectRef* grEffect; 1170 shader->asNewEffect(context, paint, NULL, &grColor, &grEffect); 1171 return grEffect; 1172} 1173 1174GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, 1175 const GrDrawEffect& drawEffect) 1176 : INHERITED(factory) 1177 , fVSVaryingName(NULL) 1178 , fFSVaryingName(NULL) 1179 , fCachedCenterX(SK_ScalarMax) 1180 , fCachedCenterY(SK_ScalarMax) 1181 , fCachedA(SK_ScalarMax) 1182 , fCachedB(SK_ScalarMax) 1183 , fCachedC(SK_ScalarMax) 1184 , fCachedTLimit(SK_ScalarMax) { 1185 const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>(); 1186 fIsFlipped = data.isFlipped(); 1187 } 1188 1189void GLCircleOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, 1190 const GrDrawEffect&, 1191 EffectKey key, 1192 const char* outputColor, 1193 const char* inputColor, 1194 const TransformedCoordsArray& coords, 1195 const TextureSamplerArray& samplers) { 1196 this->emitUniforms(builder, key); 1197 fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 1198 kVec2f_GrSLType, "Conical2FSCenter"); 1199 fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 1200 kVec4f_GrSLType, "Conical2FSParams"); 1201 SkString tName("t"); 1202 1203 GrGLShaderVar center = builder->getUniformVariable(fCenterUni); 1204 // params.x = A 1205 // params.y = B 1206 // params.z = C 1207 GrGLShaderVar params = builder->getUniformVariable(fParamUni); 1208 1209 // if we have a vec3 from being in perspective, convert it to a vec2 first 1210 SkString coords2DString = builder->ensureFSCoords2D(coords, 0); 1211 const char* coords2D = coords2DString.c_str(); 1212 1213 // output will default to transparent black (we simply won't write anything 1214 // else to it if invalid, instead of discarding or returning prematurely) 1215 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); 1216 1217 // p = coords2D 1218 // e = center end 1219 // r = radius end 1220 // A = dot(e, e) - r^2 + 2 * r - 1 1221 // B = (r -1) / A 1222 // C = 1 / A 1223 // d = dot(e, p) + B 1224 // t = d +/- sqrt(d^2 - A * dot(p, p) + C) 1225 1226 builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); 1227 builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str()); 1228 builder->fsCodeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(), params.c_str()); 1229 1230 // Must check to see if we flipped the circle order (to make sure start radius < end radius) 1231 // If so we must also flip sign on sqrt 1232 if (!fIsFlipped) { 1233 builder->fsCodeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str()); 1234 } else { 1235 builder->fsCodeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str()); 1236 } 1237 1238 builder->fsCodeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str()); 1239 builder->fsCodeAppend("\t\t"); 1240 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 1241 builder->fsCodeAppend("\t}\n"); 1242} 1243 1244void GLCircleOutside2PtConicalEffect::setData(const GrGLUniformManager& uman, 1245 const GrDrawEffect& drawEffect) { 1246 INHERITED::setData(uman, drawEffect); 1247 const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>(); 1248 SkASSERT(data.isFlipped() == fIsFlipped); 1249 SkScalar centerX = data.centerX(); 1250 SkScalar centerY = data.centerY(); 1251 SkScalar A = data.A(); 1252 SkScalar B = data.B(); 1253 SkScalar C = data.C(); 1254 SkScalar tLimit = data.tLimit(); 1255 1256 if (fCachedCenterX != centerX || fCachedCenterY != centerY || 1257 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) { 1258 1259 uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); 1260 uman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C), 1261 SkScalarToFloat(tLimit)); 1262 1263 fCachedCenterX = centerX; 1264 fCachedCenterY = centerY; 1265 fCachedA = A; 1266 fCachedB = B; 1267 fCachedC = C; 1268 fCachedTLimit = tLimit; 1269 } 1270} 1271 1272GrGLEffect::EffectKey GLCircleOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, 1273 const GrGLCaps&) { 1274 enum { 1275 kIsFlipped = 1 << kBaseKeyBitCnt, 1276 }; 1277 1278 EffectKey key = GenBaseGradientKey(drawEffect); 1279 1280 if (drawEffect.castEffect<CircleOutside2PtConicalEffect>().isFlipped()) { 1281 key |= kIsFlipped; 1282 } 1283 return key; 1284} 1285 1286////////////////////////////////////////////////////////////////////////////// 1287 1288GrEffectRef* Gr2PtConicalGradientEffect::Create(GrContext* ctx, 1289 const SkTwoPointConicalGradient& shader, 1290 SkShader::TileMode tm, 1291 const SkMatrix* localMatrix) { 1292 SkMatrix matrix; 1293 if (!shader.getLocalMatrix().invert(&matrix)) { 1294 return NULL; 1295 } 1296 if (localMatrix) { 1297 SkMatrix inv; 1298 if (!localMatrix->invert(&inv)) { 1299 return NULL; 1300 } 1301 matrix.postConcat(inv); 1302 } 1303 1304 if (shader.getStartRadius() < kErrorTol) { 1305 SkScalar focalX; 1306 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX); 1307 if (type == kInside_ConicalType) { 1308 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX); 1309 } else if(type == kEdge_ConicalType) { 1310 set_matrix_edge_conical(shader, &matrix); 1311 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm); 1312 } else { 1313 return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX); 1314 } 1315 } 1316 1317 CircleConicalInfo info; 1318 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info); 1319 1320 if (type == kInside_ConicalType) { 1321 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info); 1322 } else if (type == kEdge_ConicalType) { 1323 set_matrix_edge_conical(shader, &matrix); 1324 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm); 1325 } else { 1326 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info); 1327 } 1328} 1329 1330#endif 1331