Home | History | Annotate | Download | only in gradients
      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 "SkTwoPointConicalGradient.h"
     10 
     11 static int valid_divide(float numer, float denom, float* ratio) {
     12     SkASSERT(ratio);
     13     if (0 == denom) {
     14         return 0;
     15     }
     16     *ratio = numer / denom;
     17     return 1;
     18 }
     19 
     20 // Return the number of distinct real roots, and write them into roots[] in
     21 // ascending order
     22 static int find_quad_roots(float A, float B, float C, float roots[2]) {
     23     SkASSERT(roots);
     24 
     25     if (A == 0) {
     26         return valid_divide(-C, B, roots);
     27     }
     28 
     29     float R = B*B - 4*A*C;
     30     if (R < 0) {
     31         return 0;
     32     }
     33     R = sk_float_sqrt(R);
     34 
     35 #if 1
     36     float Q = B;
     37     if (Q < 0) {
     38         Q -= R;
     39     } else {
     40         Q += R;
     41     }
     42 #else
     43     // on 10.6 this was much slower than the above branch :(
     44     float Q = B + copysignf(R, B);
     45 #endif
     46     Q *= -0.5f;
     47     if (0 == Q) {
     48         roots[0] = 0;
     49         return 1;
     50     }
     51 
     52     float r0 = Q / A;
     53     float r1 = C / Q;
     54     roots[0] = r0 < r1 ? r0 : r1;
     55     roots[1] = r0 > r1 ? r0 : r1;
     56     return 2;
     57 }
     58 
     59 static float lerp(float x, float dx, float t) {
     60     return x + t * dx;
     61 }
     62 
     63 static float sqr(float x) { return x * x; }
     64 
     65 void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
     66                        const SkPoint& center1, SkScalar rad1) {
     67     fCenterX = SkScalarToFloat(center0.fX);
     68     fCenterY = SkScalarToFloat(center0.fY);
     69     fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
     70     fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
     71     fRadius = SkScalarToFloat(rad0);
     72     fDRadius = SkScalarToFloat(rad1) - fRadius;
     73 
     74     fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
     75     fRadius2 = sqr(fRadius);
     76     fRDR = fRadius * fDRadius;
     77 }
     78 
     79 void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
     80     fRelX = SkScalarToFloat(fx) - fCenterX;
     81     fRelY = SkScalarToFloat(fy) - fCenterY;
     82     fIncX = SkScalarToFloat(dfx);
     83     fIncY = SkScalarToFloat(dfy);
     84     fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR);
     85     fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY);
     86 }
     87 
     88 SkFixed TwoPtRadial::nextT() {
     89     float roots[2];
     90 
     91     float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
     92     int countRoots = find_quad_roots(fA, fB, C, roots);
     93 
     94     fRelX += fIncX;
     95     fRelY += fIncY;
     96     fB += fDB;
     97 
     98     if (0 == countRoots) {
     99         return kDontDrawT;
    100     }
    101 
    102     // Prefer the bigger t value if both give a radius(t) > 0
    103     // find_quad_roots returns the values sorted, so we start with the last
    104     float t = roots[countRoots - 1];
    105     float r = lerp(fRadius, fDRadius, t);
    106     if (r <= 0) {
    107         t = roots[0];   // might be the same as roots[countRoots-1]
    108         r = lerp(fRadius, fDRadius, t);
    109         if (r <= 0) {
    110             return kDontDrawT;
    111         }
    112     }
    113     return SkFloatToFixed(t);
    114 }
    115 
    116 typedef void (*TwoPointConicalProc)(TwoPtRadial* rec, SkPMColor* dstC,
    117                                     const SkPMColor* cache, int toggle, int count);
    118 
    119 static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
    120                            const SkPMColor* SK_RESTRICT cache, int toggle,
    121                            int count) {
    122     for (; count > 0; --count) {
    123         SkFixed t = rec->nextT();
    124         if (TwoPtRadial::DontDrawT(t)) {
    125             *dstC++ = 0;
    126         } else {
    127             SkFixed index = SkClampMax(t, 0xFFFF);
    128             SkASSERT(index <= 0xFFFF);
    129             *dstC++ = cache[toggle +
    130                             (index >> SkGradientShaderBase::kCache32Shift)];
    131         }
    132         toggle = next_dither_toggle(toggle);
    133     }
    134 }
    135 
    136 static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
    137                             const SkPMColor* SK_RESTRICT cache, int toggle,
    138                             int count) {
    139     for (; count > 0; --count) {
    140         SkFixed t = rec->nextT();
    141         if (TwoPtRadial::DontDrawT(t)) {
    142             *dstC++ = 0;
    143         } else {
    144             SkFixed index = repeat_tileproc(t);
    145             SkASSERT(index <= 0xFFFF);
    146             *dstC++ = cache[toggle +
    147                             (index >> SkGradientShaderBase::kCache32Shift)];
    148         }
    149         toggle = next_dither_toggle(toggle);
    150     }
    151 }
    152 
    153 static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
    154                             const SkPMColor* SK_RESTRICT cache, int toggle,
    155                             int count) {
    156     for (; count > 0; --count) {
    157         SkFixed t = rec->nextT();
    158         if (TwoPtRadial::DontDrawT(t)) {
    159             *dstC++ = 0;
    160         } else {
    161             SkFixed index = mirror_tileproc(t);
    162             SkASSERT(index <= 0xFFFF);
    163             *dstC++ = cache[toggle +
    164                             (index >> SkGradientShaderBase::kCache32Shift)];
    165         }
    166         toggle = next_dither_toggle(toggle);
    167     }
    168 }
    169 
    170 void SkTwoPointConicalGradient::init() {
    171     fRec.init(fCenter1, fRadius1, fCenter2, fRadius2);
    172     fPtsToUnit.reset();
    173 }
    174 
    175 /////////////////////////////////////////////////////////////////////
    176 
    177 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
    178         const SkPoint& start, SkScalar startRadius,
    179         const SkPoint& end, SkScalar endRadius,
    180         const Descriptor& desc)
    181     : SkGradientShaderBase(desc),
    182     fCenter1(start),
    183     fCenter2(end),
    184     fRadius1(startRadius),
    185     fRadius2(endRadius) {
    186     // this is degenerate, and should be caught by our caller
    187     SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
    188     this->init();
    189 }
    190 
    191 bool SkTwoPointConicalGradient::isOpaque() const {
    192     // Because areas outside the cone are left untouched, we cannot treat the
    193     // shader as opaque even if the gradient itself is opaque.
    194     // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
    195     return false;
    196 }
    197 
    198 void SkTwoPointConicalGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
    199                                           int count) {
    200     int toggle = init_dither_toggle(x, y);
    201 
    202     SkASSERT(count > 0);
    203 
    204     SkPMColor* SK_RESTRICT dstC = dstCParam;
    205 
    206     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    207 
    208     const SkPMColor* SK_RESTRICT cache = this->getCache32();
    209 
    210     TwoPointConicalProc shadeProc = twopoint_repeat;
    211     if (SkShader::kClamp_TileMode == fTileMode) {
    212         shadeProc = twopoint_clamp;
    213     } else if (SkShader::kMirror_TileMode == fTileMode) {
    214         shadeProc = twopoint_mirror;
    215     } else {
    216         SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
    217     }
    218 
    219     if (fDstToIndexClass != kPerspective_MatrixClass) {
    220         SkPoint srcPt;
    221         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    222                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    223         SkScalar dx, fx = srcPt.fX;
    224         SkScalar dy, fy = srcPt.fY;
    225 
    226         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    227             SkFixed fixedX, fixedY;
    228             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
    229             dx = SkFixedToScalar(fixedX);
    230             dy = SkFixedToScalar(fixedY);
    231         } else {
    232             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    233             dx = fDstToIndex.getScaleX();
    234             dy = fDstToIndex.getSkewY();
    235         }
    236 
    237         fRec.setup(fx, fy, dx, dy);
    238         (*shadeProc)(&fRec, dstC, cache, toggle, count);
    239     } else {    // perspective case
    240         SkScalar dstX = SkIntToScalar(x);
    241         SkScalar dstY = SkIntToScalar(y);
    242         for (; count > 0; --count) {
    243             SkPoint srcPt;
    244             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    245             dstX += SK_Scalar1;
    246 
    247             fRec.setup(srcPt.fX, srcPt.fY, 0, 0);
    248             (*shadeProc)(&fRec, dstC, cache, toggle, 1);
    249             toggle = next_dither_toggle(toggle);
    250         }
    251     }
    252 }
    253 
    254 bool SkTwoPointConicalGradient::setContext(const SkBitmap& device,
    255                                            const SkPaint& paint,
    256                                            const SkMatrix& matrix) {
    257     if (!this->INHERITED::setContext(device, paint, matrix)) {
    258         return false;
    259     }
    260 
    261     // we don't have a span16 proc
    262     fFlags &= ~kHasSpan16_Flag;
    263 
    264     // in general, we might discard based on computed-radius, so clear
    265     // this flag (todo: sometimes we can detect that we never discard...)
    266     fFlags &= ~kOpaqueAlpha_Flag;
    267 
    268     return true;
    269 }
    270 
    271 SkShader::BitmapType SkTwoPointConicalGradient::asABitmap(
    272     SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const {
    273     SkPoint diff = fCenter2 - fCenter1;
    274     SkScalar diffLen = 0;
    275 
    276     if (bitmap) {
    277         this->getGradientTableBitmap(bitmap);
    278     }
    279     if (matrix) {
    280         diffLen = diff.length();
    281     }
    282     if (matrix) {
    283         if (diffLen) {
    284             SkScalar invDiffLen = SkScalarInvert(diffLen);
    285             // rotate to align circle centers with the x-axis
    286             matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
    287                               SkScalarMul(invDiffLen, diff.fX));
    288         } else {
    289             matrix->reset();
    290         }
    291         matrix->preTranslate(-fCenter1.fX, -fCenter1.fY);
    292     }
    293     if (xy) {
    294         xy[0] = fTileMode;
    295         xy[1] = kClamp_TileMode;
    296     }
    297     return kTwoPointConical_BitmapType;
    298 }
    299 
    300 SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
    301     GradientInfo* info) const {
    302     if (info) {
    303         commonAsAGradient(info);
    304         info->fPoint[0] = fCenter1;
    305         info->fPoint[1] = fCenter2;
    306         info->fRadius[0] = fRadius1;
    307         info->fRadius[1] = fRadius2;
    308     }
    309     return kConical_GradientType;
    310 }
    311 
    312 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
    313     SkFlattenableReadBuffer& buffer)
    314     : INHERITED(buffer),
    315     fCenter1(buffer.readPoint()),
    316     fCenter2(buffer.readPoint()),
    317     fRadius1(buffer.readScalar()),
    318     fRadius2(buffer.readScalar()) {
    319     this->init();
    320 };
    321 
    322 void SkTwoPointConicalGradient::flatten(
    323     SkFlattenableWriteBuffer& buffer) const {
    324     this->INHERITED::flatten(buffer);
    325     buffer.writePoint(fCenter1);
    326     buffer.writePoint(fCenter2);
    327     buffer.writeScalar(fRadius1);
    328     buffer.writeScalar(fRadius2);
    329 }
    330 
    331 /////////////////////////////////////////////////////////////////////
    332 
    333 #if SK_SUPPORT_GPU
    334 
    335 #include "GrTBackendEffectFactory.h"
    336 
    337 // For brevity
    338 typedef GrGLUniformManager::UniformHandle UniformHandle;
    339 static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
    340 
    341 class GrGLConical2Gradient : public GrGLGradientEffect {
    342 public:
    343 
    344     GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    345     virtual ~GrGLConical2Gradient() { }
    346 
    347     virtual void emitCode(GrGLShaderBuilder*,
    348                           const GrDrawEffect&,
    349                           EffectKey,
    350                           const char* outputColor,
    351                           const char* inputColor,
    352                           const TextureSamplerArray&) SK_OVERRIDE;
    353     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    354 
    355     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    356 
    357 protected:
    358 
    359     UniformHandle           fVSParamUni;
    360     UniformHandle           fFSParamUni;
    361 
    362     const char* fVSVaryingName;
    363     const char* fFSVaryingName;
    364 
    365     bool fIsDegenerate;
    366 
    367     // @{
    368     /// Values last uploaded as uniforms
    369 
    370     SkScalar fCachedCenter;
    371     SkScalar fCachedRadius;
    372     SkScalar fCachedDiffRadius;
    373 
    374     // @}
    375 
    376 private:
    377 
    378     typedef GrGLGradientEffect INHERITED;
    379 
    380 };
    381 
    382 /////////////////////////////////////////////////////////////////////
    383 
    384 class GrConical2Gradient : public GrGradientEffect {
    385 public:
    386 
    387     static GrEffectRef* Create(GrContext* ctx,
    388                                const SkTwoPointConicalGradient& shader,
    389                                const SkMatrix& matrix,
    390                                SkShader::TileMode tm) {
    391         AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm)));
    392         return CreateEffectRef(effect);
    393     }
    394 
    395     virtual ~GrConical2Gradient() { }
    396 
    397     static const char* Name() { return "Two-Point Conical Gradient"; }
    398     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    399         return GrTBackendEffectFactory<GrConical2Gradient>::getInstance();
    400     }
    401 
    402     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
    403     bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
    404     SkScalar center() const { return fCenterX1; }
    405     SkScalar diffRadius() const { return fDiffRadius; }
    406     SkScalar radius() const { return fRadius0; }
    407 
    408     typedef GrGLConical2Gradient GLEffect;
    409 
    410 private:
    411     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    412         const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase);
    413         return (INHERITED::onIsEqual(sBase) &&
    414                 this->fCenterX1 == s.fCenterX1 &&
    415                 this->fRadius0 == s.fRadius0 &&
    416                 this->fDiffRadius == s.fDiffRadius);
    417     }
    418 
    419     GrConical2Gradient(GrContext* ctx,
    420                        const SkTwoPointConicalGradient& shader,
    421                        const SkMatrix& matrix,
    422                        SkShader::TileMode tm)
    423         : INHERITED(ctx, shader, matrix, tm)
    424         , fCenterX1(shader.getCenterX1())
    425         , fRadius0(shader.getStartRadius())
    426         , fDiffRadius(shader.getDiffRadius()) { }
    427 
    428     GR_DECLARE_EFFECT_TEST;
    429 
    430     // @{
    431     // Cache of values - these can change arbitrarily, EXCEPT
    432     // we shouldn't change between degenerate and non-degenerate?!
    433 
    434     SkScalar fCenterX1;
    435     SkScalar fRadius0;
    436     SkScalar fDiffRadius;
    437 
    438     // @}
    439 
    440     typedef GrGradientEffect INHERITED;
    441 };
    442 
    443 GR_DEFINE_EFFECT_TEST(GrConical2Gradient);
    444 
    445 GrEffectRef* GrConical2Gradient::TestCreate(SkMWCRandom* random,
    446                                             GrContext* context,
    447                                             const GrDrawTargetCaps&,
    448                                             GrTexture**) {
    449     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    450     SkScalar radius1 = random->nextUScalar1();
    451     SkPoint center2;
    452     SkScalar radius2;
    453     do {
    454         center2.set(random->nextUScalar1(), random->nextUScalar1());
    455         radius2 = random->nextUScalar1 ();
    456         // If the circles are identical the factory will give us an empty shader.
    457     } while (radius1 == radius2 && center1 == center2);
    458 
    459     SkColor colors[kMaxRandomGradientColors];
    460     SkScalar stopsArray[kMaxRandomGradientColors];
    461     SkScalar* stops = stopsArray;
    462     SkShader::TileMode tm;
    463     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    464     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
    465                                                                           center2, radius2,
    466                                                                           colors, stops, colorCount,
    467                                                                           tm));
    468     SkPaint paint;
    469     return shader->asNewEffect(context, paint);
    470 }
    471 
    472 
    473 /////////////////////////////////////////////////////////////////////
    474 
    475 GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory,
    476                                            const GrDrawEffect& drawEffect)
    477     : INHERITED(factory)
    478     , fVSParamUni(kInvalidUniformHandle)
    479     , fFSParamUni(kInvalidUniformHandle)
    480     , fVSVaryingName(NULL)
    481     , fFSVaryingName(NULL)
    482     , fCachedCenter(SK_ScalarMax)
    483     , fCachedRadius(-SK_ScalarMax)
    484     , fCachedDiffRadius(-SK_ScalarMax) {
    485 
    486     const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
    487     fIsDegenerate = data.isDegenerate();
    488 }
    489 
    490 void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder,
    491                                     const GrDrawEffect&,
    492                                     EffectKey key,
    493                                     const char* outputColor,
    494                                     const char* inputColor,
    495                                     const TextureSamplerArray& samplers) {
    496     const char* fsCoords;
    497     const char* vsCoordsVarying;
    498     GrSLType coordsVaryingType;
    499     this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
    500 
    501     this->emitYCoordUniform(builder);
    502     // 2 copies of uniform array, 1 for each of vertex & fragment shader,
    503     // to work around Xoom bug. Doesn't seem to cause performance decrease
    504     // in test apps, but need to keep an eye on it.
    505     fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
    506                                            kFloat_GrSLType, "Conical2VSParams", 6);
    507     fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
    508                                            kFloat_GrSLType, "Conical2FSParams", 6);
    509 
    510     // For radial gradients without perspective we can pass the linear
    511     // part of the quadratic as a varying.
    512     if (kVec2f_GrSLType == coordsVaryingType) {
    513         builder->addVarying(kFloat_GrSLType, "Conical2BCoeff",
    514                             &fVSVaryingName, &fFSVaryingName);
    515     }
    516 
    517     // VS
    518     {
    519         SkString p2; // distance between centers
    520         SkString p3; // start radius
    521         SkString p5; // difference in radii (r1 - r0)
    522         builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
    523         builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
    524         builder->getUniformVariable(fVSParamUni).appendArrayAccess(5, &p5);
    525 
    526         // For radial gradients without perspective we can pass the linear
    527         // part of the quadratic as a varying.
    528         if (kVec2f_GrSLType == coordsVaryingType) {
    529             // r2Var = -2 * (r2Parm[2] * varCoord.x - r2Param[3] * r2Param[5])
    530             builder->vsCodeAppendf("\t%s = -2.0 * (%s * %s.x + %s * %s);\n",
    531                                    fVSVaryingName, p2.c_str(),
    532                                    vsCoordsVarying, p3.c_str(), p5.c_str());
    533         }
    534     }
    535 
    536     // FS
    537     {
    538 
    539         SkString cName("c");
    540         SkString ac4Name("ac4");
    541         SkString dName("d");
    542         SkString qName("q");
    543         SkString r0Name("r0");
    544         SkString r1Name("r1");
    545         SkString tName("t");
    546         SkString p0; // 4a
    547         SkString p1; // 1/a
    548         SkString p2; // distance between centers
    549         SkString p3; // start radius
    550         SkString p4; // start radius squared
    551         SkString p5; // difference in radii (r1 - r0)
    552 
    553         builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
    554         builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
    555         builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
    556         builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
    557         builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
    558         builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
    559 
    560         // If we we're able to interpolate the linear component,
    561         // bVar is the varying; otherwise compute it
    562         SkString bVar;
    563         if (kVec2f_GrSLType == coordsVaryingType) {
    564             bVar = fFSVaryingName;
    565         } else {
    566             bVar = "b";
    567             builder->fsCodeAppendf("\tfloat %s = -2.0 * (%s * %s.x + %s * %s);\n",
    568                                    bVar.c_str(), p2.c_str(), fsCoords,
    569                                    p3.c_str(), p5.c_str());
    570         }
    571 
    572         // output will default to transparent black (we simply won't write anything
    573         // else to it if invalid, instead of discarding or returning prematurely)
    574         builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
    575 
    576         // c = (x^2)+(y^2) - params[4]
    577         builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", cName.c_str(),
    578                                fsCoords, fsCoords,
    579                                p4.c_str());
    580 
    581         // Non-degenerate case (quadratic)
    582         if (!fIsDegenerate) {
    583 
    584             // ac4 = params[0] * c
    585             builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
    586                                    cName.c_str());
    587 
    588             // d = b^2 - ac4
    589             builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
    590                                    bVar.c_str(), bVar.c_str(), ac4Name.c_str());
    591 
    592             // only proceed if discriminant is >= 0
    593             builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str());
    594 
    595             // intermediate value we'll use to compute the roots
    596             // q = -0.5 * (b +/- sqrt(d))
    597             builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
    598                                    " * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
    599                                    bVar.c_str(), dName.c_str());
    600 
    601             // compute both roots
    602             // r0 = q * params[1]
    603             builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
    604                                    qName.c_str(), p1.c_str());
    605             // r1 = c / q
    606             builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
    607                                    cName.c_str(), qName.c_str());
    608 
    609             // Note: If there are two roots that both generate radius(t) > 0, the
    610             // Canvas spec says to choose the larger t.
    611 
    612             // so we'll look at the larger one first:
    613             builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
    614                                    r0Name.c_str(), r1Name.c_str());
    615 
    616             // if r(t) > 0, then we're done; t will be our x coordinate
    617             builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
    618                                    p5.c_str(), p3.c_str());
    619 
    620             builder->fsCodeAppend("\t\t");
    621             this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]);
    622 
    623             // otherwise, if r(t) for the larger root was <= 0, try the other root
    624             builder->fsCodeAppend("\t\t} else {\n");
    625             builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
    626                                    r0Name.c_str(), r1Name.c_str());
    627 
    628             // if r(t) > 0 for the smaller root, then t will be our x coordinate
    629             builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
    630                                    tName.c_str(), p5.c_str(), p3.c_str());
    631 
    632             builder->fsCodeAppend("\t\t\t");
    633             this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]);
    634 
    635             // end if (r(t) > 0) for smaller root
    636             builder->fsCodeAppend("\t\t\t}\n");
    637             // end if (r(t) > 0), else, for larger root
    638             builder->fsCodeAppend("\t\t}\n");
    639             // end if (discriminant >= 0)
    640             builder->fsCodeAppend("\t}\n");
    641         } else {
    642 
    643             // linear case: t = -c/b
    644             builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
    645                                    cName.c_str(), bVar.c_str());
    646 
    647             // if r(t) > 0, then t will be the x coordinate
    648             builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
    649                                    p5.c_str(), p3.c_str());
    650             builder->fsCodeAppend("\t");
    651             this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]);
    652             builder->fsCodeAppend("\t}\n");
    653         }
    654     }
    655 }
    656 
    657 void GrGLConical2Gradient::setData(const GrGLUniformManager& uman,
    658                                    const GrDrawEffect& drawEffect) {
    659     INHERITED::setData(uman, drawEffect);
    660     const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
    661     GrAssert(data.isDegenerate() == fIsDegenerate);
    662     SkScalar centerX1 = data.center();
    663     SkScalar radius0 = data.radius();
    664     SkScalar diffRadius = data.diffRadius();
    665 
    666     if (fCachedCenter != centerX1 ||
    667         fCachedRadius != radius0 ||
    668         fCachedDiffRadius != diffRadius) {
    669 
    670         SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius;
    671 
    672         // When we're in the degenerate (linear) case, the second
    673         // value will be INF but the program doesn't read it. (We
    674         // use the same 6 uniforms even though we don't need them
    675         // all in the linear case just to keep the code complexity
    676         // down).
    677         float values[6] = {
    678             SkScalarToFloat(a * 4),
    679             1.f / (SkScalarToFloat(a)),
    680             SkScalarToFloat(centerX1),
    681             SkScalarToFloat(radius0),
    682             SkScalarToFloat(SkScalarMul(radius0, radius0)),
    683             SkScalarToFloat(diffRadius)
    684         };
    685 
    686         uman.set1fv(fVSParamUni, 0, 6, values);
    687         uman.set1fv(fFSParamUni, 0, 6, values);
    688         fCachedCenter = centerX1;
    689         fCachedRadius = radius0;
    690         fCachedDiffRadius = diffRadius;
    691     }
    692 }
    693 
    694 GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffect,
    695                                                    const GrGLCaps&) {
    696     enum {
    697         kIsDegenerate = 1 << kMatrixKeyBitCnt,
    698     };
    699 
    700     EffectKey key = GenMatrixKey(drawEffect);
    701     if (drawEffect.castEffect<GrConical2Gradient>().isDegenerate()) {
    702         key |= kIsDegenerate;
    703     }
    704     return key;
    705 }
    706 
    707 /////////////////////////////////////////////////////////////////////
    708 
    709 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const {
    710     SkASSERT(NULL != context);
    711     SkASSERT(fPtsToUnit.isIdentity());
    712     // invert the localM, translate to center1, rotate so center2 is on x axis.
    713     SkMatrix matrix;
    714     if (!this->getLocalMatrix().invert(&matrix)) {
    715         return NULL;
    716     }
    717     matrix.postTranslate(-fCenter1.fX, -fCenter1.fY);
    718 
    719     SkPoint diff = fCenter2 - fCenter1;
    720     SkScalar diffLen = diff.length();
    721     if (0 != diffLen) {
    722         SkScalar invDiffLen = SkScalarInvert(diffLen);
    723         SkMatrix rot;
    724         rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
    725                        SkScalarMul(invDiffLen, diff.fX));
    726         matrix.postConcat(rot);
    727     }
    728 
    729     return GrConical2Gradient::Create(context, *this, matrix, fTileMode);
    730 }
    731 
    732 #else
    733 
    734 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&) const {
    735     SkDEBUGFAIL("Should not call in GPU-less build");
    736     return NULL;
    737 }
    738 
    739 #endif
    740 
    741 #ifdef SK_DEVELOPER
    742 void SkTwoPointConicalGradient::toString(SkString* str) const {
    743     str->append("SkTwoPointConicalGradient: (");
    744 
    745     str->append("center1: (");
    746     str->appendScalar(fCenter1.fX);
    747     str->append(", ");
    748     str->appendScalar(fCenter1.fY);
    749     str->append(") radius1: ");
    750     str->appendScalar(fRadius1);
    751     str->append(" ");
    752 
    753     str->append("center2: (");
    754     str->appendScalar(fCenter2.fX);
    755     str->append(", ");
    756     str->appendScalar(fCenter2.fY);
    757     str->append(") radius2: ");
    758     str->appendScalar(fRadius2);
    759     str->append(" ");
    760 
    761     this->INHERITED::toString(str);
    762 
    763     str->append(")");
    764 }
    765 #endif
    766