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