| 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
|
|
|