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