LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkLinearBitmapPipeline_sample.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 495 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 284 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2016 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             : #ifndef SkLinearBitmapPipeline_sampler_DEFINED
       9             : #define SkLinearBitmapPipeline_sampler_DEFINED
      10             : 
      11             : #include <tuple>
      12             : 
      13             : #include "SkAutoMalloc.h"
      14             : #include "SkColor.h"
      15             : #include "SkColorPriv.h"
      16             : #include "SkFixed.h"  // for SkFixed1 only. Don't use SkFixed in this file.
      17             : #include "SkHalf.h"
      18             : #include "SkLinearBitmapPipeline_core.h"
      19             : #include "SkNx.h"
      20             : #include "SkPM4fPriv.h"
      21             : 
      22             : namespace {
      23             : // Explaination of the math:
      24             : //              1 - x      x
      25             : //           +--------+--------+
      26             : //           |        |        |
      27             : //  1 - y    |  px00  |  px10  |
      28             : //           |        |        |
      29             : //           +--------+--------+
      30             : //           |        |        |
      31             : //    y      |  px01  |  px11  |
      32             : //           |        |        |
      33             : //           +--------+--------+
      34             : //
      35             : //
      36             : // Given a pixelxy each is multiplied by a different factor derived from the fractional part of x
      37             : // and y:
      38             : // * px00 -> (1 - x)(1 - y) = 1 - x - y + xy
      39             : // * px10 -> x(1 - y) = x - xy
      40             : // * px01 -> (1 - x)y = y - xy
      41             : // * px11 -> xy
      42             : // So x * y is calculated first and then used to calculate all the other factors.
      43           0 : static Sk4s SK_VECTORCALL bilerp4(Sk4s xs, Sk4s ys, Sk4f px00, Sk4f px10,
      44             :                                                     Sk4f px01, Sk4f px11) {
      45             :     // Calculate fractional xs and ys.
      46           0 :     Sk4s fxs = xs - xs.floor();
      47           0 :     Sk4s fys = ys - ys.floor();
      48           0 :     Sk4s fxys{fxs * fys};
      49           0 :     Sk4f sum = px11 * fxys;
      50           0 :     sum = sum + px01 * (fys - fxys);
      51           0 :     sum = sum + px10 * (fxs - fxys);
      52           0 :     sum = sum + px00 * (Sk4f{1.0f} - fxs - fys + fxys);
      53           0 :     return sum;
      54             : }
      55             : 
      56             : ////////////////////////////////////////////////////////////////////////////////////////////////////
      57             : // PixelGetter is the lowest level interface to the source data. There is a PixelConverter for each
      58             : // of the different SkColorTypes.
      59             : template <SkColorType, SkGammaType> class PixelConverter;
      60             : 
      61             : // Alpha handling:
      62             : //   The alpha from the paint (tintColor) is used in the blend part of the pipeline to modulate
      63             : // the entire bitmap. So, the tint color is given an alpha of 1.0 so that the later alpha can
      64             : // modulate this color later.
      65             : template <>
      66             : class PixelConverter<kAlpha_8_SkColorType, kLinear_SkGammaType> {
      67             : public:
      68             :     using Element = uint8_t;
      69           0 :     PixelConverter(const SkPixmap& srcPixmap, SkColor tintColor) {
      70           0 :         fTintColor = SkColor4f::FromColor(tintColor);
      71           0 :         fTintColor.fA = 1.0f;
      72           0 :     }
      73             : 
      74           0 :     Sk4f toSk4f(const Element pixel) const {
      75           0 :         return Sk4f::Load(&fTintColor) * (pixel * (1.0f/255.0f));
      76             :     }
      77             : 
      78             : private:
      79             :     SkColor4f fTintColor;
      80             : };
      81             : 
      82             : template <SkGammaType gammaType>
      83           0 : static inline Sk4f pmcolor_to_rgba(SkPMColor pixel) {
      84           0 :     return swizzle_rb_if_bgra(
      85             :             (gammaType == kSRGB_SkGammaType) ? Sk4f_fromS32(pixel)
      86           0 :                                              : Sk4f_fromL32(pixel));
      87             : }
      88             : 
      89             : template <SkGammaType gammaType>
      90             : class PixelConverter<kRGB_565_SkColorType, gammaType> {
      91             : public:
      92             :     using Element = uint16_t;
      93           0 :     PixelConverter(const SkPixmap& srcPixmap) { }
      94             : 
      95           0 :     Sk4f toSk4f(Element pixel) const {
      96           0 :         return pmcolor_to_rgba<gammaType>(SkPixel16ToPixel32(pixel));
      97             :     }
      98             : };
      99             : 
     100             : template <SkGammaType gammaType>
     101             : class PixelConverter<kARGB_4444_SkColorType, gammaType> {
     102             : public:
     103             :     using Element = uint16_t;
     104           0 :     PixelConverter(const SkPixmap& srcPixmap) { }
     105             : 
     106           0 :     Sk4f toSk4f(Element pixel) const {
     107           0 :         return pmcolor_to_rgba<gammaType>(SkPixel4444ToPixel32(pixel));
     108             :     }
     109             : };
     110             : 
     111             : template <SkGammaType gammaType>
     112             : class PixelConverter<kRGBA_8888_SkColorType, gammaType> {
     113             : public:
     114             :     using Element = uint32_t;
     115           0 :     PixelConverter(const SkPixmap& srcPixmap) { }
     116             : 
     117           0 :     Sk4f toSk4f(Element pixel) const {
     118             :         return gammaType == kSRGB_SkGammaType
     119             :                ? Sk4f_fromS32(pixel)
     120           0 :                : Sk4f_fromL32(pixel);
     121             :     }
     122             : };
     123             : 
     124             : template <SkGammaType gammaType>
     125             : class PixelConverter<kBGRA_8888_SkColorType, gammaType> {
     126             : public:
     127             :     using Element = uint32_t;
     128           0 :     PixelConverter(const SkPixmap& srcPixmap) { }
     129             : 
     130           0 :     Sk4f toSk4f(Element pixel) const {
     131           0 :         return swizzle_rb(
     132           0 :                    gammaType == kSRGB_SkGammaType ? Sk4f_fromS32(pixel) : Sk4f_fromL32(pixel));
     133             :     }
     134             : };
     135             : 
     136             : template <SkGammaType gammaType>
     137           0 : class PixelConverter<kIndex_8_SkColorType, gammaType> {
     138             : public:
     139             :     using Element = uint8_t;
     140           0 :     PixelConverter(const SkPixmap& srcPixmap)
     141           0 :     : fColorTableSize(srcPixmap.ctable()->count()){
     142           0 :         SkColorTable* skColorTable = srcPixmap.ctable();
     143           0 :         SkASSERT(skColorTable != nullptr);
     144             : 
     145           0 :         fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
     146           0 :         for (int i = 0; i < fColorTableSize; i++) {
     147           0 :             fColorTable[i] = pmcolor_to_rgba<gammaType>((*skColorTable)[i]);
     148             :         }
     149           0 :     }
     150             : 
     151             :     PixelConverter(const PixelConverter& strategy)
     152             :     : fColorTableSize{strategy.fColorTableSize}{
     153             :         fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
     154             :         for (int i = 0; i < fColorTableSize; i++) {
     155             :             fColorTable[i] = strategy.fColorTable[i];
     156             :         }
     157             :     }
     158             : 
     159           0 :     Sk4f toSk4f(Element index) const {
     160           0 :         return fColorTable[index];
     161             :     }
     162             : 
     163             : private:
     164             :     static const size_t kColorTableSize = sizeof(Sk4f[256]) + 12;
     165             :     const int           fColorTableSize;
     166             :     SkAutoMalloc        fColorTableStorage{kColorTableSize};
     167             :     Sk4f*               fColorTable;
     168             : };
     169             : 
     170             : template <SkGammaType gammaType>
     171             : class PixelConverter<kGray_8_SkColorType, gammaType> {
     172             : public:
     173             :     using Element = uint8_t;
     174           0 :     PixelConverter(const SkPixmap& srcPixmap) { }
     175             : 
     176           0 :     Sk4f toSk4f(Element pixel) const {
     177             :         float gray = (gammaType == kSRGB_SkGammaType)
     178             :             ? sk_linear_from_srgb[pixel]
     179           0 :             : pixel * (1/255.0f);
     180           0 :         return {gray, gray, gray, 1.0f};
     181             :     }
     182             : };
     183             : 
     184             : template <>
     185             : class PixelConverter<kRGBA_F16_SkColorType, kLinear_SkGammaType> {
     186             : public:
     187             :     using Element = uint64_t;
     188           0 :     PixelConverter(const SkPixmap& srcPixmap) { }
     189             : 
     190           0 :     Sk4f toSk4f(const Element pixel) const {
     191           0 :         return SkHalfToFloat_finite_ftz(pixel);
     192             :     }
     193             : };
     194             : 
     195             : class PixelAccessorShim {
     196             : public:
     197           0 :     explicit PixelAccessorShim(SkLinearBitmapPipeline::PixelAccessorInterface* accessor)
     198           0 :         : fPixelAccessor(accessor) { }
     199             : 
     200           0 :     void SK_VECTORCALL getFewPixels(
     201             :         int n, Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const {
     202           0 :         fPixelAccessor->getFewPixels(n, xs, ys, px0, px1, px2);
     203           0 :     }
     204             : 
     205           0 :     void SK_VECTORCALL get4Pixels(
     206             :         Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const {
     207           0 :         fPixelAccessor->get4Pixels(xs, ys, px0, px1, px2, px3);
     208           0 :     }
     209             : 
     210           0 :     void get4Pixels(
     211             :         const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const {
     212           0 :         fPixelAccessor->get4Pixels(src, index, px0, px1, px2, px3);
     213           0 :     }
     214             : 
     215           0 :     Sk4f getPixelFromRow(const void* row, int index) const {
     216           0 :         return fPixelAccessor->getPixelFromRow(row, index);
     217             :     }
     218             : 
     219             :     Sk4f getPixelAt(int index) const {
     220             :         return fPixelAccessor->getPixelAt(index);
     221             :     }
     222             : 
     223           0 :     const void* row(int y) const {
     224           0 :         return fPixelAccessor->row(y);
     225             :     }
     226             : 
     227             : private:
     228             :     SkLinearBitmapPipeline::PixelAccessorInterface* const fPixelAccessor;
     229             : };
     230             : 
     231             : ////////////////////////////////////////////////////////////////////////////////////////////////////
     232             : // PixelAccessor handles all the same plumbing for all the PixelGetters.
     233             : template <SkColorType colorType, SkGammaType gammaType>
     234           0 : class PixelAccessor final : public SkLinearBitmapPipeline::PixelAccessorInterface {
     235             :     using Element = typename PixelConverter<colorType, gammaType>::Element;
     236             : public:
     237             :     template <typename... Args>
     238           0 :     PixelAccessor(const SkPixmap& srcPixmap, Args&&... args)
     239           0 :         : fSrc{static_cast<const Element*>(srcPixmap.addr())}
     240           0 :         , fWidth{srcPixmap.rowBytesAsPixels()}
     241           0 :         , fConverter{srcPixmap, std::move<Args>(args)...} { }
     242             : 
     243           0 :     void SK_VECTORCALL getFewPixels (
     244             :         int n, Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const override {
     245           0 :         Sk4i bufferLoc = ys * fWidth + xs;
     246           0 :         switch (n) {
     247             :             case 3:
     248           0 :                 *px2 = this->getPixelAt(bufferLoc[2]);
     249             :             case 2:
     250           0 :                 *px1 = this->getPixelAt(bufferLoc[1]);
     251             :             case 1:
     252           0 :                 *px0 = this->getPixelAt(bufferLoc[0]);
     253             :             default:
     254           0 :                 break;
     255             :         }
     256           0 :     }
     257             : 
     258           0 :     void SK_VECTORCALL get4Pixels(
     259             :         Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const override {
     260           0 :         Sk4i bufferLoc = ys * fWidth + xs;
     261           0 :         *px0 = this->getPixelAt(bufferLoc[0]);
     262           0 :         *px1 = this->getPixelAt(bufferLoc[1]);
     263           0 :         *px2 = this->getPixelAt(bufferLoc[2]);
     264           0 :         *px3 = this->getPixelAt(bufferLoc[3]);
     265           0 :     }
     266             : 
     267           0 :     void get4Pixels(
     268             :         const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const override {
     269           0 :         *px0 = this->getPixelFromRow(src, index + 0);
     270           0 :         *px1 = this->getPixelFromRow(src, index + 1);
     271           0 :         *px2 = this->getPixelFromRow(src, index + 2);
     272           0 :         *px3 = this->getPixelFromRow(src, index + 3);
     273           0 :     }
     274             : 
     275           0 :     Sk4f getPixelFromRow(const void* row, int index) const override {
     276           0 :         const Element* src = static_cast<const Element*>(row);
     277           0 :         return fConverter.toSk4f(src[index]);
     278             :     }
     279             : 
     280           0 :     Sk4f getPixelAt(int index) const override {
     281           0 :         return this->getPixelFromRow(fSrc, index);
     282             :     }
     283             : 
     284           0 :     const void* row(int y) const override { return fSrc + y * fWidth; }
     285             : 
     286             : private:
     287             :     const Element* const                 fSrc;
     288             :     const int                            fWidth;
     289             :     PixelConverter<colorType, gammaType> fConverter;
     290             : };
     291             : 
     292             : // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
     293             : // We'll never re-use pixels, but we can at least load contiguous pixels.
     294             : template <typename Next, typename Strategy>
     295           0 : static void src_strategy_blend(Span span, Next* next, Strategy* strategy) {
     296             :     SkPoint start;
     297             :     SkScalar length;
     298             :     int count;
     299           0 :     std::tie(start, length, count) = span;
     300           0 :     int ix = SkScalarFloorToInt(X(start));
     301           0 :     const void* row = strategy->row((int)std::floor(Y(start)));
     302           0 :     if (length > 0) {
     303           0 :         while (count >= 4) {
     304             :             Sk4f px0, px1, px2, px3;
     305           0 :             strategy->get4Pixels(row, ix, &px0, &px1, &px2, &px3);
     306           0 :             next->blend4Pixels(px0, px1, px2, px3);
     307           0 :             ix += 4;
     308           0 :             count -= 4;
     309             :         }
     310             : 
     311           0 :         while (count > 0) {
     312           0 :             next->blendPixel(strategy->getPixelFromRow(row, ix));
     313           0 :             ix += 1;
     314           0 :             count -= 1;
     315             :         }
     316             :     } else {
     317           0 :         while (count >= 4) {
     318             :             Sk4f px0, px1, px2, px3;
     319           0 :             strategy->get4Pixels(row, ix - 3, &px3, &px2, &px1, &px0);
     320           0 :             next->blend4Pixels(px0, px1, px2, px3);
     321           0 :             ix -= 4;
     322           0 :             count -= 4;
     323             :         }
     324             : 
     325           0 :         while (count > 0) {
     326           0 :             next->blendPixel(strategy->getPixelFromRow(row, ix));
     327           0 :             ix -= 1;
     328           0 :             count -= 1;
     329             :         }
     330             :     }
     331           0 : }
     332             : 
     333             : // -- NearestNeighborSampler -----------------------------------------------------------------------
     334             : // NearestNeighborSampler - use nearest neighbor filtering to create runs of destination pixels.
     335             : template<typename Accessor, typename Next>
     336           0 : class NearestNeighborSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
     337             : public:
     338             :     template<typename... Args>
     339           0 :     NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
     340           0 :     : fNext{next}, fAccessor{std::forward<Args>(args)...} { }
     341             : 
     342             :     NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
     343             :     const NearestNeighborSampler& sampler)
     344             :     : fNext{next}, fAccessor{sampler.fAccessor} { }
     345             : 
     346           0 :     void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
     347           0 :         SkASSERT(0 < n && n < 4);
     348             :         Sk4f px0, px1, px2;
     349           0 :         fAccessor.getFewPixels(n, SkNx_cast<int>(xs), SkNx_cast<int>(ys), &px0, &px1, &px2);
     350           0 :         if (n >= 1) fNext->blendPixel(px0);
     351           0 :         if (n >= 2) fNext->blendPixel(px1);
     352           0 :         if (n >= 3) fNext->blendPixel(px2);
     353           0 :     }
     354             : 
     355           0 :     void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
     356             :         Sk4f px0, px1, px2, px3;
     357           0 :         fAccessor.get4Pixels(SkNx_cast<int>(xs), SkNx_cast<int>(ys), &px0, &px1, &px2, &px3);
     358           0 :         fNext->blend4Pixels(px0, px1, px2, px3);
     359           0 :     }
     360             : 
     361           0 :     void pointSpan(Span span) override {
     362           0 :         SkASSERT(!span.isEmpty());
     363             :         SkPoint start;
     364             :         SkScalar length;
     365             :         int count;
     366           0 :         std::tie(start, length, count) = span;
     367           0 :         SkScalar absLength = SkScalarAbs(length);
     368           0 :         if (absLength < (count - 1)) {
     369           0 :             this->spanSlowRate(span);
     370           0 :         } else if (absLength == (count - 1)) {
     371           0 :             src_strategy_blend(span, fNext, &fAccessor);
     372             :         } else {
     373           0 :             this->spanFastRate(span);
     374             :         }
     375           0 :     }
     376             : 
     377           0 :     void repeatSpan(Span span, int32_t repeatCount) override {
     378           0 :         while (repeatCount > 0) {
     379           0 :             this->pointSpan(span);
     380           0 :             repeatCount--;
     381             :         }
     382           0 :     }
     383             : 
     384             : private:
     385             :     // When moving through source space more slowly than dst space (zoomed in),
     386             :     // we'll be sampling from the same source pixel more than once.
     387           0 :     void spanSlowRate(Span span) {
     388             :         SkPoint start; SkScalar length; int count;
     389           0 :         std::tie(start, length, count) = span;
     390           0 :         SkScalar x = X(start);
     391             :         // fx is a fixed 48.16 number.
     392           0 :         int64_t fx = static_cast<int64_t>(x * SK_Fixed1);
     393           0 :         SkScalar dx = length / (count - 1);
     394             :         // fdx is a fixed 48.16 number.
     395           0 :         int64_t fdx = static_cast<int64_t>(dx * SK_Fixed1);
     396             : 
     397           0 :         const void* row = fAccessor.row((int)std::floor(Y(start)));
     398           0 :         Next* next = fNext;
     399             : 
     400           0 :         int64_t ix = fx >> 16;
     401           0 :         int64_t prevIX = ix;
     402           0 :         Sk4f fpixel = fAccessor.getPixelFromRow(row, ix);
     403             : 
     404             :         // When dx is less than one, each pixel is used more than once. Using the fixed point fx
     405             :         // allows the code to quickly check that the same pixel is being used. The code uses this
     406             :         // same pixel check to do the sRGB and normalization only once.
     407           0 :         auto getNextPixel = [&]() {
     408           0 :             if (ix != prevIX) {
     409           0 :                 fpixel = fAccessor.getPixelFromRow(row, ix);
     410           0 :                 prevIX = ix;
     411             :             }
     412           0 :             fx += fdx;
     413           0 :             ix = fx >> 16;
     414           0 :             return fpixel;
     415           0 :         };
     416             : 
     417           0 :         while (count >= 4) {
     418           0 :             Sk4f px0 = getNextPixel();
     419           0 :             Sk4f px1 = getNextPixel();
     420           0 :             Sk4f px2 = getNextPixel();
     421           0 :             Sk4f px3 = getNextPixel();
     422           0 :             next->blend4Pixels(px0, px1, px2, px3);
     423           0 :             count -= 4;
     424             :         }
     425           0 :         while (count > 0) {
     426           0 :             next->blendPixel(getNextPixel());
     427           0 :             count -= 1;
     428             :         }
     429           0 :     }
     430             : 
     431             :     // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
     432             :     // We'll never re-use pixels, but we can at least load contiguous pixels.
     433             :     void spanUnitRate(Span span) {
     434             :         src_strategy_blend(span, fNext, &fAccessor);
     435             :     }
     436             : 
     437             :     // We're moving through source space faster than dst (zoomed out),
     438             :     // so we'll never reuse a source pixel or be able to do contiguous loads.
     439           0 :     void spanFastRate(Span span) {
     440           0 :         span_fallback(span, this);
     441           0 :     }
     442             : 
     443             :     Next* const fNext;
     444             :     Accessor    fAccessor;
     445             : };
     446             : 
     447             : // From an edgeType, the integer value of a pixel vs, and the integer value of the extreme edge
     448             : // vMax, take the point which might be off the tile by one pixel and either wrap it or pin it to
     449             : // generate the right pixel. The value vs is on the interval [-1, vMax + 1]. It produces a value
     450             : // on the interval [0, vMax].
     451             : // Note: vMax is not width or height, but width-1 or height-1 because it is the largest valid pixel.
     452           0 : static inline int adjust_edge(SkShader::TileMode edgeType, int vs, int vMax) {
     453           0 :     SkASSERT(-1 <= vs && vs <= vMax + 1);
     454           0 :     switch (edgeType) {
     455             :         case SkShader::kClamp_TileMode:
     456             :         case SkShader::kMirror_TileMode:
     457           0 :             vs = std::max(vs, 0);
     458           0 :             vs = std::min(vs, vMax);
     459           0 :             break;
     460             :         case SkShader::kRepeat_TileMode:
     461           0 :             vs = (vs <= vMax) ? vs : 0;
     462           0 :             vs =    (vs >= 0) ? vs : vMax;
     463           0 :             break;
     464             :     }
     465           0 :     SkASSERT(0 <= vs && vs <= vMax);
     466           0 :     return vs;
     467             : }
     468             : 
     469             : // From a sample point on the tile, return the top or left filter value.
     470             : // The result r should be in the range (0, 1]. Since this represents the weight given to the top
     471             : // left element, then if x == 0.5 the filter value should be 1.0.
     472             : // The input sample point must be on the tile, therefore it must be >= 0.
     473           0 : static SkScalar sample_to_filter(SkScalar x) {
     474           0 :     SkASSERT(x >= 0.0f);
     475             :     // The usual form of the top or left edge is x - .5, but since we are working on the unit
     476             :     // square, then x + .5 works just as well. This also guarantees that v > 0.0 allowing the use
     477             :     // of trunc.
     478           0 :     SkScalar v = x + 0.5f;
     479             :     // Produce the top or left offset a value on the range [0, 1).
     480           0 :     SkScalar f = v - SkScalarTruncToScalar(v);
     481             :     // Produce the filter value which is on the range (0, 1].
     482           0 :     SkScalar r =  1.0f - f;
     483           0 :     SkASSERT(0.0f < r && r <= 1.0f);
     484           0 :     return r;
     485             : }
     486             : 
     487             : // -- BilerpSampler --------------------------------------------------------------------------------
     488             : // BilerpSampler - use a bilerp filter to create runs of destination pixels.
     489             : // Note: in the code below, there are two types of points
     490             : //       * sample points - these are the points passed in by pointList* and Spans.
     491             : //       * filter points - are created from a sample point to form the coordinates of the points
     492             : //                         to use in the filter and to generate the filter values.
     493             : template<typename Accessor, typename Next>
     494           0 : class BilerpSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
     495             : public:
     496             :     template<typename... Args>
     497           0 :     BilerpSampler(
     498             :         SkLinearBitmapPipeline::BlendProcessorInterface* next,
     499             :         SkISize dimensions,
     500             :         SkShader::TileMode xTile, SkShader::TileMode yTile,
     501             :         Args&& ... args
     502             :     )
     503             :         : fNext{next}
     504             :         , fXEdgeType{xTile}
     505           0 :         , fXMax{dimensions.width() - 1}
     506             :         , fYEdgeType{yTile}
     507           0 :         , fYMax{dimensions.height() - 1}
     508           0 :         , fAccessor{std::forward<Args>(args)...} { }
     509             : 
     510             :     BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
     511             :                    const BilerpSampler& sampler)
     512             :         : fNext{next}
     513             :         , fXEdgeType{sampler.fXEdgeType}
     514             :         , fXMax{sampler.fXMax}
     515             :         , fYEdgeType{sampler.fYEdgeType}
     516             :         , fYMax{sampler.fYMax}
     517             :         , fAccessor{sampler.fAccessor} { }
     518             : 
     519           0 :     void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
     520           0 :         SkASSERT(0 < n && n < 4);
     521           0 :         auto bilerpPixel = [&](int index) {
     522           0 :             return this->bilerpSamplePoint(SkPoint{xs[index], ys[index]});
     523           0 :         };
     524             : 
     525           0 :         if (n >= 1) fNext->blendPixel(bilerpPixel(0));
     526           0 :         if (n >= 2) fNext->blendPixel(bilerpPixel(1));
     527           0 :         if (n >= 3) fNext->blendPixel(bilerpPixel(2));
     528           0 :     }
     529             : 
     530           0 :     void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
     531           0 :         auto bilerpPixel = [&](int index) {
     532           0 :             return this->bilerpSamplePoint(SkPoint{xs[index], ys[index]});
     533           0 :         };
     534           0 :         fNext->blend4Pixels(bilerpPixel(0), bilerpPixel(1), bilerpPixel(2), bilerpPixel(3));
     535           0 :     }
     536             : 
     537           0 :     void pointSpan(Span span) override {
     538           0 :         SkASSERT(!span.isEmpty());
     539             :         SkPoint start;
     540             :         SkScalar length;
     541             :         int count;
     542           0 :         std::tie(start, length, count) = span;
     543             : 
     544             :         // Nothing to do.
     545           0 :         if (count == 0) {
     546           0 :             return;
     547             :         }
     548             : 
     549             :         // Trivial case. No sample points are generated other than start.
     550           0 :         if (count == 1) {
     551           0 :             fNext->blendPixel(this->bilerpSamplePoint(start));
     552           0 :             return;
     553             :         }
     554             : 
     555             :         // Note: the following code could be done in terms of dx = length / (count -1), but that
     556             :         // would introduce a divide that is not needed for the most common dx == 1 cases.
     557           0 :         SkScalar absLength = SkScalarAbs(length);
     558           0 :         if (absLength == 0.0f) {
     559             :             // |dx| == 0
     560             :             // length is zero, so clamp an edge pixel.
     561           0 :             this->spanZeroRate(span);
     562           0 :         } else if (absLength < (count - 1)) {
     563             :             // 0 < |dx| < 1.
     564           0 :             this->spanSlowRate(span);
     565           0 :         } else if (absLength == (count - 1)) {
     566             :             // |dx| == 1.
     567           0 :             if (sample_to_filter(span.startX()) == 1.0f
     568           0 :                 && sample_to_filter(span.startY()) == 1.0f) {
     569             :                 // All the pixels are aligned with the dest; go fast.
     570           0 :                 src_strategy_blend(span, fNext, &fAccessor);
     571             :             } else {
     572             :                 // There is some sub-pixel offsets, so bilerp.
     573           0 :                 this->spanUnitRate(span);
     574             :             }
     575           0 :         } else if (absLength < 2.0f * (count - 1)) {
     576             :             // 1 < |dx| < 2.
     577           0 :             this->spanMediumRate(span);
     578             :         } else {
     579             :             // |dx| >= 2.
     580           0 :             this->spanFastRate(span);
     581             :         }
     582             :     }
     583             : 
     584           0 :     void repeatSpan(Span span, int32_t repeatCount) override {
     585           0 :         while (repeatCount > 0) {
     586           0 :             this->pointSpan(span);
     587           0 :             repeatCount--;
     588             :         }
     589           0 :     }
     590             : 
     591             : private:
     592             : 
     593             :     // Convert a sample point to the points used by the filter.
     594           0 :     void filterPoints(SkPoint sample, Sk4i* filterXs, Sk4i* filterYs) {
     595             :         // May be less than zero. Be careful to use Floor.
     596           0 :         int x0 = adjust_edge(fXEdgeType, SkScalarFloorToInt(X(sample) - 0.5), fXMax);
     597             :         // Always greater than zero. Use the faster Trunc.
     598           0 :         int x1 = adjust_edge(fXEdgeType, SkScalarTruncToInt(X(sample) + 0.5), fXMax);
     599           0 :         int y0 = adjust_edge(fYEdgeType, SkScalarFloorToInt(Y(sample) - 0.5), fYMax);
     600           0 :         int y1 = adjust_edge(fYEdgeType, SkScalarTruncToInt(Y(sample) + 0.5), fYMax);
     601             : 
     602           0 :         *filterXs = Sk4i{x0, x1, x0, x1};
     603           0 :         *filterYs = Sk4i{y0, y0, y1, y1};
     604           0 :     }
     605             : 
     606             :     // Given a sample point, generate a color by bilerping the four filter points.
     607           0 :     Sk4f bilerpSamplePoint(SkPoint sample) {
     608             :         Sk4i iXs, iYs;
     609           0 :         filterPoints(sample, &iXs, &iYs);
     610             :         Sk4f px00, px10, px01, px11;
     611           0 :         fAccessor.get4Pixels(iXs, iYs, &px00, &px10, &px01, &px11);
     612           0 :         return bilerp4(Sk4f{X(sample) - 0.5f}, Sk4f{Y(sample) - 0.5f}, px00, px10, px01, px11);
     613             :     }
     614             : 
     615             :     // Get two pixels at x from row0 and row1.
     616           0 :     void get2PixelColumn(const void* row0, const void* row1, int x, Sk4f* px0, Sk4f* px1) {
     617           0 :         *px0 = fAccessor.getPixelFromRow(row0, x);
     618           0 :         *px1 = fAccessor.getPixelFromRow(row1, x);
     619           0 :     }
     620             : 
     621             :     // |dx| == 0. This code assumes that length is zero.
     622           0 :     void spanZeroRate(Span span) {
     623             :         SkPoint start; SkScalar length; int count;
     624           0 :         std::tie(start, length, count) = span;
     625           0 :         SkASSERT(length == 0.0f);
     626             : 
     627             :         // Filter for the blending of the top and bottom pixels.
     628           0 :         SkScalar filterY = sample_to_filter(Y(start));
     629             : 
     630             :         // Generate the four filter points from the sample point start. Generate the row* values.
     631             :         Sk4i iXs, iYs;
     632           0 :         this->filterPoints(start, &iXs, &iYs);
     633           0 :         const void* const row0 = fAccessor.row(iYs[0]);
     634           0 :         const void* const row1 = fAccessor.row(iYs[2]);
     635             : 
     636             :         // Get the two pixels that make up the clamping pixel.
     637             :         Sk4f pxTop, pxBottom;
     638           0 :         this->get2PixelColumn(row0, row1, SkScalarFloorToInt(X(start)), &pxTop, &pxBottom);
     639           0 :         Sk4f pixel = pxTop * filterY + (1.0f - filterY) * pxBottom;
     640             : 
     641           0 :         while (count >= 4) {
     642           0 :             fNext->blend4Pixels(pixel, pixel, pixel, pixel);
     643           0 :             count -= 4;
     644             :         }
     645           0 :         while (count > 0) {
     646           0 :             fNext->blendPixel(pixel);
     647           0 :             count -= 1;
     648             :         }
     649           0 :     }
     650             : 
     651             :     // 0 < |dx| < 1. This code reuses the calculations from previous pixels to reduce
     652             :     // computation. In particular, several destination pixels maybe generated from the same four
     653             :     // source pixels.
     654             :     // In the following code a "part" is a combination of two pixels from the same column of the
     655             :     // filter.
     656           0 :     void spanSlowRate(Span span) {
     657             :         SkPoint start; SkScalar length; int count;
     658           0 :         std::tie(start, length, count) = span;
     659             : 
     660             :         // Calculate the distance between each sample point.
     661           0 :         const SkScalar dx = length / (count - 1);
     662           0 :         SkASSERT(-1.0f < dx && dx < 1.0f && dx != 0.0f);
     663             : 
     664             :         // Generate the filter values for the top-left corner.
     665             :         // Note: these values are in filter space; this has implications about how to adjust
     666             :         // these values at each step. For example, as the sample point increases, the filter
     667             :         // value decreases, this is because the filter and position are related by
     668             :         // (1 - (X(sample) - .5)) % 1. The (1 - stuff) causes the filter to move in the opposite
     669             :         // direction of the sample point which is increasing by dx.
     670           0 :         SkScalar filterX = sample_to_filter(X(start));
     671           0 :         SkScalar filterY = sample_to_filter(Y(start));
     672             : 
     673             :         // Generate the four filter points from the sample point start. Generate the row* values.
     674             :         Sk4i iXs, iYs;
     675           0 :         this->filterPoints(start, &iXs, &iYs);
     676           0 :         const void* const row0 = fAccessor.row(iYs[0]);
     677           0 :         const void* const row1 = fAccessor.row(iYs[2]);
     678             : 
     679             :         // Generate part of the filter value at xColumn.
     680           0 :         auto partAtColumn = [&](int xColumn) {
     681           0 :             int adjustedColumn = adjust_edge(fXEdgeType, xColumn, fXMax);
     682             :             Sk4f pxTop, pxBottom;
     683           0 :             this->get2PixelColumn(row0, row1, adjustedColumn, &pxTop, &pxBottom);
     684           0 :             return pxTop * filterY + (1.0f - filterY) * pxBottom;
     685           0 :         };
     686             : 
     687             :         // The leftPart is made up of two pixels from the left column of the filter, right part
     688             :         // is similar. The top and bottom pixels in the *Part are created as a linear blend of
     689             :         // the top and bottom pixels using filterY. See the partAtColumn function above.
     690           0 :         Sk4f leftPart  = partAtColumn(iXs[0]);
     691           0 :         Sk4f rightPart = partAtColumn(iXs[1]);
     692             : 
     693             :         // Create a destination color by blending together a left and right part using filterX.
     694           0 :         auto bilerp = [&](const Sk4f& leftPart, const Sk4f& rightPart) {
     695           0 :             Sk4f pixel = leftPart * filterX + rightPart * (1.0f - filterX);
     696           0 :             return check_pixel(pixel);
     697           0 :         };
     698             : 
     699             :         // Send the first pixel to the destination. This simplifies the loop structure so that no
     700             :         // extra pixels are fetched for the last iteration of the loop.
     701           0 :         fNext->blendPixel(bilerp(leftPart, rightPart));
     702           0 :         count -= 1;
     703             : 
     704           0 :         if (dx > 0.0f) {
     705             :             // * positive direction - generate destination pixels by sliding the filter from left
     706             :             //                        to right.
     707           0 :             int rightPartCursor = iXs[1];
     708             : 
     709             :             // Advance the filter from left to right. Remember that moving the top-left corner of
     710             :             // the filter to the right actually makes the filter value smaller.
     711           0 :             auto advanceFilter = [&]() {
     712           0 :                 filterX -= dx;
     713           0 :                 if (filterX <= 0.0f) {
     714           0 :                     filterX += 1.0f;
     715           0 :                     leftPart = rightPart;
     716           0 :                     rightPartCursor += 1;
     717           0 :                     rightPart = partAtColumn(rightPartCursor);
     718             :                 }
     719           0 :                 SkASSERT(0.0f < filterX && filterX <= 1.0f);
     720             : 
     721           0 :                 return bilerp(leftPart, rightPart);
     722           0 :             };
     723             : 
     724           0 :             while (count >= 4) {
     725           0 :                 Sk4f px0 = advanceFilter(),
     726           0 :                      px1 = advanceFilter(),
     727           0 :                      px2 = advanceFilter(),
     728           0 :                      px3 = advanceFilter();
     729           0 :                 fNext->blend4Pixels(px0, px1, px2, px3);
     730           0 :                 count -= 4;
     731             :             }
     732             : 
     733           0 :             while (count > 0) {
     734           0 :                 fNext->blendPixel(advanceFilter());
     735           0 :                 count -= 1;
     736             :             }
     737             :         } else {
     738             :             // * negative direction - generate destination pixels by sliding the filter from
     739             :             //                        right to left.
     740           0 :             int leftPartCursor = iXs[0];
     741             : 
     742             :             // Advance the filter from right to left. Remember that moving the top-left corner of
     743             :             // the filter to the left actually makes the filter value larger.
     744           0 :             auto advanceFilter = [&]() {
     745             :                 // Remember, dx < 0 therefore this adds |dx| to filterX.
     746           0 :                 filterX -= dx;
     747             :                 // At this point filterX may be > 1, and needs to be wrapped back on to the filter
     748             :                 // interval, and the next column in the filter is calculated.
     749           0 :                 if (filterX > 1.0f) {
     750           0 :                     filterX -= 1.0f;
     751           0 :                     rightPart = leftPart;
     752           0 :                     leftPartCursor -= 1;
     753           0 :                     leftPart = partAtColumn(leftPartCursor);
     754             :                 }
     755           0 :                 SkASSERT(0.0f < filterX && filterX <= 1.0f);
     756             : 
     757           0 :                 return bilerp(leftPart, rightPart);
     758           0 :             };
     759             : 
     760           0 :             while (count >= 4) {
     761           0 :                 Sk4f px0 = advanceFilter(),
     762           0 :                      px1 = advanceFilter(),
     763           0 :                      px2 = advanceFilter(),
     764           0 :                      px3 = advanceFilter();
     765           0 :                 fNext->blend4Pixels(px0, px1, px2, px3);
     766           0 :                 count -= 4;
     767             :             }
     768             : 
     769           0 :             while (count > 0) {
     770           0 :                 fNext->blendPixel(advanceFilter());
     771           0 :                 count -= 1;
     772             :             }
     773             :         }
     774           0 :     }
     775             : 
     776             :     // |dx| == 1. Moving through source space at a rate of 1 source pixel per 1 dst pixel.
     777             :     // Every filter part is used for two destination pixels, and the code can bulk load four
     778             :     // pixels at a time.
     779           0 :     void spanUnitRate(Span span) {
     780             :         SkPoint start; SkScalar length; int count;
     781           0 :         std::tie(start, length, count) = span;
     782           0 :         SkASSERT(SkScalarAbs(length) == (count - 1));
     783             : 
     784             :         // Calculate the four filter points of start, and use the two different Y values to
     785             :         // generate the row pointers.
     786             :         Sk4i iXs, iYs;
     787           0 :         filterPoints(start, &iXs, &iYs);
     788           0 :         const void* row0 = fAccessor.row(iYs[0]);
     789           0 :         const void* row1 = fAccessor.row(iYs[2]);
     790             : 
     791             :         // Calculate the filter values for the top-left filter element.
     792           0 :         const SkScalar filterX = sample_to_filter(X(start));
     793           0 :         const SkScalar filterY = sample_to_filter(Y(start));
     794             : 
     795             :         // Generate part of the filter value at xColumn.
     796           0 :         auto partAtColumn = [&](int xColumn) {
     797           0 :             int adjustedColumn = adjust_edge(fXEdgeType, xColumn, fXMax);
     798             :             Sk4f pxTop, pxBottom;
     799           0 :             this->get2PixelColumn(row0, row1, adjustedColumn, &pxTop, &pxBottom);
     800           0 :             return pxTop * filterY + (1.0f - filterY) * pxBottom;
     801           0 :         };
     802             : 
     803           0 :         auto get4Parts = [&](int ix, Sk4f* part0, Sk4f* part1, Sk4f* part2, Sk4f* part3) {
     804             :             // Check if the pixels needed are near the edges. If not go fast using bulk pixels,
     805             :             // otherwise be careful.
     806           0 :             if (0 <= ix && ix <= fXMax - 3) {
     807             :                 Sk4f px00, px10, px20, px30,
     808             :                      px01, px11, px21, px31;
     809           0 :                 fAccessor.get4Pixels(row0, ix, &px00, &px10, &px20, &px30);
     810           0 :                 fAccessor.get4Pixels(row1, ix, &px01, &px11, &px21, &px31);
     811           0 :                 *part0 = filterY * px00 + (1.0f - filterY) * px01;
     812           0 :                 *part1 = filterY * px10 + (1.0f - filterY) * px11;
     813           0 :                 *part2 = filterY * px20 + (1.0f - filterY) * px21;
     814           0 :                 *part3 = filterY * px30 + (1.0f - filterY) * px31;
     815             :             } else {
     816           0 :                 *part0 = partAtColumn(ix + 0);
     817           0 :                 *part1 = partAtColumn(ix + 1);
     818           0 :                 *part2 = partAtColumn(ix + 2);
     819           0 :                 *part3 = partAtColumn(ix + 3);
     820             :             }
     821           0 :         };
     822             : 
     823           0 :         auto bilerp = [&](const Sk4f& part0, const Sk4f& part1) {
     824           0 :             return part0 * filterX + part1 * (1.0f - filterX);
     825           0 :         };
     826             : 
     827           0 :         if (length > 0) {
     828             :             // * positive direction - generate destination pixels by sliding the filter from left
     829             :             //                        to right.
     830             : 
     831             :             // overlapPart is the filter part from the end of the previous four pixels used at
     832             :             // the start of the next four pixels.
     833           0 :             Sk4f overlapPart = partAtColumn(iXs[0]);
     834           0 :             int rightColumnCursor = iXs[1];
     835           0 :             while (count >= 4) {
     836             :                 Sk4f part0, part1, part2, part3;
     837           0 :                 get4Parts(rightColumnCursor, &part0, &part1, &part2, &part3);
     838           0 :                 Sk4f px0 = bilerp(overlapPart, part0);
     839           0 :                 Sk4f px1 = bilerp(part0, part1);
     840           0 :                 Sk4f px2 = bilerp(part1, part2);
     841           0 :                 Sk4f px3 = bilerp(part2, part3);
     842           0 :                 overlapPart = part3;
     843           0 :                 fNext->blend4Pixels(px0, px1, px2, px3);
     844           0 :                 rightColumnCursor += 4;
     845           0 :                 count -= 4;
     846             :             }
     847             : 
     848           0 :             while (count > 0) {
     849           0 :                 Sk4f rightPart = partAtColumn(rightColumnCursor);
     850             : 
     851           0 :                 fNext->blendPixel(bilerp(overlapPart, rightPart));
     852           0 :                 overlapPart = rightPart;
     853           0 :                 rightColumnCursor += 1;
     854           0 :                 count -= 1;
     855             :             }
     856             :         } else {
     857             :             // * negative direction - generate destination pixels by sliding the filter from
     858             :             //                        right to left.
     859           0 :             Sk4f overlapPart = partAtColumn(iXs[1]);
     860           0 :             int leftColumnCursor = iXs[0];
     861             : 
     862           0 :             while (count >= 4) {
     863             :                 Sk4f part0, part1, part2, part3;
     864           0 :                 get4Parts(leftColumnCursor - 3, &part3, &part2, &part1, &part0);
     865           0 :                 Sk4f px0 = bilerp(part0, overlapPart);
     866           0 :                 Sk4f px1 = bilerp(part1, part0);
     867           0 :                 Sk4f px2 = bilerp(part2, part1);
     868           0 :                 Sk4f px3 = bilerp(part3, part2);
     869           0 :                 overlapPart = part3;
     870           0 :                 fNext->blend4Pixels(px0, px1, px2, px3);
     871           0 :                 leftColumnCursor -= 4;
     872           0 :                 count -= 4;
     873             :             }
     874             : 
     875           0 :             while (count > 0) {
     876           0 :                 Sk4f leftPart = partAtColumn(leftColumnCursor);
     877             : 
     878           0 :                 fNext->blendPixel(bilerp(leftPart, overlapPart));
     879           0 :                 overlapPart = leftPart;
     880           0 :                 leftColumnCursor -= 1;
     881           0 :                 count -= 1;
     882             :             }
     883             :         }
     884           0 :     }
     885             : 
     886             :     // 1 < |dx| < 2. Going through the source pixels at a faster rate than the dest pixels, but
     887             :     // still slow enough to take advantage of previous calculations.
     888           0 :     void spanMediumRate(Span span) {
     889             :         SkPoint start; SkScalar length; int count;
     890           0 :         std::tie(start, length, count) = span;
     891             : 
     892             :         // Calculate the distance between each sample point.
     893           0 :         const SkScalar dx = length / (count - 1);
     894           0 :         SkASSERT((-2.0f < dx && dx < -1.0f) || (1.0f < dx && dx < 2.0f));
     895             : 
     896             :         // Generate the filter values for the top-left corner.
     897             :         // Note: these values are in filter space; this has implications about how to adjust
     898             :         // these values at each step. For example, as the sample point increases, the filter
     899             :         // value decreases, this is because the filter and position are related by
     900             :         // (1 - (X(sample) - .5)) % 1. The (1 - stuff) causes the filter to move in the opposite
     901             :         // direction of the sample point which is increasing by dx.
     902           0 :         SkScalar filterX = sample_to_filter(X(start));
     903           0 :         SkScalar filterY = sample_to_filter(Y(start));
     904             : 
     905             :         // Generate the four filter points from the sample point start. Generate the row* values.
     906             :         Sk4i iXs, iYs;
     907           0 :         this->filterPoints(start, &iXs, &iYs);
     908           0 :         const void* const row0 = fAccessor.row(iYs[0]);
     909           0 :         const void* const row1 = fAccessor.row(iYs[2]);
     910             : 
     911             :         // Generate part of the filter value at xColumn.
     912           0 :         auto partAtColumn = [&](int xColumn) {
     913           0 :             int adjustedColumn = adjust_edge(fXEdgeType, xColumn, fXMax);
     914             :             Sk4f pxTop, pxBottom;
     915           0 :             this->get2PixelColumn(row0, row1, adjustedColumn, &pxTop, &pxBottom);
     916           0 :             return pxTop * filterY + (1.0f - filterY) * pxBottom;
     917           0 :         };
     918             : 
     919             :         // The leftPart is made up of two pixels from the left column of the filter, right part
     920             :         // is similar. The top and bottom pixels in the *Part are created as a linear blend of
     921             :         // the top and bottom pixels using filterY. See the nextPart function below.
     922           0 :         Sk4f leftPart  = partAtColumn(iXs[0]);
     923           0 :         Sk4f rightPart = partAtColumn(iXs[1]);
     924             : 
     925             :         // Create a destination color by blending together a left and right part using filterX.
     926           0 :         auto bilerp = [&](const Sk4f& leftPart, const Sk4f& rightPart) {
     927           0 :             Sk4f pixel = leftPart * filterX + rightPart * (1.0f - filterX);
     928           0 :             return check_pixel(pixel);
     929           0 :         };
     930             : 
     931             :         // Send the first pixel to the destination. This simplifies the loop structure so that no
     932             :         // extra pixels are fetched for the last iteration of the loop.
     933           0 :         fNext->blendPixel(bilerp(leftPart, rightPart));
     934           0 :         count -= 1;
     935             : 
     936           0 :         if (dx > 0.0f) {
     937             :             // * positive direction - generate destination pixels by sliding the filter from left
     938             :             //                        to right.
     939           0 :             int rightPartCursor = iXs[1];
     940             : 
     941             :             // Advance the filter from left to right. Remember that moving the top-left corner of
     942             :             // the filter to the right actually makes the filter value smaller.
     943           0 :             auto advanceFilter = [&]() {
     944           0 :                 filterX -= dx;
     945             :                 // At this point filterX is less than zero, but might actually be less than -1.
     946           0 :                 if (filterX > -1.0f) {
     947           0 :                     filterX += 1.0f;
     948           0 :                     leftPart = rightPart;
     949           0 :                     rightPartCursor += 1;
     950           0 :                     rightPart = partAtColumn(rightPartCursor);
     951             :                 } else {
     952           0 :                     filterX += 2.0f;
     953           0 :                     rightPartCursor += 2;
     954           0 :                     leftPart = partAtColumn(rightPartCursor - 1);
     955           0 :                     rightPart = partAtColumn(rightPartCursor);
     956             :                 }
     957           0 :                 SkASSERT(0.0f < filterX && filterX <= 1.0f);
     958             : 
     959           0 :                 return bilerp(leftPart, rightPart);
     960           0 :             };
     961             : 
     962           0 :             while (count >= 4) {
     963           0 :                 Sk4f px0 = advanceFilter(),
     964           0 :                      px1 = advanceFilter(),
     965           0 :                      px2 = advanceFilter(),
     966           0 :                      px3 = advanceFilter();
     967           0 :                 fNext->blend4Pixels(px0, px1, px2, px3);
     968           0 :                 count -= 4;
     969             :             }
     970             : 
     971           0 :             while (count > 0) {
     972           0 :                 fNext->blendPixel(advanceFilter());
     973           0 :                 count -= 1;
     974             :             }
     975             :         } else {
     976             :             // * negative direction - generate destination pixels by sliding the filter from
     977             :             //                        right to left.
     978           0 :             int leftPartCursor = iXs[0];
     979             : 
     980           0 :             auto advanceFilter = [&]() {
     981             :                 // Remember, dx < 0 therefore this adds |dx| to filterX.
     982           0 :                 filterX -= dx;
     983             :                 // At this point, filterX is greater than one, but may actually be greater than two.
     984           0 :                 if (filterX < 2.0f) {
     985           0 :                     filterX -= 1.0f;
     986           0 :                     rightPart = leftPart;
     987           0 :                     leftPartCursor -= 1;
     988           0 :                     leftPart = partAtColumn(leftPartCursor);
     989             :                 } else {
     990           0 :                     filterX -= 2.0f;
     991           0 :                     leftPartCursor -= 2;
     992           0 :                     rightPart = partAtColumn(leftPartCursor - 1);
     993           0 :                     leftPart = partAtColumn(leftPartCursor);
     994             :                 }
     995           0 :                 SkASSERT(0.0f < filterX && filterX <= 1.0f);
     996           0 :                 return bilerp(leftPart, rightPart);
     997           0 :             };
     998             : 
     999           0 :             while (count >= 4) {
    1000           0 :                 Sk4f px0 = advanceFilter(),
    1001           0 :                      px1 = advanceFilter(),
    1002           0 :                      px2 = advanceFilter(),
    1003           0 :                      px3 = advanceFilter();
    1004           0 :                 fNext->blend4Pixels(px0, px1, px2, px3);
    1005           0 :                 count -= 4;
    1006             :             }
    1007             : 
    1008           0 :             while (count > 0) {
    1009           0 :                 fNext->blendPixel(advanceFilter());
    1010           0 :                 count -= 1;
    1011             :             }
    1012             :         }
    1013           0 :     }
    1014             : 
    1015             :     // We're moving through source space faster than dst (zoomed out),
    1016             :     // so we'll never reuse a source pixel or be able to do contiguous loads.
    1017           0 :     void spanFastRate(Span span) {
    1018             :         SkPoint start; SkScalar length; int count;
    1019           0 :         std::tie(start, length, count) = span;
    1020           0 :         SkScalar x = X(start);
    1021           0 :         SkScalar y = Y(start);
    1022             : 
    1023           0 :         SkScalar dx = length / (count - 1);
    1024           0 :         while (count > 0) {
    1025           0 :             fNext->blendPixel(this->bilerpSamplePoint(SkPoint{x, y}));
    1026           0 :             x += dx;
    1027           0 :             count -= 1;
    1028             :         }
    1029           0 :     }
    1030             : 
    1031             :     Next* const              fNext;
    1032             :     const SkShader::TileMode fXEdgeType;
    1033             :     const int                fXMax;
    1034             :     const SkShader::TileMode fYEdgeType;
    1035             :     const int                fYMax;
    1036             :     Accessor                 fAccessor;
    1037             : };
    1038             : 
    1039             : }  // namespace
    1040             : 
    1041             : #endif  // SkLinearBitmapPipeline_sampler_DEFINED

Generated by: LCOV version 1.13