C++11 override should now be supported by all of {bots,Chrome,Android,Mozilla}
[platform/upstream/libSkiaSharp.git] / 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 static SkMatrix pts_to_unit(const SkPoint& start, SkScalar diffRadius) {
171     SkScalar inv = diffRadius ? SkScalarInvert(diffRadius) : 0;
172     SkMatrix matrix;
173     matrix.setTranslate(-start.fX, -start.fY);
174     matrix.postScale(inv, inv);
175     return matrix;
176 }
177
178 SkTwoPointRadialGradient::SkTwoPointRadialGradient(const SkPoint& start, SkScalar startRadius,
179                                                    const SkPoint& end, SkScalar endRadius,
180                                                    const Descriptor& desc)
181     : SkGradientShaderBase(desc, pts_to_unit(start, endRadius - startRadius))
182     , fCenter1(start)
183     , fCenter2(end)
184     , fRadius1(startRadius)
185     , fRadius2(endRadius)
186 {
187     fDiff = fCenter1 - fCenter2;
188     fDiffRadius = fRadius2 - fRadius1;
189     // hack to avoid zero-divide for now
190     SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
191     fDiff.fX = SkScalarMul(fDiff.fX, inv);
192     fDiff.fY = SkScalarMul(fDiff.fY, inv);
193     fStartRadius = SkScalarMul(fRadius1, inv);
194     fSr2D2 = SkScalarSquare(fStartRadius);
195     fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
196     fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
197 }
198
199 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
200     SkBitmap* bitmap,
201     SkMatrix* matrix,
202     SkShader::TileMode* xy) const {
203     if (bitmap) {
204         this->getGradientTableBitmap(bitmap);
205     }
206     SkScalar diffL = 0; // just to avoid gcc warning
207     if (matrix) {
208         diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
209                              SkScalarSquare(fDiff.fY));
210     }
211     if (matrix) {
212         if (diffL) {
213             SkScalar invDiffL = SkScalarInvert(diffL);
214             matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
215                               SkScalarMul(invDiffL, fDiff.fX));
216         } else {
217             matrix->reset();
218         }
219         matrix->preConcat(fPtsToUnit);
220     }
221     if (xy) {
222         xy[0] = fTileMode;
223         xy[1] = kClamp_TileMode;
224     }
225     return kTwoPointRadial_BitmapType;
226 }
227
228 SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
229     SkShader::GradientInfo* info) const {
230     if (info) {
231         commonAsAGradient(info);
232         info->fPoint[0] = fCenter1;
233         info->fPoint[1] = fCenter2;
234         info->fRadius[0] = fRadius1;
235         info->fRadius[1] = fRadius2;
236     }
237     return kRadial2_GradientType;
238 }
239
240 size_t SkTwoPointRadialGradient::contextSize() const {
241     return sizeof(TwoPointRadialGradientContext);
242 }
243
244 SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& rec,
245                                                              void* storage) const {
246     // For now, we might have divided by zero, so detect that.
247     if (0 == fDiffRadius) {
248         return NULL;
249     }
250     return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this, rec));
251 }
252
253 SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
254         const SkTwoPointRadialGradient& shader, const ContextRec& rec)
255     : INHERITED(shader, rec)
256 {
257     // we don't have a span16 proc
258     fFlags &= ~kHasSpan16_Flag;
259 }
260
261 void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
262         int x, int y, SkPMColor* dstCParam, int count) {
263     SkASSERT(count > 0);
264
265     const SkTwoPointRadialGradient& twoPointRadialGradient =
266             static_cast<const SkTwoPointRadialGradient&>(fShader);
267
268     SkPMColor* SK_RESTRICT dstC = dstCParam;
269
270     // Zero difference between radii:  fill with transparent black.
271     if (twoPointRadialGradient.fDiffRadius == 0) {
272       sk_bzero(dstC, count * sizeof(*dstC));
273       return;
274     }
275     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
276     TileProc            proc = twoPointRadialGradient.fTileProc;
277     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
278
279     SkScalar foura = twoPointRadialGradient.fA * 4;
280     bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
281     if (fDstToIndexClass != kPerspective_MatrixClass) {
282         SkPoint srcPt;
283         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
284                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
285         SkScalar dx, fx = srcPt.fX;
286         SkScalar dy, fy = srcPt.fY;
287
288         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
289             SkFixed fixedX, fixedY;
290             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
291             dx = SkFixedToScalar(fixedX);
292             dy = SkFixedToScalar(fixedY);
293         } else {
294             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
295             dx = fDstToIndex.getScaleX();
296             dy = fDstToIndex.getSkewY();
297         }
298         SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
299                      SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
300                      twoPointRadialGradient.fStartRadius) * 2;
301         SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
302                       SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
303
304         TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
305         if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
306             shadeProc = shadeSpan_twopoint_clamp;
307         } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
308             shadeProc = shadeSpan_twopoint_mirror;
309         } else {
310             SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
311         }
312         (*shadeProc)(fx, dx, fy, dy, b, db,
313                      twoPointRadialGradient.fSr2D2, foura,
314                      twoPointRadialGradient.fOneOverTwoA, posRoot,
315                      dstC, cache, count);
316     } else {    // perspective case
317         SkScalar dstX = SkIntToScalar(x);
318         SkScalar dstY = SkIntToScalar(y);
319         for (; count > 0; --count) {
320             SkPoint             srcPt;
321             dstProc(fDstToIndex, dstX, dstY, &srcPt);
322             SkScalar fx = srcPt.fX;
323             SkScalar fy = srcPt.fY;
324             SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
325                          SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
326                          twoPointRadialGradient.fStartRadius) * 2;
327             SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
328                                          twoPointRadialGradient.fOneOverTwoA, posRoot);
329             SkFixed index = proc(t);
330             SkASSERT(index <= 0xFFFF);
331             *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
332             dstX += SK_Scalar1;
333         }
334     }
335 }
336
337 #ifndef SK_IGNORE_TO_STRING
338 void SkTwoPointRadialGradient::toString(SkString* str) const {
339     str->append("SkTwoPointRadialGradient: (");
340
341     str->append("center1: (");
342     str->appendScalar(fCenter1.fX);
343     str->append(", ");
344     str->appendScalar(fCenter1.fY);
345     str->append(") radius1: ");
346     str->appendScalar(fRadius1);
347     str->append(" ");
348
349     str->append("center2: (");
350     str->appendScalar(fCenter2.fX);
351     str->append(", ");
352     str->appendScalar(fCenter2.fY);
353     str->append(") radius2: ");
354     str->appendScalar(fRadius2);
355     str->append(" ");
356
357     this->INHERITED::toString(str);
358
359     str->append(")");
360 }
361 #endif
362
363 SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) {
364     DescriptorScope desc;
365     if (!desc.unflatten(buffer)) {
366         return NULL;
367     }
368     const SkPoint c1 = buffer.readPoint();
369     const SkPoint c2 = buffer.readPoint();
370     const SkScalar r1 = buffer.readScalar();
371     const SkScalar r2 = buffer.readScalar();
372     return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos,
373                                                   desc.fCount, desc.fTileMode, desc.fGradFlags,
374                                                   desc.fLocalMatrix);
375 }
376
377 void SkTwoPointRadialGradient::flatten(
378     SkWriteBuffer& buffer) const {
379     this->INHERITED::flatten(buffer);
380     buffer.writePoint(fCenter1);
381     buffer.writePoint(fCenter2);
382     buffer.writeScalar(fRadius1);
383     buffer.writeScalar(fRadius2);
384 }
385
386 /////////////////////////////////////////////////////////////////////
387
388 #if SK_SUPPORT_GPU
389
390 #include "SkGr.h"
391 #include "gl/builders/GrGLProgramBuilder.h"
392
393 // For brevity
394 typedef GrGLProgramDataManager::UniformHandle UniformHandle;
395
396 class GrGLRadial2Gradient : public GrGLGradientEffect {
397
398 public:
399
400     GrGLRadial2Gradient(const GrProcessor&);
401     virtual ~GrGLRadial2Gradient() { }
402
403     virtual void emitCode(GrGLFPBuilder*,
404                           const GrFragmentProcessor&,
405                           const char* outputColor,
406                           const char* inputColor,
407                           const TransformedCoordsArray&,
408                           const TextureSamplerArray&) override;
409     void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
410
411     static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
412
413 protected:
414
415     UniformHandle fParamUni;
416
417     const char* fVSVaryingName;
418     const char* fFSVaryingName;
419
420     bool fIsDegenerate;
421
422     // @{
423     /// Values last uploaded as uniforms
424
425     SkScalar fCachedCenter;
426     SkScalar fCachedRadius;
427     bool     fCachedPosRoot;
428
429     // @}
430
431 private:
432
433     typedef GrGLGradientEffect INHERITED;
434
435 };
436
437 /////////////////////////////////////////////////////////////////////
438
439 class GrRadial2Gradient : public GrGradientEffect {
440 public:
441     static GrFragmentProcessor* Create(GrContext* ctx,
442                                        const SkTwoPointRadialGradient& shader,
443                                        const SkMatrix& matrix,
444                                        SkShader::TileMode tm) {
445         return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
446     }
447
448     virtual ~GrRadial2Gradient() { }
449
450     const char* name() const override { return "Two-Point Radial Gradient"; }
451
452     virtual void getGLProcessorKey(const GrGLCaps& caps,
453                                    GrProcessorKeyBuilder* b) const override {
454         GrGLRadial2Gradient::GenKey(*this, caps, b);
455     }
456
457     GrGLFragmentProcessor* createGLInstance() const override {
458         return SkNEW_ARGS(GrGLRadial2Gradient, (*this));
459     }
460
461     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
462     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
463     SkScalar center() const { return fCenterX1; }
464     SkScalar radius() const { return fRadius0; }
465     bool isPosRoot() const { return SkToBool(fPosRoot); }
466
467 private:
468     bool onIsEqual(const GrFragmentProcessor& sBase) const override {
469         const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>();
470         return (INHERITED::onIsEqual(sBase) &&
471                 this->fCenterX1 == s.fCenterX1 &&
472                 this->fRadius0 == s.fRadius0 &&
473                 this->fPosRoot == s.fPosRoot);
474     }
475
476     GrRadial2Gradient(GrContext* ctx,
477                       const SkTwoPointRadialGradient& shader,
478                       const SkMatrix& matrix,
479                       SkShader::TileMode tm)
480         : INHERITED(ctx, shader, matrix, tm)
481         , fCenterX1(shader.getCenterX1())
482         , fRadius0(shader.getStartRadius())
483         , fPosRoot(shader.getDiffRadius() < 0) {
484         this->initClassID<GrRadial2Gradient>();
485         // We pass the linear part of the quadratic as a varying.
486         //    float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
487         fBTransform = this->getCoordTransform();
488         SkMatrix& bMatrix = *fBTransform.accessMatrix();
489         bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
490                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
491         bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
492                                           SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
493         bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
494                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
495         this->addCoordTransform(&fBTransform);
496     }
497
498     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
499
500     // @{
501     // Cache of values - these can change arbitrarily, EXCEPT
502     // we shouldn't change between degenerate and non-degenerate?!
503
504     GrCoordTransform fBTransform;
505     SkScalar         fCenterX1;
506     SkScalar         fRadius0;
507     SkBool8          fPosRoot;
508
509     // @}
510
511     typedef GrGradientEffect INHERITED;
512 };
513
514 /////////////////////////////////////////////////////////////////////
515
516 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient);
517
518 GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random,
519                                                    GrContext* context,
520                                                    const GrDrawTargetCaps&,
521                                                    GrTexture**) {
522     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
523     SkScalar radius1 = random->nextUScalar1();
524     SkPoint center2;
525     SkScalar radius2;
526     do {
527         center2.set(random->nextUScalar1(), random->nextUScalar1());
528         radius2 = random->nextUScalar1 ();
529         // There is a bug in two point radial gradients with identical radii
530     } while (radius1 == radius2);
531
532     SkColor colors[kMaxRandomGradientColors];
533     SkScalar stopsArray[kMaxRandomGradientColors];
534     SkScalar* stops = stopsArray;
535     SkShader::TileMode tm;
536     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
537     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
538                                                                          center2, radius2,
539                                                                          colors, stops, colorCount,
540                                                                          tm));
541     SkPaint paint;
542     GrFragmentProcessor* fp;
543     GrColor paintColor;
544     SkAssertResult(shader->asFragmentProcessor(context, paint,
545                                                GrProcessorUnitTest::TestMatrix(random), NULL,
546                                                &paintColor, &fp));
547     return fp;
548 }
549
550 /////////////////////////////////////////////////////////////////////
551
552 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrProcessor& processor)
553     : fVSVaryingName(NULL)
554     , fFSVaryingName(NULL)
555     , fCachedCenter(SK_ScalarMax)
556     , fCachedRadius(-SK_ScalarMax)
557     , fCachedPosRoot(0) {
558
559     const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
560     fIsDegenerate = data.isDegenerate();
561 }
562
563 void GrGLRadial2Gradient::emitCode(GrGLFPBuilder* builder,
564                                    const GrFragmentProcessor& fp,
565                                    const char* outputColor,
566                                    const char* inputColor,
567                                    const TransformedCoordsArray& coords,
568                                    const TextureSamplerArray& samplers) {
569     const GrRadial2Gradient& ge = fp.cast<GrRadial2Gradient>();
570     this->emitUniforms(builder, ge);
571     fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
572                                          kFloat_GrSLType, kDefault_GrSLPrecision,
573                                          "Radial2FSParams", 6);
574
575     SkString cName("c");
576     SkString ac4Name("ac4");
577     SkString rootName("root");
578     SkString t;
579     SkString p0;
580     SkString p1;
581     SkString p2;
582     SkString p3;
583     SkString p4;
584     SkString p5;
585     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
586     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
587     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
588     builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
589     builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
590     builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
591
592     GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
593     // We interpolate the linear component in coords[1].
594     SkASSERT(coords[0].getType() == coords[1].getType());
595     const char* coords2D;
596     SkString bVar;
597     if (kVec3f_GrSLType == coords[0].getType()) {
598         fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
599                                coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
600         coords2D = "interpolants.xy";
601         bVar = "interpolants.z";
602     } else {
603         coords2D = coords[0].c_str();
604         bVar.printf("%s.x", coords[1].c_str());
605     }
606
607     // c = (x^2)+(y^2) - params[4]
608     fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
609                            cName.c_str(), coords2D, coords2D, p4.c_str());
610
611     // If we aren't degenerate, emit some extra code, and accept a slightly
612     // more complex coord.
613     if (!fIsDegenerate) {
614
615         // ac4 = 4.0 * params[0] * c
616         fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
617                                ac4Name.c_str(), p0.c_str(),
618                                cName.c_str());
619
620         // root = sqrt(b^2-4ac)
621         // (abs to avoid exception due to fp precision)
622         fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
623                                rootName.c_str(), bVar.c_str(), bVar.c_str(),
624                                ac4Name.c_str());
625
626         // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
627         t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
628                  rootName.c_str(), p1.c_str());
629     } else {
630         // t is: -c/b
631         t.printf("-%s / %s", cName.c_str(), bVar.c_str());
632     }
633
634     this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
635 }
636
637 void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
638                                   const GrProcessor& processor) {
639     INHERITED::setData(pdman, processor);
640     const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
641     SkASSERT(data.isDegenerate() == fIsDegenerate);
642     SkScalar centerX1 = data.center();
643     SkScalar radius0 = data.radius();
644     if (fCachedCenter != centerX1 ||
645         fCachedRadius != radius0 ||
646         fCachedPosRoot != data.isPosRoot()) {
647
648         SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
649
650         // When we're in the degenerate (linear) case, the second
651         // value will be INF but the program doesn't read it. (We
652         // use the same 6 uniforms even though we don't need them
653         // all in the linear case just to keep the code complexity
654         // down).
655         float values[6] = {
656             SkScalarToFloat(a),
657             1 / (2.f * SkScalarToFloat(a)),
658             SkScalarToFloat(centerX1),
659             SkScalarToFloat(radius0),
660             SkScalarToFloat(SkScalarMul(radius0, radius0)),
661             data.isPosRoot() ? 1.f : -1.f
662         };
663
664         pdman.set1fv(fParamUni, 6, values);
665         fCachedCenter = centerX1;
666         fCachedRadius = radius0;
667         fCachedPosRoot = data.isPosRoot();
668     }
669 }
670
671 void GrGLRadial2Gradient::GenKey(const GrProcessor& processor,
672                                  const GrGLCaps&, GrProcessorKeyBuilder* b) {
673     uint32_t* key = b->add32n(2);
674     key[0] = GenBaseGradientKey(processor);
675     key[1] = processor.cast<GrRadial2Gradient>().isDegenerate();
676 }
677
678 /////////////////////////////////////////////////////////////////////
679
680 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
681                                                    const SkMatrix&,
682                                                    const SkMatrix* localMatrix, GrColor* paintColor,
683                                                    GrFragmentProcessor** fp)  const {
684     SkASSERT(context);
685
686     // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
687     SkMatrix matrix;
688     if (!this->getLocalMatrix().invert(&matrix)) {
689         return false;
690     }
691     if (localMatrix) {
692         SkMatrix inv;
693         if (!localMatrix->invert(&inv)) {
694             return false;
695         }
696         matrix.postConcat(inv);
697     }
698     matrix.postConcat(fPtsToUnit);
699
700     SkScalar diffLen = fDiff.length();
701     if (0 != diffLen) {
702         SkScalar invDiffLen = SkScalarInvert(diffLen);
703         SkMatrix rot;
704         rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
705                        SkScalarMul(invDiffLen, fDiff.fX));
706         matrix.postConcat(rot);
707     }
708
709     *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
710     *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
711
712     return true;
713 }
714
715 #else
716
717 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
718                                                    const SkMatrix*,
719                                                    GrColor*, GrFragmentProcessor**)  const {
720     SkDEBUGFAIL("Should not call in GPU-less build");
721     return false;
722 }
723
724 #endif