Index: src/core/SkLinearBitmapPipeline_core.h |
diff --git a/src/core/SkLinearBitmapPipeline_core.h b/src/core/SkLinearBitmapPipeline_core.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9a8ec0e37eb0b7ffd8dd5cf5227e25963a80b724 |
--- /dev/null |
+++ b/src/core/SkLinearBitmapPipeline_core.h |
@@ -0,0 +1,211 @@ |
+/* |
+ * Copyright 2016 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#ifndef SkLinearBitmapPipeline_core_DEFINED |
+#define SkLinearBitmapPipeline_core_DEFINED |
+ |
+// 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; |
+}; |
+ |
+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; |
+ } |
+} |
+} // namespace |
+ |
+#endif // SkLinearBitmapPipeline_core_DEFINED |