Index: src/core/SkLinearBitmapPipeline.cpp |
diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp |
index 236f5bf8768712e13a19096d9b1fd55148229827..4ab0b90f550ef87063943f6fac9ac2816dc81236 100644 |
--- a/src/core/SkLinearBitmapPipeline.cpp |
+++ b/src/core/SkLinearBitmapPipeline.cpp |
@@ -132,6 +132,39 @@ private: |
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 |
class SkLinearBitmapPipeline::PointProcessorInterface { |
@@ -158,6 +191,7 @@ public: |
// These pixels coordinates are arranged in the following order in xs and ys: |
// px00 px10 px01 px11 |
virtual void VECTORCALL bilerpList(Sk4s xs, Sk4s ys) = 0; |
+ virtual void bilerpSpan(BilerpSpan span) = 0; |
}; |
class SkLinearBitmapPipeline::PixelPlacerInterface { |
@@ -197,6 +231,26 @@ void span_fallback(Span span, Stage* stage) { |
} |
} |
+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: |
// * processPoints(xs, ys) - must mutate the xs and ys for the stage. |
@@ -268,6 +322,13 @@ public: |
} |
} |
+ void bilerpSpan(BilerpSpan bSpan) override { |
+ SkASSERT(!bSpan.isEmpty()); |
+ if (!fStrategy.maybeProcessBilerpSpan(bSpan, fNext)) { |
+ bilerp_span_fallback(bSpan, this); |
+ } |
+ } |
+ |
private: |
Next* const fNext; |
Strategy fStrategy; |
@@ -409,17 +470,9 @@ public: |
SkASSERT(!span.isEmpty()); |
SkPoint start; SkScalar length; int count; |
std::tie(start, length, count) = span; |
- float dx = length / (count - 1); |
- |
- Sk4f Xs = Sk4f{X(start)} + Sk4f{-0.5f, 0.5f, -0.5f, 0.5f}; |
- Sk4f Ys = Sk4f{Y(start)} + Sk4f{-0.5f, -0.5f, 0.5f, 0.5f}; |
- |
- Sk4f dXs{dx}; |
- while (count > 0) { |
- fNext->bilerpList(Xs, Ys); |
- Xs = Xs + dXs; |
- count -= 1; |
- } |
+ // Adjust the span so that it is in the correct phase with the pixel. |
+ BilerpSpan bSpan{X(start) - 0.5f, Y(start) - 0.5f, Y(start) + 0.5f, length, count}; |
+ fNext->bilerpSpan(bSpan); |
} |
private: |
@@ -540,6 +593,11 @@ public: |
return true; |
} |
+ template <typename Next> |
+ bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { |
+ return false; |
+ } |
+ |
private: |
const Sk4s fXMin{SK_FloatNegativeInfinity}; |
const Sk4s fYMin{SK_FloatNegativeInfinity}; |
@@ -643,6 +701,11 @@ public: |
return true; |
} |
+ template <typename Next> |
+ bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { |
+ return false; |
+ } |
+ |
private: |
const Sk4s fXMax{0.0f}; |
const Sk4s fXInvMax{0.0f}; |
@@ -924,6 +987,10 @@ private: |
span_fallback(span, this); |
} |
+ void bilerpSpan(BilerpSpan span) override { |
+ bilerp_span_fallback(span, this); |
+ } |
+ |
private: |
SkLinearBitmapPipeline::PixelPlacerInterface* const fNext; |
SourceStrategy fStrategy; |