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     : SkGradientShaderBase(desc)
     57     , fStart(pts[0])
     58     , fEnd(pts[1]) {
     59     pts_to_unit_matrix(pts, &fPtsToUnit);
     60 }
     61 
     62 SkLinearGradient::SkLinearGradient(SkFlattenableReadBuffer& buffer)
     63     : INHERITED(buffer)
     64     , fStart(buffer.readPoint())
     65     , fEnd(buffer.readPoint()) {
     66 }
     67 
     68 void SkLinearGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
     69     this->INHERITED::flatten(buffer);
     70     buffer.writePoint(fStart);
     71     buffer.writePoint(fEnd);
     72 }
     73 
     74 bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint,
     75                                  const SkMatrix& matrix) {
     76     if (!this->INHERITED::setContext(device, paint, matrix)) {
     77         return false;
     78     }
     79 
     80     unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
     81     if ((fDstToIndex.getType() & ~mask) == 0) {
     82         // when we dither, we are (usually) not const-in-Y
     83         if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
     84             // only claim this if we do have a 16bit mode (i.e. none of our
     85             // colors have alpha), and if we are not dithering (which obviously
     86             // is not const in Y).
     87             fFlags |= SkShader::kConstInY16_Flag;
     88         }
     89     }
     90     return true;
     91 }
     92 
     93 #define NO_CHECK_ITER               \
     94     do {                            \
     95     unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \
     96     SkASSERT(fi <= 0xFF);           \
     97     fx += dx;                       \
     98     *dstC++ = cache[toggle + fi];   \
     99     toggle = next_dither_toggle(toggle); \
    100     } while (0)
    101 
    102 namespace {
    103 
    104 typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
    105                                 SkPMColor* dstC, const SkPMColor* cache,
    106                                 int toggle, int count);
    107 
    108 // Linear interpolation (lerp) is unnecessary if there are no sharp
    109 // discontinuities in the gradient - which must be true if there are
    110 // only 2 colors - but it's cheap.
    111 void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
    112                                     SkPMColor* SK_RESTRICT dstC,
    113                                     const SkPMColor* SK_RESTRICT cache,
    114                                     int toggle, int count) {
    115     // We're a vertical gradient, so no change in a span.
    116     // If colors change sharply across the gradient, dithering is
    117     // insufficient (it subsamples the color space) and we need to lerp.
    118     unsigned fullIndex = proc(fx);
    119     unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
    120     unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
    121 
    122     int index0 = fi + toggle;
    123     int index1 = index0;
    124     if (fi < SkGradientShaderBase::kCache32Count - 1) {
    125         index1 += 1;
    126     }
    127     SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
    128     index0 ^= SkGradientShaderBase::kDitherStride32;
    129     index1 ^= SkGradientShaderBase::kDitherStride32;
    130     SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
    131     sk_memset32_dither(dstC, lerp, dlerp, count);
    132 }
    133 
    134 void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
    135                             SkPMColor* SK_RESTRICT dstC,
    136                             const SkPMColor* SK_RESTRICT cache,
    137                             int toggle, int count) {
    138     SkClampRange range;
    139     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
    140 
    141     if ((count = range.fCount0) > 0) {
    142         sk_memset32_dither(dstC,
    143             cache[toggle + range.fV0],
    144             cache[next_dither_toggle(toggle) + range.fV0],
    145             count);
    146         dstC += count;
    147     }
    148     if ((count = range.fCount1) > 0) {
    149         int unroll = count >> 3;
    150         fx = range.fFx1;
    151         for (int i = 0; i < unroll; i++) {
    152             NO_CHECK_ITER;  NO_CHECK_ITER;
    153             NO_CHECK_ITER;  NO_CHECK_ITER;
    154             NO_CHECK_ITER;  NO_CHECK_ITER;
    155             NO_CHECK_ITER;  NO_CHECK_ITER;
    156         }
    157         if ((count &= 7) > 0) {
    158             do {
    159                 NO_CHECK_ITER;
    160             } while (--count != 0);
    161         }
    162     }
    163     if ((count = range.fCount2) > 0) {
    164         sk_memset32_dither(dstC,
    165             cache[toggle + range.fV1],
    166             cache[next_dither_toggle(toggle) + range.fV1],
    167             count);
    168     }
    169 }
    170 
    171 void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
    172                              SkPMColor* SK_RESTRICT dstC,
    173                              const SkPMColor* SK_RESTRICT cache,
    174                              int toggle, int count) {
    175     do {
    176         unsigned fi = mirror_8bits(fx >> 8);
    177         SkASSERT(fi <= 0xFF);
    178         fx += dx;
    179         *dstC++ = cache[toggle + fi];
    180         toggle = next_dither_toggle(toggle);
    181     } while (--count != 0);
    182 }
    183 
    184 void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
    185         SkPMColor* SK_RESTRICT dstC,
    186         const SkPMColor* SK_RESTRICT cache,
    187         int toggle, int count) {
    188     do {
    189         unsigned fi = repeat_8bits(fx >> 8);
    190         SkASSERT(fi <= 0xFF);
    191         fx += dx;
    192         *dstC++ = cache[toggle + fi];
    193         toggle = next_dither_toggle(toggle);
    194     } while (--count != 0);
    195 }
    196 
    197 }
    198 
    199 void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
    200                                 int count) {
    201     SkASSERT(count > 0);
    202 
    203     SkPoint             srcPt;
    204     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    205     TileProc            proc = fTileProc;
    206     const SkPMColor* SK_RESTRICT cache = this->getCache32();
    207     int                 toggle = init_dither_toggle(x, y);
    208 
    209     if (fDstToIndexClass != kPerspective_MatrixClass) {
    210         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    211                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    212         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
    213 
    214         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    215             SkFixed dxStorage[1];
    216             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
    217             dx = dxStorage[0];
    218         } else {
    219             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    220             dx = SkScalarToFixed(fDstToIndex.getScaleX());
    221         }
    222 
    223         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
    224         if (0 == dx) {
    225             shadeProc = shadeSpan_linear_vertical_lerp;
    226         } else if (SkShader::kClamp_TileMode == fTileMode) {
    227             shadeProc = shadeSpan_linear_clamp;
    228         } else if (SkShader::kMirror_TileMode == fTileMode) {
    229             shadeProc = shadeSpan_linear_mirror;
    230         } else {
    231             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
    232         }
    233         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
    234     } else {
    235         SkScalar    dstX = SkIntToScalar(x);
    236         SkScalar    dstY = SkIntToScalar(y);
    237         do {
    238             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    239             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
    240             SkASSERT(fi <= 0xFFFF);
    241             *dstC++ = cache[toggle + (fi >> kCache32Shift)];
    242             toggle = next_dither_toggle(toggle);
    243             dstX += SK_Scalar1;
    244         } while (--count != 0);
    245     }
    246 }
    247 
    248 SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap,
    249                                                 SkMatrix* matrix,
    250                                                 TileMode xy[]) const {
    251     if (bitmap) {
    252         this->getGradientTableBitmap(bitmap);
    253     }
    254     if (matrix) {
    255         matrix->preConcat(fPtsToUnit);
    256     }
    257     if (xy) {
    258         xy[0] = fTileMode;
    259         xy[1] = kClamp_TileMode;
    260     }
    261     return kLinear_BitmapType;
    262 }
    263 
    264 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
    265     if (info) {
    266         commonAsAGradient(info);
    267         info->fPoint[0] = fStart;
    268         info->fPoint[1] = fEnd;
    269     }
    270     return kLinear_GradientType;
    271 }
    272 
    273 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
    274                             int count) {
    275     if (reinterpret_cast<uintptr_t>(dst) & 2) {
    276         *dst++ = value;
    277         count -= 1;
    278         SkTSwap(value, other);
    279     }
    280 
    281     sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
    282 
    283     if (count & 1) {
    284         dst[count - 1] = value;
    285     }
    286 }
    287 
    288 #define NO_CHECK_ITER_16                \
    289     do {                                \
    290     unsigned fi = fx >> SkGradientShaderBase::kCache16Shift;  \
    291     SkASSERT(fi < SkGradientShaderBase::kCache16Count);       \
    292     fx += dx;                           \
    293     *dstC++ = cache[toggle + fi];       \
    294     toggle = next_dither_toggle16(toggle);            \
    295     } while (0)
    296 
    297 namespace {
    298 
    299 typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
    300                                   uint16_t* dstC, const uint16_t* cache,
    301                                   int toggle, int count);
    302 
    303 void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
    304                                  uint16_t* SK_RESTRICT dstC,
    305                                  const uint16_t* SK_RESTRICT cache,
    306                                  int toggle, int count) {
    307     // we're a vertical gradient, so no change in a span
    308     unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
    309     SkASSERT(fi < SkGradientShaderBase::kCache16Count);
    310     dither_memset16(dstC, cache[toggle + fi],
    311         cache[next_dither_toggle16(toggle) + fi], count);
    312 }
    313 
    314 void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
    315                               uint16_t* SK_RESTRICT dstC,
    316                               const uint16_t* SK_RESTRICT cache,
    317                               int toggle, int count) {
    318     SkClampRange range;
    319     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
    320 
    321     if ((count = range.fCount0) > 0) {
    322         dither_memset16(dstC,
    323             cache[toggle + range.fV0],
    324             cache[next_dither_toggle16(toggle) + range.fV0],
    325             count);
    326         dstC += count;
    327     }
    328     if ((count = range.fCount1) > 0) {
    329         int unroll = count >> 3;
    330         fx = range.fFx1;
    331         for (int i = 0; i < unroll; i++) {
    332             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
    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         }
    337         if ((count &= 7) > 0) {
    338             do {
    339                 NO_CHECK_ITER_16;
    340             } while (--count != 0);
    341         }
    342     }
    343     if ((count = range.fCount2) > 0) {
    344         dither_memset16(dstC,
    345             cache[toggle + range.fV1],
    346             cache[next_dither_toggle16(toggle) + range.fV1],
    347             count);
    348     }
    349 }
    350 
    351 void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
    352                                uint16_t* SK_RESTRICT dstC,
    353                                const uint16_t* SK_RESTRICT cache,
    354                                int toggle, int count) {
    355     do {
    356         unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift,
    357                                         SkGradientShaderBase::kCache16Bits);
    358         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
    359         fx += dx;
    360         *dstC++ = cache[toggle + fi];
    361         toggle = next_dither_toggle16(toggle);
    362     } while (--count != 0);
    363 }
    364 
    365 void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
    366                                uint16_t* SK_RESTRICT dstC,
    367                                const uint16_t* SK_RESTRICT cache,
    368                                int toggle, int count) {
    369     do {
    370         unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift,
    371                                   SkGradientShaderBase::kCache16Bits);
    372         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
    373         fx += dx;
    374         *dstC++ = cache[toggle + fi];
    375         toggle = next_dither_toggle16(toggle);
    376     } while (--count != 0);
    377 }
    378 }
    379 
    380 void SkLinearGradient::shadeSpan16(int x, int y,
    381                                   uint16_t* SK_RESTRICT dstC, int count) {
    382     SkASSERT(count > 0);
    383 
    384     SkPoint             srcPt;
    385     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    386     TileProc            proc = fTileProc;
    387     const uint16_t* SK_RESTRICT cache = this->getCache16();
    388     int                 toggle = init_dither_toggle16(x, y);
    389 
    390     if (fDstToIndexClass != kPerspective_MatrixClass) {
    391         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    392                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    393         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
    394 
    395         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    396             SkFixed dxStorage[1];
    397             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
    398             dx = dxStorage[0];
    399         } else {
    400             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    401             dx = SkScalarToFixed(fDstToIndex.getScaleX());
    402         }
    403 
    404         LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
    405         if (SkFixedNearlyZero(dx)) {
    406             shadeProc = shadeSpan16_linear_vertical;
    407         } else if (SkShader::kClamp_TileMode == fTileMode) {
    408             shadeProc = shadeSpan16_linear_clamp;
    409         } else if (SkShader::kMirror_TileMode == fTileMode) {
    410             shadeProc = shadeSpan16_linear_mirror;
    411         } else {
    412             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
    413         }
    414         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
    415     } else {
    416         SkScalar    dstX = SkIntToScalar(x);
    417         SkScalar    dstY = SkIntToScalar(y);
    418         do {
    419             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    420             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
    421             SkASSERT(fi <= 0xFFFF);
    422 
    423             int index = fi >> kCache16Shift;
    424             *dstC++ = cache[toggle + index];
    425             toggle = next_dither_toggle16(toggle);
    426 
    427             dstX += SK_Scalar1;
    428         } while (--count != 0);
    429     }
    430 }
    431 
    432 #if SK_SUPPORT_GPU
    433 
    434 #include "GrTBackendEffectFactory.h"
    435 
    436 /////////////////////////////////////////////////////////////////////
    437 
    438 class GrGLLinearGradient : public GrGLGradientEffect {
    439 public:
    440 
    441     GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrDrawEffect&)
    442                        : INHERITED (factory) { }
    443 
    444     virtual ~GrGLLinearGradient() { }
    445 
    446     virtual void emitCode(GrGLShaderBuilder*,
    447                           const GrDrawEffect&,
    448                           EffectKey,
    449                           const char* outputColor,
    450                           const char* inputColor,
    451                           const TransformedCoordsArray&,
    452                           const TextureSamplerArray&) SK_OVERRIDE;
    453 
    454     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    455         return GenBaseGradientKey(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(SkRandom* 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 TransformedCoordsArray& coords,
    527                                   const TextureSamplerArray& samplers) {
    528     this->emitUniforms(builder, key);
    529     SkString t = builder->ensureFSCoords2D(coords, 0);
    530     t.append(".x");
    531     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
    532 }
    533 
    534 /////////////////////////////////////////////////////////////////////
    535 
    536 GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const {
    537     SkASSERT(NULL != context);
    538     SkMatrix matrix;
    539     if (!this->getLocalMatrix().invert(&matrix)) {
    540         return NULL;
    541     }
    542     matrix.postConcat(fPtsToUnit);
    543     return GrLinearGradient::Create(context, *this, matrix, fTileMode);
    544 }
    545 
    546 #else
    547 
    548 GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const {
    549     SkDEBUGFAIL("Should not call in GPU-less build");
    550     return NULL;
    551 }
    552 
    553 #endif
    554 
    555 #ifdef SK_DEVELOPER
    556 void SkLinearGradient::toString(SkString* str) const {
    557     str->append("SkLinearGradient (");
    558 
    559     str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
    560     str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
    561 
    562     this->INHERITED::toString(str);
    563 
    564     str->append(")");
    565 }
    566 #endif
    567