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