Chromium Code Reviews| Index: src/core/SkLinearBitmapPipeline.cpp |
| diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp |
| index 02a4bd39f3df869a5d6a75284da472bb636646e2..b9a987b45f7d66960a9e1ac597c43cb577352ae6 100644 |
| --- a/src/core/SkLinearBitmapPipeline.cpp |
| +++ b/src/core/SkLinearBitmapPipeline.cpp |
| @@ -15,19 +15,19 @@ |
| #include "SkSize.h" |
| // 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 |
| 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; |
| + virtual void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0; |
| + virtual void VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0; |
| // The pointSpan method efficiently process horizontal spans of pixels. |
| // * start - the point where to start the span. |
| @@ -58,7 +58,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 { |
| @@ -76,9 +76,9 @@ struct X { |
| 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;} |
| + operator SkScalar () const {return fVal;} |
| private: |
| - float fVal; |
| + SkScalar fVal; |
| }; |
| struct Y { |
| @@ -86,28 +86,30 @@ struct Y { |
| 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;} |
| + operator SkScalar () const {return fVal;} |
| private: |
| - float fVal; |
| + SkScalar 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}; |
| + 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); |
| } |
| } |
| @@ -132,12 +134,12 @@ 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); |
| } |
| @@ -162,17 +164,17 @@ 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); |
| } |
| @@ -189,13 +191,13 @@ 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 { |
| @@ -209,7 +211,7 @@ 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; |
| } |
| @@ -221,7 +223,7 @@ public: |
| } |
| private: |
| - const Sk4f fXOffset, fYOffset; |
| + const Sk4s fXOffset, fYOffset; |
| }; |
| template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> |
| using TranslateMatrix = PointProcessor<TranslateMatrixStrategy, Next>; |
| @@ -231,7 +233,7 @@ 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; |
| } |
| @@ -246,8 +248,8 @@ public: |
| } |
| 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,9 +260,9 @@ 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; |
| @@ -272,9 +274,9 @@ public: |
| } |
| 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,24 +314,24 @@ 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 { |
| @@ -353,6 +355,84 @@ static SkLinearBitmapPipeline::PointProcessorInterface* choose_filter( |
| } |
| } |
| +class Span { |
| +public: |
| + Span(SkPoint start, SkScalar length, int count) |
| + : fStart{start} |
| + , fLength(length) |
| + , fCount{count} { |
| + SkASSERT(std::isfinite(length)); |
| + } |
| + |
| + bool isEmpty() const { return 0 == count(); } |
| + int count() const { return fCount; } |
| + SkScalar length() const { return fLength; } |
| + SkScalar startX() const { return X(fStart); } |
| + SkScalar endX() const { return startX() + length(); } |
| + |
| + bool inRange(SkScalar xMin, SkScalar xMax) const { |
|
mtklein
2016/02/25 21:47:41
-> completelyWithin? inRange seems ambiguous abou
herb_g
2016/02/26 04:05:53
Done.
|
| + 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) { |
|
mtklein
2016/02/25 21:47:41
I find the countToBreak() and lengthToBreakAndAdju
herb_g
2016/02/26 04:05:54
Done.
|
| + SkPoint newStart = fStart; |
| + |
| + int newCount = countToBreak(breakX, dx); |
|
mtklein
2016/02/25 21:47:41
all self calls need this->
herb_g
2016/02/26 04:05:54
Done.
|
| + |
| + // The length is one dx less than the count * dx like the example above. There are |
|
mtklein
2016/02/25 21:47:40
this comment doesn't belong here given the code th
herb_g
2016/02/26 04:05:54
Done.
|
| + // middleCount samples, but dx is advanced one less time to get to the last sample. |
| + SkScalar newLength = lengthToBreakAndAdjust(newCount, dx); |
| + |
| + return Span{newStart, newLength, newCount}; |
| + } |
| + |
| + Span breakAtSingleColor(SkScalar breakX, SkScalar dx) { |
|
mtklein
2016/02/25 21:47:41
I don't understand where colors are getting involv
herb_g
2016/02/26 04:05:54
Changed the way this is done.
|
| + |
| + int newCount = countToBreak(breakX, dx); |
| + |
| + lengthToBreakAndAdjust(newCount, dx); |
| + return Span{{breakX, Y(fStart)}, 0.0f, newCount}; |
| + } |
| + |
| + template <typename Next> |
| + void nextStage(Next* next) { |
| + SkASSERT(next != nullptr); |
| + SkASSERT(fCount > 0); |
| + next->pointSpan(fStart, fLength, fCount); |
|
mtklein
2016/02/25 21:47:41
why does this function exist? doesn't this sugges
herb_g
2016/02/26 04:05:53
This is still a work in progress. Doing it now.
|
| + } |
| + |
| +private: |
| + int countToBreak(SkScalar breakX, SkScalar dx) { |
| + SkASSERT(std::isfinite(breakX)); |
| + SkASSERT(std::isfinite(dx)); |
| + SkScalar x = X(fStart); |
| + // newCount will be at least 1. |
| + int newCount = SkScalarFloorToInt((breakX - x) / dx) + 1; |
| + SkASSERT(newCount > 0); |
| + return newCount; |
| + } |
| + |
| + SkScalar lengthToBreakAndAdjust(int count, SkScalar dx) { |
| + SkScalar lengthToStart = count * dx; |
| + fLength -= lengthToStart; |
| + fCount -= count; |
| + fStart = {X(fStart) + lengthToStart, Y(fStart)}; |
| + // The length is one dx less than the count * dx like the example above. There are |
|
mtklein
2016/02/25 21:47:41
there is no example above
herb_g
2016/02/26 04:05:54
Done.
|
| + // middleCount samples, but dx is advanced one less time to get to the last sample. |
| + return lengthToStart - dx; |
| + } |
| + |
| + SkPoint fStart; |
| + SkScalar fLength; |
| + int fCount; |
| +}; |
| + |
| class ClampStrategy { |
| public: |
| ClampStrategy(X max) |
| @@ -367,25 +447,128 @@ 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; |
| + SkASSERT(count > 0); |
| + SkASSERT(std::isfinite(length)); |
|
mtklein
2016/02/25 21:47:41
We seem to assert this multiple times, but count >
herb_g
2016/02/26 04:05:54
Acknowledged.
|
| + 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}; |
| + |
| + // Handle the Span is on the tile if xMin and xMax not infinities or handle the case where |
|
mtklein
2016/02/25 21:47:40
please make this a sentence or delete it?
herb_g
2016/02/26 04:05:53
Done.
|
| + // the x component is a pass through when xMin and xMax are infinities. |
| + if (span.inRange(xMin, xMax)) { |
| + span.nextStage(next); |
| + return true; |
| + } |
| + |
| + SkScalar dx = length / (count - 1); |
| + |
| + // This is only the case where the count is one and it is in x < xMin || xMax < x, |
|
mtklein
2016/02/25 21:47:41
...and it it outside [xMin, xMax]?
Is it really i
herb_g
2016/02/26 04:05:54
Acknowledged.
|
| + // otherwise it was handled above. |
| + if (1 == count || dx < 0) { |
|
mtklein
2016/02/25 21:47:41
if (count == 1 || length < 0) {
return false;
herb_g
2016/02/26 04:05:53
Changed logic
|
| + return false; |
| + } |
| + |
| + // 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) { |
| + if (span.startX() < xMin) { |
| + if (span.endX() > xMin) { |
| + Span toDraw = span.breakAtSingleColor(xMin, dx); |
|
mtklein
2016/02/25 21:47:40
This breakAtSingleColor seems like something Span
mtklein
2016/02/25 21:47:41
We're not drawing anything here...
herb_g
2016/02/26 04:05:54
Totally redid logic.
|
| + toDraw.nextStage(next); |
| + } else { |
| + span.nextStage(next); |
|
mtklein
2016/02/25 21:47:41
I don't think I follow this case. It's entirely o
herb_g
2016/02/26 04:05:54
Redid logic
|
| + return true; |
| + } |
| + } |
| + if (!span.isEmpty() && span.startX() < xMax) { |
|
mtklein
2016/02/25 21:47:40
All these if cases are really complicated. Seems
herb_g
2016/02/26 04:05:53
Done.
|
| + if (span.endX() > xMax) { |
| + Span toDraw = span.breakAt(xMax, dx); |
| + toDraw.nextStage(next); |
| + } else { |
| + span.nextStage(next); |
| + return true; |
| + }; |
| + } |
| + if (!span.isEmpty()) { |
| + next->pointSpan({xMax - 1.0f, y}, 0.0f, span.count()); |
| + } |
| + } else { |
|
mtklein
2016/02/25 21:47:41
How did we get dx == 0 here? Some sort of degener
herb_g
2016/02/26 04:05:54
Done.
|
| + if (span.startX() > xMax) { |
| + if (span.endX() < xMax) { |
| + Span toDraw = span.breakAtSingleColor(xMax - 1.0f, dx); |
| + toDraw.nextStage(next); |
| + } else { |
| + span.nextStage(next); |
| + return true; |
| + } |
| + } |
| + if (!span.isEmpty() && span.startX() > xMin) { |
| + if (span.endX() < xMin) { |
| + Span toDraw = span.breakAt(xMin, dx); |
| + toDraw.nextStage(next); |
| + } else { |
| + span.nextStage(next); |
| + return true; |
| + } |
| + } |
| + if (!span.isEmpty()) { |
| + next->pointSpan({xMin, y}, 0.0f, span.count()); |
| + } |
| + } |
| + 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 +579,89 @@ 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; |
| + SkASSERT(count > 0); |
| + SkASSERT(std::isfinite(length)); |
| + // 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); |
| + |
| + if (SkScalarAbs(dx) >= xMax) { |
| + 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); |
| + toDraw.nextStage(next); |
| + span.offset(-xMax); |
| + } |
| + } else { |
| + while (!span.isEmpty() && span.endX() < xMin) { |
| + Span toDraw = span.breakAt(xMin, dx); |
| + toDraw.nextStage(next); |
| + span.offset(xMax); |
| + } |
| + } |
| + |
| + // All on a single tile. |
| + if (!span.isEmpty()) { |
| + span.nextStage(next); |
| + } |
| + |
| + 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 +716,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 +728,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 +744,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 +790,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 +811,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,13 +820,13 @@ 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); |
| @@ -697,14 +944,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(SkPoint{x + 0.5f, y + 0.5f}, count - 1, count); |
| } |