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