diff options
author | 2016-03-23 09:00:33 -0700 | |
---|---|---|
committer | 2016-03-23 09:00:33 -0700 | |
commit | 6eff52afb458bf6702a715d88611fd571544ef73 (patch) | |
tree | a2ffdedd2b0b1fe888dbc3faff6b1f894803f5c2 /src/core/SkLinearBitmapPipeline.cpp | |
parent | 0b8321e19b565f3a13af85b55f046c0a74396a7d (diff) |
WIP: experimental bilerp pipeline.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1775963002
Review URL: https://codereview.chromium.org/1775963002
Diffstat (limited to 'src/core/SkLinearBitmapPipeline.cpp')
-rw-r--r-- | src/core/SkLinearBitmapPipeline.cpp | 652 |
1 files changed, 299 insertions, 353 deletions
diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp index 4c21180a16..3a9a0196f5 100644 --- a/src/core/SkLinearBitmapPipeline.cpp +++ b/src/core/SkLinearBitmapPipeline.cpp @@ -17,12 +17,20 @@ #include "SkLinearBitmapPipeline_core.h" #include "SkLinearBitmapPipeline_matrix.h" #include "SkLinearBitmapPipeline_tile.h" +#include "SkLinearBitmapPipeline_sample.h" class SkLinearBitmapPipeline::PointProcessorInterface { public: virtual ~PointProcessorInterface() { } + // Take the first n (where 0 < n && n < 4) items from xs and ys and sample those points. For + // nearest neighbor, that means just taking the floor xs and ys. For bilerp, this means + // to expand the bilerp filter around the point and sample using that filter. virtual void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0; + // Same as pointListFew, but n = 4. virtual void VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0; + // A span is a compact form of sample points that are obtained by mapping points from + // destination space to source space. This is used for horizontal lines only, and is mainly + // used to take advantage of memory coherence for horizontal spans. virtual void pointSpan(Span span) = 0; }; @@ -41,8 +49,13 @@ public: // +--------+--------+ // These pixels coordinates are arranged in the following order in xs and ys: // px00 px10 px01 px11 - virtual void VECTORCALL bilerpList(Sk4s xs, Sk4s ys) = 0; - virtual void bilerpSpan(BilerpSpan span) = 0; + virtual void VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) = 0; + + // A span represents sample points that have been mapped from destination space to source + // space. Each sample point is then expanded to the four bilerp points by add +/- 0.5. The + // resulting Y values my be off the tile. When y +/- 0.5 are more than 1 apart because of + // tiling, the second Y is used to denote the retiled Y value. + virtual void bilerpSpan(Span span, SkScalar y) = 0; }; class SkLinearBitmapPipeline::PixelPlacerInterface { @@ -54,6 +67,9 @@ public: }; namespace { + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Matrix Stage // PointProcessor uses a strategy to help complete the work of the different stages. The strategy // must implement the following methods: // * processPoints(xs, ys) - must mutate the xs and ys for the stage. @@ -64,10 +80,10 @@ namespace { // maybeProcessSpan - returns false if it can not process the span and needs to fallback to // point lists for processing. template<typename Strategy, typename Next> -class PointProcessor final : public SkLinearBitmapPipeline::PointProcessorInterface { +class MatrixStage final : public SkLinearBitmapPipeline::PointProcessorInterface { public: template <typename... Args> - PointProcessor(Next* next, Args&&... args) + MatrixStage(Next* next, Args&&... args) : fNext{next} , fStrategy{std::forward<Args>(args)...}{ } @@ -94,66 +110,31 @@ private: Strategy fStrategy; }; -// See PointProcessor for responsibilities of Strategy. -template<typename Strategy, typename Next> -class BilerpProcessor final : public SkLinearBitmapPipeline::BilerpProcessorInterface { -public: - template <typename... Args> - BilerpProcessor(Next* next, Args&&... args) - : fNext{next} - , fStrategy{std::forward<Args>(args)...}{ } - - void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { - fStrategy.processPoints(&xs, &ys); - fNext->pointListFew(n, xs, ys); - } - - void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { - fStrategy.processPoints(&xs, &ys); - fNext->pointList4(xs, ys); - } - - void VECTORCALL bilerpList(Sk4s xs, Sk4s ys) override { - fStrategy.processPoints(&xs, &ys); - fNext->bilerpList(xs, ys); - } - - void pointSpan(Span span) override { - SkASSERT(!span.isEmpty()); - if (!fStrategy.maybeProcessSpan(span, fNext)) { - span_fallback(span, this); - } - } - - void bilerpSpan(BilerpSpan bSpan) override { - SkASSERT(!bSpan.isEmpty()); - if (!fStrategy.maybeProcessBilerpSpan(bSpan, fNext)) { - bilerp_span_fallback(bSpan, this); - } - } - -private: - Next* const fNext; - Strategy fStrategy; -}; +template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> +using TranslateMatrix = MatrixStage<TranslateMatrixStrategy, Next>; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Matrix Stage template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> -using TranslateMatrix = PointProcessor<TranslateMatrixStrategy, Next>; +using ScaleMatrix = MatrixStage<ScaleMatrixStrategy, Next>; template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> -using ScaleMatrix = PointProcessor<ScaleMatrixStrategy, Next>; +using AffineMatrix = MatrixStage<AffineMatrixStrategy, Next>; template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> -using AffineMatrix = PointProcessor<AffineMatrixStrategy, Next>; +using PerspectiveMatrix = MatrixStage<PerspectiveMatrixStrategy, Next>; + static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix( SkLinearBitmapPipeline::PointProcessorInterface* next, const SkMatrix& inverse, SkLinearBitmapPipeline::MatrixStage* matrixProc) { if (inverse.hasPerspective()) { - SkFAIL("Not implemented."); + matrixProc->Initialize<PerspectiveMatrix<>>( + next, + SkVector{inverse.getTranslateX(), inverse.getTranslateY()}, + SkVector{inverse.getScaleX(), inverse.getScaleY()}, + SkVector{inverse.getSkewX(), inverse.getSkewY()}, + SkVector{inverse.getPerspX(), inverse.getPerspY()}, + inverse.get(SkMatrix::kMPersp2)); } else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) { matrixProc->Initialize<AffineMatrix<>>( next, @@ -176,370 +157,305 @@ static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix( } //////////////////////////////////////////////////////////////////////////////////////////////////// -// Bilerp Expansion Stage -template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> -class ExpandBilerp final : public SkLinearBitmapPipeline::PointProcessorInterface { +// Tile Stage + +template<typename XStrategy, typename YStrategy, typename Next> +class NearestTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface { public: - ExpandBilerp(Next* next) : fNext{next} { } + template <typename... Args> + NearestTileStage(Next* next, SkISize dimensions) + : fNext{next} + , fXStrategy{dimensions.width()} + , fYStrategy{dimensions.height()}{ } void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { - SkASSERT(0 < n && n < 4); - // px00 px10 px01 px11 - const Sk4s kXOffsets{-0.5f, 0.5f, -0.5f, 0.5f}, - kYOffsets{-0.5f, -0.5f, 0.5f, 0.5f}; - if (n >= 1) fNext->bilerpList(Sk4s{xs[0]} + kXOffsets, Sk4s{ys[0]} + kYOffsets); - if (n >= 2) fNext->bilerpList(Sk4s{xs[1]} + kXOffsets, Sk4s{ys[1]} + kYOffsets); - if (n >= 3) fNext->bilerpList(Sk4s{xs[2]} + kXOffsets, Sk4s{ys[2]} + kYOffsets); + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + fNext->pointListFew(n, xs, ys); } - void VECTORCALL pointList4(Sk4f xs, Sk4f ys) override { - // px00 px10 px01 px11 - const Sk4f kXOffsets{-0.5f, 0.5f, -0.5f, 0.5f}, - kYOffsets{-0.5f, -0.5f, 0.5f, 0.5f}; - fNext->bilerpList(Sk4s{xs[0]} + kXOffsets, Sk4s{ys[0]} + kYOffsets); - fNext->bilerpList(Sk4s{xs[1]} + kXOffsets, Sk4s{ys[1]} + kYOffsets); - fNext->bilerpList(Sk4s{xs[2]} + kXOffsets, Sk4s{ys[2]} + kYOffsets); - fNext->bilerpList(Sk4s{xs[3]} + kXOffsets, Sk4s{ys[3]} + kYOffsets); + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + fNext->pointList4(xs, ys); } + // The span you pass must not be empty. void pointSpan(Span span) override { SkASSERT(!span.isEmpty()); SkPoint start; SkScalar length; int count; std::tie(start, length, count) = span; - // Adjust the span so that it is in the correct phase with the pixel. - BilerpSpan bSpan{X(start) - 0.5f, Y(start) - 0.5f, Y(start) + 0.5f, length, count}; - fNext->bilerpSpan(bSpan); + SkScalar x = X(start); + SkScalar y = fYStrategy.tileY(Y(start)); + Span yAdjustedSpan{{x, y}, length, count}; + if (!fXStrategy.maybeProcessSpan(yAdjustedSpan, fNext)) { + span_fallback(span, this); + } } private: Next* const fNext; + XStrategy fXStrategy; + YStrategy fYStrategy; }; -static SkLinearBitmapPipeline::PointProcessorInterface* choose_filter( - SkLinearBitmapPipeline::BilerpProcessorInterface* next, - SkFilterQuality filterQuailty, - SkLinearBitmapPipeline::FilterStage* filterProc) { - if (SkFilterQuality::kNone_SkFilterQuality == filterQuailty) { - return next; - } else { - filterProc->Initialize<ExpandBilerp<>>(next); - return filterProc->get(); +template<typename XStrategy, typename YStrategy, typename Next> +class BilerpTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface { +public: + template <typename... Args> + BilerpTileStage(Next* next, SkISize dimensions) + : fXMax(dimensions.width()) + , fYMax(dimensions.height()) + , fNext{next} + , fXStrategy{dimensions.width()} + , fYStrategy{dimensions.height()}{ } + + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + // TODO: check to see if xs and ys are in range then just call pointListFew on next. + if (n >= 1) this->bilerpPoint(xs[0], ys[0]); + if (n >= 2) this->bilerpPoint(xs[1], ys[1]); + if (n >= 3) this->bilerpPoint(xs[2], ys[2]); } -} -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Tile Stage -template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> -using Clamp = BilerpProcessor<ClampStrategy, Next>; + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + // TODO: check to see if xs and ys are in range then just call pointList4 on next. + this->bilerpPoint(xs[0], ys[0]); + this->bilerpPoint(xs[1], ys[1]); + this->bilerpPoint(xs[2], ys[2]); + this->bilerpPoint(xs[3], ys[3]); + } + + struct Wrapper { + void pointSpan(Span span) { + processor->breakIntoEdges(span); + } -template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> -using Repeat = BilerpProcessor<RepeatStrategy, Next>; + BilerpTileStage* processor; + }; -static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_tiler( - SkLinearBitmapPipeline::BilerpProcessorInterface* next, - SkSize dimensions, - SkShader::TileMode xMode, - SkShader::TileMode yMode, - SkLinearBitmapPipeline::TileStage* tileProcXOrBoth, - SkLinearBitmapPipeline::TileStage* tileProcY) { - if (xMode == yMode) { - switch (xMode) { - case SkShader::kClamp_TileMode: - tileProcXOrBoth->Initialize<Clamp<>>(next, dimensions); - break; - case SkShader::kRepeat_TileMode: - tileProcXOrBoth->Initialize<Repeat<>>(next, dimensions); - break; - case SkShader::kMirror_TileMode: - SkFAIL("Not implemented."); - break; - } - } else { - switch (yMode) { - case SkShader::kClamp_TileMode: - tileProcY->Initialize<Clamp<>>(next, Y(dimensions)); - break; - case SkShader::kRepeat_TileMode: - tileProcY->Initialize<Repeat<>>(next, Y(dimensions)); - break; - case SkShader::kMirror_TileMode: - SkFAIL("Not implemented."); - break; - } - switch (xMode) { - case SkShader::kClamp_TileMode: - tileProcXOrBoth->Initialize<Clamp<>>(tileProcY->get(), X(dimensions)); - break; - case SkShader::kRepeat_TileMode: - tileProcXOrBoth->Initialize<Repeat<>>(tileProcY->get(), X(dimensions)); - break; - case SkShader::kMirror_TileMode: - SkFAIL("Not implemented."); - break; + // The span you pass must not be empty. + void pointSpan(Span span) override { + SkASSERT(!span.isEmpty()); + + Wrapper wrapper = {this}; + if (!fXStrategy.maybeProcessSpan(span, &wrapper)) { + span_fallback(span, this); } } - return tileProcXOrBoth->get(); -} -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Source Sampling Stage -class sRGBFast { -public: - static Sk4s VECTORCALL sRGBToLinear(Sk4s pixel) { - Sk4s l = pixel * pixel; - return Sk4s{l[0], l[1], l[2], pixel[3]}; +private: + void bilerpPoint(SkScalar x, SkScalar y) { + Sk4f txs = Sk4f{x} + Sk4f{-0.5f, 0.5f, -0.5f, 0.5f}; + Sk4f tys = Sk4f{y} + Sk4f{-0.5f, -0.5f, 0.5f, 0.5f}; + fXStrategy.tileXPoints(&txs); + fYStrategy.tileYPoints(&tys); + fNext->bilerpEdge(txs, tys); } -}; -enum class ColorOrder { - kRGBA = false, - kBGRA = true, -}; -template <SkColorProfileType colorProfile, ColorOrder colorOrder> -class Pixel8888 { -public: - Pixel8888(int width, const uint32_t* src) : fSrc{src}, fWidth{width}{ } - Pixel8888(const SkPixmap& srcPixmap) - : fSrc{srcPixmap.addr32()} - , fWidth{static_cast<int>(srcPixmap.rowBytes() / 4)} { } - - void VECTORCALL getFewPixels(int n, Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) { - Sk4i XIs = SkNx_cast<int, SkScalar>(xs); - Sk4i YIs = SkNx_cast<int, SkScalar>(ys); - Sk4i bufferLoc = YIs * fWidth + XIs; - switch (n) { - case 3: - *px2 = this->getPixel(fSrc, bufferLoc[2]); - case 2: - *px1 = this->getPixel(fSrc, bufferLoc[1]); - case 1: - *px0 = this->getPixel(fSrc, bufferLoc[0]); - default: - break; + void handleEdges(Span span, SkScalar dx) { + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = span; + SkScalar x = X(start); + SkScalar y = Y(start); + SkScalar tiledY = fYStrategy.tileY(y); + while (count > 0) { + this->bilerpPoint(x, tiledY); + x += dx; + count -= 1; } } - void VECTORCALL get4Pixels(Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) { - Sk4i XIs = SkNx_cast<int, SkScalar>(xs); - Sk4i YIs = SkNx_cast<int, SkScalar>(ys); - Sk4i bufferLoc = YIs * fWidth + XIs; - *px0 = this->getPixel(fSrc, bufferLoc[0]); - *px1 = this->getPixel(fSrc, bufferLoc[1]); - *px2 = this->getPixel(fSrc, bufferLoc[2]); - *px3 = this->getPixel(fSrc, bufferLoc[3]); - } - - void get4Pixels(const void* vsrc, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) { - const uint32_t* src = static_cast<const uint32_t*>(vsrc); - *px0 = this->getPixel(src, index + 0); - *px1 = this->getPixel(src, index + 1); - *px2 = this->getPixel(src, index + 2); - *px3 = this->getPixel(src, index + 3); + void yProcessSpan(Span span) { + SkScalar tiledY = fYStrategy.tileY(span.startY()); + if (0.5f <= tiledY && tiledY < fYMax - 0.5f ) { + Span tiledSpan{{span.startX(), tiledY}, span.length(), span.count()}; + fNext->pointSpan(tiledSpan); + } else { + // Convert to the Y0 bilerp sample set by shifting by -0.5f. Then tile that new y + // value and shift it back resulting in the working Y0. Do the same thing with Y1 but + // in the opposite direction. + SkScalar y0 = fYStrategy.tileY(span.startY() - 0.5f) + 0.5f; + SkScalar y1 = fYStrategy.tileY(span.startY() + 0.5f) - 0.5f; + Span newSpan{{span.startX(), y0}, span.length(), span.count()}; + fNext->bilerpSpan(newSpan, y1); + } } + void breakIntoEdges(Span span) { + if (span.length() == 0) { + yProcessSpan(span); + } else { + SkScalar dx = span.length() / (span.count() - 1); + if (span.length() > 0) { + Span leftBorder = span.breakAt(0.5f, dx); + if (!leftBorder.isEmpty()) { + this->handleEdges(leftBorder, dx); + } + Span center = span.breakAt(fXMax - 0.5f, dx); + if (!center.isEmpty()) { + this->yProcessSpan(center); + } + + if (!span.isEmpty()) { + this->handleEdges(span, dx); + } + } else { + Span center = span.breakAt(fXMax + 0.5f, dx); + if (!span.isEmpty()) { + this->handleEdges(span, dx); + } + Span leftEdge = center.breakAt(0.5f, dx); + if (!center.isEmpty()) { + this->yProcessSpan(center); + } + if (!leftEdge.isEmpty()) { + this->handleEdges(leftEdge, dx); + } - Sk4f getPixel(const void* vsrc, int index) { - const uint32_t* src = static_cast<const uint32_t*>(vsrc); - Sk4b bytePixel = Sk4b::Load((uint8_t *)(&src[index])); - Sk4f pixel = SkNx_cast<float, uint8_t>(bytePixel); - if (colorOrder == ColorOrder::kBGRA) { - pixel = SkNx_shuffle<2, 1, 0, 3>(pixel); - } - pixel = pixel * Sk4f{1.0f/255.0f}; - if (colorProfile == kSRGB_SkColorProfileType) { - pixel = sRGBFast::sRGBToLinear(pixel); + } } - return pixel; } - const uint32_t* row(int y) { return fSrc + y * fWidth[0]; } + SkScalar fXMax; + SkScalar fYMax; + Next* const fNext; + XStrategy fXStrategy; + YStrategy fYStrategy; +}; -private: - const uint32_t* const fSrc; - const Sk4i fWidth; +template <typename XStrategy, typename YStrategy, typename Next> +void make_tile_stage( + SkFilterQuality filterQuality, SkISize dimensions, + Next* next, SkLinearBitmapPipeline::TileStage* tileStage) { + if (filterQuality == kNone_SkFilterQuality) { + tileStage->Initialize<NearestTileStage<XStrategy, YStrategy, Next>>(next, dimensions); + } else { + tileStage->Initialize<BilerpTileStage<XStrategy, YStrategy, Next>>(next, dimensions); + } +} +template <typename XStrategy> +void choose_tiler_ymode( + SkShader::TileMode yMode, SkFilterQuality filterQuality, SkISize dimensions, + SkLinearBitmapPipeline::BilerpProcessorInterface* next, + SkLinearBitmapPipeline::TileStage* tileStage) { + switch (yMode) { + case SkShader::kClamp_TileMode: + make_tile_stage<XStrategy, YClampStrategy>(filterQuality, dimensions, next, tileStage); + break; + case SkShader::kRepeat_TileMode: + make_tile_stage<XStrategy, YRepeatStrategy>(filterQuality, dimensions, next, tileStage); + break; + case SkShader::kMirror_TileMode: + make_tile_stage<XStrategy, YMirrorStrategy>(filterQuality, dimensions, next, tileStage); + break; + } }; -// Explaination of the math: -// 1 - x x -// +--------+--------+ -// | | | -// 1 - y | px00 | px10 | -// | | | -// +--------+--------+ -// | | | -// y | px01 | px11 | -// | | | -// +--------+--------+ -// -// -// Given a pixelxy each is multiplied by a different factor derived from the fractional part of x -// and y: -// * px00 -> (1 - x)(1 - y) = 1 - x - y + xy -// * px10 -> x(1 - y) = x - xy -// * px01 -> (1 - x)y = y - xy -// * px11 -> xy -// So x * y is calculated first and then used to calculate all the other factors. -static Sk4s VECTORCALL bilerp4(Sk4s xs, Sk4s ys, Sk4f px00, Sk4f px10, - Sk4f px01, Sk4f px11) { - // Calculate fractional xs and ys. - Sk4s fxs = xs - xs.floor(); - Sk4s fys = ys - ys.floor(); - Sk4s fxys{fxs * fys}; - Sk4f sum = px11 * fxys; - sum = sum + px01 * (fys - fxys); - sum = sum + px10 * (fxs - fxys); - sum = sum + px00 * (Sk4f{1.0f} - fxs - fys + fxys); - return sum; +static SkLinearBitmapPipeline::PointProcessorInterface* choose_tiler( + SkLinearBitmapPipeline::BilerpProcessorInterface* next, + SkISize dimensions, + SkShader::TileMode xMode, + SkShader::TileMode yMode, + SkFilterQuality filterQuality, + SkLinearBitmapPipeline::TileStage* tileStage) { + switch (xMode) { + case SkShader::kClamp_TileMode: + choose_tiler_ymode<XClampStrategy>(yMode, filterQuality, dimensions, next, tileStage); + break; + case SkShader::kRepeat_TileMode: + choose_tiler_ymode<XRepeatStrategy>(yMode, filterQuality, dimensions, next, tileStage); + break; + case SkShader::kMirror_TileMode: + choose_tiler_ymode<XMirrorStrategy>(yMode, filterQuality, dimensions, next, tileStage); + break; + } + + return tileStage->get(); } -template <typename SourceStrategy> -class Sampler final : public SkLinearBitmapPipeline::BilerpProcessorInterface { + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Source Sampling Stage +template <typename SourceStrategy, typename Next> +class NearestNeighborSampler final : public SkLinearBitmapPipeline::BilerpProcessorInterface { public: template <typename... Args> - Sampler(SkLinearBitmapPipeline::PixelPlacerInterface* next, Args&&... args) - : fNext{next} - , fStrategy{std::forward<Args>(args)...} { } + NearestNeighborSampler(Next* next, Args&&... args) + : fSampler{next, std::forward<Args>(args)...} { } void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { - SkASSERT(0 < n && n < 4); - Sk4f px0, px1, px2; - fStrategy.getFewPixels(n, xs, ys, &px0, &px1, &px2); - if (n >= 1) fNext->placePixel(px0); - if (n >= 2) fNext->placePixel(px1); - if (n >= 3) fNext->placePixel(px2); + fSampler.nearestListFew(n, xs, ys); } - void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { - Sk4f px0, px1, px2, px3; - fStrategy.get4Pixels(xs, ys, &px0, &px1, &px2, &px3); - fNext->place4Pixels(px0, px1, px2, px3); + fSampler.nearestList4(xs, ys); } - - void VECTORCALL bilerpList(Sk4s xs, Sk4s ys) override { - Sk4f px00, px10, px01, px11; - fStrategy.get4Pixels(xs, ys, &px00, &px10, &px01, &px11); - Sk4f pixel = bilerp4(xs, ys, px00, px10, px01, px11); - fNext->placePixel(pixel); + void pointSpan(Span span) override { + fSampler.nearestSpan(span); + } + void VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override { + SkFAIL("Using nearest neighbor sampler, but calling a bilerpEdge."); } - void pointSpan(Span span) override { - SkASSERT(!span.isEmpty()); - SkPoint start; SkScalar length; int count; - std::tie(start, length, count) = span; - if (length < (count - 1)) { - this->pointSpanSlowRate(span); - } else if (length == (count - 1)) { - this->pointSpanUnitRate(span); - } else { - this->pointSpanFastRate(span); - } + virtual void bilerpSpan(Span span, SkScalar y) override { + SkFAIL("Using nearest neighbor sampler, but calling a bilerpSpan."); } private: - // When moving through source space more slowly than dst space (zoomed in), - // we'll be sampling from the same source pixel more than once. - void pointSpanSlowRate(Span span) { - SkPoint start; SkScalar length; int count; - std::tie(start, length, count) = span; - SkScalar x = X(start); - SkFixed fx = SkScalarToFixed(x); - SkScalar dx = length / (count - 1); - SkFixed fdx = SkScalarToFixed(dx); - - const void* row = fStrategy.row((int)std::floor(Y(start))); - SkLinearBitmapPipeline::PixelPlacerInterface* next = fNext; - - int ix = SkFixedFloorToInt(fx); - int prevIX = ix; - Sk4f fpixel = fStrategy.getPixel(row, ix); - - // When dx is less than one, each pixel is used more than once. Using the fixed point fx - // allows the code to quickly check that the same pixel is being used. The code uses this - // same pixel check to do the sRGB and normalization only once. - auto getNextPixel = [&]() { - if (ix != prevIX) { - fpixel = fStrategy.getPixel(row, ix); - prevIX = ix; - } - fx += fdx; - ix = SkFixedFloorToInt(fx); - return fpixel; - }; - - while (count >= 4) { - Sk4f px0 = getNextPixel(); - Sk4f px1 = getNextPixel(); - Sk4f px2 = getNextPixel(); - Sk4f px3 = getNextPixel(); - next->place4Pixels(px0, px1, px2, px3); - count -= 4; - } - while (count > 0) { - next->placePixel(getNextPixel()); - count -= 1; - } - } + GeneralSampler<SourceStrategy, Next> fSampler; +}; - // We're moving through source space at a rate of 1 source pixel per 1 dst pixel. - // We'll never re-use pixels, but we can at least load contiguous pixels. - void pointSpanUnitRate(Span span) { - SkPoint start; SkScalar length; int count; - std::tie(start, length, count) = span; - int ix = SkScalarFloorToInt(X(start)); - const void* row = fStrategy.row((int)std::floor(Y(start))); - SkLinearBitmapPipeline::PixelPlacerInterface* next = fNext; - while (count >= 4) { - Sk4f px0, px1, px2, px3; - fStrategy.get4Pixels(row, ix, &px0, &px1, &px2, &px3); - next->place4Pixels(px0, px1, px2, px3); - ix += 4; - count -= 4; - } +template <typename SourceStrategy, typename Next> +class BilerpSampler final : public SkLinearBitmapPipeline::BilerpProcessorInterface { +public: + template <typename... Args> + BilerpSampler(Next* next, Args&&... args) + : fSampler{next, std::forward<Args>(args)...} { } - while (count > 0) { - next->placePixel(fStrategy.getPixel(row, ix)); - ix += 1; - count -= 1; - } + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { + fSampler.bilerpListFew(n, xs, ys); } - - // We're moving through source space faster than dst (zoomed out), - // so we'll never reuse a source pixel or be able to do contiguous loads. - void pointSpanFastRate(Span span) { - span_fallback(span, this); + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fSampler.bilerpList4(xs, ys); + } + void pointSpan(Span span) override { + fSampler.bilerpSpan(span); + } + void VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override { + fSampler.bilerpEdge(xs, ys); } - void bilerpSpan(BilerpSpan span) override { - bilerp_span_fallback(span, this); + virtual void bilerpSpan(Span span, SkScalar y) override { + fSampler.bilerpSpanWithY(span, y); } private: - SkLinearBitmapPipeline::PixelPlacerInterface* const fNext; - SourceStrategy fStrategy; + GeneralSampler<SourceStrategy, Next> fSampler; }; -using Pixel8888SRGB = Pixel8888<kSRGB_SkColorProfileType, ColorOrder::kRGBA>; -using Pixel8888LRGB = Pixel8888<kLinear_SkColorProfileType, ColorOrder::kRGBA>; -using Pixel8888SBGR = Pixel8888<kSRGB_SkColorProfileType, ColorOrder::kBGRA>; -using Pixel8888LBGR = Pixel8888<kLinear_SkColorProfileType, ColorOrder::kBGRA>; +using Placer = SkLinearBitmapPipeline::PixelPlacerInterface; -static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_pixel_sampler( - SkLinearBitmapPipeline::PixelPlacerInterface* next, +template<template <typename, typename> class Sampler> +static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_pixel_sampler_base( + Placer* next, const SkPixmap& srcPixmap, SkLinearBitmapPipeline::SampleStage* sampleStage) { const SkImageInfo& imageInfo = srcPixmap.info(); switch (imageInfo.colorType()) { case kRGBA_8888_SkColorType: if (imageInfo.profileType() == kSRGB_SkColorProfileType) { - sampleStage->Initialize<Sampler<Pixel8888SRGB>>(next, srcPixmap); + sampleStage->Initialize<Sampler<Pixel8888SRGB, Placer>>(next, srcPixmap); } else { - sampleStage->Initialize<Sampler<Pixel8888LRGB>>(next, srcPixmap); + sampleStage->Initialize<Sampler<Pixel8888LRGB, Placer>>(next, srcPixmap); } break; case kBGRA_8888_SkColorType: if (imageInfo.profileType() == kSRGB_SkColorProfileType) { - sampleStage->Initialize<Sampler<Pixel8888SBGR>>(next, srcPixmap); + sampleStage->Initialize<Sampler<Pixel8888SBGR, Placer>>(next, srcPixmap); } else { - sampleStage->Initialize<Sampler<Pixel8888LBGR>>(next, srcPixmap); + sampleStage->Initialize<Sampler<Pixel8888LBGR, Placer>>(next, srcPixmap); } break; default: @@ -549,11 +465,24 @@ static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_pixel_sampler( return sampleStage->get(); } +SkLinearBitmapPipeline::BilerpProcessorInterface* choose_pixel_sampler( + Placer* next, + SkFilterQuality filterQuality, + const SkPixmap& srcPixmap, + SkLinearBitmapPipeline::SampleStage* sampleStage) { + if (filterQuality == kNone_SkFilterQuality) { + return choose_pixel_sampler_base<NearestNeighborSampler>(next, srcPixmap, sampleStage); + } else { + return choose_pixel_sampler_base<BilerpSampler>(next, srcPixmap, sampleStage); + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Pixel Placement Stage template <SkAlphaType alphaType> class PlaceFPPixel final : public SkLinearBitmapPipeline::PixelPlacerInterface { public: + PlaceFPPixel(float postAlpha) : fPostAlpha{postAlpha} { } void VECTORCALL placePixel(Sk4f pixel) override { PlacePixel(fDst, pixel, 0); fDst += 1; @@ -573,11 +502,12 @@ public: } private: - static void VECTORCALL PlacePixel(SkPM4f* dst, Sk4f pixel, int index) { + void VECTORCALL PlacePixel(SkPM4f* dst, Sk4f pixel, int index) { Sk4f newPixel = pixel; if (alphaType == kUnpremul_SkAlphaType) { newPixel = Premultiply(pixel); } + newPixel = newPixel * fPostAlpha; newPixel.store(dst + index); } static Sk4f VECTORCALL Premultiply(Sk4f pixel) { @@ -586,16 +516,18 @@ private: } SkPM4f* fDst; + Sk4f fPostAlpha; }; static SkLinearBitmapPipeline::PixelPlacerInterface* choose_pixel_placer( SkAlphaType alphaType, + float postAlpha, SkLinearBitmapPipeline::PixelStage* placerStage) { if (alphaType == kUnpremul_SkAlphaType) { - placerStage->Initialize<PlaceFPPixel<kUnpremul_SkAlphaType>>(); + placerStage->Initialize<PlaceFPPixel<kUnpremul_SkAlphaType>>(postAlpha); } else { // kOpaque_SkAlphaType is treated the same as kPremul_SkAlphaType - placerStage->Initialize<PlaceFPPixel<kPremul_SkAlphaType>>(); + placerStage->Initialize<PlaceFPPixel<kPremul_SkAlphaType>>(postAlpha); } return placerStage->get(); } @@ -608,18 +540,31 @@ SkLinearBitmapPipeline::SkLinearBitmapPipeline( const SkMatrix& inverse, SkFilterQuality filterQuality, SkShader::TileMode xTile, SkShader::TileMode yTile, + float postAlpha, const SkPixmap& srcPixmap) { - SkSize size = SkSize::Make(srcPixmap.width(), srcPixmap.height()); + SkISize dimensions = srcPixmap.info().dimensions(); const SkImageInfo& srcImageInfo = srcPixmap.info(); + SkMatrix adjustedInverse = inverse; + if (filterQuality == kNone_SkFilterQuality) { + if (inverse.getScaleX() >= 0.0f) { + adjustedInverse.setTranslateX( + nextafterf(inverse.getTranslateX(), std::floor(inverse.getTranslateX()))); + } + if (inverse.getScaleY() >= 0.0f) { + adjustedInverse.setTranslateY( + nextafterf(inverse.getTranslateY(), std::floor(inverse.getTranslateY()))); + } + } + // As the stages are built, the chooser function may skip a stage. For example, with the // identity matrix, the matrix stage is skipped, and the tilerStage is the first stage. - auto placementStage = choose_pixel_placer(srcImageInfo.alphaType(), &fPixelStage); - auto samplerStage = choose_pixel_sampler(placementStage, srcPixmap, &fSampleStage); - auto tilerStage = choose_tiler(samplerStage, size, xTile, yTile, &fTileXOrBothStage, - &fTileYStage); - auto filterStage = choose_filter(tilerStage, filterQuality, &fFilterStage); - fFirstStage = choose_matrix(filterStage, inverse, &fMatrixStage); + auto placementStage = choose_pixel_placer(srcImageInfo.alphaType(), postAlpha, &fPixelStage); + auto samplerStage = choose_pixel_sampler(placementStage, + filterQuality, srcPixmap, &fSampleStage); + auto tilerStage = choose_tiler(samplerStage, + dimensions, xTile, yTile, filterQuality, &fTiler); + fFirstStage = choose_matrix(tilerStage, adjustedInverse, &fMatrixStage); } void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) { @@ -629,5 +574,6 @@ void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) { // math correct through the different stages. Count is the number of pixel to produce. // Since the code samples at pixel centers, length is the distance from the center of the // first pixel to the center of the last pixel. This implies that length is count-1. - fFirstStage->pointSpan(Span{SkPoint{x + 0.5f, y + 0.5f}, count - 1.0f, count}); + fFirstStage->pointSpan(Span{{x + 0.5f, y + 0.5f}, count - 1.0f, count}); } + |