aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/core/SkLinearBitmapPipeline.cpp48
-rw-r--r--src/core/SkLinearBitmapPipeline_tile.h89
2 files changed, 125 insertions, 12 deletions
diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp
index 4e4226a320..3410ec1727 100644
--- a/src/core/SkLinearBitmapPipeline.cpp
+++ b/src/core/SkLinearBitmapPipeline.cpp
@@ -65,7 +65,10 @@ public:
class SkLinearBitmapPipeline::PixelPlacerInterface {
public:
virtual ~PixelPlacerInterface() { }
- virtual void setDestination(SkPM4f* dst) = 0;
+ // Count is normally not needed, but in these early stages of development it is useful to
+ // check bounds.
+ // TODO(herb): 4/6/2016 - remove count when code is stable.
+ virtual void setDestination(void* dst, int count) = 0;
virtual void VECTORCALL placePixel(Sk4f pixel0) = 0;
virtual void VECTORCALL place4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) = 0;
};
@@ -238,6 +241,12 @@ public:
processor->breakIntoEdges(span);
}
+ void repeatSpan(Span span, int32_t repeatCount) {
+ while (repeatCount --> 0) {
+ processor->pointSpan(span);
+ }
+ }
+
BilerpTileStage* processor;
};
@@ -364,13 +373,21 @@ static SkLinearBitmapPipeline::PointProcessorInterface* choose_tiler(
SkShader::TileMode xMode,
SkShader::TileMode yMode,
SkFilterQuality filterQuality,
- SkLinearBitmapPipeline::TileStage* tileStage) {
+ SkScalar dx,
+ SkLinearBitmapPipeline::TileStage* tileStage)
+{
switch (xMode) {
case SkShader::kClamp_TileMode:
choose_tiler_ymode<XClampStrategy>(yMode, filterQuality, dimensions, next, tileStage);
break;
case SkShader::kRepeat_TileMode:
- choose_tiler_ymode<XRepeatStrategy>(yMode, filterQuality, dimensions, next, tileStage);
+ if (dx == 1.0f && filterQuality == kNone_SkFilterQuality) {
+ choose_tiler_ymode<XRepeatUnitScaleStrategy>(
+ yMode, kNone_SkFilterQuality, dimensions, next, tileStage);
+ } else {
+ choose_tiler_ymode<XRepeatStrategy>(
+ yMode, filterQuality, dimensions, next, tileStage);
+ }
break;
case SkShader::kMirror_TileMode:
choose_tiler_ymode<XMirrorStrategy>(yMode, filterQuality, dimensions, next, tileStage);
@@ -402,7 +419,7 @@ public:
fSampler.nearestSpan(span);
}
- virtual void repeatSpan(Span span, int32_t repeatCount) override {
+ void repeatSpan(Span span, int32_t repeatCount) override {
while (repeatCount > 0) {
fSampler.nearestSpan(span);
repeatCount--;
@@ -413,7 +430,7 @@ public:
SkFAIL("Using nearest neighbor sampler, but calling a bilerpEdge.");
}
- virtual void bilerpSpan(Span span, SkScalar y) override {
+ void bilerpSpan(Span span, SkScalar y) override {
SkFAIL("Using nearest neighbor sampler, but calling a bilerpSpan.");
}
@@ -440,7 +457,7 @@ public:
fSampler.bilerpSpan(span);
}
- virtual void repeatSpan(Span span, int32_t repeatCount) override {
+ void repeatSpan(Span span, int32_t repeatCount) override {
while (repeatCount > 0) {
fSampler.bilerpSpan(span);
repeatCount--;
@@ -451,7 +468,7 @@ public:
fSampler.bilerpEdge(xs, ys);
}
- virtual void bilerpSpan(Span span, SkScalar y) override {
+ void bilerpSpan(Span span, SkScalar y) override {
fSampler.bilerpSpanWithY(span, y);
}
@@ -515,11 +532,13 @@ class PlaceFPPixel final : public SkLinearBitmapPipeline::PixelPlacerInterface {
public:
PlaceFPPixel(float postAlpha) : fPostAlpha{postAlpha} { }
void VECTORCALL placePixel(Sk4f pixel) override {
+ SkASSERT(fDst + 1 <= fEnd );
PlacePixel(fDst, pixel, 0);
fDst += 1;
}
void VECTORCALL place4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) override {
+ SkASSERT(fDst + 4 <= fEnd);
SkPM4f* dst = fDst;
PlacePixel(dst, p0, 0);
PlacePixel(dst, p1, 1);
@@ -528,8 +547,9 @@ public:
fDst += 4;
}
- void setDestination(SkPM4f* dst) override {
- fDst = dst;
+ void setDestination(void* dst, int count) override {
+ fDst = static_cast<SkPM4f*>(dst);
+ fEnd = fDst + count;
}
private:
@@ -547,6 +567,7 @@ private:
}
SkPM4f* fDst;
+ SkPM4f* fEnd;
Sk4f fPostAlpha;
};
@@ -572,7 +593,8 @@ SkLinearBitmapPipeline::SkLinearBitmapPipeline(
SkFilterQuality filterQuality,
SkShader::TileMode xTile, SkShader::TileMode yTile,
float postAlpha,
- const SkPixmap& srcPixmap) {
+ const SkPixmap& srcPixmap)
+{
SkISize dimensions = srcPixmap.info().dimensions();
const SkImageInfo& srcImageInfo = srcPixmap.info();
@@ -588,6 +610,8 @@ SkLinearBitmapPipeline::SkLinearBitmapPipeline(
}
}
+ SkScalar dx = adjustedInverse.getScaleX();
+
// If it is an index 8 color type, the sampler converts to unpremul for better fidelity.
SkAlphaType alphaType = srcImageInfo.alphaType();
if (srcPixmap.colorType() == kIndex_8_SkColorType) {
@@ -600,13 +624,13 @@ SkLinearBitmapPipeline::SkLinearBitmapPipeline(
auto samplerStage = choose_pixel_sampler(placementStage,
filterQuality, srcPixmap, &fSampleStage);
auto tilerStage = choose_tiler(samplerStage,
- dimensions, xTile, yTile, filterQuality, &fTiler);
+ dimensions, xTile, yTile, filterQuality, dx, &fTiler);
fFirstStage = choose_matrix(tilerStage, adjustedInverse, &fMatrixStage);
}
void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) {
SkASSERT(count > 0);
- fPixelStage->setDestination(dst);
+ fPixelStage->setDestination(dst, count);
// The count and length arguments start out in a precise relation in order to keep the
// math correct through the different stages. Count is the number of pixel to produce.
// Since the code samples at pixel centers, length is the distance from the center of the
diff --git a/src/core/SkLinearBitmapPipeline_tile.h b/src/core/SkLinearBitmapPipeline_tile.h
index 60cc2a5ef0..997ab65c47 100644
--- a/src/core/SkLinearBitmapPipeline_tile.h
+++ b/src/core/SkLinearBitmapPipeline_tile.h
@@ -234,6 +234,95 @@ private:
const Sk4s fXsInvMax;
};
+// The XRepeatUnitScaleStrategy exploits the situation where dx = 1.0. The main advantage is that
+// the relationship between the sample points and the source pixels does not change from tile to
+// repeated tile. This allows the tiler to calculate the span once and re-use it for each
+// repeated tile. This is later exploited by some samplers to avoid converting pixels to linear
+// space allowing the use of memmove to place pixel in the destination.
+class XRepeatUnitScaleStrategy {
+public:
+ XRepeatUnitScaleStrategy(int32_t max)
+ : fXMax{SkScalar(max)}
+ , fXsMax{SkScalar(max)}
+ , fXsCap{SkScalar(nextafterf(SkScalar(max), 0.0f))}
+ , fXsInvMax{1.0f / SkScalar(max)} { }
+
+ void tileXPoints(Sk4s* xs) {
+ Sk4s divX = *xs * fXsInvMax;
+ Sk4s modX = *xs - divX.floor() * fXsMax;
+ *xs = Sk4s::Min(fXsCap, modX);
+ SkASSERT(0 <= (*xs)[0] && (*xs)[0] < fXMax);
+ SkASSERT(0 <= (*xs)[1] && (*xs)[1] < fXMax);
+ SkASSERT(0 <= (*xs)[2] && (*xs)[2] < fXMax);
+ SkASSERT(0 <= (*xs)[3] && (*xs)[3] < fXMax);
+ }
+
+ 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);
+ SkScalar y = Y(start);
+
+ // No need trying to go fast because the steps are larger than a tile or there is one point.
+ if (fXMax == 1 || count <= 1) {
+ return false;
+ }
+
+ // x should be on the tile.
+ SkASSERT(0.0f <= x && x < fXMax);
+ Span span({x, y}, length, count);
+
+ if (SkScalarFloorToScalar(x) != 0.0f) {
+ Span toDraw = span.breakAt(fXMax, 1.0f);
+ SkASSERT(0.0f <= toDraw.startX() && toDraw.endX() < fXMax);
+ next->pointSpan(toDraw);
+ span.offset(-fXMax);
+ }
+
+ // All of the span could have been on the first tile. If so, then no work to do.
+ if (span.isEmpty()) return true;
+
+ // At this point the span should be aligned to zero.
+ SkASSERT(SkScalarFloorToScalar(span.startX()) == 0.0f);
+
+ // Note: The span length has an unintuitive relation to the tile width. The tile width is
+ // a half open interval [tb, te), but the span is a closed interval [sb, se]. In order to
+ // compare the two, you need to convert the span to a half open interval. This is done by
+ // adding dx to se. So, the span becomes: [sb, se + dx). Hence the + 1.0f below.
+ SkScalar div = (span.length() + 1.0f) / fXMax;
+ int32_t repeatCount = SkScalarFloorToInt(div);
+ Span repeatableSpan{{0.0f, y}, fXMax - 1.0f, SkScalarFloorToInt(fXMax)};
+
+ // Repeat the center section.
+ SkASSERT(0.0f <= repeatableSpan.startX() && repeatableSpan.endX() < fXMax);
+ next->repeatSpan(repeatableSpan, repeatCount);
+
+ // Calculate the advance past the center portion.
+ SkScalar advance = SkScalar(repeatCount) * fXMax;
+
+ // There may be some of the span left over.
+ span.breakAt(advance, 1.0f);
+
+ // All on a single tile.
+ if (!span.isEmpty()) {
+ span.offset(-advance);
+ SkASSERT(0.0f <= span.startX() && span.endX() < fXMax);
+ next->pointSpan(span);
+ }
+
+ return true;
+ }
+
+private:
+ const SkScalar fXMax;
+ const Sk4s fXsMax;
+ const Sk4s fXsCap;
+ const Sk4s fXsInvMax;
+};
+
class YRepeatStrategy {
public:
YRepeatStrategy(int32_t max)