Index: src/core/SkLinearBitmapPipeline.cpp |
diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp |
index 4c21180a163a32ed9664d7857e028b15e4482ee3..3a9a0196f5659d8a50a18fcfd3c74bc93bf99932 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}); |
} |
+ |