Inset subset rect in texture op when nearest sampling
Fixes incorrect bleeding in bleed GM
Change-Id: I11337c068f531c59856ae5b5f6d9da765e2e5b0e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/290196
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 92904e9..6beebd4 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -120,21 +120,13 @@
}
}
-static SkRect inset_subset_for_bilerp(const NormalizationParams& params, const SkRect& subsetRect) {
- // Normalized pixel size is also equal to iw and ih, so the insets for bilerp are just
- // in those units and can be applied safely after normalization. However, if the subset is
- // smaller than a texel, it should clamp to the center of that axis.
- float dw = subsetRect.width() < params.fIW ? subsetRect.width() : params.fIW;
- float dh = subsetRect.height() < params.fIH ? subsetRect.height() : params.fIH;
- return subsetRect.makeInset(0.5f * dw, 0.5f * dh);
-}
-
// Normalize the subset. If 'subsetRect' is null, it is assumed no subset constraint is desired,
// so a sufficiently large rect is returned even if the quad ends up batched with an op that uses
-// subsets overall.
-static SkRect normalize_subset(GrSamplerState::Filter filter,
- const NormalizationParams& params,
- const SkRect* subsetRect) {
+// subsets overall. When there is a subset it will be inset based on the filter mode. Normalization
+// and y-flipping are applied as indicated by NormalizationParams.
+static SkRect normalize_and_inset_subset(GrSamplerState::Filter filter,
+ const NormalizationParams& params,
+ const SkRect* subsetRect) {
static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
if (!subsetRect) {
// Either the quad has no subset constraint and is batched with a subset constrained op
@@ -144,6 +136,16 @@
}
auto ltrb = skvx::Vec<4, float>::Load(subsetRect);
+ auto flipHi = skvx::Vec<4, float>({1.f, 1.f, -1.f, -1.f});
+ if (filter == GrSamplerState::Filter::kNearest) {
+ // Make sure our insetting puts us at pixel centers.
+ ltrb = skvx::floor(ltrb*flipHi)*flipHi;
+ }
+ // Inset with pin to the rect center.
+ ltrb += skvx::Vec<4, float>({.5f, .5f, -.5f, -.5f});
+ auto mid = (skvx::shuffle<2, 3, 0, 1>(ltrb) + ltrb)*0.5f;
+ ltrb = skvx::min(ltrb*flipHi, mid*flipHi)*flipHi;
+
// Normalize and offset
ltrb = mad(ltrb, {params.fIW, params.fIH, params.fIW, params.fIH},
{0.f, params.fYOffset, 0.f, params.fYOffset});
@@ -456,7 +458,7 @@
NormalizationParams params = proxy_normalization_params(proxyView.proxy(),
proxyView.origin());
normalize_src_quad(params, &quad->fLocal);
- SkRect subset = normalize_subset(filter, params, subsetRect);
+ SkRect subset = normalize_and_inset_subset(filter, params, subsetRect);
// Set bounds before clipping so we don't have to worry about unioning the bounds of
// the two potential quads (GrQuad::bounds() is perspective-safe).
@@ -577,7 +579,7 @@
// This subset may represent a no-op, otherwise it will have the origin and dimensions
// of the texture applied to it. Insetting for bilinear filtering is deferred until
// on[Pre]Prepare so that the overall filter can be lazily determined.
- SkRect subset = normalize_subset(filter, proxyParams, subsetForQuad);
+ SkRect subset = normalize_and_inset_subset(filter, proxyParams, subsetForQuad);
// Always append a quad (or 2 if perspective clipped), it just may refer back to a prior
// ViewCountPair (this frequently happens when Chrome draws 9-patches).
@@ -689,21 +691,12 @@
const int quadCnt = op.fViewCountPairs[p].fQuadCnt;
SkDEBUGCODE(int meshVertexCnt = quadCnt * desc->fVertexSpec.verticesPerQuad());
- // Can just use top-left for origin here since we only need the dimensions to
- // determine the texel size for insetting.
- NormalizationParams params = proxy_normalization_params(
- op.fViewCountPairs[p].fProxy.get(), kTopLeft_GrSurfaceOrigin);
-
- bool inset = texOp->fMetadata.filter() != GrSamplerState::Filter::kNearest;
-
for (int i = 0; i < quadCnt && iter.next(); ++i) {
SkASSERT(iter.isLocalValid());
const ColorSubsetAndAA& info = iter.metadata();
tessellator.append(iter.deviceQuad(), iter.localQuad(), info.fColor,
- inset ? inset_subset_for_bilerp(params, info.fSubsetRect)
- : info.fSubsetRect,
- info.aaFlags());
+ info.fSubsetRect, info.aaFlags());
}
SkASSERT((totVerticesSeen + meshVertexCnt) * vertexSize