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