gfx/skia/trunk/src/effects/gradients/SkTwoPointRadialGradient.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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  */
     9  #include "SkTwoPointRadialGradient.h"
    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).
    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):
    22       Cx = (1 - t) * Sx + t * Ex        (0 <= t <= 1)
    23       Cy = (1 - t) * Sy + t * Ey
    25    The radius of the desired circle (r) is also a linear interpolation t
    26    between the start and end radii (Sr and Er):
    28       r = (1 - t) * Sr + t * Er
    30    But
    32       (x - Cx)^2 + (y - Cy)^2 = r^2
    34    so
    36      (x - ((1 - t) * Sx + t * Ex))^2
    37    + (y - ((1 - t) * Sy + t * Ey))^2
    38    = ((1 - t) * Sr + t * Er)^2
    40    Solving for t yields
    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
    46    To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
    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
    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.
    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.
    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
    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
    72    (x' and y' are computed by appending the subtract and scale to the
    73    fDstToIndex matrix in the constructor).
    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.
    81 */
    83 namespace {
    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     }
    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 }
   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);
   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 }
   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 }
   168 /////////////////////////////////////////////////////////////////////
   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 }
   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 }
   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 }
   223 void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
   224                                          int count) {
   225     SkASSERT(count > 0);
   227     SkPMColor* SK_RESTRICT dstC = dstCParam;
   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();
   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;
   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;
   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 }
   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     }
   301     if (!this->INHERITED::setContext(device, paint, matrix)) {
   302         return false;
   303     }
   305     // we don't have a span16 proc
   306     fFlags &= ~kHasSpan16_Flag;
   307     return true;
   308 }
   310 #ifndef SK_IGNORE_TO_STRING
   311 void SkTwoPointRadialGradient::toString(SkString* str) const {
   312     str->append("SkTwoPointRadialGradient: (");
   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(" ");
   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(" ");
   330     this->INHERITED::toString(str);
   332     str->append(")");
   333 }
   334 #endif
   336 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
   337     SkReadBuffer& buffer)
   338     : INHERITED(buffer),
   339       fCenter1(buffer.readPoint()),
   340       fCenter2(buffer.readPoint()),
   341       fRadius1(buffer.readScalar()),
   342       fRadius2(buffer.readScalar()) {
   343     init();
   344 };
   346 void SkTwoPointRadialGradient::flatten(
   347     SkWriteBuffer& buffer) const {
   348     this->INHERITED::flatten(buffer);
   349     buffer.writePoint(fCenter1);
   350     buffer.writePoint(fCenter2);
   351     buffer.writeScalar(fRadius1);
   352     buffer.writeScalar(fRadius2);
   353 }
   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;
   367     fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
   368     fPtsToUnit.postScale(inv, inv);
   369 }
   371 /////////////////////////////////////////////////////////////////////
   373 #if SK_SUPPORT_GPU
   375 #include "GrTBackendEffectFactory.h"
   377 // For brevity
   378 typedef GrGLUniformManager::UniformHandle UniformHandle;
   380 class GrGLRadial2Gradient : public GrGLGradientEffect {
   382 public:
   384     GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
   385     virtual ~GrGLRadial2Gradient() { }
   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;
   396     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
   398 protected:
   400     UniformHandle fParamUni;
   402     const char* fVSVaryingName;
   403     const char* fFSVaryingName;
   405     bool fIsDegenerate;
   407     // @{
   408     /// Values last uploaded as uniforms
   410     SkScalar fCachedCenter;
   411     SkScalar fCachedRadius;
   412     bool     fCachedPosRoot;
   414     // @}
   416 private:
   418     typedef GrGLGradientEffect INHERITED;
   420 };
   422 /////////////////////////////////////////////////////////////////////
   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     }
   434     virtual ~GrRadial2Gradient() { }
   436     static const char* Name() { return "Two-Point Radial Gradient"; }
   437     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
   438         return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
   439     }
   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); }
   447     typedef GrGLRadial2Gradient GLEffect;
   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     }
   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     }
   479     GR_DECLARE_EFFECT_TEST;
   481     // @{
   482     // Cache of values - these can change arbitrarily, EXCEPT
   483     // we shouldn't change between degenerate and non-degenerate?!
   485     GrCoordTransform fBTransform;
   486     SkScalar         fCenterX1;
   487     SkScalar         fRadius0;
   488     SkBool8          fPosRoot;
   490     // @}
   492     typedef GrGradientEffect INHERITED;
   493 };
   495 /////////////////////////////////////////////////////////////////////
   497 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
   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);
   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 }
   526 /////////////////////////////////////////////////////////////////////
   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) {
   537     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
   538     fIsDegenerate = data.isDegenerate();
   539 }
   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) {
   549     this->emitUniforms(builder, key);
   550     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
   551                                          kFloat_GrSLType, "Radial2FSParams", 6);
   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);
   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     }
   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());
   588     // If we aren't degenerate, emit some extra code, and accept a slightly
   589     // more complex coord.
   590     if (!fIsDegenerate) {
   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());
   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());
   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     }
   611     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
   612 }
   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()) {
   625         SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
   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         };
   641         uman.set1fv(fParamUni, 6, values);
   642         fCachedCenter = centerX1;
   643         fCachedRadius = radius0;
   644         fCachedPosRoot = data.isPosRoot();
   645     }
   646 }
   648 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
   649                                                   const GrGLCaps&) {
   650     enum {
   651         kIsDegenerate = 1 << kBaseKeyBitCnt,
   652     };
   654     EffectKey key = GenBaseGradientKey(drawEffect);
   655     if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
   656         key |= kIsDegenerate;
   657     }
   658     return key;
   659 }
   661 /////////////////////////////////////////////////////////////////////
   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);
   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     }
   681     return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
   682 }
   684 #else
   686 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
   687     SkDEBUGFAIL("Should not call in GPU-less build");
   688     return NULL;
   689 }
   691 #endif

mercurial