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 "Sk4fLinearGradient.h"
      9 #include "SkLinearGradient.h"
     10 
     11 // define to test the 4f gradient path
     12 // #define FORCE_4F_CONTEXT
     13 
     14 static const float kInv255Float = 1.0f / 255;
     15 
     16 static inline int repeat_8bits(int x) {
     17     return x & 0xFF;
     18 }
     19 
     20 // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
     21 // See http://code.google.com/p/skia/issues/detail?id=472
     22 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
     23 #pragma optimize("", off)
     24 #endif
     25 
     26 static inline int mirror_8bits(int x) {
     27     if (x & 256) {
     28         x = ~x;
     29     }
     30     return x & 255;
     31 }
     32 
     33 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
     34 #pragma optimize("", on)
     35 #endif
     36 
     37 static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
     38     SkVector    vec = pts[1] - pts[0];
     39     SkScalar    mag = vec.length();
     40     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
     41 
     42     vec.scale(inv);
     43     SkMatrix matrix;
     44     matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
     45     matrix.postTranslate(-pts[0].fX, -pts[0].fY);
     46     matrix.postScale(inv, inv);
     47     return matrix;
     48 }
     49 
     50 static bool use_4f_context(const SkShader::ContextRec& rec, uint32_t flags) {
     51 #ifdef FORCE_4F_CONTEXT
     52     return true;
     53 #else
     54     // Perspective not supported in 4f yet.
     55     if (rec.fMatrix->hasPerspective()
     56         || (rec.fLocalMatrix && rec.fLocalMatrix->hasPerspective())) {
     57         return false;
     58     }
     59 
     60     return rec.fPreferredDstType == SkShader::ContextRec::kPM4f_DstType
     61         || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag);
     62 #endif
     63 }
     64 
     65 ///////////////////////////////////////////////////////////////////////////////
     66 
     67 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
     68     : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
     69     , fStart(pts[0])
     70     , fEnd(pts[1]) {
     71 }
     72 
     73 SkFlattenable* SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
     74     DescriptorScope desc;
     75     if (!desc.unflatten(buffer)) {
     76         return nullptr;
     77     }
     78     SkPoint pts[2];
     79     pts[0] = buffer.readPoint();
     80     pts[1] = buffer.readPoint();
     81     return SkGradientShader::CreateLinear(pts, desc.fColors, desc.fPos, desc.fCount,
     82                                           desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
     83 }
     84 
     85 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
     86     this->INHERITED::flatten(buffer);
     87     buffer.writePoint(fStart);
     88     buffer.writePoint(fEnd);
     89 }
     90 
     91 size_t SkLinearGradient::contextSize(const ContextRec& rec) const {
     92     return use_4f_context(rec, fGradFlags)
     93         ? sizeof(LinearGradient4fContext)
     94         : sizeof(LinearGradientContext);
     95 }
     96 
     97 SkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void* storage) const {
     98     return use_4f_context(rec, fGradFlags)
     99         ? static_cast<SkShader::Context*>(new (storage) LinearGradient4fContext(*this, rec))
    100         : static_cast<SkShader::Context*>(new (storage) LinearGradientContext(*this, rec));
    101 }
    102 
    103 // This swizzles SkColor into the same component order as SkPMColor, but does not actually
    104 // "pre" multiply the color components.
    105 //
    106 // This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
    107 // SkPMColor from the floats, without having to swizzle each time.
    108 //
    109 static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
    110     return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
    111 }
    112 
    113 SkLinearGradient::LinearGradientContext::LinearGradientContext(
    114         const SkLinearGradient& shader, const ContextRec& ctx)
    115     : INHERITED(shader, ctx)
    116 {
    117     // setup for Sk4f
    118     int count = shader.fColorCount;
    119     fRecs.setCount(count);
    120     Rec* rec = fRecs.begin();
    121     if (shader.fOrigPos) {
    122         rec[0].fPos = 0;
    123         SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;)   // should never get used
    124         for (int i = 1; i < count; ++i) {
    125             rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
    126             float diff = rec[i].fPos - rec[i - 1].fPos;
    127             if (diff > 0) {
    128                 rec[i].fPosScale = 1.0f / diff;
    129             } else {
    130                 rec[i].fPosScale = 0;
    131             }
    132         }
    133         rec[count - 1].fPos = 1;    // overwrite the last value just to be sure we end at 1.0
    134     } else {
    135         // no pos specified, so we compute evenly spaced values
    136         const float scale = float(count - 1);
    137         float invScale = 1.0f / scale;
    138         for (int i = 0; i < count; ++i) {
    139             rec[i].fPos = i * invScale;
    140             rec[i].fPosScale = scale;
    141         }
    142     }
    143 
    144     fApplyAlphaAfterInterp = true;
    145     if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
    146         shader.colorsAreOpaque())
    147     {
    148         fApplyAlphaAfterInterp = false;
    149     }
    150 
    151     if (fApplyAlphaAfterInterp) {
    152         // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
    153         // interpolate in unpremultiplied space first, and then scale by alpha right before we
    154         // convert to SkPMColor bytes.
    155         const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
    156         const Sk4f scale(1, 1, 1, paintAlpha);
    157         for (int i = 0; i < count; ++i) {
    158             uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
    159             rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale;
    160             if (i > 0) {
    161                 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
    162             }
    163         }
    164     } else {
    165         // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
    166         // of converting the floats down to bytes.
    167         unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
    168         for (int i = 0; i < count; ++i) {
    169             SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
    170             pmc = SkAlphaMulQ(pmc, alphaScale);
    171             rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc));
    172             if (i > 0) {
    173                 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
    174             }
    175         }
    176     }
    177 }
    178 
    179 #define NO_CHECK_ITER               \
    180     do {                            \
    181     unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
    182     SkASSERT(fi <= 0xFF);           \
    183     fx += dx;                       \
    184     *dstC++ = cache[toggle + fi];   \
    185     toggle = next_dither_toggle(toggle); \
    186     } while (0)
    187 
    188 namespace {
    189 
    190 typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
    191                                 SkPMColor* dstC, const SkPMColor* cache,
    192                                 int toggle, int count);
    193 
    194 // Linear interpolation (lerp) is unnecessary if there are no sharp
    195 // discontinuities in the gradient - which must be true if there are
    196 // only 2 colors - but it's cheap.
    197 void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
    198                                     SkPMColor* SK_RESTRICT dstC,
    199                                     const SkPMColor* SK_RESTRICT cache,
    200                                     int toggle, int count) {
    201     // We're a vertical gradient, so no change in a span.
    202     // If colors change sharply across the gradient, dithering is
    203     // insufficient (it subsamples the color space) and we need to lerp.
    204     unsigned fullIndex = proc(SkGradFixedToFixed(fx));
    205     unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
    206     unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
    207 
    208     int index0 = fi + toggle;
    209     int index1 = index0;
    210     if (fi < SkGradientShaderBase::kCache32Count - 1) {
    211         index1 += 1;
    212     }
    213     SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
    214     index0 ^= SkGradientShaderBase::kDitherStride32;
    215     index1 ^= SkGradientShaderBase::kDitherStride32;
    216     SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
    217     sk_memset32_dither(dstC, lerp, dlerp, count);
    218 }
    219 
    220 void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
    221                             SkPMColor* SK_RESTRICT dstC,
    222                             const SkPMColor* SK_RESTRICT cache,
    223                             int toggle, int count) {
    224     SkClampRange range;
    225     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
    226     range.validate(count);
    227 
    228     if ((count = range.fCount0) > 0) {
    229         sk_memset32_dither(dstC,
    230             cache[toggle + range.fV0],
    231             cache[next_dither_toggle(toggle) + range.fV0],
    232             count);
    233         dstC += count;
    234     }
    235     if ((count = range.fCount1) > 0) {
    236         int unroll = count >> 3;
    237         fx = range.fFx1;
    238         for (int i = 0; i < unroll; i++) {
    239             NO_CHECK_ITER;  NO_CHECK_ITER;
    240             NO_CHECK_ITER;  NO_CHECK_ITER;
    241             NO_CHECK_ITER;  NO_CHECK_ITER;
    242             NO_CHECK_ITER;  NO_CHECK_ITER;
    243         }
    244         if ((count &= 7) > 0) {
    245             do {
    246                 NO_CHECK_ITER;
    247             } while (--count != 0);
    248         }
    249     }
    250     if ((count = range.fCount2) > 0) {
    251         sk_memset32_dither(dstC,
    252             cache[toggle + range.fV1],
    253             cache[next_dither_toggle(toggle) + range.fV1],
    254             count);
    255     }
    256 }
    257 
    258 void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
    259                              SkPMColor* SK_RESTRICT dstC,
    260                              const SkPMColor* SK_RESTRICT cache,
    261                              int toggle, int count) {
    262     do {
    263         unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
    264         SkASSERT(fi <= 0xFF);
    265         fx += dx;
    266         *dstC++ = cache[toggle + fi];
    267         toggle = next_dither_toggle(toggle);
    268     } while (--count != 0);
    269 }
    270 
    271 void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
    272         SkPMColor* SK_RESTRICT dstC,
    273         const SkPMColor* SK_RESTRICT cache,
    274         int toggle, int count) {
    275     do {
    276         unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
    277         SkASSERT(fi <= 0xFF);
    278         fx += dx;
    279         *dstC++ = cache[toggle + fi];
    280         toggle = next_dither_toggle(toggle);
    281     } while (--count != 0);
    282 }
    283 
    284 }
    285 
    286 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
    287                                                         int count) {
    288     SkASSERT(count > 0);
    289     const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
    290 
    291 // Only use the Sk4f impl when known to be fast.
    292 #if defined(SKNX_IS_FAST)
    293     if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
    294         kLinear_MatrixClass == fDstToIndexClass)
    295     {
    296         this->shade4_clamp(x, y, dstC, count);
    297         return;
    298     }
    299 #endif
    300 
    301     SkPoint             srcPt;
    302     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    303     TileProc            proc = linearGradient.fTileProc;
    304     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
    305     int                 toggle = init_dither_toggle(x, y);
    306 
    307     if (fDstToIndexClass != kPerspective_MatrixClass) {
    308         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    309                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    310         SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX);
    311 
    312         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    313             const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
    314             // todo: do we need a real/high-precision value for dx here?
    315             dx = SkScalarToGradFixed(step.fX);
    316         } else {
    317             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    318             dx = SkScalarToGradFixed(fDstToIndex.getScaleX());
    319         }
    320 
    321         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
    322         if (0 == dx) {
    323             shadeProc = shadeSpan_linear_vertical_lerp;
    324         } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
    325             shadeProc = shadeSpan_linear_clamp;
    326         } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
    327             shadeProc = shadeSpan_linear_mirror;
    328         } else {
    329             SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
    330         }
    331         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
    332     } else {
    333         SkScalar    dstX = SkIntToScalar(x);
    334         SkScalar    dstY = SkIntToScalar(y);
    335         do {
    336             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    337             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
    338             SkASSERT(fi <= 0xFFFF);
    339             *dstC++ = cache[toggle + (fi >> kCache32Shift)];
    340             toggle = next_dither_toggle(toggle);
    341             dstX += SK_Scalar1;
    342         } while (--count != 0);
    343     }
    344 }
    345 
    346 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
    347     if (info) {
    348         commonAsAGradient(info);
    349         info->fPoint[0] = fStart;
    350         info->fPoint[1] = fEnd;
    351     }
    352     return kLinear_GradientType;
    353 }
    354 
    355 #if SK_SUPPORT_GPU
    356 
    357 #include "glsl/GrGLSLCaps.h"
    358 #include "glsl/GrGLSLFragmentShaderBuilder.h"
    359 #include "SkGr.h"
    360 
    361 /////////////////////////////////////////////////////////////////////
    362 
    363 class GrGLLinearGradient : public GrGLGradientEffect {
    364 public:
    365 
    366     GrGLLinearGradient(const GrProcessor&) {}
    367 
    368     virtual ~GrGLLinearGradient() { }
    369 
    370     virtual void emitCode(EmitArgs&) override;
    371 
    372     static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
    373         b->add32(GenBaseGradientKey(processor));
    374     }
    375 
    376 private:
    377 
    378     typedef GrGLGradientEffect INHERITED;
    379 };
    380 
    381 /////////////////////////////////////////////////////////////////////
    382 
    383 class GrLinearGradient : public GrGradientEffect {
    384 public:
    385 
    386     static GrFragmentProcessor* Create(GrContext* ctx,
    387                                        const SkLinearGradient& shader,
    388                                        const SkMatrix& matrix,
    389                                        SkShader::TileMode tm) {
    390         return new GrLinearGradient(ctx, shader, matrix, tm);
    391     }
    392 
    393     virtual ~GrLinearGradient() { }
    394 
    395     const char* name() const override { return "Linear Gradient"; }
    396 
    397 private:
    398     GrLinearGradient(GrContext* ctx,
    399                      const SkLinearGradient& shader,
    400                      const SkMatrix& matrix,
    401                      SkShader::TileMode tm)
    402         : INHERITED(ctx, shader, matrix, tm) {
    403         this->initClassID<GrLinearGradient>();
    404     }
    405 
    406     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
    407         return new GrGLLinearGradient(*this);
    408     }
    409 
    410     virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
    411                                        GrProcessorKeyBuilder* b) const override {
    412         GrGLLinearGradient::GenKey(*this, caps, b);
    413     }
    414 
    415     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
    416 
    417     typedef GrGradientEffect INHERITED;
    418 };
    419 
    420 /////////////////////////////////////////////////////////////////////
    421 
    422 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
    423 
    424 const GrFragmentProcessor* GrLinearGradient::TestCreate(GrProcessorTestData* d) {
    425     SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
    426                         {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
    427 
    428     SkColor colors[kMaxRandomGradientColors];
    429     SkScalar stopsArray[kMaxRandomGradientColors];
    430     SkScalar* stops = stopsArray;
    431     SkShader::TileMode tm;
    432     int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
    433     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
    434                                                                  colors, stops, colorCount,
    435                                                                  tm));
    436     const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
    437         GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
    438     GrAlwaysAssert(fp);
    439     return fp;
    440 }
    441 
    442 /////////////////////////////////////////////////////////////////////
    443 
    444 void GrGLLinearGradient::emitCode(EmitArgs& args) {
    445     const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
    446     this->emitUniforms(args.fUniformHandler, ge);
    447     SkString t = args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0);
    448     t.append(".x");
    449     this->emitColor(args.fFragBuilder,
    450                     args.fUniformHandler,
    451                     args.fGLSLCaps,
    452                     ge, t.c_str(),
    453                     args.fOutputColor,
    454                     args.fInputColor,
    455                     args.fSamplers);
    456 }
    457 
    458 /////////////////////////////////////////////////////////////////////
    459 
    460 const GrFragmentProcessor* SkLinearGradient::asFragmentProcessor(
    461                                                  GrContext* context,
    462                                                  const SkMatrix& viewm,
    463                                                  const SkMatrix* localMatrix,
    464                                                  SkFilterQuality) const {
    465     SkASSERT(context);
    466 
    467     SkMatrix matrix;
    468     if (!this->getLocalMatrix().invert(&matrix)) {
    469         return nullptr;
    470     }
    471     if (localMatrix) {
    472         SkMatrix inv;
    473         if (!localMatrix->invert(&inv)) {
    474             return nullptr;
    475         }
    476         matrix.postConcat(inv);
    477     }
    478     matrix.postConcat(fPtsToUnit);
    479 
    480     SkAutoTUnref<const GrFragmentProcessor> inner(
    481         GrLinearGradient::Create(context, *this, matrix, fTileMode));
    482     return GrFragmentProcessor::MulOutputByInputAlpha(inner);
    483 }
    484 
    485 
    486 #endif
    487 
    488 #ifndef SK_IGNORE_TO_STRING
    489 void SkLinearGradient::toString(SkString* str) const {
    490     str->append("SkLinearGradient (");
    491 
    492     str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
    493     str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
    494 
    495     this->INHERITED::toString(str);
    496 
    497     str->append(")");
    498 }
    499 #endif
    500 
    501 ///////////////////////////////////////////////////////////////////////////////////////////////////
    502 
    503 #include "SkNx.h"
    504 
    505 static const SkLinearGradient::LinearGradientContext::Rec*
    506 find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
    507     SkASSERT(tiledX >= 0 && tiledX <= 1);
    508 
    509     SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
    510     SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
    511     SkASSERT(rec[0].fPos <= rec[1].fPos);
    512     rec += 1;
    513     while (rec->fPos < tiledX || rec->fPosScale == 0) {
    514         SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
    515         SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
    516         SkASSERT(rec[0].fPos <= rec[1].fPos);
    517         rec += 1;
    518     }
    519     return rec - 1;
    520 }
    521 
    522 static const SkLinearGradient::LinearGradientContext::Rec*
    523 find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
    524     SkASSERT(tiledX >= 0 && tiledX <= 1);
    525 
    526     SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
    527     SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
    528     SkASSERT(rec[0].fPos <= rec[1].fPos);
    529     while (tiledX < rec->fPos || rec[1].fPosScale == 0) {
    530         rec -= 1;
    531         SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
    532         SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
    533         SkASSERT(rec[0].fPos <= rec[1].fPos);
    534     }
    535     return rec;
    536 }
    537 
    538 template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x) {
    539     SkPMColor c;
    540     SkNx_cast<uint8_t>(x).store(&c);
    541     if (apply_alpha) {
    542         c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c),
    543                               SkGetPackedG32(c), SkGetPackedB32(c));
    544     }
    545     return c;
    546 }
    547 
    548 template <bool apply_alpha> void fill(SkPMColor dst[], int count,
    549                                       const Sk4f& c4, const Sk4f& c4other) {
    550     sk_memset32_dither(dst, trunc_from_255<apply_alpha>(c4),
    551                        trunc_from_255<apply_alpha>(c4other), count);
    552 }
    553 
    554 template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
    555     // Assumes that c4 does not need to be dithered.
    556     sk_memset32(dst, trunc_from_255<apply_alpha>(c4), count);
    557 }
    558 
    559 /*
    560  *  TODOs
    561  *
    562  *  - tilemodes
    563  *  - interp before or after premul
    564  *  - perspective
    565  *  - optimizations
    566  *      - use fixed (32bit or 16bit) instead of floats?
    567  */
    568 
    569 static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
    570     SkASSERT(fx >= rec[0].fPos);
    571     SkASSERT(fx <= rec[1].fPos);
    572 
    573     const float p0 = rec[0].fPos;
    574     const Sk4f c0 = rec[0].fColor;
    575     const Sk4f c1 = rec[1].fColor;
    576     const Sk4f diffc = c1 - c0;
    577     const float scale = rec[1].fPosScale;
    578     const float t = (fx - p0) * scale;
    579     return c0 + Sk4f(t) * diffc;
    580 }
    581 
    582 template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
    583                                       const Sk4f& dither0, const Sk4f& dither1) {
    584     Sk4f dc2 = dc + dc;
    585     Sk4f dc4 = dc2 + dc2;
    586     Sk4f cd0 = c + dither0;
    587     Sk4f cd1 = c + dc + dither1;
    588     Sk4f cd2 = cd0 + dc2;
    589     Sk4f cd3 = cd1 + dc2;
    590     while (n >= 4) {
    591         if (!apply_alpha) {
    592             Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
    593             dstC += 4;
    594         } else {
    595             *dstC++ = trunc_from_255<apply_alpha>(cd0);
    596             *dstC++ = trunc_from_255<apply_alpha>(cd1);
    597             *dstC++ = trunc_from_255<apply_alpha>(cd2);
    598             *dstC++ = trunc_from_255<apply_alpha>(cd3);
    599         }
    600         cd0 = cd0 + dc4;
    601         cd1 = cd1 + dc4;
    602         cd2 = cd2 + dc4;
    603         cd3 = cd3 + dc4;
    604         n -= 4;
    605     }
    606     if (n & 2) {
    607         *dstC++ = trunc_from_255<apply_alpha>(cd0);
    608         *dstC++ = trunc_from_255<apply_alpha>(cd1);
    609         cd0 = cd0 + dc2;
    610     }
    611     if (n & 1) {
    612         *dstC++ = trunc_from_255<apply_alpha>(cd0);
    613     }
    614 }
    615 
    616 template <bool apply_alpha, bool dx_is_pos>
    617 void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
    618                                                               float fx, float dx, float invDx,
    619                                                               const float dither[2]) {
    620     Sk4f dither0(dither[0]);
    621     Sk4f dither1(dither[1]);
    622     const Rec* rec = fRecs.begin();
    623 
    624     const Sk4f dx4 = Sk4f(dx);
    625     SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
    626 
    627     if (dx_is_pos) {
    628         if (fx < 0) {
    629             int n = SkTMin(SkFloatToIntFloor(-fx * invDx) + 1, count);
    630             fill<apply_alpha>(dstC, n, rec[0].fColor);
    631             count -= n;
    632             dstC += n;
    633             fx += n * dx;
    634             SkASSERT(0 == count || fx >= 0);
    635             if (n & 1) {
    636                 SkTSwap(dither0, dither1);
    637             }
    638         }
    639     } else { // dx < 0
    640         if (fx > 1) {
    641             int n = SkTMin(SkFloatToIntFloor((1 - fx) * invDx) + 1, count);
    642             fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
    643             count -= n;
    644             dstC += n;
    645             fx += n * dx;
    646             SkASSERT(0 == count || fx <= 1);
    647             if (n & 1) {
    648                 SkTSwap(dither0, dither1);
    649             }
    650         }
    651     }
    652     SkASSERT(count >= 0);
    653 
    654     const Rec* r;
    655     if (dx_is_pos) {
    656         r = fRecs.begin();                      // start at the beginning
    657     } else {
    658         r = fRecs.begin() + fRecs.count() - 2;  // start at the end
    659     }
    660 
    661     while (count > 0) {
    662         if (dx_is_pos) {
    663             if (fx >= 1) {
    664                 fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
    665                 return;
    666             }
    667         } else {    // dx < 0
    668             if (fx <= 0) {
    669                 fill<apply_alpha>(dstC, count, rec[0].fColor);
    670                 return;
    671             }
    672         }
    673 
    674         if (dx_is_pos) {
    675             r = find_forward(r, fx);
    676         } else {
    677             r = find_backward(r, fx);
    678         }
    679         SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
    680 
    681         const float p0 = r[0].fPos;
    682         const Sk4f c0 = r[0].fColor;
    683         const float p1 = r[1].fPos;
    684         const Sk4f diffc = Sk4f(r[1].fColor) - c0;
    685         const float scale = r[1].fPosScale;
    686         const float t = (fx - p0) * scale;
    687         const Sk4f c = c0 + Sk4f(t) * diffc;
    688         const Sk4f dc = diffc * dx4 * Sk4f(scale);
    689 
    690         int n;
    691         if (dx_is_pos) {
    692             n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
    693         } else {
    694             n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
    695         }
    696 
    697         fx += n * dx;
    698         // fx should now outside of the p0..p1 interval. However, due to float precision loss,
    699         // its possible that fx is slightly too small/large, so we clamp it.
    700         if (dx_is_pos) {
    701             fx = SkTMax(fx, p1);
    702         } else {
    703             fx = SkTMin(fx, p0);
    704         }
    705 
    706         ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
    707         dstC += n;
    708         SkASSERT(dstC <= endDstC);
    709 
    710         if (n & 1) {
    711             SkTSwap(dither0, dither1);
    712         }
    713 
    714         count -= n;
    715         SkASSERT(count >= 0);
    716     }
    717 }
    718 
    719 void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
    720                                                            int count) {
    721     SkASSERT(count > 0);
    722     SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
    723 
    724     SkPoint srcPt;
    725     fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
    726     float fx = srcPt.x();
    727     const float dx = fDstToIndex.getScaleX();
    728 
    729     // Default our dither bias values to 1/2, (rounding), which is no dithering
    730     float dither0 = 0.5f;
    731     float dither1 = 0.5f;
    732     if (fDither) {
    733         const float ditherCell[] = {
    734             1/8.0f,   5/8.0f,
    735             7/8.0f,   3/8.0f,
    736         };
    737         const int rowIndex = (y & 1) << 1;
    738         dither0 = ditherCell[rowIndex];
    739         dither1 = ditherCell[rowIndex + 1];
    740         if (x & 1) {
    741             SkTSwap(dither0, dither1);
    742         }
    743     }
    744     const float dither[2] = { dither0, dither1 };
    745     const float invDx = 1 / dx;
    746 
    747     if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
    748         const float pinFx = SkTPin(fx, 0.0f, 1.0f);
    749         Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
    750         if (fApplyAlphaAfterInterp) {
    751             fill<true>(dstC, count, c + dither0, c + dither1);
    752         } else {
    753             fill<false>(dstC, count, c + dither0, c + dither1);
    754         }
    755         return;
    756     }
    757 
    758     if (dx > 0) {
    759         if (fApplyAlphaAfterInterp) {
    760             this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
    761         } else {
    762             this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
    763         }
    764     } else {
    765         if (fApplyAlphaAfterInterp) {
    766             this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
    767         } else {
    768             this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
    769         }
    770     }
    771 }
    772