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