Index: src/core/SkLinearBitmapPipeline_tile.h |
diff --git a/src/core/SkLinearBitmapPipeline_tile.h b/src/core/SkLinearBitmapPipeline_tile.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2f855a36af4a704cdf8ab599d6f1e63eb7e841f3 |
--- /dev/null |
+++ b/src/core/SkLinearBitmapPipeline_tile.h |
@@ -0,0 +1,238 @@ |
+/* |
+ * 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_tile_DEFINED |
+#define SkLinearBitmapPipeline_tile_DEFINED |
+ |
+#include "SkLinearBitmapPipeline_core.h" |
+#include "SkPM4f.h" |
+#include <algorithm> |
+#include <cmath> |
+#include <limits> |
+ |
+namespace { |
+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}; |
+}; |
+ |
+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 = TileMod(X(start), fXMax[0]); |
+ SkScalar y = TileMod(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: |
+ SkScalar TileMod(SkScalar x, SkScalar base) { |
+ return x - std::floor(x / base) * base; |
+ } |
+ const Sk4s fXMax{0.0f}; |
+ const Sk4s fXInvMax{0.0f}; |
+ const Sk4s fYMax{0.0f}; |
+ const Sk4s fYInvMax{0.0f}; |
+}; |
+ |
+} // namespace |
+#endif // SkLinearBitmapPipeline_tile_DEFINED |