| Index: src/core/SkLinearBitmapPipeline.cpp
|
| diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp
|
| index 02a4bd39f3df869a5d6a75284da472bb636646e2..1d08a8bd7c295efc21be311337bebe53261da47f 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});
|
| }
|
|
|