207f3db65377bfc6a2aa206a6c000bdf62db37e9
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / effects / gradients / SkTwoPointRadialGradient.cpp
1
2 /*
3  * Copyright 2012 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 "SkTwoPointRadialGradient.h"
10
11 /* Two-point radial gradients are specified by two circles, each with a center
12    point and radius.  The gradient can be considered to be a series of
13    concentric circles, with the color interpolated from the start circle
14    (at t=0) to the end circle (at t=1).
15
16    For each point (x, y) in the span, we want to find the
17    interpolated circle that intersects that point.  The center
18    of the desired circle (Cx, Cy) falls at some distance t
19    along the line segment between the start point (Sx, Sy) and
20    end point (Ex, Ey):
21
22       Cx = (1 - t) * Sx + t * Ex        (0 <= t <= 1)
23       Cy = (1 - t) * Sy + t * Ey
24
25    The radius of the desired circle (r) is also a linear interpolation t
26    between the start and end radii (Sr and Er):
27
28       r = (1 - t) * Sr + t * Er
29
30    But
31
32       (x - Cx)^2 + (y - Cy)^2 = r^2
33
34    so
35
36      (x - ((1 - t) * Sx + t * Ex))^2
37    + (y - ((1 - t) * Sy + t * Ey))^2
38    = ((1 - t) * Sr + t * Er)^2
39
40    Solving for t yields
41
42      [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
43    + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
44    + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
45
46    To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
47
48      [Dx^2 + Dy^2 - Dr^2)] * t^2
49    + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
50    + [dx^2 + dy^2 - Sr^2] = 0
51
52    A quadratic in t.  The two roots of the quadratic reflect the two
53    possible circles on which the point may fall.  Solving for t yields
54    the gradient value to use.
55
56    If a<0, the start circle is entirely contained in the
57    end circle, and one of the roots will be <0 or >1 (off the line
58    segment).  If a>0, the start circle falls at least partially
59    outside the end circle (or vice versa), and the gradient
60    defines a "tube" where a point may be on one circle (on the
61    inside of the tube) or the other (outside of the tube).  We choose
62    one arbitrarily.
63
64    In order to keep the math to within the limits of fixed point,
65    we divide the entire quadratic by Dr^2, and replace
66    (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
67
68    [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
69    + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
70    + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
71
72    (x' and y' are computed by appending the subtract and scale to the
73    fDstToIndex matrix in the constructor).
74
75    Since the 'A' component of the quadratic is independent of x' and y', it
76    is precomputed in the constructor.  Since the 'B' component is linear in
77    x' and y', if x and y are linear in the span, 'B' can be computed
78    incrementally with a simple delta (db below).  If it is not (e.g.,
79    a perspective projection), it must be computed in the loop.
80
81 */
82
83 namespace {
84
85 inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
86                                 SkScalar sr2d2, SkScalar foura,
87                                 SkScalar oneOverTwoA, bool posRoot) {
88     SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
89     if (0 == foura) {
90         return SkScalarToFixed(SkScalarDiv(-c, b));
91     }
92
93     SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
94     if (discrim < 0) {
95         discrim = -discrim;
96     }
97     SkScalar rootDiscrim = SkScalarSqrt(discrim);
98     SkScalar result;
99     if (posRoot) {
100         result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
101     } else {
102         result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
103     }
104     return SkScalarToFixed(result);
105 }
106
107 typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
108         SkScalar fy, SkScalar dy,
109         SkScalar b, SkScalar db,
110         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
111         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
112         int count);
113
114 void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
115         SkScalar fy, SkScalar dy,
116         SkScalar b, SkScalar db,
117         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
118         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
119         int count) {
120     for (; count > 0; --count) {
121         SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
122                                      fOneOverTwoA, posRoot);
123         SkFixed index = SkClampMax(t, 0xFFFF);
124         SkASSERT(index <= 0xFFFF);
125         *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
126         fx += dx;
127         fy += dy;
128         b += db;
129     }
130 }
131 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
132         SkScalar fy, SkScalar dy,
133         SkScalar b, SkScalar db,
134         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
135         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
136         int count) {
137     for (; count > 0; --count) {
138         SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
139                                      fOneOverTwoA, posRoot);
140         SkFixed index = mirror_tileproc(t);
141         SkASSERT(index <= 0xFFFF);
142         *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
143         fx += dx;
144         fy += dy;
145         b += db;
146     }
147 }
148
149 void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
150         SkScalar fy, SkScalar dy,
151         SkScalar b, SkScalar db,
152         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
153         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
154         int count) {
155     for (; count > 0; --count) {
156         SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
157                                      fOneOverTwoA, posRoot);
158         SkFixed index = repeat_tileproc(t);
159         SkASSERT(index <= 0xFFFF);
160         *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
161         fx += dx;
162         fy += dy;
163         b += db;
164     }
165 }
166 }
167
168 /////////////////////////////////////////////////////////////////////
169
170 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
171     const SkPoint& start, SkScalar startRadius,
172     const SkPoint& end, SkScalar endRadius,
173     const Descriptor& desc)
174     : SkGradientShaderBase(desc),
175       fCenter1(start),
176       fCenter2(end),
177       fRadius1(startRadius),
178       fRadius2(endRadius) {
179     init();
180 }
181
182 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
183     SkBitmap* bitmap,
184     SkMatrix* matrix,
185     SkShader::TileMode* xy) const {
186     if (bitmap) {
187         this->getGradientTableBitmap(bitmap);
188     }
189     SkScalar diffL = 0; // just to avoid gcc warning
190     if (matrix) {
191         diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
192                              SkScalarSquare(fDiff.fY));
193     }
194     if (matrix) {
195         if (diffL) {
196             SkScalar invDiffL = SkScalarInvert(diffL);
197             matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
198                               SkScalarMul(invDiffL, fDiff.fX));
199         } else {
200             matrix->reset();
201         }
202         matrix->preConcat(fPtsToUnit);
203     }
204     if (xy) {
205         xy[0] = fTileMode;
206         xy[1] = kClamp_TileMode;
207     }
208     return kTwoPointRadial_BitmapType;
209 }
210
211 SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
212     SkShader::GradientInfo* info) const {
213     if (info) {
214         commonAsAGradient(info);
215         info->fPoint[0] = fCenter1;
216         info->fPoint[1] = fCenter2;
217         info->fRadius[0] = fRadius1;
218         info->fRadius[1] = fRadius2;
219     }
220     return kRadial2_GradientType;
221 }
222
223 void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
224                                          int count) {
225     SkASSERT(count > 0);
226
227     SkPMColor* SK_RESTRICT dstC = dstCParam;
228
229     // Zero difference between radii:  fill with transparent black.
230     if (fDiffRadius == 0) {
231       sk_bzero(dstC, count * sizeof(*dstC));
232       return;
233     }
234     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
235     TileProc            proc = fTileProc;
236     const SkPMColor* SK_RESTRICT cache = this->getCache32();
237
238     SkScalar foura = fA * 4;
239     bool posRoot = fDiffRadius < 0;
240     if (fDstToIndexClass != kPerspective_MatrixClass) {
241         SkPoint srcPt;
242         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
243                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
244         SkScalar dx, fx = srcPt.fX;
245         SkScalar dy, fy = srcPt.fY;
246
247         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
248             SkFixed fixedX, fixedY;
249             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
250             dx = SkFixedToScalar(fixedX);
251             dy = SkFixedToScalar(fixedY);
252         } else {
253             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
254             dx = fDstToIndex.getScaleX();
255             dy = fDstToIndex.getSkewY();
256         }
257         SkScalar b = (SkScalarMul(fDiff.fX, fx) +
258                      SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
259         SkScalar db = (SkScalarMul(fDiff.fX, dx) +
260                       SkScalarMul(fDiff.fY, dy)) * 2;
261
262         TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
263         if (SkShader::kClamp_TileMode == fTileMode) {
264             shadeProc = shadeSpan_twopoint_clamp;
265         } else if (SkShader::kMirror_TileMode == fTileMode) {
266             shadeProc = shadeSpan_twopoint_mirror;
267         } else {
268             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
269         }
270         (*shadeProc)(fx, dx, fy, dy, b, db,
271                      fSr2D2, foura, fOneOverTwoA, posRoot,
272                      dstC, cache, count);
273     } else {    // perspective case
274         SkScalar dstX = SkIntToScalar(x);
275         SkScalar dstY = SkIntToScalar(y);
276         for (; count > 0; --count) {
277             SkPoint             srcPt;
278             dstProc(fDstToIndex, dstX, dstY, &srcPt);
279             SkScalar fx = srcPt.fX;
280             SkScalar fy = srcPt.fY;
281             SkScalar b = (SkScalarMul(fDiff.fX, fx) +
282                          SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
283             SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
284                                          fOneOverTwoA, posRoot);
285             SkFixed index = proc(t);
286             SkASSERT(index <= 0xFFFF);
287             *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
288             dstX += SK_Scalar1;
289         }
290     }
291 }
292
293 bool SkTwoPointRadialGradient::setContext( const SkBitmap& device,
294                                           const SkPaint& paint,
295                                           const SkMatrix& matrix){
296     // For now, we might have divided by zero, so detect that
297     if (0 == fDiffRadius) {
298         return false;
299     }
300
301     if (!this->INHERITED::setContext(device, paint, matrix)) {
302         return false;
303     }
304
305     // we don't have a span16 proc
306     fFlags &= ~kHasSpan16_Flag;
307     return true;
308 }
309
310 #ifdef SK_DEVELOPER
311 void SkTwoPointRadialGradient::toString(SkString* str) const {
312     str->append("SkTwoPointRadialGradient: (");
313
314     str->append("center1: (");
315     str->appendScalar(fCenter1.fX);
316     str->append(", ");
317     str->appendScalar(fCenter1.fY);
318     str->append(") radius1: ");
319     str->appendScalar(fRadius1);
320     str->append(" ");
321
322     str->append("center2: (");
323     str->appendScalar(fCenter2.fX);
324     str->append(", ");
325     str->appendScalar(fCenter2.fY);
326     str->append(") radius2: ");
327     str->appendScalar(fRadius2);
328     str->append(" ");
329
330     this->INHERITED::toString(str);
331
332     str->append(")");
333 }
334 #endif
335
336 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
337     SkFlattenableReadBuffer& buffer)
338     : INHERITED(buffer),
339       fCenter1(buffer.readPoint()),
340       fCenter2(buffer.readPoint()),
341       fRadius1(buffer.readScalar()),
342       fRadius2(buffer.readScalar()) {
343     init();
344 };
345
346 void SkTwoPointRadialGradient::flatten(
347     SkFlattenableWriteBuffer& buffer) const {
348     this->INHERITED::flatten(buffer);
349     buffer.writePoint(fCenter1);
350     buffer.writePoint(fCenter2);
351     buffer.writeScalar(fRadius1);
352     buffer.writeScalar(fRadius2);
353 }
354
355 void SkTwoPointRadialGradient::init() {
356     fDiff = fCenter1 - fCenter2;
357     fDiffRadius = fRadius2 - fRadius1;
358     // hack to avoid zero-divide for now
359     SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
360     fDiff.fX = SkScalarMul(fDiff.fX, inv);
361     fDiff.fY = SkScalarMul(fDiff.fY, inv);
362     fStartRadius = SkScalarMul(fRadius1, inv);
363     fSr2D2 = SkScalarSquare(fStartRadius);
364     fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
365     fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
366
367     fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
368     fPtsToUnit.postScale(inv, inv);
369 }
370
371 /////////////////////////////////////////////////////////////////////
372
373 #if SK_SUPPORT_GPU
374
375 #include "GrTBackendEffectFactory.h"
376
377 // For brevity
378 typedef GrGLUniformManager::UniformHandle UniformHandle;
379
380 class GrGLRadial2Gradient : public GrGLGradientEffect {
381
382 public:
383
384     GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
385     virtual ~GrGLRadial2Gradient() { }
386
387     virtual void emitCode(GrGLShaderBuilder*,
388                           const GrDrawEffect&,
389                           EffectKey,
390                           const char* outputColor,
391                           const char* inputColor,
392                           const TransformedCoordsArray&,
393                           const TextureSamplerArray&) SK_OVERRIDE;
394     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
395
396     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
397
398 protected:
399
400     UniformHandle fParamUni;
401
402     const char* fVSVaryingName;
403     const char* fFSVaryingName;
404
405     bool fIsDegenerate;
406
407     // @{
408     /// Values last uploaded as uniforms
409
410     SkScalar fCachedCenter;
411     SkScalar fCachedRadius;
412     bool     fCachedPosRoot;
413
414     // @}
415
416 private:
417
418     typedef GrGLGradientEffect INHERITED;
419
420 };
421
422 /////////////////////////////////////////////////////////////////////
423
424 class GrRadial2Gradient : public GrGradientEffect {
425 public:
426     static GrEffectRef* Create(GrContext* ctx,
427                                const SkTwoPointRadialGradient& shader,
428                                const SkMatrix& matrix,
429                                SkShader::TileMode tm) {
430         AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
431         return CreateEffectRef(effect);
432     }
433
434     virtual ~GrRadial2Gradient() { }
435
436     static const char* Name() { return "Two-Point Radial Gradient"; }
437     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
438         return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
439     }
440
441     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
442     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
443     SkScalar center() const { return fCenterX1; }
444     SkScalar radius() const { return fRadius0; }
445     bool isPosRoot() const { return SkToBool(fPosRoot); }
446
447     typedef GrGLRadial2Gradient GLEffect;
448
449 private:
450     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
451         const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
452         return (INHERITED::onIsEqual(sBase) &&
453                 this->fCenterX1 == s.fCenterX1 &&
454                 this->fRadius0 == s.fRadius0 &&
455                 this->fPosRoot == s.fPosRoot);
456     }
457
458     GrRadial2Gradient(GrContext* ctx,
459                       const SkTwoPointRadialGradient& shader,
460                       const SkMatrix& matrix,
461                       SkShader::TileMode tm)
462         : INHERITED(ctx, shader, matrix, tm)
463         , fCenterX1(shader.getCenterX1())
464         , fRadius0(shader.getStartRadius())
465         , fPosRoot(shader.getDiffRadius() < 0) {
466         // We pass the linear part of the quadratic as a varying.
467         //    float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
468         fBTransform = this->getCoordTransform();
469         SkMatrix& bMatrix = *fBTransform.accessMatrix();
470         bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
471                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
472         bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
473                                           SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
474         bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
475                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
476         this->addCoordTransform(&fBTransform);
477     }
478
479     GR_DECLARE_EFFECT_TEST;
480
481     // @{
482     // Cache of values - these can change arbitrarily, EXCEPT
483     // we shouldn't change between degenerate and non-degenerate?!
484
485     GrCoordTransform fBTransform;
486     SkScalar         fCenterX1;
487     SkScalar         fRadius0;
488     SkBool8          fPosRoot;
489
490     // @}
491
492     typedef GrGradientEffect INHERITED;
493 };
494
495 /////////////////////////////////////////////////////////////////////
496
497 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
498
499 GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
500                                            GrContext* context,
501                                            const GrDrawTargetCaps&,
502                                            GrTexture**) {
503     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
504     SkScalar radius1 = random->nextUScalar1();
505     SkPoint center2;
506     SkScalar radius2;
507     do {
508         center2.set(random->nextUScalar1(), random->nextUScalar1());
509         radius2 = random->nextUScalar1 ();
510         // There is a bug in two point radial gradients with identical radii
511     } while (radius1 == radius2);
512
513     SkColor colors[kMaxRandomGradientColors];
514     SkScalar stopsArray[kMaxRandomGradientColors];
515     SkScalar* stops = stopsArray;
516     SkShader::TileMode tm;
517     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
518     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
519                                                                          center2, radius2,
520                                                                          colors, stops, colorCount,
521                                                                          tm));
522     SkPaint paint;
523     return shader->asNewEffect(context, paint);
524 }
525
526 /////////////////////////////////////////////////////////////////////
527
528 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
529                                          const GrDrawEffect& drawEffect)
530     : INHERITED(factory)
531     , fVSVaryingName(NULL)
532     , fFSVaryingName(NULL)
533     , fCachedCenter(SK_ScalarMax)
534     , fCachedRadius(-SK_ScalarMax)
535     , fCachedPosRoot(0) {
536
537     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
538     fIsDegenerate = data.isDegenerate();
539 }
540
541 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
542                                    const GrDrawEffect& drawEffect,
543                                    EffectKey key,
544                                    const char* outputColor,
545                                    const char* inputColor,
546                                    const TransformedCoordsArray& coords,
547                                    const TextureSamplerArray& samplers) {
548
549     this->emitUniforms(builder, key);
550     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
551                                          kFloat_GrSLType, "Radial2FSParams", 6);
552
553     SkString cName("c");
554     SkString ac4Name("ac4");
555     SkString rootName("root");
556     SkString t;
557     SkString p0;
558     SkString p1;
559     SkString p2;
560     SkString p3;
561     SkString p4;
562     SkString p5;
563     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
564     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
565     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
566     builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
567     builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
568     builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
569
570     // We interpolate the linear component in coords[1].
571     SkASSERT(coords[0].type() == coords[1].type());
572     const char* coords2D;
573     SkString bVar;
574     if (kVec3f_GrSLType == coords[0].type()) {
575         builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
576                                coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
577         coords2D = "interpolants.xy";
578         bVar = "interpolants.z";
579     } else {
580         coords2D = coords[0].c_str();
581         bVar.printf("%s.x", coords[1].c_str());
582     }
583
584     // c = (x^2)+(y^2) - params[4]
585     builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
586                            cName.c_str(), coords2D, coords2D, p4.c_str());
587
588     // If we aren't degenerate, emit some extra code, and accept a slightly
589     // more complex coord.
590     if (!fIsDegenerate) {
591
592         // ac4 = 4.0 * params[0] * c
593         builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
594                                ac4Name.c_str(), p0.c_str(),
595                                cName.c_str());
596
597         // root = sqrt(b^2-4ac)
598         // (abs to avoid exception due to fp precision)
599         builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
600                                rootName.c_str(), bVar.c_str(), bVar.c_str(),
601                                ac4Name.c_str());
602
603         // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
604         t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
605                  rootName.c_str(), p1.c_str());
606     } else {
607         // t is: -c/b
608         t.printf("-%s / %s", cName.c_str(), bVar.c_str());
609     }
610
611     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
612 }
613
614 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
615                                   const GrDrawEffect& drawEffect) {
616     INHERITED::setData(uman, drawEffect);
617     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
618     SkASSERT(data.isDegenerate() == fIsDegenerate);
619     SkScalar centerX1 = data.center();
620     SkScalar radius0 = data.radius();
621     if (fCachedCenter != centerX1 ||
622         fCachedRadius != radius0 ||
623         fCachedPosRoot != data.isPosRoot()) {
624
625         SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
626
627         // When we're in the degenerate (linear) case, the second
628         // value will be INF but the program doesn't read it. (We
629         // use the same 6 uniforms even though we don't need them
630         // all in the linear case just to keep the code complexity
631         // down).
632         float values[6] = {
633             SkScalarToFloat(a),
634             1 / (2.f * SkScalarToFloat(a)),
635             SkScalarToFloat(centerX1),
636             SkScalarToFloat(radius0),
637             SkScalarToFloat(SkScalarMul(radius0, radius0)),
638             data.isPosRoot() ? 1.f : -1.f
639         };
640
641         uman.set1fv(fParamUni, 6, values);
642         fCachedCenter = centerX1;
643         fCachedRadius = radius0;
644         fCachedPosRoot = data.isPosRoot();
645     }
646 }
647
648 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
649                                                   const GrGLCaps&) {
650     enum {
651         kIsDegenerate = 1 << kBaseKeyBitCnt,
652     };
653
654     EffectKey key = GenBaseGradientKey(drawEffect);
655     if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
656         key |= kIsDegenerate;
657     }
658     return key;
659 }
660
661 /////////////////////////////////////////////////////////////////////
662
663 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
664     SkASSERT(NULL != context);
665     // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
666     SkMatrix matrix;
667     if (!this->getLocalMatrix().invert(&matrix)) {
668         return NULL;
669     }
670     matrix.postConcat(fPtsToUnit);
671
672     SkScalar diffLen = fDiff.length();
673     if (0 != diffLen) {
674         SkScalar invDiffLen = SkScalarInvert(diffLen);
675         SkMatrix rot;
676         rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
677                        SkScalarMul(invDiffLen, fDiff.fX));
678         matrix.postConcat(rot);
679     }
680
681     return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
682 }
683
684 #else
685
686 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
687     SkDEBUGFAIL("Should not call in GPU-less build");
688     return NULL;
689 }
690
691 #endif