Index: src/core/SkLinearBitmapPipeline.cpp |
diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp |
index 4ab0b90f550ef87063943f6fac9ac2816dc81236..91e54fbe998ca6a6160fe08aa0ddbefea78046d9 100644 |
--- a/src/core/SkLinearBitmapPipeline.cpp |
+++ b/src/core/SkLinearBitmapPipeline.cpp |
@@ -6,166 +6,17 @@ |
*/ |
#include "SkLinearBitmapPipeline.h" |
-#include "SkPM4f.h" |
+#include "SkPM4f.h" |
#include <algorithm> |
#include <cmath> |
#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 |
- #define VECTORCALL __vectorcall |
- #elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) |
- #define VECTORCALL __attribute__((pcs("aapcs-vfp"))) |
- #else |
- #define VECTORCALL |
- #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; |
-}; |
- |
-// BilerpSpans are similar to Spans, but they represent four source samples converting to single |
-// destination pixel per count. The pixels for the four samples are collect along two horizontal |
-// lines; one starting at {x, y0} and the other starting at {x, y1}. There are two distinct lines |
-// to deal with the edge case of the tile mode. For example, y0 may be at the last y position in |
-// a tile while y1 would be at the first. |
-// The step of a Bilerp (dx) is still length / (count - 1) and the start to the next sample is |
-// still dx * count, but the bounds are complicated by the sampling kernel so that the pixels |
-// touched are from x to x + length + 1. |
-class BilerpSpan { |
-public: |
- BilerpSpan(SkScalar x, SkScalar y0, SkScalar y1, SkScalar length, int count) |
- : fX{x}, fY0{y0}, fY1{y1}, fLength{length}, fCount{count} { |
- SkASSERT(count >= 0); |
- SkASSERT(std::isfinite(length)); |
- SkASSERT(std::isfinite(x)); |
- SkASSERT(std::isfinite(y0)); |
- SkASSERT(std::isfinite(y1)); |
- } |
- |
- operator std::tuple<SkScalar&, SkScalar&, SkScalar&, SkScalar&, int&>() { |
- return std::tie(fX, fY0, fY1, fLength, fCount); |
- } |
- |
- bool isEmpty() const { return 0 == fCount; } |
- |
-private: |
- SkScalar fX; |
- SkScalar fY0; |
- SkScalar fY1; |
- SkScalar fLength; |
- int fCount; |
-}; |
-} // namespace |
+#include "SkLinearBitmapPipeline_core.h" |
+#include "SkLinearBitmapPipeline_matrix.h" |
+#include "SkLinearBitmapPipeline_tile.h" |
class SkLinearBitmapPipeline::PointProcessorInterface { |
public: |
@@ -203,53 +54,6 @@ public: |
}; |
namespace { |
-template <typename Stage> |
-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)}; |
- |
- // Initializing this is not needed, but some compilers can't figure this out. |
- Sk4s fourDx{0.0f}; |
- 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; |
- count -= 4; |
- } |
- if (count > 0) { |
- stage->pointListFew(count, xs, ys); |
- } |
-} |
- |
-template <typename Next> |
-void bilerp_span_fallback(BilerpSpan span, Next* next) { |
- SkScalar x, y0, y1; SkScalar length; int count; |
- std::tie(x, y0, y1, length, count) = span; |
- |
- SkASSERT(!span.isEmpty()); |
- float dx = length / (count - 1); |
- |
- Sk4f xs = Sk4f{x} + Sk4f{0.0f, 1.0f, 0.0f, 1.0f}; |
- Sk4f ys = Sk4f{y0, y0, y1, y1}; |
- |
- // If count == 1 then dx will be inf or NaN, but that is ok because the resulting addition is |
- // never used. |
- while (count > 0) { |
- next->bilerpList(xs, ys); |
- xs = xs + dx; |
- count -= 1; |
- } |
-} |
// PointProcessor uses a strategy to help complete the work of the different stages. The strategy |
// must implement the following methods: |
@@ -334,83 +138,14 @@ private: |
Strategy fStrategy; |
}; |
-class TranslateMatrixStrategy { |
-public: |
- TranslateMatrixStrategy(SkVector offset) |
- : fXOffset{X(offset)} |
- , fYOffset{Y(offset)} { } |
- |
- void processPoints(Sk4s* xs, Sk4s* ys) { |
- *xs = *xs + fXOffset; |
- *ys = *ys + fYOffset; |
- } |
- |
- template <typename Next> |
- 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 Sk4s fXOffset, fYOffset; |
-}; |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
+// Matrix Stage |
template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> |
using TranslateMatrix = PointProcessor<TranslateMatrixStrategy, Next>; |
-class ScaleMatrixStrategy { |
-public: |
- ScaleMatrixStrategy(SkVector offset, SkVector scale) |
- : fXOffset{X(offset)}, fYOffset{Y(offset)} |
- , fXScale{X(scale)}, fYScale{Y(scale)} { } |
- void processPoints(Sk4s* xs, Sk4s* ys) { |
- *xs = *xs * fXScale + fXOffset; |
- *ys = *ys * fYScale + fYOffset; |
- } |
- |
- template <typename 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(Span{newStart, newLength, count}); |
- return true; |
- } |
- |
-private: |
- const Sk4s fXOffset, fYOffset; |
- const Sk4s fXScale, fYScale; |
-}; |
template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> |
using ScaleMatrix = PointProcessor<ScaleMatrixStrategy, Next>; |
-class AffineMatrixStrategy { |
-public: |
- AffineMatrixStrategy(SkVector offset, SkVector scale, SkVector skew) |
- : fXOffset{X(offset)}, fYOffset{Y(offset)} |
- , fXScale{X(scale)}, fYScale{Y(scale)} |
- , fXSkew{X(skew)}, fYSkew{Y(skew)} { } |
- 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(Span span, Next* next) { |
- return false; |
- } |
- |
-private: |
- const Sk4s fXOffset, fYOffset; |
- const Sk4s fXScale, fYScale; |
- const Sk4s fXSkew, fYSkew; |
-}; |
template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> |
using AffineMatrix = PointProcessor<AffineMatrixStrategy, Next>; |
@@ -441,6 +176,8 @@ static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix( |
return matrixProc->get(); |
} |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
+// Bilerp Expansion Stage |
template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> |
class ExpandBilerp final : public SkLinearBitmapPipeline::PointProcessorInterface { |
public: |
@@ -491,228 +228,11 @@ static SkLinearBitmapPipeline::PointProcessorInterface* choose_filter( |
} |
} |
-class ClampStrategy { |
-public: |
- ClampStrategy(X max) |
- : fXMin{0.0f} |
- , fXMax{max - 1.0f} { } |
- ClampStrategy(Y max) |
- : fYMin{0.0f} |
- , fYMax{max - 1.0f} { } |
- ClampStrategy(SkSize max) |
- : fXMin{0.0f} |
- , fYMin{0.0f} |
- , fXMax{X(max) - 1.0f} |
- , fYMax{Y(max) - 1.0f} { } |
- |
- 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(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; |
- } |
- |
- template <typename Next> |
- bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { |
- return false; |
- } |
- |
-private: |
- const Sk4s fXMin{SK_FloatNegativeInfinity}; |
- const Sk4s fYMin{SK_FloatNegativeInfinity}; |
- const Sk4s fXMax{SK_FloatInfinity}; |
- const Sk4s fYMax{SK_FloatInfinity}; |
-}; |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
+// Tile Stage |
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} { } |
- RepeatStrategy(Y max) : fYMax{max}, fYInvMax{1.0f/max} { } |
- RepeatStrategy(SkSize max) |
- : fXMax{X(max)} |
- , fXInvMax{1.0f / X(max)} |
- , fYMax{Y(max)} |
- , fYInvMax{1.0f / Y(max)} { } |
- |
- 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(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; |
- } |
- |
- template <typename Next> |
- bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { |
- return false; |
- } |
- |
-private: |
- 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> |
using Repeat = BilerpProcessor<RepeatStrategy, Next>; |
@@ -762,6 +282,8 @@ static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_tiler( |
return tileProcXOrBoth->get(); |
} |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
+// Source Sampling Stage |
class sRGBFast { |
public: |
static Sk4s VECTORCALL sRGBToLinear(Sk4s pixel) { |
@@ -1028,6 +550,8 @@ static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_pixel_sampler( |
return sampleStage->get(); |
} |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
+// Pixel Placement Stage |
template <SkAlphaType alphaType> |
class PlaceFPPixel final : public SkLinearBitmapPipeline::PixelPlacerInterface { |
public: |
@@ -1078,6 +602,7 @@ static SkLinearBitmapPipeline::PixelPlacerInterface* choose_pixel_placer( |
} |
} // namespace |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
SkLinearBitmapPipeline::~SkLinearBitmapPipeline() {} |
SkLinearBitmapPipeline::SkLinearBitmapPipeline( |