| Index: src/core/SkLinearBitmapPipeline.cpp
|
| diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp
|
| index 01227657098d9aaf2479a9f0cff29d419f8a09cf..088e829345f016f3b06a84dc83cdef2f13dcbef2 100644
|
| --- a/src/core/SkLinearBitmapPipeline.cpp
|
| +++ b/src/core/SkLinearBitmapPipeline.cpp
|
| @@ -165,14 +165,15 @@
|
| // Tile Stage
|
|
|
| template<typename XStrategy, typename YStrategy, typename Next>
|
| -class CombinedTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
|
| +class NearestTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
|
| public:
|
| - CombinedTileStage(Next* next, SkISize dimensions)
|
| + template <typename... Args>
|
| + NearestTileStage(Next* next, SkISize dimensions)
|
| : fNext{next}
|
| , fXStrategy{dimensions.width()}
|
| , fYStrategy{dimensions.height()}{ }
|
|
|
| - CombinedTileStage(Next* next, const CombinedTileStage& stage)
|
| + NearestTileStage(Next* next, const NearestTileStage& stage)
|
| : fNext{next}
|
| , fXStrategy{stage.fXStrategy}
|
| , fYStrategy{stage.fYStrategy} { }
|
| @@ -194,16 +195,9 @@
|
| SkASSERT(!span.isEmpty());
|
| SkPoint start; SkScalar length; int count;
|
| std::tie(start, length, count) = span;
|
| -
|
| - if (span.count() == 1) {
|
| - this->pointListFew(1, span.startX(), span.startY());
|
| - return;
|
| - }
|
| -
|
| 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);
|
| }
|
| @@ -215,27 +209,173 @@
|
| YStrategy fYStrategy;
|
| };
|
|
|
| -template <typename XStrategy, typename Next>
|
| +template<typename XStrategy, typename YStrategy, typename Next>
|
| +class BilerpTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
|
| +public:
|
| + template <typename... Args>
|
| + BilerpTileStage(Next* next, SkISize dimensions)
|
| + : fNext{next}
|
| + , fXMax(dimensions.width())
|
| + , fYMax(dimensions.height())
|
| + , fXStrategy{dimensions.width()}
|
| + , fYStrategy{dimensions.height()} { }
|
| +
|
| + BilerpTileStage(Next* next, const BilerpTileStage& stage)
|
| + : fNext{next}
|
| + , fXMax{stage.fXMax}
|
| + , fYMax{stage.fYMax}
|
| + , fXStrategy{stage.fXStrategy}
|
| + , fYStrategy{stage.fYStrategy} { }
|
| +
|
| + void SK_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]);
|
| + }
|
| +
|
| + void SK_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);
|
| + }
|
| +
|
| + void repeatSpan(Span span, int32_t repeatCount) {
|
| + while (repeatCount --> 0) {
|
| + processor->pointSpan(span);
|
| + }
|
| + }
|
| +
|
| + BilerpTileStage* processor;
|
| + };
|
| +
|
| + // 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);
|
| + }
|
| + }
|
| +
|
| +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);
|
| + }
|
| +
|
| + 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 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.count() == 1) {
|
| + this->bilerpPoint(span.startX(), span.startY());
|
| + } else 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);
|
| + }
|
| +
|
| + }
|
| + }
|
| + }
|
| +
|
| + Next* const fNext;
|
| + SkScalar fXMax;
|
| + SkScalar fYMax;
|
| + XStrategy fXStrategy;
|
| + YStrategy fYStrategy;
|
| +};
|
| +
|
| +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->initStage<NearestTileStage<XStrategy, YStrategy, Next>>(next, dimensions);
|
| + } else {
|
| + tileStage->initStage<BilerpTileStage<XStrategy, YStrategy, Next>>(next, dimensions);
|
| + }
|
| +}
|
| +template <typename XStrategy>
|
| void choose_tiler_ymode(
|
| SkShader::TileMode yMode, SkFilterQuality filterQuality, SkISize dimensions,
|
| - Next* next,
|
| + SkLinearBitmapPipeline::SampleProcessorInterface* next,
|
| SkLinearBitmapPipeline::TileStage* tileStage) {
|
| switch (yMode) {
|
| - case SkShader::kClamp_TileMode: {
|
| - using Tiler = CombinedTileStage<XStrategy, YClampStrategy, Next>;
|
| - tileStage->initStage<Tiler>(next, dimensions);
|
| - break;
|
| - }
|
| - case SkShader::kRepeat_TileMode: {
|
| - using Tiler = CombinedTileStage<XStrategy, YRepeatStrategy, Next>;
|
| - tileStage->initStage<Tiler>(next, dimensions);
|
| - break;
|
| - }
|
| - case SkShader::kMirror_TileMode: {
|
| - using Tiler = CombinedTileStage<XStrategy, YMirrorStrategy, Next>;
|
| - tileStage->initStage<Tiler>(next, dimensions);
|
| - break;
|
| - }
|
| + 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;
|
| }
|
| };
|
|
|
| @@ -327,6 +467,10 @@
|
| fDest = dest;
|
| }
|
|
|
| + void SK_VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override { SkFAIL("Not Implemented"); }
|
| +
|
| + void bilerpSpan(Span span, SkScalar y) override { SkFAIL("Not Implemented"); }
|
| +
|
| void setDestination(void* dst, int count) override {
|
| fDest = static_cast<uint32_t*>(dst);
|
| fEnd = fDest + count;
|
| @@ -393,6 +537,10 @@
|
|
|
| SkASSERT(fDest <= fEnd);
|
| }
|
| +
|
| + void SK_VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override { SkFAIL("Not Implemented"); }
|
| +
|
| + void bilerpSpan(Span span, SkScalar y) override { SkFAIL("Not Implemented"); }
|
|
|
| void setDestination(void* dst, int count) override {
|
| SkASSERT(count > 0);
|
| @@ -434,9 +582,12 @@
|
| }
|
| }
|
|
|
| -static SkLinearBitmapPipeline::PixelAccessorInterface* choose_pixel_accessor(
|
| +template<template <typename, typename> class Sampler>
|
| +static SkLinearBitmapPipeline::SampleProcessorInterface* choose_pixel_sampler_base(
|
| + Blender* next,
|
| const SkPixmap& srcPixmap,
|
| const SkColor A8TintColor,
|
| + SkLinearBitmapPipeline::SampleStage* sampleStage,
|
| SkLinearBitmapPipeline::Accessor* accessor)
|
| {
|
| const SkImageInfo& imageInfo = srcPixmap.info();
|
| @@ -478,19 +629,19 @@
|
| break;
|
| }
|
|
|
| - return pixelAccessor;
|
| + using S = Sampler<PixelAccessorShim, Blender>;
|
| + sampleStage->initStage<S>(next, pixelAccessor);
|
| + return sampleStage->get();
|
| }
|
|
|
| SkLinearBitmapPipeline::SampleProcessorInterface* choose_pixel_sampler(
|
| Blender* next,
|
| SkFilterQuality filterQuality,
|
| - SkShader::TileMode xTile, SkShader::TileMode yTile,
|
| const SkPixmap& srcPixmap,
|
| const SkColor A8TintColor,
|
| SkLinearBitmapPipeline::SampleStage* sampleStage,
|
| SkLinearBitmapPipeline::Accessor* accessor) {
|
| const SkImageInfo& imageInfo = srcPixmap.info();
|
| - SkISize dimensions = imageInfo.dimensions();
|
|
|
| // Special case samplers with fully expanded templates
|
| if (imageInfo.gammaCloseToSRGB()) {
|
| @@ -519,14 +670,14 @@
|
| using S =
|
| BilerpSampler<
|
| PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>;
|
| - sampleStage->initStage<S>(next, dimensions, xTile, yTile, srcPixmap);
|
| + sampleStage->initStage<S>(next, srcPixmap);
|
| return sampleStage->get();
|
| }
|
| case kIndex_8_SkColorType: {
|
| using S =
|
| BilerpSampler<
|
| PixelAccessor<kIndex_8_SkColorType, kSRGB_SkGammaType>, Blender>;
|
| - sampleStage->initStage<S>(next, dimensions, xTile, yTile, srcPixmap);
|
| + sampleStage->initStage<S>(next, srcPixmap);
|
| return sampleStage->get();
|
| }
|
| default:
|
| @@ -535,16 +686,14 @@
|
| }
|
| }
|
|
|
| - auto pixelAccessor = choose_pixel_accessor(srcPixmap, A8TintColor, accessor);
|
| // General cases.
|
| if (filterQuality == kNone_SkFilterQuality) {
|
| - using S = NearestNeighborSampler<PixelAccessorShim, Blender>;
|
| - sampleStage->initStage<S>(next, pixelAccessor);
|
| + return choose_pixel_sampler_base<NearestNeighborSampler>(
|
| + next, srcPixmap, A8TintColor, sampleStage, accessor);
|
| } else {
|
| - using S = BilerpSampler<PixelAccessorShim, Blender>;
|
| - sampleStage->initStage<S>(next, dimensions, xTile, yTile, pixelAccessor);
|
| - }
|
| - return sampleStage->get();
|
| + return choose_pixel_sampler_base<BilerpSampler>(
|
| + next, srcPixmap, A8TintColor, sampleStage, accessor);
|
| + }
|
| }
|
|
|
| ////////////////////////////////////////////////////////////////////////////////////////////////////
|
| @@ -556,17 +705,17 @@
|
| SrcFPPixel(const SrcFPPixel& Blender) : fPostAlpha(Blender.fPostAlpha) {}
|
| void SK_VECTORCALL blendPixel(Sk4f pixel) override {
|
| SkASSERT(fDst + 1 <= fEnd );
|
| - this->srcPixel(fDst, pixel, 0);
|
| + SrcPixel(fDst, pixel, 0);
|
| fDst += 1;
|
| }
|
|
|
| void SK_VECTORCALL blend4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) override {
|
| SkASSERT(fDst + 4 <= fEnd);
|
| SkPM4f* dst = fDst;
|
| - this->srcPixel(dst, p0, 0);
|
| - this->srcPixel(dst, p1, 1);
|
| - this->srcPixel(dst, p2, 2);
|
| - this->srcPixel(dst, p3, 3);
|
| + SrcPixel(dst, p0, 0);
|
| + SrcPixel(dst, p1, 1);
|
| + SrcPixel(dst, p2, 2);
|
| + SrcPixel(dst, p3, 3);
|
| fDst += 4;
|
| }
|
|
|
| @@ -576,9 +725,7 @@
|
| }
|
|
|
| private:
|
| - void SK_VECTORCALL srcPixel(SkPM4f* dst, Sk4f pixel, int index) {
|
| - check_pixel(pixel);
|
| -
|
| + void SK_VECTORCALL SrcPixel(SkPM4f* dst, Sk4f pixel, int index) {
|
| Sk4f newPixel = pixel;
|
| if (alphaType == kUnpremul_SkAlphaType) {
|
| newPixel = Premultiply(pixel);
|
| @@ -650,8 +797,7 @@
|
| // identity matrix, the matrix stage is skipped, and the tilerStage is the first stage.
|
| auto blenderStage = choose_blender_for_shading(alphaType, postAlpha, &fBlenderStage);
|
| auto samplerStage = choose_pixel_sampler(
|
| - blenderStage, filterQuality, xTile, yTile,
|
| - srcPixmap, paintColor, &fSampleStage, &fAccessor);
|
| + blenderStage, filterQuality, srcPixmap, paintColor, &fSampleStage, &fAccessor);
|
| auto tilerStage = choose_tiler(samplerStage, dimensions, xTile, yTile,
|
| filterQuality, dx, &fTileStage);
|
| fFirstStage = choose_matrix(tilerStage, adjustedInverse, &fMatrixStage);
|
|
|