c9f2d2f50ae74e263d05128c3f204757ec46807f
[platform/upstream/libSkiaSharp.git] / src / effects / gradients / SkTwoPointConicalGradient_gpu.cpp
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
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
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
67     virtual ~Edge2PtConicalEffect() {}
68
69     const char* name() const SK_OVERRIDE {
70         return "Two-Point Conical Gradient Edge Touching";
71     }
72
73     void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
74
75     GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
76
77     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
78     SkScalar center() const { return fCenterX1; }
79     SkScalar diffRadius() const { return fDiffRadius; }
80     SkScalar radius() const { return fRadius0; }
81
82 private:
83     bool onIsEqual(const GrFragmentProcessor& sBase) const SK_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
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&);
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&) SK_OVERRIDE;
150     void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
151
152     static void GenKey(const GrProcessor&, const GrGLCaps& 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
173 void Edge2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
174                                              GrProcessorKeyBuilder* b) const {
175     GLEdge2PtConicalEffect::GenKey(*this, caps, b);
176 }
177
178 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  */
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                                                GrProcessorUnitTest::TestMatrix(random), NULL,
221                                                &paintColor, &fp));
222     return fp;
223 }
224
225 GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrProcessor&)
226     : fVSVaryingName(NULL)
227     , fFSVaryingName(NULL)
228     , fCachedRadius(-SK_ScalarMax)
229     , fCachedDiffRadius(-SK_ScalarMax) {}
230
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     GrGLFPFragmentBuilder* 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
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
311 void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
312                                     const GrGLCaps&, GrProcessorKeyBuilder* b) {
313     b->add32(GenBaseGradientKey(processor));
314 }
315
316 //////////////////////////////////////////////////////////////////////////////
317 // Focal Conical Gradients
318 //////////////////////////////////////////////////////////////////////////////
319
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 = SkScalarDiv(1.f, 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
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
389     virtual ~FocalOutside2PtConicalEffect() { }
390
391     const char* name() const SK_OVERRIDE {
392         return "Two-Point Conical Gradient Focal Outside";
393     }
394
395     void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
396
397     GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
398
399     bool isFlipped() const { return fIsFlipped; }
400     SkScalar focal() const { return fFocalX; }
401
402 private:
403     bool onIsEqual(const GrFragmentProcessor& sBase) const SK_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
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&);
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&) SK_OVERRIDE;
438     void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
439
440     static void GenKey(const GrProcessor&, const GrGLCaps& 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
462 void FocalOutside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
463                                                      GrProcessorKeyBuilder* b) const {
464     GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
465 }
466
467 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  */
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                                                GrProcessorUnitTest::TestMatrix(random), NULL,
508                                                &paintColor, &effect));
509     return effect;
510 }
511
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
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     GrGLFPFragmentBuilder* 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
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
589 void GLFocalOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
590                                             const GrGLCaps&, 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
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
611     virtual ~FocalInside2PtConicalEffect() {}
612
613     const char* name() const SK_OVERRIDE {
614         return "Two-Point Conical Gradient Focal Inside";
615     }
616
617     void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
618
619     GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
620
621     SkScalar focal() const { return fFocalX; }
622
623     typedef GLFocalInside2PtConicalEffect GLProcessor;
624
625 private:
626     bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
627         const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
628         return (INHERITED::onIsEqual(sBase) &&
629                 this->fFocalX == s.fFocalX);
630     }
631
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&);
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&) SK_OVERRIDE;
659     void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
660
661     static void GenKey(const GrProcessor&, const GrGLCaps& 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
681 void FocalInside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
682                                GrProcessorKeyBuilder* b) const {
683     GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
684 }
685
686 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  */
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                                                GrProcessorUnitTest::TestMatrix(random), NULL,
728                                                &paintColor, &fp));
729     return fp;
730 }
731
732 GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&)
733     : fVSVaryingName(NULL)
734     , fFSVaryingName(NULL)
735     , fCachedFocal(SK_ScalarMax) {}
736
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     GrGLFPFragmentBuilder* 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
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
778 void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
779                                            const GrGLCaps&, 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
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
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
858     virtual ~CircleInside2PtConicalEffect() {}
859
860     const char* name() const SK_OVERRIDE { return "Two-Point Conical Gradient Inside"; }
861
862     virtual void getGLProcessorKey(const GrGLCaps& caps,
863                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
864
865     GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
866
867     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
868     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
869     SkScalar A() const { return fInfo.fA; }
870     SkScalar B() const { return fInfo.fB; }
871     SkScalar C() const { return fInfo.fC; }
872
873 private:
874     bool onIsEqual(const GrFragmentProcessor& sBase) const SK_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
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&);
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&) SK_OVERRIDE;
910     void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
911
912     static void GenKey(const GrProcessor&, const GrGLCaps& 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
937 void CircleInside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
938                                                      GrProcessorKeyBuilder* b) const {
939     GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
940 }
941
942 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  */
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                                                GrProcessorUnitTest::TestMatrix(random), NULL,
983                                                &paintColor, &processor));
984     return processor;
985 }
986
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
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     GrGLFPFragmentBuilder* 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
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
1064 void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
1065                                             const GrGLCaps&, GrProcessorKeyBuilder* b) {
1066     b->add32(GenBaseGradientKey(processor));
1067 }
1068
1069 //////////////////////////////////////////////////////////////////////////////
1070
1071 class CircleOutside2PtConicalEffect : public GrGradientEffect {
1072 public:
1073
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
1082     virtual ~CircleOutside2PtConicalEffect() {}
1083
1084     const char* name() const SK_OVERRIDE { return "Two-Point Conical Gradient Outside"; }
1085
1086     void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
1087
1088     GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
1089
1090     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1091     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1092     SkScalar A() const { return fInfo.fA; }
1093     SkScalar B() const { return fInfo.fB; }
1094     SkScalar C() const { return fInfo.fC; }
1095     SkScalar tLimit() const { return fTLimit; }
1096     bool isFlipped() const { return fIsFlipped; }
1097
1098 private:
1099     bool onIsEqual(const GrFragmentProcessor& sBase) const SK_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
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 = SkScalarDiv(shader.getStartRadius(),
1119                                   (shader.getStartRadius() - shader.getEndRadius()));
1120         } else {
1121             fTLimit = SK_ScalarMin;
1122         }
1123
1124         fIsFlipped = shader.isFlippedGrad();
1125     }
1126
1127     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
1128
1129     const CircleConicalInfo fInfo;
1130     SkScalar fTLimit;
1131     bool fIsFlipped;
1132
1133     typedef GrGradientEffect INHERITED;
1134 };
1135
1136 class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1137 public:
1138     GLCircleOutside2PtConicalEffect(const GrProcessor&);
1139     virtual ~GLCircleOutside2PtConicalEffect() {}
1140
1141     virtual void emitCode(GrGLFPBuilder*,
1142                           const GrFragmentProcessor&,
1143                           const char* outputColor,
1144                           const char* inputColor,
1145                           const TransformedCoordsArray&,
1146                           const TextureSamplerArray&) SK_OVERRIDE;
1147     void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
1148
1149     static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
1150
1151 protected:
1152     UniformHandle fCenterUni;
1153     UniformHandle fParamUni;
1154
1155     const char* fVSVaryingName;
1156     const char* fFSVaryingName;
1157
1158     bool fIsFlipped;
1159
1160     // @{
1161     /// Values last uploaded as uniforms
1162
1163     SkScalar fCachedCenterX;
1164     SkScalar fCachedCenterY;
1165     SkScalar fCachedA;
1166     SkScalar fCachedB;
1167     SkScalar fCachedC;
1168     SkScalar fCachedTLimit;
1169
1170     // @}
1171
1172 private:
1173     typedef GrGLGradientEffect INHERITED;
1174
1175 };
1176
1177 void CircleOutside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
1178                                                       GrProcessorKeyBuilder* b) const {
1179     GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
1180 }
1181
1182 GrGLFragmentProcessor* CircleOutside2PtConicalEffect::createGLInstance() const {
1183     return SkNEW_ARGS(GLCircleOutside2PtConicalEffect, (*this));
1184 }
1185
1186 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
1187
1188 /*
1189  * All Two point conical gradient test create functions may occasionally create edge case shaders
1190  */
1191 GrFragmentProcessor* CircleOutside2PtConicalEffect::TestCreate(SkRandom* random,
1192                                                                GrContext* context,
1193                                                                const GrDrawTargetCaps&,
1194                                                                GrTexture**) {
1195     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
1196     SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0
1197     SkPoint center2;
1198     SkScalar radius2;
1199     SkScalar diffLen;
1200     do {
1201         center2.set(random->nextUScalar1(), random->nextUScalar1());
1202         // If the circles share a center than we can't be in the outside case
1203     } while (center1 == center2);
1204     SkPoint diff = center2 - center1;
1205     diffLen = diff.length();
1206     // Below makes sure that circle one is not contained within circle two
1207     // and have radius2 >= radius to match sorting on cpu side
1208     radius2 = radius1 + random->nextRangeF(0.f, diffLen);
1209
1210     SkColor colors[kMaxRandomGradientColors];
1211     SkScalar stopsArray[kMaxRandomGradientColors];
1212     SkScalar* stops = stopsArray;
1213     SkShader::TileMode tm;
1214     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
1215     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
1216                                                                           center2, radius2,
1217                                                                           colors, stops, colorCount,
1218                                                                           tm));
1219     SkPaint paint;
1220     GrColor paintColor;
1221     GrFragmentProcessor* processor;
1222
1223     SkAssertResult(shader->asFragmentProcessor(context, paint,
1224                                                GrProcessorUnitTest::TestMatrix(random), NULL,
1225                                                &paintColor, &processor));
1226     return processor;
1227 }
1228
1229 GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrProcessor& processor)
1230     : fVSVaryingName(NULL)
1231     , fFSVaryingName(NULL)
1232     , fCachedCenterX(SK_ScalarMax)
1233     , fCachedCenterY(SK_ScalarMax)
1234     , fCachedA(SK_ScalarMax)
1235     , fCachedB(SK_ScalarMax)
1236     , fCachedC(SK_ScalarMax)
1237     , fCachedTLimit(SK_ScalarMax) {
1238     const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
1239     fIsFlipped = data.isFlipped();
1240     }
1241
1242 void GLCircleOutside2PtConicalEffect::emitCode(GrGLFPBuilder* builder,
1243                                                const GrFragmentProcessor& fp,
1244                                                const char* outputColor,
1245                                                const char* inputColor,
1246                                                const TransformedCoordsArray& coords,
1247                                                const TextureSamplerArray& samplers) {
1248     const CircleOutside2PtConicalEffect& ge = fp.cast<CircleOutside2PtConicalEffect>();
1249     this->emitUniforms(builder, ge);
1250     fCenterUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
1251                                      kVec2f_GrSLType, kDefault_GrSLPrecision,
1252                                      "Conical2FSCenter");
1253     fParamUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
1254                                     kVec4f_GrSLType, kDefault_GrSLPrecision,
1255                                     "Conical2FSParams");
1256     SkString tName("t");
1257
1258     GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
1259     // params.x = A
1260     // params.y = B
1261     // params.z = C
1262     GrGLShaderVar params = builder->getUniformVariable(fParamUni);
1263
1264     // if we have a vec3 from being in perspective, convert it to a vec2 first
1265     GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
1266     SkString coords2DString = fsBuilder->ensureFSCoords2D(coords, 0);
1267     const char* coords2D = coords2DString.c_str();
1268
1269     // output will default to transparent black (we simply won't write anything
1270     // else to it if invalid, instead of discarding or returning prematurely)
1271     fsBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
1272
1273     // p = coords2D
1274     // e = center end
1275     // r = radius end
1276     // A = dot(e, e) - r^2 + 2 * r - 1
1277     // B = (r -1) / A
1278     // C = 1 / A
1279     // d = dot(e, p) + B
1280     // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
1281
1282     fsBuilder->codeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
1283     fsBuilder->codeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
1284                            params.c_str());
1285     fsBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
1286                            params.c_str());
1287
1288     // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1289     // If so we must also flip sign on sqrt
1290     if (!fIsFlipped) {
1291         fsBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
1292     } else {
1293         fsBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
1294     }
1295
1296     fsBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str());
1297     fsBuilder->codeAppend("\t\t");
1298     this->emitColor(builder, ge, tName.c_str(), outputColor, inputColor, samplers);
1299     fsBuilder->codeAppend("\t}\n");
1300 }
1301
1302 void GLCircleOutside2PtConicalEffect::setData(const GrGLProgramDataManager& pdman,
1303                                               const GrProcessor& processor) {
1304     INHERITED::setData(pdman, processor);
1305     const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
1306     SkASSERT(data.isFlipped() == fIsFlipped);
1307     SkScalar centerX = data.centerX();
1308     SkScalar centerY = data.centerY();
1309     SkScalar A = data.A();
1310     SkScalar B = data.B();
1311     SkScalar C = data.C();
1312     SkScalar tLimit = data.tLimit();
1313
1314     if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1315         fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
1316
1317         pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1318         pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
1319                    SkScalarToFloat(tLimit));
1320
1321         fCachedCenterX = centerX;
1322         fCachedCenterY = centerY;
1323         fCachedA = A;
1324         fCachedB = B;
1325         fCachedC = C;
1326         fCachedTLimit = tLimit;
1327     }
1328 }
1329
1330 void GLCircleOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
1331                                              const GrGLCaps&, GrProcessorKeyBuilder* b) {
1332     uint32_t* key = b->add32n(2);
1333     key[0] = GenBaseGradientKey(processor);
1334     key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
1335 }
1336
1337 //////////////////////////////////////////////////////////////////////////////
1338
1339 GrFragmentProcessor* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1340                                                         const SkTwoPointConicalGradient& shader,
1341                                                         SkShader::TileMode tm,
1342                                                         const SkMatrix* localMatrix) {
1343     SkMatrix matrix;
1344     if (!shader.getLocalMatrix().invert(&matrix)) {
1345         return NULL;
1346     }
1347     if (localMatrix) {
1348         SkMatrix inv;
1349         if (!localMatrix->invert(&inv)) {
1350             return NULL;
1351         }
1352         matrix.postConcat(inv);
1353     }
1354
1355     if (shader.getStartRadius() < kErrorTol) {
1356         SkScalar focalX;
1357         ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1358         if (type == kInside_ConicalType) {
1359             return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
1360         } else if(type == kEdge_ConicalType) {
1361             set_matrix_edge_conical(shader, &matrix);
1362             return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
1363         } else {
1364             return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
1365         }
1366     }
1367
1368     CircleConicalInfo info;
1369     ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1370
1371     if (type == kInside_ConicalType) {
1372         return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
1373     } else if (type == kEdge_ConicalType) {
1374         set_matrix_edge_conical(shader, &matrix);
1375         return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
1376     } else {
1377         return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
1378     }
1379 }
1380
1381 #endif