Home | History | Annotate | Download | only in gradients
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkLinearGradient.h"
      9 
     10 static inline int repeat_bits(int x, const int bits) {
     11     return x & ((1 << bits) - 1);
     12 }
     13 
     14 static inline int repeat_8bits(int x) {
     15     return x & 0xFF;
     16 }
     17 
     18 // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
     19 // See http://code.google.com/p/skia/issues/detail?id=472
     20 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
     21 #pragma optimize("", off)
     22 #endif
     23 
     24 static inline int mirror_bits(int x, const int bits) {
     25     if (x & (1 << bits)) {
     26         x = ~x;
     27     }
     28     return x & ((1 << bits) - 1);
     29 }
     30 
     31 static inline int mirror_8bits(int x) {
     32     if (x & 256) {
     33         x = ~x;
     34     }
     35     return x & 255;
     36 }
     37 
     38 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
     39 #pragma optimize("", on)
     40 #endif
     41 
     42 static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
     43     SkVector    vec = pts[1] - pts[0];
     44     SkScalar    mag = vec.length();
     45     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
     46 
     47     vec.scale(inv);
     48     matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
     49     matrix->postTranslate(-pts[0].fX, -pts[0].fY);
     50     matrix->postScale(inv, inv);
     51 }
     52 
     53 ///////////////////////////////////////////////////////////////////////////////
     54 
     55 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc,
     56                                    const SkMatrix* localMatrix)
     57     : SkGradientShaderBase(desc, localMatrix)
     58     , fStart(pts[0])
     59     , fEnd(pts[1]) {
     60     pts_to_unit_matrix(pts, &fPtsToUnit);
     61 }
     62 
     63 SkLinearGradient::SkLinearGradient(SkReadBuffer& buffer)
     64     : INHERITED(buffer)
     65     , fStart(buffer.readPoint())
     66     , fEnd(buffer.readPoint()) {
     67 }
     68 
     69 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
     70     this->INHERITED::flatten(buffer);
     71     buffer.writePoint(fStart);
     72     buffer.writePoint(fEnd);
     73 }
     74 
     75 size_t SkLinearGradient::contextSize() const {
     76     return sizeof(LinearGradientContext);
     77 }
     78 
     79 SkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void* storage) const {
     80     return SkNEW_PLACEMENT_ARGS(storage, LinearGradientContext, (*this, rec));
     81 }
     82 
     83 SkLinearGradient::LinearGradientContext::LinearGradientContext(
     84         const SkLinearGradient& shader, const ContextRec& rec)
     85     : INHERITED(shader, rec)
     86 {
     87     unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
     88     if ((fDstToIndex.getType() & ~mask) == 0) {
     89         // when we dither, we are (usually) not const-in-Y
     90         if ((fFlags & SkShader::kHasSpan16_Flag) && !rec.fPaint->isDither()) {
     91             // only claim this if we do have a 16bit mode (i.e. none of our
     92             // colors have alpha), and if we are not dithering (which obviously
     93             // is not const in Y).
     94             fFlags |= SkShader::kConstInY16_Flag;
     95         }
     96     }
     97 }
     98 
     99 #define NO_CHECK_ITER               \
    100     do {                            \
    101     unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \
    102     SkASSERT(fi <= 0xFF);           \
    103     fx += dx;                       \
    104     *dstC++ = cache[toggle + fi];   \
    105     toggle = next_dither_toggle(toggle); \
    106     } while (0)
    107 
    108 namespace {
    109 
    110 typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
    111                                 SkPMColor* dstC, const SkPMColor* cache,
    112                                 int toggle, int count);
    113 
    114 // Linear interpolation (lerp) is unnecessary if there are no sharp
    115 // discontinuities in the gradient - which must be true if there are
    116 // only 2 colors - but it's cheap.
    117 void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
    118                                     SkPMColor* SK_RESTRICT dstC,
    119                                     const SkPMColor* SK_RESTRICT cache,
    120                                     int toggle, int count) {
    121     // We're a vertical gradient, so no change in a span.
    122     // If colors change sharply across the gradient, dithering is
    123     // insufficient (it subsamples the color space) and we need to lerp.
    124     unsigned fullIndex = proc(fx);
    125     unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
    126     unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
    127 
    128     int index0 = fi + toggle;
    129     int index1 = index0;
    130     if (fi < SkGradientShaderBase::kCache32Count - 1) {
    131         index1 += 1;
    132     }
    133     SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
    134     index0 ^= SkGradientShaderBase::kDitherStride32;
    135     index1 ^= SkGradientShaderBase::kDitherStride32;
    136     SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
    137     sk_memset32_dither(dstC, lerp, dlerp, count);
    138 }
    139 
    140 void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
    141                             SkPMColor* SK_RESTRICT dstC,
    142                             const SkPMColor* SK_RESTRICT cache,
    143                             int toggle, int count) {
    144     SkClampRange range;
    145     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
    146 
    147     if ((count = range.fCount0) > 0) {
    148         sk_memset32_dither(dstC,
    149             cache[toggle + range.fV0],
    150             cache[next_dither_toggle(toggle) + range.fV0],
    151             count);
    152         dstC += count;
    153     }
    154     if ((count = range.fCount1) > 0) {
    155         int unroll = count >> 3;
    156         fx = range.fFx1;
    157         for (int i = 0; i < unroll; i++) {
    158             NO_CHECK_ITER;  NO_CHECK_ITER;
    159             NO_CHECK_ITER;  NO_CHECK_ITER;
    160             NO_CHECK_ITER;  NO_CHECK_ITER;
    161             NO_CHECK_ITER;  NO_CHECK_ITER;
    162         }
    163         if ((count &= 7) > 0) {
    164             do {
    165                 NO_CHECK_ITER;
    166             } while (--count != 0);
    167         }
    168     }
    169     if ((count = range.fCount2) > 0) {
    170         sk_memset32_dither(dstC,
    171             cache[toggle + range.fV1],
    172             cache[next_dither_toggle(toggle) + range.fV1],
    173             count);
    174     }
    175 }
    176 
    177 void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
    178                              SkPMColor* SK_RESTRICT dstC,
    179                              const SkPMColor* SK_RESTRICT cache,
    180                              int toggle, int count) {
    181     do {
    182         unsigned fi = mirror_8bits(fx >> 8);
    183         SkASSERT(fi <= 0xFF);
    184         fx += dx;
    185         *dstC++ = cache[toggle + fi];
    186         toggle = next_dither_toggle(toggle);
    187     } while (--count != 0);
    188 }
    189 
    190 void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
    191         SkPMColor* SK_RESTRICT dstC,
    192         const SkPMColor* SK_RESTRICT cache,
    193         int toggle, int count) {
    194     do {
    195         unsigned fi = repeat_8bits(fx >> 8);
    196         SkASSERT(fi <= 0xFF);
    197         fx += dx;
    198         *dstC++ = cache[toggle + fi];
    199         toggle = next_dither_toggle(toggle);
    200     } while (--count != 0);
    201 }
    202 
    203 }
    204 
    205 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
    206                                                         int count) {
    207     SkASSERT(count > 0);
    208 
    209     const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
    210 
    211     SkPoint             srcPt;
    212     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    213     TileProc            proc = linearGradient.fTileProc;
    214     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
    215     int                 toggle = init_dither_toggle(x, y);
    216 
    217     if (fDstToIndexClass != kPerspective_MatrixClass) {
    218         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    219                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    220         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
    221 
    222         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    223             SkFixed dxStorage[1];
    224             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
    225             dx = dxStorage[0];
    226         } else {
    227             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    228             dx = SkScalarToFixed(fDstToIndex.getScaleX());
    229         }
    230 
    231         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
    232         if (0 == dx) {
    233             shadeProc = shadeSpan_linear_vertical_lerp;
    234         } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
    235             shadeProc = shadeSpan_linear_clamp;
    236         } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
    237             shadeProc = shadeSpan_linear_mirror;
    238         } else {
    239             SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
    240         }
    241         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
    242     } else {
    243         SkScalar    dstX = SkIntToScalar(x);
    244         SkScalar    dstY = SkIntToScalar(y);
    245         do {
    246             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    247             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
    248             SkASSERT(fi <= 0xFFFF);
    249             *dstC++ = cache[toggle + (fi >> kCache32Shift)];
    250             toggle = next_dither_toggle(toggle);
    251             dstX += SK_Scalar1;
    252         } while (--count != 0);
    253     }
    254 }
    255 
    256 SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap,
    257                                                 SkMatrix* matrix,
    258                                                 TileMode xy[]) const {
    259     if (bitmap) {
    260         this->getGradientTableBitmap(bitmap);
    261     }
    262     if (matrix) {
    263         matrix->preConcat(fPtsToUnit);
    264     }
    265     if (xy) {
    266         xy[0] = fTileMode;
    267         xy[1] = kClamp_TileMode;
    268     }
    269     return kLinear_BitmapType;
    270 }
    271 
    272 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
    273     if (info) {
    274         commonAsAGradient(info);
    275         info->fPoint[0] = fStart;
    276         info->fPoint[1] = fEnd;
    277     }
    278     return kLinear_GradientType;
    279 }
    280 
    281 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
    282                             int count) {
    283     if (reinterpret_cast<uintptr_t>(dst) & 2) {
    284         *dst++ = value;
    285         count -= 1;
    286         SkTSwap(value, other);
    287     }
    288 
    289     sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
    290 
    291     if (count & 1) {
    292         dst[count - 1] = value;
    293     }
    294 }
    295 
    296 #define NO_CHECK_ITER_16                \
    297     do {                                \
    298     unsigned fi = fx >> SkGradientShaderBase::kCache16Shift;  \
    299     SkASSERT(fi < SkGradientShaderBase::kCache16Count);       \
    300     fx += dx;                           \
    301     *dstC++ = cache[toggle + fi];       \
    302     toggle = next_dither_toggle16(toggle);            \
    303     } while (0)
    304 
    305 namespace {
    306 
    307 typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
    308                                   uint16_t* dstC, const uint16_t* cache,
    309                                   int toggle, int count);
    310 
    311 void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
    312                                  uint16_t* SK_RESTRICT dstC,
    313                                  const uint16_t* SK_RESTRICT cache,
    314                                  int toggle, int count) {
    315     // we're a vertical gradient, so no change in a span
    316     unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
    317     SkASSERT(fi < SkGradientShaderBase::kCache16Count);
    318     dither_memset16(dstC, cache[toggle + fi],
    319         cache[next_dither_toggle16(toggle) + fi], count);
    320 }
    321 
    322 void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
    323                               uint16_t* SK_RESTRICT dstC,
    324                               const uint16_t* SK_RESTRICT cache,
    325                               int toggle, int count) {
    326     SkClampRange range;
    327     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
    328 
    329     if ((count = range.fCount0) > 0) {
    330         dither_memset16(dstC,
    331             cache[toggle + range.fV0],
    332             cache[next_dither_toggle16(toggle) + range.fV0],
    333             count);
    334         dstC += count;
    335     }
    336     if ((count = range.fCount1) > 0) {
    337         int unroll = count >> 3;
    338         fx = range.fFx1;
    339         for (int i = 0; i < unroll; i++) {
    340             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
    341             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
    342             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
    343             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
    344         }
    345         if ((count &= 7) > 0) {
    346             do {
    347                 NO_CHECK_ITER_16;
    348             } while (--count != 0);
    349         }
    350     }
    351     if ((count = range.fCount2) > 0) {
    352         dither_memset16(dstC,
    353             cache[toggle + range.fV1],
    354             cache[next_dither_toggle16(toggle) + range.fV1],
    355             count);
    356     }
    357 }
    358 
    359 void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
    360                                uint16_t* SK_RESTRICT dstC,
    361                                const uint16_t* SK_RESTRICT cache,
    362                                int toggle, int count) {
    363     do {
    364         unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift,
    365                                         SkGradientShaderBase::kCache16Bits);
    366         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
    367         fx += dx;
    368         *dstC++ = cache[toggle + fi];
    369         toggle = next_dither_toggle16(toggle);
    370     } while (--count != 0);
    371 }
    372 
    373 void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
    374                                uint16_t* SK_RESTRICT dstC,
    375                                const uint16_t* SK_RESTRICT cache,
    376                                int toggle, int count) {
    377     do {
    378         unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift,
    379                                   SkGradientShaderBase::kCache16Bits);
    380         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
    381         fx += dx;
    382         *dstC++ = cache[toggle + fi];
    383         toggle = next_dither_toggle16(toggle);
    384     } while (--count != 0);
    385 }
    386 }
    387 
    388 static bool fixed_nearly_zero(SkFixed x) {
    389     return SkAbs32(x) < (SK_Fixed1 >> 12);
    390 }
    391 
    392 void SkLinearGradient::LinearGradientContext::shadeSpan16(int x, int y,
    393                                                           uint16_t* SK_RESTRICT dstC, int count) {
    394     SkASSERT(count > 0);
    395 
    396     const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
    397 
    398     SkPoint             srcPt;
    399     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    400     TileProc            proc = linearGradient.fTileProc;
    401     const uint16_t* SK_RESTRICT cache = fCache->getCache16();
    402     int                 toggle = init_dither_toggle16(x, y);
    403 
    404     if (fDstToIndexClass != kPerspective_MatrixClass) {
    405         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    406                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    407         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
    408 
    409         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    410             SkFixed dxStorage[1];
    411             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
    412             dx = dxStorage[0];
    413         } else {
    414             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    415             dx = SkScalarToFixed(fDstToIndex.getScaleX());
    416         }
    417 
    418         LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
    419         if (fixed_nearly_zero(dx)) {
    420             shadeProc = shadeSpan16_linear_vertical;
    421         } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
    422             shadeProc = shadeSpan16_linear_clamp;
    423         } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
    424             shadeProc = shadeSpan16_linear_mirror;
    425         } else {
    426             SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
    427         }
    428         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
    429     } else {
    430         SkScalar    dstX = SkIntToScalar(x);
    431         SkScalar    dstY = SkIntToScalar(y);
    432         do {
    433             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    434             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
    435             SkASSERT(fi <= 0xFFFF);
    436 
    437             int index = fi >> kCache16Shift;
    438             *dstC++ = cache[toggle + index];
    439             toggle = next_dither_toggle16(toggle);
    440 
    441             dstX += SK_Scalar1;
    442         } while (--count != 0);
    443     }
    444 }
    445 
    446 #if SK_SUPPORT_GPU
    447 
    448 #include "GrTBackendEffectFactory.h"
    449 #include "SkGr.h"
    450 
    451 /////////////////////////////////////////////////////////////////////
    452 
    453 class GrGLLinearGradient : public GrGLGradientEffect {
    454 public:
    455 
    456     GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrDrawEffect&)
    457                        : INHERITED (factory) { }
    458 
    459     virtual ~GrGLLinearGradient() { }
    460 
    461     virtual void emitCode(GrGLShaderBuilder*,
    462                           const GrDrawEffect&,
    463                           EffectKey,
    464                           const char* outputColor,
    465                           const char* inputColor,
    466                           const TransformedCoordsArray&,
    467                           const TextureSamplerArray&) SK_OVERRIDE;
    468 
    469     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    470         return GenBaseGradientKey(drawEffect);
    471     }
    472 
    473 private:
    474 
    475     typedef GrGLGradientEffect INHERITED;
    476 };
    477 
    478 /////////////////////////////////////////////////////////////////////
    479 
    480 class GrLinearGradient : public GrGradientEffect {
    481 public:
    482 
    483     static GrEffectRef* Create(GrContext* ctx,
    484                                const SkLinearGradient& shader,
    485                                const SkMatrix& matrix,
    486                                SkShader::TileMode tm) {
    487         AutoEffectUnref effect(SkNEW_ARGS(GrLinearGradient, (ctx, shader, matrix, tm)));
    488         return CreateEffectRef(effect);
    489     }
    490 
    491     virtual ~GrLinearGradient() { }
    492 
    493     static const char* Name() { return "Linear Gradient"; }
    494     const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    495         return GrTBackendEffectFactory<GrLinearGradient>::getInstance();
    496     }
    497 
    498     typedef GrGLLinearGradient GLEffect;
    499 
    500 private:
    501     GrLinearGradient(GrContext* ctx,
    502                      const SkLinearGradient& shader,
    503                      const SkMatrix& matrix,
    504                      SkShader::TileMode tm)
    505         : INHERITED(ctx, shader, matrix, tm) { }
    506     GR_DECLARE_EFFECT_TEST;
    507 
    508     typedef GrGradientEffect INHERITED;
    509 };
    510 
    511 /////////////////////////////////////////////////////////////////////
    512 
    513 GR_DEFINE_EFFECT_TEST(GrLinearGradient);
    514 
    515 GrEffectRef* GrLinearGradient::TestCreate(SkRandom* random,
    516                                           GrContext* context,
    517                                           const GrDrawTargetCaps&,
    518                                           GrTexture**) {
    519     SkPoint points[] = {{random->nextUScalar1(), random->nextUScalar1()},
    520                         {random->nextUScalar1(), random->nextUScalar1()}};
    521 
    522     SkColor colors[kMaxRandomGradientColors];
    523     SkScalar stopsArray[kMaxRandomGradientColors];
    524     SkScalar* stops = stopsArray;
    525     SkShader::TileMode tm;
    526     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    527     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
    528                                                                  colors, stops, colorCount,
    529                                                                  tm));
    530     SkPaint paint;
    531     GrColor grColor;
    532     GrEffectRef* effect;
    533     shader->asNewEffect(context, paint, NULL, &grColor, &effect);
    534     return effect;
    535 }
    536 
    537 /////////////////////////////////////////////////////////////////////
    538 
    539 void GrGLLinearGradient::emitCode(GrGLShaderBuilder* builder,
    540                                   const GrDrawEffect&,
    541                                   EffectKey key,
    542                                   const char* outputColor,
    543                                   const char* inputColor,
    544                                   const TransformedCoordsArray& coords,
    545                                   const TextureSamplerArray& samplers) {
    546     this->emitUniforms(builder, key);
    547     SkString t = builder->ensureFSCoords2D(coords, 0);
    548     t.append(".x");
    549     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
    550 }
    551 
    552 /////////////////////////////////////////////////////////////////////
    553 
    554 bool SkLinearGradient::asNewEffect(GrContext* context, const SkPaint& paint,
    555                                    const SkMatrix* localMatrix, GrColor* grColor,
    556                                    GrEffectRef** grEffect)  const {
    557     SkASSERT(NULL != context);
    558 
    559     SkMatrix matrix;
    560     if (!this->getLocalMatrix().invert(&matrix)) {
    561         return false;
    562     }
    563     if (localMatrix) {
    564         SkMatrix inv;
    565         if (!localMatrix->invert(&inv)) {
    566             return false;
    567         }
    568         matrix.postConcat(inv);
    569     }
    570     matrix.postConcat(fPtsToUnit);
    571 
    572     *grColor = SkColor2GrColorJustAlpha(paint.getColor());
    573     *grEffect = GrLinearGradient::Create(context, *this, matrix, fTileMode);
    574 
    575     return true;
    576 }
    577 
    578 #else
    579 
    580 bool SkLinearGradient::asNewEffect(GrContext* context, const SkPaint& paint,
    581                                    const SkMatrix* localMatrix, GrColor* grColor,
    582                                    GrEffectRef** grEffect)  const {
    583     SkDEBUGFAIL("Should not call in GPU-less build");
    584     return false;
    585 }
    586 
    587 #endif
    588 
    589 #ifndef SK_IGNORE_TO_STRING
    590 void SkLinearGradient::toString(SkString* str) const {
    591     str->append("SkLinearGradient (");
    592 
    593     str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
    594     str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
    595 
    596     this->INHERITED::toString(str);
    597 
    598     str->append(")");
    599 }
    600 #endif
    601