From 865a289bfec169cb19970c734e9aa855c267f060 Mon Sep 17 00:00:00 2001 From: herb Date: Thu, 31 Mar 2016 08:48:21 -0700 Subject: Add unit repeat tiler. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1837893004 Review URL: https://codereview.chromium.org/1837893004 --- src/core/SkLinearBitmapPipeline.cpp | 22 ++++++++-- src/core/SkLinearBitmapPipeline_tile.h | 73 ++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp index 4e4226a320..59960ebcb7 100644 --- a/src/core/SkLinearBitmapPipeline.cpp +++ b/src/core/SkLinearBitmapPipeline.cpp @@ -238,6 +238,12 @@ public: processor->breakIntoEdges(span); } + void repeatSpan(Span span, int32_t repeatCount) { + while (repeatCount --> 0) { + processor->pointSpan(span); + } + } + BilerpTileStage* processor; }; @@ -364,13 +370,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(yMode, filterQuality, dimensions, next, tileStage); break; case SkShader::kRepeat_TileMode: - choose_tiler_ymode(yMode, filterQuality, dimensions, next, tileStage); + if (dx == 1.0f && filterQuality == kNone_SkFilterQuality) { + choose_tiler_ymode( + yMode, kNone_SkFilterQuality, dimensions, next, tileStage); + } else { + choose_tiler_ymode( + yMode, filterQuality, dimensions, next, tileStage); + } break; case SkShader::kMirror_TileMode: choose_tiler_ymode(yMode, filterQuality, dimensions, next, tileStage); @@ -588,6 +602,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,7 +616,7 @@ 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); } diff --git a/src/core/SkLinearBitmapPipeline_tile.h b/src/core/SkLinearBitmapPipeline_tile.h index 60cc2a5ef0..19bdedcfba 100644 --- a/src/core/SkLinearBitmapPipeline_tile.h +++ b/src/core/SkLinearBitmapPipeline_tile.h @@ -234,6 +234,79 @@ private: const Sk4s fXsInvMax; }; +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 + 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); + 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); + + SkScalar div = span.length() / fXMax; + int32_t repeatCount = SkScalarFloorToInt(div); + Span repeatableSpan{{0.0f, y}, fXMax - 1.0f, SkScalarFloorToInt(fXMax)}; + + // Repeat the center section. + next->repeatSpan(repeatableSpan, repeatCount); + + // There may be some of the span left over. + span.breakAt(SkScalar(repeatCount) * fXMax, 1.0f); + + // All on a single tile. + if (!span.isEmpty()) { + 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) -- cgit v1.2.3