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
16typedef GrGLProgramDataManager::UniformHandle UniformHandle;
17
18static const SkScalar kErrorTol = 0.00001f;
19static 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 */
29enum ConicalType {
30    kInside_ConicalType,
31    kOutside_ConicalType,
32    kEdge_ConicalType,
33};
34
35//////////////////////////////////////////////////////////////////////////////
36
37static 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
57class Edge2PtConicalEffect : public GrGradientEffect {
58public:
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 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.
78    SkScalar center() const { return fCenterX1; }
79    SkScalar diffRadius() const { return fDiffRadius; }
80    SkScalar radius() const { return fRadius0; }
81
82private:
83    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
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
139class GLEdge2PtConicalEffect : public GrGLGradientEffect {
140public:
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&) override;
150    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
151
152    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
153
154protected:
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
168private:
169    typedef GrGLGradientEffect INHERITED;
170
171};
172
173void Edge2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
174                                             GrProcessorKeyBuilder* b) const {
175    GLEdge2PtConicalEffect::GenKey(*this, caps, b);
176}
177
178GrGLFragmentProcessor* Edge2PtConicalEffect::createGLInstance() const {
179    return SkNEW_ARGS(GLEdge2PtConicalEffect, (*this));
180}
181
182GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
183
184/*
185 * All Two point conical gradient test create functions may occasionally create edge case shaders
186 */
187GrFragmentProcessor* 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
225GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrProcessor&)
226    : fVSVaryingName(NULL)
227    , fFSVaryingName(NULL)
228    , fCachedRadius(-SK_ScalarMax)
229    , fCachedDiffRadius(-SK_ScalarMax) {}
230
231void 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
289void 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
311void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
312                                    const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
313    b->add32(GenBaseGradientKey(processor));
314}
315
316//////////////////////////////////////////////////////////////////////////////
317// Focal Conical Gradients
318//////////////////////////////////////////////////////////////////////////////
319
320static 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
378class FocalOutside2PtConicalEffect : public GrGradientEffect {
379public:
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 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
399    bool isFlipped() const { return fIsFlipped; }
400    SkScalar focal() const { return fFocalX; }
401
402private:
403    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
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
427class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
428public:
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&) override;
438    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
439
440    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
441
442protected:
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
457private:
458    typedef GrGLGradientEffect INHERITED;
459
460};
461
462void FocalOutside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
463                                                     GrProcessorKeyBuilder* b) const {
464    GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
465}
466
467GrGLFragmentProcessor* FocalOutside2PtConicalEffect::createGLInstance() const {
468    return SkNEW_ARGS(GLFocalOutside2PtConicalEffect, (*this));
469}
470
471GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
472
473/*
474 * All Two point conical gradient test create functions may occasionally create edge case shaders
475 */
476GrFragmentProcessor* 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
512GLFocalOutside2PtConicalEffect::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
520void 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
569void 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
589void 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
598class GLFocalInside2PtConicalEffect;
599
600class FocalInside2PtConicalEffect : public GrGradientEffect {
601public:
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 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
621    SkScalar focal() const { return fFocalX; }
622
623    typedef GLFocalInside2PtConicalEffect GLProcessor;
624
625private:
626    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
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
648class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
649public:
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&) override;
659    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
660
661    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
662
663protected:
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
676private:
677    typedef GrGLGradientEffect INHERITED;
678
679};
680
681void FocalInside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
682                               GrProcessorKeyBuilder* b) const {
683    GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
684}
685
686GrGLFragmentProcessor* FocalInside2PtConicalEffect::createGLInstance() const {
687    return SkNEW_ARGS(GLFocalInside2PtConicalEffect, (*this));
688}
689
690GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
691
692/*
693 * All Two point conical gradient test create functions may occasionally create edge case shaders
694 */
695GrFragmentProcessor* 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
732GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&)
733    : fVSVaryingName(NULL)
734    , fFSVaryingName(NULL)
735    , fCachedFocal(SK_ScalarMax) {}
736
737void 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
766void 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
778void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
779                                           const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
780    b->add32(GenBaseGradientKey(processor));
781}
782
783//////////////////////////////////////////////////////////////////////////////
784// Circle Conical Gradients
785//////////////////////////////////////////////////////////////////////////////
786
787struct 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
795static 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
847class CircleInside2PtConicalEffect : public GrGradientEffect {
848public:
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 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
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
873private:
874    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
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
899class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
900public:
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&) override;
910    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
911
912    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
913
914protected:
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
932private:
933    typedef GrGLGradientEffect INHERITED;
934
935};
936
937void CircleInside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
938                                                     GrProcessorKeyBuilder* b) const {
939    GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
940}
941
942GrGLFragmentProcessor* CircleInside2PtConicalEffect::createGLInstance() const {
943    return SkNEW_ARGS(GLCircleInside2PtConicalEffect, (*this));
944}
945
946GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
947
948/*
949 * All Two point conical gradient test create functions may occasionally create edge case shaders
950 */
951GrFragmentProcessor* 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
987GLCircleInside2PtConicalEffect::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
996void 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
1040void 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
1064void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
1065                                            const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
1066    b->add32(GenBaseGradientKey(processor));
1067}
1068
1069//////////////////////////////////////////////////////////////////////////////
1070
1071class CircleOutside2PtConicalEffect : public GrGradientEffect {
1072public:
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 override { return "Two-Point Conical Gradient Outside"; }
1085
1086    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
1087
1088    GrGLFragmentProcessor* createGLInstance() const 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
1098private:
1099    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
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
1135class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1136public:
1137    GLCircleOutside2PtConicalEffect(const GrProcessor&);
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
1150protected:
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
1171private:
1172    typedef GrGLGradientEffect INHERITED;
1173
1174};
1175
1176void CircleOutside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
1177                                                      GrProcessorKeyBuilder* b) const {
1178    GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
1179}
1180
1181GrGLFragmentProcessor* CircleOutside2PtConicalEffect::createGLInstance() const {
1182    return SkNEW_ARGS(GLCircleOutside2PtConicalEffect, (*this));
1183}
1184
1185GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
1186
1187/*
1188 * All Two point conical gradient test create functions may occasionally create edge case shaders
1189 */
1190GrFragmentProcessor* 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
1228GLCircleOutside2PtConicalEffect::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
1241void 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
1301void 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
1329void 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
1338GrFragmentProcessor* 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