diff options
author | herb <herb@google.com> | 2016-02-29 07:53:22 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-02-29 07:53:22 -0800 |
commit | 9466571f8ebde1429cca7b6dcd2d4b85bad1b088 (patch) | |
tree | 628d5bac5a86c104b1260cc4c4c151aa7305f6c6 | |
parent | b95c772d45efc98f0159fa65c7aa06314cecb9de (diff) |
tile spans
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1719333002
Review URL: https://codereview.chromium.org/1719333002
-rw-r--r-- | gm/SkLinearBitmapPipelineGM.cpp | 10 | ||||
-rw-r--r-- | src/core/SkLinearBitmapPipeline.cpp | 527 |
2 files changed, 387 insertions, 150 deletions
diff --git a/gm/SkLinearBitmapPipelineGM.cpp b/gm/SkLinearBitmapPipelineGM.cpp index 74068179ba..6b354df947 100644 --- a/gm/SkLinearBitmapPipelineGM.cpp +++ b/gm/SkLinearBitmapPipelineGM.cpp @@ -143,7 +143,7 @@ static void draw_rect_none(SkCanvas* canvas, const SkRect& r, SkColor c) { /* * Test SkXfer4fProcs directly for src-over, comparing them to current SkColor blits. */ -DEF_SIMPLE_GM(linear_pipeline, canvas, 580, 1400) { +DEF_SIMPLE_GM(linear_pipeline, canvas, 580, 2200) { const int IW = 50; const SkScalar W = IW; const SkScalar H = 100; @@ -156,14 +156,20 @@ DEF_SIMPLE_GM(linear_pipeline, canvas, 580, 1400) { canvas->translate(20, 20); SkMatrix mi = SkMatrix::I(); + SkMatrix mlr; + mlr.setScale(-1.0f, 1.0f, 20, 0.0f); SkMatrix mt; mt.setTranslate(8, 8); + SkMatrix mt2; + mt2.setTranslate(-18, -18); SkMatrix ms; ms.setScale(2.7f, 2.7f); + SkMatrix ms2; + ms2.setScale(-0.2f, 0.2f); SkMatrix mr; mr.setRotate(10); - const SkMatrix* mats[] = {nullptr, &mi, &mt, &ms, &mr}; + const SkMatrix* mats[] = {nullptr, &mi, &mlr, &mt, &mt2, &ms, &ms2, &mr}; const SkRect r = SkRect::MakeWH(W, H); bool useBilerp = false; diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp index 02a4bd39f3..1d08a8bd7c 100644 --- a/src/core/SkLinearBitmapPipeline.cpp +++ b/src/core/SkLinearBitmapPipeline.cpp @@ -13,34 +13,133 @@ #include <limits> #include "SkColor.h" #include "SkSize.h" +#include <tuple> // Tweak ABI of functions that pass Sk4f by value to pass them via registers. -#if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 + #if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 #define VECTORCALL __vectorcall - #elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) + #elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) #define VECTORCALL __attribute__((pcs("aapcs-vfp"))) - #else + #else #define VECTORCALL - #endif + #endif + +namespace { +struct X { + explicit X(SkScalar val) : fVal{val} { } + explicit X(SkPoint pt) : fVal{pt.fX} { } + explicit X(SkSize s) : fVal{s.fWidth} { } + explicit X(SkISize s) : fVal(s.fWidth) { } + operator SkScalar () const {return fVal;} +private: + SkScalar fVal; +}; + +struct Y { + explicit Y(SkScalar val) : fVal{val} { } + explicit Y(SkPoint pt) : fVal{pt.fY} { } + explicit Y(SkSize s) : fVal{s.fHeight} { } + explicit Y(SkISize s) : fVal(s.fHeight) { } + operator SkScalar () const {return fVal;} +private: + SkScalar fVal; +}; + +// The Span class enables efficient processing horizontal spans of pixels. +// * start - the point where to start the span. +// * length - the number of pixels to traverse in source space. +// * count - the number of pixels to produce in destination space. +// Both start and length are mapped through the inversion matrix to produce values in source +// space. After the matrix operation, the tilers may break the spans up into smaller spans. +// The tilers can produce spans that seem nonsensical. +// * The clamp tiler can create spans with length of 0. This indicates to copy an edge pixel out +// to the edge of the destination scan. +// * The mirror tiler can produce spans with negative length. This indicates that the source +// should be traversed in the opposite direction to the destination pixels. +class Span { +public: + Span(SkPoint start, SkScalar length, int count) + : fStart(start) + , fLength(length) + , fCount{count} { + SkASSERT(std::isfinite(length)); + } + + operator std::tuple<SkPoint&, SkScalar&, int&>() { + return std::tie(fStart, fLength, fCount); + } + + bool isEmpty() const { return 0 == fCount; } + SkScalar length() const { return fLength; } + SkScalar startX() const { return X(fStart); } + SkScalar endX() const { return startX() + length(); } + void clear() { + fCount = 0; + } + + bool completelyWithin(SkScalar xMin, SkScalar xMax) const { + SkScalar sMin, sMax; + std::tie(sMin, sMax) = std::minmax(startX(), endX()); + return xMin <= sMin && sMax <= xMax; + } + + void offset(SkScalar offsetX) { + fStart.offset(offsetX, 0.0f); + } + + Span breakAt(SkScalar breakX, SkScalar dx) { + SkASSERT(std::isfinite(breakX)); + SkASSERT(std::isfinite(dx)); + SkASSERT(dx != 0.0f); + + if (this->isEmpty()) { + return Span{{0.0, 0.0}, 0.0f, 0}; + } + + int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx); + if (dxSteps < 0) { + // The span is wholly after breakX. + return Span{{0.0, 0.0}, 0.0f, 0}; + } else if (dxSteps > fCount) { + // The span is wholly before breakX. + Span answer = *this; + this->clear(); + return answer; + } + + // Calculate the values for the span to cleave off. + SkPoint newStart = fStart; + SkScalar newLength = dxSteps * dx; + int newCount = dxSteps + 1; + SkASSERT(newCount > 0); + + // Update this span to reflect the break. + SkScalar lengthToStart = newLength + dx; + fLength -= lengthToStart; + fCount -= newCount; + fStart = {this->startX() + lengthToStart, Y(fStart)}; + + return Span{newStart, newLength, newCount}; + } + + void clampToSinglePixel(SkPoint pixel) { + fStart = pixel; + fLength = 0.0f; + } + +private: + SkPoint fStart; + SkScalar fLength; + int fCount; +}; +} // namespace class SkLinearBitmapPipeline::PointProcessorInterface { public: virtual ~PointProcessorInterface() { } - virtual void VECTORCALL pointListFew(int n, Sk4f xs, Sk4f ys) = 0; - virtual void VECTORCALL pointList4(Sk4f xs, Sk4f ys) = 0; - - // The pointSpan method efficiently process horizontal spans of pixels. - // * start - the point where to start the span. - // * length - the number of pixels to traverse in source space. - // * count - the number of pixels to produce in destination space. - // Both start and length are mapped through the inversion matrix to produce values in source - // space. After the matrix operation, the tilers may break the spans up into smaller spans. - // The tilers can produce spans that seem nonsensical. - // * The clamp tiler can create spans with length of 0. This indicates to copy an edge pixel out - // to the edge of the destination scan. - // * The mirror tiler can produce spans with negative length. This indicates that the source - // should be traversed in the opposite direction to the destination pixels. - virtual void pointSpan(SkPoint start, SkScalar length, int count) = 0; + virtual void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0; + virtual void VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0; + virtual void pointSpan(Span span) = 0; }; class SkLinearBitmapPipeline::BilerpProcessorInterface @@ -58,7 +157,7 @@ public: // +--------+--------+ // These pixels coordinates are arranged in the following order in xs and ys: // px00 px10 px01 px11 - virtual void VECTORCALL bilerpList(Sk4f xs, Sk4f ys) = 0; + virtual void VECTORCALL bilerpList(Sk4s xs, Sk4s ys) = 0; }; class SkLinearBitmapPipeline::PixelPlacerInterface { @@ -70,57 +169,38 @@ public: }; namespace { - -struct X { - explicit X(SkScalar val) : fVal{val} { } - explicit X(SkPoint pt) : fVal{pt.fX} { } - explicit X(SkSize s) : fVal{s.fWidth} { } - explicit X(SkISize s) : fVal(s.fWidth) { } - operator float () const {return fVal;} -private: - float fVal; -}; - -struct Y { - explicit Y(SkScalar val) : fVal{val} { } - explicit Y(SkPoint pt) : fVal{pt.fY} { } - explicit Y(SkSize s) : fVal{s.fHeight} { } - explicit Y(SkISize s) : fVal(s.fHeight) { } - operator float () const {return fVal;} -private: - float fVal; -}; - template <typename Stage> -void span_fallback(SkPoint start, SkScalar length, int count, Stage* stage) { - // If count == 1 use PointListFew instead. - SkASSERT(count > 1); - - float dx = length / (count - 1); - Sk4f Xs = Sk4f(X(start)) + Sk4f{0.0f, 1.0f, 2.0f, 3.0f} * Sk4f{dx}; - Sk4f Ys{Y(start)}; - Sk4f fourDx = {4.0f * dx}; +void span_fallback(Span span, Stage* stage) { + SkPoint start; + SkScalar length; + int count; + std::tie(start, length, count) = span; + Sk4f xs{X(start)}; + Sk4f ys{Y(start)}; + Sk4s fourDx; + if (count > 1) { + SkScalar dx = length / (count - 1); + xs = xs + Sk4f{0.0f, 1.0f, 2.0f, 3.0f} * dx; + // Only used if count is >= 4. + fourDx = Sk4f{4.0f * dx}; + } while (count >= 4) { - stage->pointList4(Xs, Ys); - Xs = Xs + fourDx; + stage->pointList4(xs, ys); + xs = xs + fourDx; count -= 4; } if (count > 0) { - stage->pointListFew(count, Xs, Ys); + stage->pointListFew(count, xs, ys); } } // 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. -// * maybeProcessSpan(start, length, count) - This represents a horizontal series of pixels +// * maybeProcessSpan(span, next) - This represents a horizontal series of pixels // to work over. -// start - is the starting pixel. This is in destination space before the matrix stage, and in -// source space after the matrix stage. -// length - is this distance between the first pixel center and the last pixel center. Like start, -// this is in destination space before the matrix stage, and in source space after. -// count - the number of pixels in source space to produce. +// span - encapsulation of span. // next - a pointer to the next stage. // maybeProcessSpan - returns false if it can not process the span and needs to fallback to // point lists for processing. @@ -132,19 +212,21 @@ public: : fNext{next} , fStrategy{std::forward<Args>(args)...}{ } - void VECTORCALL pointListFew(int n, Sk4f xs, Sk4f ys) override { + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { fStrategy.processPoints(&xs, &ys); fNext->pointListFew(n, xs, ys); } - void VECTORCALL pointList4(Sk4f xs, Sk4f ys) override { + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { fStrategy.processPoints(&xs, &ys); fNext->pointList4(xs, ys); } - void pointSpan(SkPoint start, SkScalar length, int count) override { - if (!fStrategy.maybeProcessSpan(start, length, count, fNext)) { - span_fallback(start, length, count, this); + // The span you pass must not be empty. + void pointSpan(Span span) override { + SkASSERT(!span.isEmpty()); + if (!fStrategy.maybeProcessSpan(span, fNext)) { + span_fallback(span, this); } } @@ -162,24 +244,25 @@ public: : fNext{next} , fStrategy{std::forward<Args>(args)...}{ } - void VECTORCALL pointListFew(int n, Sk4f xs, Sk4f ys) override { + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { fStrategy.processPoints(&xs, &ys); fNext->pointListFew(n, xs, ys); } - void VECTORCALL pointList4(Sk4f xs, Sk4f ys) override { + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { fStrategy.processPoints(&xs, &ys); fNext->pointList4(xs, ys); } - void VECTORCALL bilerpList(Sk4f xs, Sk4f ys) override { + void VECTORCALL bilerpList(Sk4s xs, Sk4s ys) override { fStrategy.processPoints(&xs, &ys); fNext->bilerpList(xs, ys); } - void pointSpan(SkPoint start, SkScalar length, int count) override { - if (!fStrategy.maybeProcessSpan(start, length, count, fNext)) { - span_fallback(start, length, count, this); + void pointSpan(Span span) override { + SkASSERT(!span.isEmpty()); + if (!fStrategy.maybeProcessSpan(span, fNext)) { + span_fallback(span, this); } } @@ -189,16 +272,16 @@ private: }; class SkippedStage final : public SkLinearBitmapPipeline::BilerpProcessorInterface { - void VECTORCALL pointListFew(int n, Sk4f xs, Sk4f ys) override { + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { SkFAIL("Skipped stage."); } - void VECTORCALL pointList4(Sk4f xs, Sk4f ys) override { + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { SkFAIL("Skipped stage."); } - void VECTORCALL bilerpList(Sk4f xs, Sk4f ys) override { + void VECTORCALL bilerpList(Sk4s xs, Sk4s ys) override { SkFAIL("Skipped stage."); } - void pointSpan(SkPoint start, SkScalar length, int count) override { + void pointSpan(Span span) override { SkFAIL("Skipped stage."); } }; @@ -209,19 +292,21 @@ public: : fXOffset{X(offset)} , fYOffset{Y(offset)} { } - void processPoints(Sk4f* xs, Sk4f* ys) { + void processPoints(Sk4s* xs, Sk4s* ys) { *xs = *xs + fXOffset; *ys = *ys + fYOffset; } template <typename Next> - bool maybeProcessSpan(SkPoint start, SkScalar length, int count, Next* next) { - next->pointSpan(start + SkPoint{fXOffset[0], fYOffset[0]}, length, count); + bool maybeProcessSpan(Span span, Next* next) { + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = span; + next->pointSpan(Span{start + SkPoint{fXOffset[0], fYOffset[0]}, length, count}); return true; } private: - const Sk4f fXOffset, fYOffset; + const Sk4s fXOffset, fYOffset; }; template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> using TranslateMatrix = PointProcessor<TranslateMatrixStrategy, Next>; @@ -231,23 +316,25 @@ public: ScaleMatrixStrategy(SkVector offset, SkVector scale) : fXOffset{X(offset)}, fYOffset{Y(offset)} , fXScale{X(scale)}, fYScale{Y(scale)} { } - void processPoints(Sk4f* xs, Sk4f* ys) { + void processPoints(Sk4s* xs, Sk4s* ys) { *xs = *xs * fXScale + fXOffset; *ys = *ys * fYScale + fYOffset; } template <typename Next> - bool maybeProcessSpan(SkPoint start, SkScalar length, int count, Next* next) { + bool maybeProcessSpan(Span span, Next* next) { + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = span; SkPoint newStart = SkPoint{X(start) * fXScale[0] + fXOffset[0], Y(start) * fYScale[0] + fYOffset[0]}; SkScalar newLength = length * fXScale[0]; - next->pointSpan(newStart, newLength, count); + next->pointSpan(Span{newStart, newLength, count}); return true; } private: - const Sk4f fXOffset, fYOffset; - const Sk4f fXScale, fYScale; + const Sk4s fXOffset, fYOffset; + const Sk4s fXScale, fYScale; }; template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> using ScaleMatrix = PointProcessor<ScaleMatrixStrategy, Next>; @@ -258,23 +345,23 @@ public: : fXOffset{X(offset)}, fYOffset{Y(offset)} , fXScale{X(scale)}, fYScale{Y(scale)} , fXSkew{X(skew)}, fYSkew{Y(skew)} { } - void processPoints(Sk4f* xs, Sk4f* ys) { - Sk4f newXs = fXScale * *xs + fXSkew * *ys + fXOffset; - Sk4f newYs = fYSkew * *xs + fYScale * *ys + fYOffset; + void processPoints(Sk4s* xs, Sk4s* ys) { + Sk4s newXs = fXScale * *xs + fXSkew * *ys + fXOffset; + Sk4s newYs = fYSkew * *xs + fYScale * *ys + fYOffset; *xs = newXs; *ys = newYs; } template <typename Next> - bool maybeProcessSpan(SkPoint start, SkScalar length, int count, Next* next) { + bool maybeProcessSpan(Span span, Next* next) { return false; } private: - const Sk4f fXOffset, fYOffset; - const Sk4f fXScale, fYScale; - const Sk4f fXSkew, fYSkew; + const Sk4s fXOffset, fYOffset; + const Sk4s fXScale, fYScale; + const Sk4s fXSkew, fYSkew; }; template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> using AffineMatrix = PointProcessor<AffineMatrixStrategy, Next>; @@ -312,28 +399,29 @@ class ExpandBilerp final : public SkLinearBitmapPipeline::PointProcessorInterfac public: ExpandBilerp(Next* next) : fNext{next} { } - void VECTORCALL pointListFew(int n, Sk4f xs, Sk4f ys) override { + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { SkASSERT(0 < n && n < 4); // px00 px10 px01 px11 - const Sk4f kXOffsets{-0.5f, 0.5f, -0.5f, 0.5f}, + 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(Sk4f{xs[0]} + kXOffsets, Sk4f{ys[0]} + kYOffsets); - if (n >= 2) fNext->bilerpList(Sk4f{xs[1]} + kXOffsets, Sk4f{ys[1]} + kYOffsets); - if (n >= 3) fNext->bilerpList(Sk4f{xs[2]} + kXOffsets, Sk4f{ys[2]} + kYOffsets); + 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); } 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(Sk4f{xs[0]} + kXOffsets, Sk4f{ys[0]} + kYOffsets); - fNext->bilerpList(Sk4f{xs[1]} + kXOffsets, Sk4f{ys[1]} + kYOffsets); - fNext->bilerpList(Sk4f{xs[2]} + kXOffsets, Sk4f{ys[2]} + kYOffsets); - fNext->bilerpList(Sk4f{xs[3]} + kXOffsets, Sk4f{ys[3]} + kYOffsets); + 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 pointSpan(SkPoint start, SkScalar length, int count) override { - span_fallback(start, length, count, this); + void pointSpan(Span span) override { + SkASSERT(!span.isEmpty()); + span_fallback(span, fNext); } private: @@ -367,25 +455,107 @@ public: , fXMax{X(max) - 1.0f} , fYMax{Y(max) - 1.0f} { } - void processPoints(Sk4f* xs, Sk4f* ys) { - *xs = Sk4f::Min(Sk4f::Max(*xs, fXMin), fXMax); - *ys = Sk4f::Min(Sk4f::Max(*ys, fYMin), fYMax); + void processPoints(Sk4s* xs, Sk4s* ys) { + *xs = Sk4s::Min(Sk4s::Max(*xs, fXMin), fXMax); + *ys = Sk4s::Min(Sk4s::Max(*ys, fYMin), fYMax); } template <typename Next> - bool maybeProcessSpan(SkPoint start, SkScalar length, int count, Next* next) { - return false; + bool maybeProcessSpan(Span originalSpan, Next* next) { + SkASSERT(!originalSpan.isEmpty()); + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = originalSpan; + SkScalar xMin = fXMin[0]; + SkScalar xMax = fXMax[0] + 1.0f; + SkScalar yMin = fYMin[0]; + SkScalar yMax = fYMax[0]; + SkScalar x = X(start); + SkScalar y = std::min(std::max<SkScalar>(yMin, Y(start)), yMax); + + Span span{{x, y}, length, count}; + + if (span.completelyWithin(xMin, xMax)) { + next->pointSpan(span); + return true; + } + if (1 == count || 0.0f == length) { + return false; + } + + SkScalar dx = length / (count - 1); + + // A B C + // +-------+-------+-------++-------+-------+-------+ +-------+-------++------ + // | *---*|---*---|*---*--||-*---*-|---*---|*---...| |--*---*|---*---||*---*.... + // | | | || | | | ... | | || + // | | | || | | | | | || + // +-------+-------+-------++-------+-------+-------+ +-------+-------++------ + // ^ ^ + // | xMin xMax-1 | xMax + // + // *---*---*---... - track of samples. * = sample + // + // +-+ || + // | | - pixels in source space. || - tile border. + // +-+ || + // + // The length from A to B is the length in source space or 4 * dx or (count - 1) * dx + // where dx is the distance between samples. There are 5 destination pixels + // corresponding to 5 samples specified in the A, B span. The distance from A to the next + // span starting at C is 5 * dx, so count * dx. + // Remember, count is the number of pixels needed for the destination and the number of + // samples. + // Overall Strategy: + // * Under - for portions of the span < xMin, take the color at pixel {xMin, y} and use it + // to fill in the 5 pixel sampled from A to B. + // * Middle - for the portion of the span between xMin and xMax sample normally. + // * Over - for the portion of the span > xMax, take the color at pixel {xMax-1, y} and + // use it to fill in the rest of the destination pixels. + if (dx >= 0) { + Span leftClamped = span.breakAt(xMin, dx); + if (!leftClamped.isEmpty()) { + leftClamped.clampToSinglePixel({xMin, y}); + next->pointSpan(leftClamped); + } + Span middle = span.breakAt(xMax, dx); + if (!middle.isEmpty()) { + next->pointSpan(middle); + } + if (!span.isEmpty()) { + span.clampToSinglePixel({xMax - 1, y}); + next->pointSpan(span); + } + } else { + Span rightClamped = span.breakAt(xMax, dx); + if (!rightClamped.isEmpty()) { + rightClamped.clampToSinglePixel({xMax - 1, y}); + next->pointSpan(rightClamped); + } + Span middle = span.breakAt(xMin, dx); + if (!middle.isEmpty()) { + next->pointSpan(middle); + } + if (!span.isEmpty()) { + span.clampToSinglePixel({xMin, y}); + next->pointSpan(span); + } + } + return true; } private: - const Sk4f fXMin{SK_FloatNegativeInfinity}; - const Sk4f fYMin{SK_FloatNegativeInfinity}; - const Sk4f fXMax{SK_FloatInfinity}; - const Sk4f fYMax{SK_FloatInfinity}; + const Sk4s fXMin{SK_FloatNegativeInfinity}; + const Sk4s fYMin{SK_FloatNegativeInfinity}; + const Sk4s fXMax{SK_FloatInfinity}; + const Sk4s fYMax{SK_FloatInfinity}; }; template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> using Clamp = BilerpProcessor<ClampStrategy, Next>; +static SkScalar tile_mod(SkScalar x, SkScalar base) { + return x - std::floor(x / base) * base; +} + class RepeatStrategy { public: RepeatStrategy(X max) : fXMax{max}, fXInvMax{1.0f/max} { } @@ -396,25 +566,91 @@ public: , fYMax{Y(max)} , fYInvMax{1.0f / Y(max)} { } - void processPoints(Sk4f* xs, Sk4f* ys) { - Sk4f divX = (*xs * fXInvMax).floor(); - Sk4f divY = (*ys * fYInvMax).floor(); - Sk4f baseX = (divX * fXMax); - Sk4f baseY = (divY * fYMax); + void processPoints(Sk4s* xs, Sk4s* ys) { + Sk4s divX = (*xs * fXInvMax).floor(); + Sk4s divY = (*ys * fYInvMax).floor(); + Sk4s baseX = (divX * fXMax); + Sk4s baseY = (divY * fYMax); *xs = *xs - baseX; *ys = *ys - baseY; } template <typename Next> - bool maybeProcessSpan(SkPoint start, SkScalar length, int count, Next* next) { - return false; + bool maybeProcessSpan(Span originalSpan, Next* next) { + SkASSERT(!originalSpan.isEmpty()); + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = originalSpan; + // Make x and y in range on the tile. + SkScalar x = tile_mod(X(start), fXMax[0]); + SkScalar y = tile_mod(Y(start), fYMax[0]); + SkScalar xMax = fXMax[0]; + SkScalar xMin = 0.0f; + SkScalar dx = length / (count - 1); + + // No need trying to go fast because the steps are larger than a tile or there is one point. + if (SkScalarAbs(dx) >= xMax || count <= 1) { + return false; + } + + // A B C D Z + // +-------+-------+-------++-------+-------+-------++ +-------+-------++------ + // | | *---|*---*--||-*---*-|---*---|*---*--|| |--*---*| || + // | | | || | | || ... | | || + // | | | || | | || | | || + // +-------+-------+-------++-------+-------+-------++ +-------+-------++------ + // ^^ ^^ ^^ + // xMax || xMin xMax || xMin xMax || xMin + // + // *---*---*---... - track of samples. * = sample + // + // +-+ || + // | | - pixels in source space. || - tile border. + // +-+ || + // + // + // The given span starts at A and continues on through several tiles to sample point Z. + // The idea is to break this into several spans one on each tile the entire span + // intersects. The A to B span only covers a partial tile and has a count of 3 and the + // distance from A to B is (count - 1) * dx or 2 * dx. The distance from A to the start of + // the next span is count * dx or 3 * dx. Span C to D covers an entire tile has a count + // of 5 and a length of 4 * dx. Remember, count is the number of pixels needed for the + // destination and the number of samples. + // + // Overall Strategy: + // While the span hangs over the edge of the tile, draw the span covering the tile then + // slide the span over to the next tile. + + // The guard could have been count > 0, but then a bunch of math would be done in the + // common case. + + Span span({x,y}, length, count); + if (dx > 0) { + while (!span.isEmpty() && span.endX() > xMax) { + Span toDraw = span.breakAt(xMax, dx); + next->pointSpan(toDraw); + span.offset(-xMax); + } + } else { + while (!span.isEmpty() && span.endX() < xMin) { + Span toDraw = span.breakAt(xMin, dx); + next->pointSpan(toDraw); + span.offset(xMax); + } + } + + // All on a single tile. + if (!span.isEmpty()) { + next->pointSpan(span); + } + + return true; } private: - const Sk4f fXMax{0.0f}; - const Sk4f fXInvMax{0.0f}; - const Sk4f fYMax{0.0f}; - const Sk4f fYInvMax{0.0f}; + const Sk4s fXMax{0.0f}; + const Sk4s fXInvMax{0.0f}; + const Sk4s fYMax{0.0f}; + const Sk4s fYInvMax{0.0f}; }; template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> @@ -469,9 +705,9 @@ static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_tiler( class sRGBFast { public: - static Sk4f VECTORCALL sRGBToLinear(Sk4f pixel) { - Sk4f l = pixel * pixel; - return Sk4f{l[0], l[1], l[2], pixel[3]}; + static Sk4s VECTORCALL sRGBToLinear(Sk4s pixel) { + Sk4s l = pixel * pixel; + return Sk4s{l[0], l[1], l[2], pixel[3]}; } }; @@ -481,9 +717,9 @@ public: Passthrough8888(int width, const uint32_t* src) : fSrc{src}, fWidth{width}{ } - void VECTORCALL getFewPixels(int n, Sk4f xs, Sk4f ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) { - Sk4i XIs = SkNx_cast<int, float>(xs); - Sk4i YIs = SkNx_cast<int, float>(ys); + 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: @@ -497,9 +733,9 @@ public: } } - void VECTORCALL get4Pixels(Sk4f xs, Sk4f ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) { - Sk4i XIs = SkNx_cast<int, float>(xs); - Sk4i YIs = SkNx_cast<int, float>(ys); + 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 = getPixel(fSrc, bufferLoc[0]); *px1 = getPixel(fSrc, bufferLoc[1]); @@ -543,12 +779,12 @@ private: // * px01 -> (1 - x)y = y - xy // * px11 -> xy // So x * y is calculated first and then used to calculate all the other factors. -static Sk4f VECTORCALL bilerp4(Sk4f xs, Sk4f ys, Sk4f px00, Sk4f px10, +static Sk4s VECTORCALL bilerp4(Sk4s xs, Sk4s ys, Sk4f px00, Sk4f px10, Sk4f px01, Sk4f px11) { // Calculate fractional xs and ys. - Sk4f fxs = xs - xs.floor(); - Sk4f fys = ys - ys.floor(); - Sk4f fxys{fxs * fys}; + 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); @@ -564,7 +800,7 @@ public: : fNext{next} , fStrategy{std::forward<Args>(args)...} { } - void VECTORCALL pointListFew(int n, Sk4f xs, Sk4f ys) override { + 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); @@ -573,21 +809,21 @@ public: if (n >= 3) fNext->placePixel(px2); } - void VECTORCALL pointList4(Sk4f xs, Sk4f ys) override { + 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); } - void VECTORCALL bilerpList(Sk4f xs, Sk4f ys) override { + 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(SkPoint start, SkScalar length, int count) override { - span_fallback(start, length, count, this); + void pointSpan(Span span) override { + span_fallback(span, this); } private: @@ -697,14 +933,9 @@ SkLinearBitmapPipeline::SkLinearBitmapPipeline( void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) { SkASSERT(count > 0); fPixelStage->setDestination(dst); - // Adjust points by 0.5, 0.5 to sample from the center of the pixels. - if (count == 1) { - fFirstStage->pointListFew(1, Sk4f{x + 0.5f}, Sk4f{y + 0.5f}); - } else { - // The count and length arguments start out in a precise relation in order to keep the - // 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(SkPoint{x + 0.5f, y + 0.5f}, count - 1, count); - } + // The count and length arguments start out in a precise relation in order to keep the + // 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}); } |