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