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