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 
set_matrix_edge_conical(const SkTwoPointConicalGradient & shader,SkMatrix * invLMatrix)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 
Make(const CreateArgs & args)64     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
65         return sk_sp<GrFragmentProcessor>(new Edge2PtConicalEffect(args));
66     }
67 
~Edge2PtConicalEffect()68     ~Edge2PtConicalEffect() override {}
69 
name() const70     const char* name() const override {
71         return "Two-Point Conical Gradient Edge Touching";
72     }
73 
74     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
center() const75     SkScalar center() const { return fCenterX1; }
diffRadius() const76     SkScalar diffRadius() const { return fDiffRadius; }
radius() const77     SkScalar radius() const { return fRadius0; }
78 
79 private:
80     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
81 
82     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
83 
onIsEqual(const GrFragmentProcessor & sBase) const84     bool onIsEqual(const GrFragmentProcessor& sBase) const override {
85         const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
86         return (INHERITED::onIsEqual(sBase) &&
87                 this->fCenterX1 == s.fCenterX1 &&
88                 this->fRadius0 == s.fRadius0 &&
89                 this->fDiffRadius == s.fDiffRadius);
90     }
91 
Edge2PtConicalEffect(const CreateArgs & args)92     Edge2PtConicalEffect(const CreateArgs& args)
93             : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) {
94         const SkTwoPointConicalGradient& shader =
95             *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
96         fCenterX1 = shader.getCenterX1();
97         fRadius0 = shader.getStartRadius();
98         fDiffRadius = shader.getDiffRadius();
99         this->initClassID<Edge2PtConicalEffect>();
100         // We should only be calling this shader if we are degenerate case with touching circles
101         // When deciding if we are in edge case, we scaled by the end radius for cases when the
102         // start radius was close to zero, otherwise we scaled by the start radius.  In addition
103         // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
104         // need the sqrt value below
105         SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
106                  (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
107                                          fRadius0 * sqrt(kEdgeErrorTol)));
108 
109         // We pass the linear part of the quadratic as a varying.
110         //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
111         fBTransform = this->getCoordTransform();
112         SkMatrix& bMatrix = *fBTransform.accessMatrix();
113         SkScalar r0dr = fRadius0 * fDiffRadius;
114         bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] +
115                                             r0dr * bMatrix[SkMatrix::kMPersp0]);
116         bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] +
117                                            r0dr * bMatrix[SkMatrix::kMPersp1]);
118         bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] +
119                                             r0dr * bMatrix[SkMatrix::kMPersp2]);
120         this->addCoordTransform(&fBTransform);
121     }
122 
123     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
124 
125     // @{
126     // Cache of values - these can change arbitrarily, EXCEPT
127     // we shouldn't change between degenerate and non-degenerate?!
128 
129     GrCoordTransform fBTransform;
130     SkScalar         fCenterX1;
131     SkScalar         fRadius0;
132     SkScalar         fDiffRadius;
133 
134     // @}
135 
136     typedef GrGradientEffect INHERITED;
137 };
138 
139 class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor {
140 public:
141     GLSLEdge2PtConicalProcessor(const GrProcessor&);
~GLSLEdge2PtConicalProcessor()142     ~GLSLEdge2PtConicalProcessor() override {}
143 
144     virtual void emitCode(EmitArgs&) override;
145 
146     static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
147 
148 protected:
149     void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
150 
151     UniformHandle fParamUni;
152 
153     const char* fVSVaryingName;
154     const char* fFSVaryingName;
155 
156     // @{
157     /// Values last uploaded as uniforms
158 
159     SkScalar fCachedRadius;
160     SkScalar fCachedDiffRadius;
161 
162     // @}
163 
164 private:
165     typedef GrGradientEffect::GLSLProcessor INHERITED;
166 
167 };
168 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const169 void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
170                                                  GrProcessorKeyBuilder* b) const {
171     Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(*this, caps, b);
172 }
173 
onCreateGLSLInstance() const174 GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
175     return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this);
176 }
177 
178 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
179 
180 /*
181  * All Two point conical gradient test create functions may occasionally create edge case shaders
182  */
183 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)184 sk_sp<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
185     SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
186     SkScalar radius1 = d->fRandom->nextUScalar1();
187     SkPoint center2;
188     SkScalar radius2;
189     do {
190         center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
191         // If the circles are identical the factory will give us an empty shader.
192         // This will happen if we pick identical centers
193     } while (center1 == center2);
194 
195     // Below makes sure that circle one is contained within circle two
196     // and both circles are touching on an edge
197     SkPoint diff = center2 - center1;
198     SkScalar diffLen = diff.length();
199     radius2 = radius1 + diffLen;
200 
201     RandomGradientParams params(d->fRandom);
202     auto shader = params.fUseColors4f ?
203         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
204                                               params.fColors4f, params.fColorSpace, params.fStops,
205                                               params.fColorCount, params.fTileMode) :
206         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
207                                               params.fColors, params.fStops,
208                                               params.fColorCount, params.fTileMode);
209     GrTest::TestAsFPArgs asFPArgs(d);
210     sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
211     GrAlwaysAssert(fp);
212     return fp;
213 }
214 #endif
215 
GLSLEdge2PtConicalProcessor(const GrProcessor &)216 Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&)
217     : fVSVaryingName(nullptr)
218     , fFSVaryingName(nullptr)
219     , fCachedRadius(-SK_ScalarMax)
220     , fCachedDiffRadius(-SK_ScalarMax) {}
221 
emitCode(EmitArgs & args)222 void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) {
223     const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
224     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
225     this->emitUniforms(uniformHandler, ge);
226     fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
227                                            kVec3f_GrSLType, kDefault_GrSLPrecision,
228                                            "Conical2FSParams");
229 
230     SkString cName("c");
231     SkString tName("t");
232     SkString p0; // start radius
233     SkString p1; // start radius squared
234     SkString p2; // difference in radii (r1 - r0)
235 
236 
237     p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
238     p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
239     p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
240 
241     // We interpolate the linear component in coords[1].
242     SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType());
243     const char* coords2D;
244     SkString bVar;
245     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
246     if (kVec3f_GrSLType == args.fTransformedCoords[0].getType()) {
247         fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
248                                  args.fTransformedCoords[0].c_str(),
249                                  args.fTransformedCoords[0].c_str(),
250                                  args.fTransformedCoords[1].c_str(),
251                                  args.fTransformedCoords[1].c_str());
252         coords2D = "interpolants.xy";
253         bVar = "interpolants.z";
254     } else {
255         coords2D = args.fTransformedCoords[0].c_str();
256         bVar.printf("%s.x", args.fTransformedCoords[1].c_str());
257     }
258 
259     // output will default to transparent black (we simply won't write anything
260     // else to it if invalid, instead of discarding or returning prematurely)
261     fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
262 
263     // c = (x^2)+(y^2) - params[1]
264     fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
265                            cName.c_str(), coords2D, coords2D, p1.c_str());
266 
267     // linear case: t = -c/b
268     fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
269                            cName.c_str(), bVar.c_str());
270 
271     // if r(t) > 0, then t will be the x coordinate
272     fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
273                            p2.c_str(), p0.c_str());
274     fragBuilder->codeAppend("\t");
275     this->emitColor(fragBuilder,
276                     uniformHandler,
277                     args.fShaderCaps,
278                     ge,
279                     tName.c_str(),
280                     args.fOutputColor,
281                     args.fInputColor,
282                     args.fTexSamplers);
283     fragBuilder->codeAppend("\t}\n");
284 }
285 
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)286 void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData(
287                     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         pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius);
298         fCachedRadius = radius0;
299         fCachedDiffRadius = diffRadius;
300     }
301 }
302 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)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 
set_matrix_focal_conical(const SkTwoPointConicalGradient & shader,SkMatrix * invLMatrix,SkScalar * focalX)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 
Make(const CreateArgs & args,SkScalar focalX)373     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
374         return sk_sp<GrFragmentProcessor>(
375             new FocalOutside2PtConicalEffect(args, focalX));
376     }
377 
~FocalOutside2PtConicalEffect()378     ~FocalOutside2PtConicalEffect() override {}
379 
name() const380     const char* name() const override {
381         return "Two-Point Conical Gradient Focal Outside";
382     }
383 
isFlipped() const384     bool isFlipped() const { return fIsFlipped; }
focal() const385     SkScalar focal() const { return fFocalX; }
386 
387 private:
388     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
389 
390     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
391 
onIsEqual(const GrFragmentProcessor & sBase) const392     bool onIsEqual(const GrFragmentProcessor& sBase) const override {
393         const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
394         return (INHERITED::onIsEqual(sBase) &&
395                 this->fFocalX == s.fFocalX &&
396                 this->fIsFlipped == s.fIsFlipped);
397     }
398 
IsFlipped(const CreateArgs & args)399     static bool IsFlipped(const CreateArgs& args) {
400         // eww.
401         return static_cast<const SkTwoPointConicalGradient*>(args.fShader)->isFlippedGrad();
402     }
403 
FocalOutside2PtConicalEffect(const CreateArgs & args,SkScalar focalX)404     FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
405             : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */)
406             , fFocalX(focalX)
407             , fIsFlipped(IsFlipped(args)) {
408         this->initClassID<FocalOutside2PtConicalEffect>();
409     }
410 
411     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
412 
413     SkScalar         fFocalX;
414     bool             fIsFlipped;
415 
416     typedef GrGradientEffect INHERITED;
417 };
418 
419 class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
420     : public GrGradientEffect::GLSLProcessor {
421 public:
422     GLSLFocalOutside2PtConicalProcessor(const GrProcessor&);
~GLSLFocalOutside2PtConicalProcessor()423     ~GLSLFocalOutside2PtConicalProcessor() override {}
424 
425     virtual void emitCode(EmitArgs&) override;
426 
427     static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
428 
429 protected:
430     void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
431 
432     UniformHandle fParamUni;
433 
434     const char* fVSVaryingName;
435     const char* fFSVaryingName;
436 
437     bool fIsFlipped;
438 
439     // @{
440     /// Values last uploaded as uniforms
441 
442     SkScalar fCachedFocal;
443 
444     // @}
445 
446 private:
447     typedef GrGradientEffect::GLSLProcessor INHERITED;
448 
449 };
450 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const451 void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
452                                                          GrProcessorKeyBuilder* b) const {
453     FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(*this, caps, b);
454 }
455 
onCreateGLSLInstance() const456 GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
457     return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this);
458 }
459 
460 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
461 
462 /*
463  * All Two point conical gradient test create functions may occasionally create edge case shaders
464  */
465 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)466 sk_sp<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
467     SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
468     SkScalar radius1 = 0.f;
469     SkPoint center2;
470     SkScalar radius2;
471     do {
472         center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
473         // Need to make sure the centers are not the same or else focal point will be inside
474     } while (center1 == center2);
475 
476     SkPoint diff = center2 - center1;
477     SkScalar diffLen = diff.length();
478     // Below makes sure that the focal point is not contained within circle two
479     radius2 = d->fRandom->nextRangeF(0.f, diffLen);
480 
481     RandomGradientParams params(d->fRandom);
482     auto shader = params.fUseColors4f ?
483         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
484                                               params.fColors4f, params.fColorSpace, params.fStops,
485                                               params.fColorCount, params.fTileMode) :
486         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
487                                               params.fColors, params.fStops,
488                                               params.fColorCount, params.fTileMode);
489     GrTest::TestAsFPArgs asFPArgs(d);
490     sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
491     GrAlwaysAssert(fp);
492     return fp;
493 }
494 #endif
495 
496 FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
GLSLFocalOutside2PtConicalProcessor(const GrProcessor & processor)497                             ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor)
498     : fVSVaryingName(nullptr)
499     , fFSVaryingName(nullptr)
500     , fCachedFocal(SK_ScalarMax) {
501     const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
502     fIsFlipped = data.isFlipped();
503 }
504 
emitCode(EmitArgs & args)505 void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
506     const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
507     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
508     this->emitUniforms(uniformHandler, ge);
509     fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
510                                            kVec2f_GrSLType, kDefault_GrSLPrecision,
511                                            "Conical2FSParams");
512     SkString tName("t");
513     SkString p0; // focalX
514     SkString p1; // 1 - focalX * focalX
515 
516     p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
517     p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
518 
519     // if we have a vec3 from being in perspective, convert it to a vec2 first
520     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
521     SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
522     const char* coords2D = coords2DString.c_str();
523 
524     // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
525 
526     // output will default to transparent black (we simply won't write anything
527     // else to it if invalid, instead of discarding or returning prematurely)
528     fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
529 
530     fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
531     fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
532     fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
533 
534     // Must check to see if we flipped the circle order (to make sure start radius < end radius)
535     // If so we must also flip sign on sqrt
536     if (!fIsFlipped) {
537         fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  + sqrt(d);\n", tName.c_str(),
538                                  coords2D, p0.c_str());
539     } else {
540         fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  - sqrt(d);\n", tName.c_str(),
541                                  coords2D, p0.c_str());
542     }
543 
544     fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
545     fragBuilder->codeAppend("\t\t");
546     this->emitColor(fragBuilder,
547                     uniformHandler,
548                     args.fShaderCaps,
549                     ge,
550                     tName.c_str(),
551                     args.fOutputColor,
552                     args.fInputColor,
553                     args.fTexSamplers);
554     fragBuilder->codeAppend("\t}\n");
555 }
556 
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)557 void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData(
558                                             const GrGLSLProgramDataManager& pdman,
559                                             const GrProcessor& 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 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)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 
Make(const CreateArgs & args,SkScalar focalX)587     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
588         return sk_sp<GrFragmentProcessor>(
589             new FocalInside2PtConicalEffect(args, focalX));
590     }
591 
~FocalInside2PtConicalEffect()592     ~FocalInside2PtConicalEffect() override {}
593 
name() const594     const char* name() const override {
595         return "Two-Point Conical Gradient Focal Inside";
596     }
597 
focal() const598     SkScalar focal() const { return fFocalX; }
599 
600     typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor;
601 
602 private:
603     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
604 
605     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
606 
onIsEqual(const GrFragmentProcessor & sBase) const607     bool onIsEqual(const GrFragmentProcessor& sBase) const override {
608         const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
609         return (INHERITED::onIsEqual(sBase) &&
610                 this->fFocalX == s.fFocalX);
611     }
612 
FocalInside2PtConicalEffect(const CreateArgs & args,SkScalar focalX)613     FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
614             : INHERITED(args, args.fShader->colorsAreOpaque()), fFocalX(focalX) {
615         this->initClassID<FocalInside2PtConicalEffect>();
616     }
617 
618     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
619 
620     SkScalar         fFocalX;
621 
622     typedef GrGradientEffect INHERITED;
623 };
624 
625 class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
626     : public GrGradientEffect::GLSLProcessor {
627 public:
628     GLSLFocalInside2PtConicalProcessor(const GrProcessor&);
~GLSLFocalInside2PtConicalProcessor()629     ~GLSLFocalInside2PtConicalProcessor() override {}
630 
631     virtual void emitCode(EmitArgs&) override;
632 
633     static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
634 
635 protected:
636     void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
637 
638     UniformHandle fFocalUni;
639 
640     const char* fVSVaryingName;
641     const char* fFSVaryingName;
642 
643     // @{
644     /// Values last uploaded as uniforms
645 
646     SkScalar fCachedFocal;
647 
648     // @}
649 
650 private:
651     typedef GrGradientEffect::GLSLProcessor INHERITED;
652 
653 };
654 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const655 void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
656                                                         GrProcessorKeyBuilder* b) const {
657     FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(*this, caps, b);
658 }
659 
onCreateGLSLInstance() const660 GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
661     return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this);
662 }
663 
664 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
665 
666 /*
667  * All Two point conical gradient test create functions may occasionally create edge case shaders
668  */
669 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)670 sk_sp<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
671     SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
672     SkScalar radius1 = 0.f;
673     SkPoint center2;
674     SkScalar radius2;
675     do {
676         center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
677         // Below makes sure radius2 is larger enouch such that the focal point
678         // is inside the end circle
679         SkScalar increase = d->fRandom->nextUScalar1();
680         SkPoint diff = center2 - center1;
681         SkScalar diffLen = diff.length();
682         radius2 = diffLen + increase;
683         // If the circles are identical the factory will give us an empty shader.
684     } while (radius1 == radius2 && center1 == center2);
685 
686     RandomGradientParams params(d->fRandom);
687     auto shader = params.fUseColors4f ?
688         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
689                                               params.fColors4f, params.fColorSpace, params.fStops,
690                                               params.fColorCount, params.fTileMode) :
691         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
692                                               params.fColors, params.fStops,
693                                               params.fColorCount, params.fTileMode);
694     GrTest::TestAsFPArgs asFPArgs(d);
695     sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
696     GrAlwaysAssert(fp);
697     return fp;
698 }
699 #endif
700 
701 FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
GLSLFocalInside2PtConicalProcessor(const GrProcessor &)702                            ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&)
703     : fVSVaryingName(nullptr)
704     , fFSVaryingName(nullptr)
705     , fCachedFocal(SK_ScalarMax) {}
706 
emitCode(EmitArgs & args)707 void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) {
708     const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
709     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
710     this->emitUniforms(uniformHandler, ge);
711     fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
712                                            kFloat_GrSLType, kDefault_GrSLPrecision,
713                                            "Conical2FSParams");
714     SkString tName("t");
715 
716     // this is the distance along x-axis from the end center to focal point in
717     // transformed coordinates
718     GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
719 
720     // if we have a vec3 from being in perspective, convert it to a vec2 first
721     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
722     SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
723     const char* coords2D = coords2DString.c_str();
724 
725     // t = p.x * focalX + length(p)
726     fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  + length(%s);\n", tName.c_str(),
727                              coords2D, focal.c_str(), coords2D);
728 
729     this->emitColor(fragBuilder,
730                     uniformHandler,
731                     args.fShaderCaps,
732                     ge,
733                     tName.c_str(),
734                     args.fOutputColor,
735                     args.fInputColor,
736                     args.fTexSamplers);
737 }
738 
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)739 void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData(
740                                             const GrGLSLProgramDataManager& pdman,
741                                             const GrProcessor& 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 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)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
set_matrix_circle_conical(const SkTwoPointConicalGradient & shader,SkMatrix * invLMatrix,CircleConicalInfo * info)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(&centerEndTrans, &centerEnd, 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 
Make(const CreateArgs & args,const CircleConicalInfo & info)826     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
827         return sk_sp<GrFragmentProcessor>(
828             new CircleInside2PtConicalEffect(args, info));
829     }
830 
~CircleInside2PtConicalEffect()831     ~CircleInside2PtConicalEffect() override {}
832 
name() const833     const char* name() const override { return "Two-Point Conical Gradient Inside"; }
834 
centerX() const835     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
centerY() const836     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
A() const837     SkScalar A() const { return fInfo.fA; }
B() const838     SkScalar B() const { return fInfo.fB; }
C() const839     SkScalar C() const { return fInfo.fC; }
840 
841 private:
842     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
843 
844     virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
845                                        GrProcessorKeyBuilder* b) const override;
846 
onIsEqual(const GrFragmentProcessor & sBase) const847     bool onIsEqual(const GrFragmentProcessor& sBase) const override {
848         const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
849         return (INHERITED::onIsEqual(sBase) &&
850                 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
851                 this->fInfo.fA == s.fInfo.fA &&
852                 this->fInfo.fB == s.fInfo.fB &&
853                 this->fInfo.fC == s.fInfo.fC);
854     }
855 
CircleInside2PtConicalEffect(const CreateArgs & args,const CircleConicalInfo & info)856     CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
857             : INHERITED(args, args.fShader->colorsAreOpaque()), fInfo(info) {
858         this->initClassID<CircleInside2PtConicalEffect>();
859     }
860 
861     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
862 
863     const CircleConicalInfo fInfo;
864 
865     typedef GrGradientEffect INHERITED;
866 };
867 
868 class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
869     : public GrGradientEffect::GLSLProcessor {
870 public:
871     GLSLCircleInside2PtConicalProcessor(const GrProcessor&);
~GLSLCircleInside2PtConicalProcessor()872     ~GLSLCircleInside2PtConicalProcessor() override {}
873 
874     virtual void emitCode(EmitArgs&) override;
875 
876     static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
877 
878 protected:
879     void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
880 
881     UniformHandle fCenterUni;
882     UniformHandle fParamUni;
883 
884     const char* fVSVaryingName;
885     const char* fFSVaryingName;
886 
887     // @{
888     /// Values last uploaded as uniforms
889 
890     SkScalar fCachedCenterX;
891     SkScalar fCachedCenterY;
892     SkScalar fCachedA;
893     SkScalar fCachedB;
894     SkScalar fCachedC;
895 
896     // @}
897 
898 private:
899     typedef GrGradientEffect::GLSLProcessor INHERITED;
900 
901 };
902 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const903 void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
904                                                          GrProcessorKeyBuilder* b) const {
905     CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(*this, caps, b);
906 }
907 
onCreateGLSLInstance() const908 GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
909     return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this);
910 }
911 
912 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
913 
914 /*
915  * All Two point conical gradient test create functions may occasionally create edge case shaders
916  */
917 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)918 sk_sp<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
919     SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
920     SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
921     SkPoint center2;
922     SkScalar radius2;
923     do {
924         center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
925         // Below makes sure that circle one is contained within circle two
926         SkScalar increase = d->fRandom->nextUScalar1();
927         SkPoint diff = center2 - center1;
928         SkScalar diffLen = diff.length();
929         radius2 = radius1 + diffLen + increase;
930         // If the circles are identical the factory will give us an empty shader.
931     } while (radius1 == radius2 && center1 == center2);
932 
933     RandomGradientParams params(d->fRandom);
934     auto shader = params.fUseColors4f ?
935         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
936                                               params.fColors4f, params.fColorSpace, params.fStops,
937                                               params.fColorCount, params.fTileMode) :
938         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
939                                               params.fColors, params.fStops,
940                                               params.fColorCount, params.fTileMode);
941     GrTest::TestAsFPArgs asFPArgs(d);
942     sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
943     GrAlwaysAssert(fp);
944     return fp;
945 }
946 #endif
947 
948 CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
GLSLCircleInside2PtConicalProcessor(const GrProcessor & processor)949                             ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor)
950     : fVSVaryingName(nullptr)
951     , fFSVaryingName(nullptr)
952     , fCachedCenterX(SK_ScalarMax)
953     , fCachedCenterY(SK_ScalarMax)
954     , fCachedA(SK_ScalarMax)
955     , fCachedB(SK_ScalarMax)
956     , fCachedC(SK_ScalarMax) {}
957 
emitCode(EmitArgs & args)958 void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) {
959     const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
960     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
961     this->emitUniforms(uniformHandler, ge);
962     fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
963                                             kVec2f_GrSLType, kDefault_GrSLPrecision,
964                                             "Conical2FSCenter");
965     fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
966                                            kVec3f_GrSLType, kDefault_GrSLPrecision,
967                                            "Conical2FSParams");
968     SkString tName("t");
969 
970     GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
971     // params.x = A
972     // params.y = B
973     // params.z = C
974     GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
975 
976     // if we have a vec3 from being in perspective, convert it to a vec2 first
977     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
978     SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
979     const char* coords2D = coords2DString.c_str();
980 
981     // p = coords2D
982     // e = center end
983     // r = radius end
984     // A = dot(e, e) - r^2 + 2 * r - 1
985     // B = (r -1) / A
986     // C = 1 / A
987     // d = dot(e, p) + B
988     // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
989     fragBuilder->codeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
990     fragBuilder->codeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
991                              params.c_str());
992     fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
993                              tName.c_str(), params.c_str(), params.c_str());
994 
995     this->emitColor(fragBuilder,
996                     uniformHandler,
997                     args.fShaderCaps,
998                     ge,
999                     tName.c_str(),
1000                     args.fOutputColor,
1001                     args.fInputColor,
1002                     args.fTexSamplers);
1003 }
1004 
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)1005 void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData(
1006                                             const GrGLSLProgramDataManager& pdman,
1007                                             const GrProcessor& 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 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)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 
Make(const CreateArgs & args,const CircleConicalInfo & info)1042     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
1043         return sk_sp<GrFragmentProcessor>(
1044             new CircleOutside2PtConicalEffect(args, info));
1045     }
1046 
~CircleOutside2PtConicalEffect()1047     ~CircleOutside2PtConicalEffect() override {}
1048 
name() const1049     const char* name() const override { return "Two-Point Conical Gradient Outside"; }
1050 
centerX() const1051     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
centerY() const1052     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
A() const1053     SkScalar A() const { return fInfo.fA; }
B() const1054     SkScalar B() const { return fInfo.fB; }
C() const1055     SkScalar C() const { return fInfo.fC; }
tLimit() const1056     SkScalar tLimit() const { return fTLimit; }
isFlipped() const1057     bool isFlipped() const { return fIsFlipped; }
1058 
1059 private:
1060     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
1061 
1062     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
1063 
onIsEqual(const GrFragmentProcessor & sBase) const1064     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 
CircleOutside2PtConicalEffect(const CreateArgs & args,const CircleConicalInfo & info)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&);
~GLSLCircleOutside2PtConicalProcessor()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 GrProcessor&) 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 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const1137 void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
1138                                                           GrProcessorKeyBuilder* b) const {
1139     CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(*this, caps, b);
1140 }
1141 
onCreateGLSLInstance() const1142 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
TestCreate(GrProcessorTestData * d)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 = shader->asFragmentProcessor(asFPArgs.args());
1178     GrAlwaysAssert(fp);
1179     return fp;
1180 }
1181 #endif
1182 
1183 CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
GLSLCircleOutside2PtConicalProcessor(const GrProcessor & processor)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 
emitCode(EmitArgs & args)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 
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)1261 void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData(
1262                                                 const GrGLSLProgramDataManager& pdman,
1263                                                 const GrProcessor& processor) {
1264     INHERITED::onSetData(pdman, processor);
1265     const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
1266     SkASSERT(data.isFlipped() == fIsFlipped);
1267     SkScalar centerX = data.centerX();
1268     SkScalar centerY = data.centerY();
1269     SkScalar A = data.A();
1270     SkScalar B = data.B();
1271     SkScalar C = data.C();
1272     SkScalar tLimit = data.tLimit();
1273 
1274     if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1275         fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
1276 
1277         pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1278         pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
1279                    SkScalarToFloat(tLimit));
1280 
1281         fCachedCenterX = centerX;
1282         fCachedCenterY = centerY;
1283         fCachedA = A;
1284         fCachedB = B;
1285         fCachedC = C;
1286         fCachedTLimit = tLimit;
1287     }
1288 }
1289 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)1290 void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(
1291                                             const GrProcessor& processor,
1292                                             const GrShaderCaps&, GrProcessorKeyBuilder* b) {
1293     uint32_t* key = b->add32n(2);
1294     key[0] = GenBaseGradientKey(processor);
1295     key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
1296 }
1297 
1298 //////////////////////////////////////////////////////////////////////////////
1299 
Make(const GrGradientEffect::CreateArgs & args)1300 sk_sp<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
1301                                                          const GrGradientEffect::CreateArgs& args) {
1302     const SkTwoPointConicalGradient& shader =
1303         *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
1304 
1305     SkMatrix matrix;
1306     if (!shader.getLocalMatrix().invert(&matrix)) {
1307         return nullptr;
1308     }
1309     if (args.fMatrix) {
1310         SkMatrix inv;
1311         if (!args.fMatrix->invert(&inv)) {
1312             return nullptr;
1313         }
1314         matrix.postConcat(inv);
1315     }
1316 
1317     GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode,
1318                                          std::move(args.fColorSpaceXform), args.fGammaCorrect);
1319 
1320     if (shader.getStartRadius() < kErrorTol) {
1321         SkScalar focalX;
1322         ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1323         if (type == kInside_ConicalType) {
1324             return FocalInside2PtConicalEffect::Make(newArgs, focalX);
1325         } else if(type == kEdge_ConicalType) {
1326             set_matrix_edge_conical(shader, &matrix);
1327             return Edge2PtConicalEffect::Make(newArgs);
1328         } else {
1329             return FocalOutside2PtConicalEffect::Make(newArgs, focalX);
1330         }
1331     }
1332 
1333     CircleConicalInfo info;
1334     ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1335 
1336     if (type == kInside_ConicalType) {
1337         return CircleInside2PtConicalEffect::Make(newArgs, info);
1338     } else if (type == kEdge_ConicalType) {
1339         set_matrix_edge_conical(shader, &matrix);
1340         return Edge2PtConicalEffect::Make(newArgs);
1341     } else {
1342         return CircleOutside2PtConicalEffect::Make(newArgs, info);
1343     }
1344 }
1345 
1346 #endif
1347