diff options
-rw-r--r-- | src/core/SkLinearBitmapPipeline.cpp | 48 | ||||
-rw-r--r-- | src/core/SkLinearBitmapPipeline_tile.h | 89 |
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) |