Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Unified Diff: src/core/SkLinearBitmapPipeline.cpp

Issue 1775963002: Bilerp + mirror + perspective (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Address comments. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/core/SkLinearBitmapPipeline.h ('k') | src/core/SkLinearBitmapPipeline_core.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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});
}
+
« no previous file with comments | « src/core/SkLinearBitmapPipeline.h ('k') | src/core/SkLinearBitmapPipeline_core.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698