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