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