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