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
83namespace {
84
85inline 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
107typedef 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
114void 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}
131void 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
149void 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
170SkTwoPointRadialGradient::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
182SkShader::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
211SkShader::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
223void 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
293bool 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
311void 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
336SkTwoPointRadialGradient::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
346void 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
355void 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
378typedef GrGLUniformManager::UniformHandle UniformHandle;
379
380class GrGLRadial2Gradient : public GrGLGradientEffect {
381
382public:
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
398protected:
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
416private:
417
418    typedef GrGLGradientEffect INHERITED;
419
420};
421
422/////////////////////////////////////////////////////////////////////
423
424class GrRadial2Gradient : public GrGradientEffect {
425public:
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
449private:
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
497GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
498
499GrEffectRef* 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
528GrGLRadial2Gradient::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
541void 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
614void 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
648GrGLEffect::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
663GrEffectRef* 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
686GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
687    SkDEBUGFAIL("Should not call in GPU-less build");
688    return NULL;
689}
690
691#endif
692