GrTextureEffect handles insets for float rects as well as integer rects.

Fix insets for clamp/nearest case.

Change-Id: I1fa2f6d172fdac59836ae73c7a5fec0d0cde020c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/268397
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Brian Salomon 2020-02-05 12:15:18 -05:00 committed by Skia Commit-Bot
parent e13cbc3f15
commit ed729f99af
6 changed files with 52 additions and 66 deletions

View File

@ -110,7 +110,9 @@ GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
return kNoDomain_DomainMode;
}
// Get the domain inset based on sampling mode (or bail if mipped)
// Get the domain inset based on sampling mode (or bail if mipped). This is used
// to evaluate whether we will read outside a non-exact proxy's dimensions.
// TODO: Let GrTextureEffect handle this.
SkScalar filterHalfWidth = 0.f;
if (filterModeOrNullForBicubic) {
switch (*filterModeOrNullForBicubic) {
@ -136,15 +138,8 @@ GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
filterHalfWidth = 1.5f;
}
// Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
// of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
static const SkScalar kDomainInset = 0.5f;
// Figure out the limits of pixels we're allowed to sample from.
// Unless we know the amount of outset and the texture matrix we have to conservatively enforce
// the domain.
if (restrictFilterToRect) {
*domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
*domainRect = constraintRect;
} else if (!proxyIsExact) {
// If we got here then: proxy is not exact, the coords are limited to the
// constraint rect, and we're allowed to filter across the constraint rect boundary. So
@ -157,32 +152,34 @@ GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
// rect in order to avoid having to add a domain.
bool needContentAreaConstraint = false;
if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
domainRect->fRight = proxyBounds.fRight - kDomainInset;
domainRect->fRight = proxyBounds.fRight;
needContentAreaConstraint = true;
}
if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
domainRect->fBottom = proxyBounds.fBottom;
needContentAreaConstraint = true;
}
if (!needContentAreaConstraint) {
return kNoDomain_DomainMode;
}
} else {
// Our sample coords for the texture are allowed to be outside the constraintRect so we
// don't consider it when computing the domain.
domainRect->fRight = proxyBounds.fRight - kDomainInset;
domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
}
} else {
return kNoDomain_DomainMode;
}
if (domainRect->fLeft > domainRect->fRight) {
domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
}
if (domainRect->fTop > domainRect->fBottom) {
domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
if (!filterModeOrNullForBicubic) {
// Bicubic doesn't yet rely on GrTextureEffect to do this insetting.
domainRect->inset(0.5f, 0.5f);
if (domainRect->fLeft > domainRect->fRight) {
domainRect->fLeft = domainRect->fRight =
SkScalarAve(domainRect->fLeft, domainRect->fRight);
}
if (domainRect->fTop > domainRect->fBottom) {
domainRect->fTop = domainRect->fBottom =
SkScalarAve(domainRect->fTop, domainRect->fBottom);
}
}
return kDomain_DomainMode;
}

View File

@ -937,29 +937,15 @@ void SkGpuDevice::drawBitmapTile(const SkBitmap& bitmap,
const auto& caps = *this->caps();
if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) {
// Use a constrained texture domain to avoid color bleeding
SkRect domain;
if (srcRect.width() > SK_Scalar1) {
domain.fLeft = srcRect.fLeft + 0.5f;
domain.fRight = srcRect.fRight - 0.5f;
} else {
domain.fLeft = domain.fRight = srcRect.centerX();
}
if (srcRect.height() > SK_Scalar1) {
domain.fTop = srcRect.fTop + 0.5f;
domain.fBottom = srcRect.fBottom - 0.5f;
} else {
domain.fTop = domain.fBottom = srcRect.centerY();
}
if (bicubic) {
static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, domain, kDir, srcAlphaType);
fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, srcRect, kDir, srcAlphaType);
} else {
GrSurfaceOrigin origin = proxy->origin();
GrSwizzle swizzle = proxy->textureSwizzle();
GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
fp = GrTextureEffect::MakeSubset(std::move(view), srcAlphaType, texMatrix, samplerState,
domain, caps);
fp = GrTextureEffect::MakeSubset(std::move(view), srcAlphaType, texMatrix,
samplerState, srcRect, caps);
}
} else if (bicubic) {
SkASSERT(GrSamplerState::Filter::kNearest == samplerState.filter());

View File

@ -78,7 +78,6 @@ GrTextureEffect::Sampling::Sampling(GrSamplerState sampler, SkISize size, const
GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
GrSamplerState sampler,
const SkRect& subset,
bool adjustForFilter,
const SkRect* domain,
const GrCaps& caps) {
using Mode = GrSamplerState::WrapMode;
@ -91,9 +90,8 @@ GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
Filter fFilter;
};
auto resolve = [adjustForFilter, filter = sampler.filter(), &caps](int size, Mode mode,
Span subset, Span domain) {
float inset;
auto resolve = [filter = sampler.filter(), &caps](int size, Mode mode, Span subset,
Span domain) {
Result1D r;
r.fFilter = filter;
bool canDoHW = (mode != Mode::kClampToBorder || caps.clampToBorderSupport()) &&
@ -104,10 +102,27 @@ GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
return r;
}
inset = (adjustForFilter && filter != Filter::kNearest) ? 0.5 : 0;
auto insetSubset = subset.makeInset(inset);
if (canDoHW && insetSubset.contains(domain)) {
bool domainIsSafe = false;
Span insetSubset;
if (filter == Filter::kNearest) {
Span isubset{sk_float_floor(subset.fA), sk_float_ceil(subset.fB)};
if (domain.fA > isubset.fA && domain.fB < isubset.fB) {
domainIsSafe = true;
}
if (mode == Mode::kClamp) {
// This inset prevents sampling neighboring texels that could occur when
// texture coords fall exactly at texel boundaries (depending on precision
// and GPU-specific snapping at the boundary).
insetSubset = isubset.makeInset(0.5f);
} else {
// TODO: Handle other modes properly in this case.
insetSubset = subset;
}
} else {
insetSubset = subset.makeInset(0.5f);
domainIsSafe = insetSubset.contains(domain);
}
if (canDoHW && domainIsSafe) {
r.fShaderMode = ShaderMode::kNone;
r.fHWMode = mode;
return r;
@ -173,8 +188,7 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeTexelSubset(GrSurfaceP
GrSamplerState sampler,
const SkIRect& subset,
const GrCaps& caps) {
Sampling sampling(*view.proxy(), sampler, SkRect::Make(subset), true, nullptr, caps);
Sampling sampling(*view.proxy(), sampler, SkRect::Make(subset), nullptr, caps);
return std::unique_ptr<GrFragmentProcessor>(
new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
}
@ -186,7 +200,7 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeTexelSubset(GrSurfaceP
const SkIRect& subset,
const SkRect& domain,
const GrCaps& caps) {
Sampling sampling(*view.proxy(), sampler, SkRect::Make(subset), true, &domain, caps);
Sampling sampling(*view.proxy(), sampler, SkRect::Make(subset), &domain, caps);
return std::unique_ptr<GrFragmentProcessor>(
new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
}
@ -197,7 +211,7 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyV
GrSamplerState sampler,
const SkRect& subset,
const GrCaps& caps) {
Sampling sampling(*view.proxy(), sampler, subset, false, nullptr, caps);
Sampling sampling(*view.proxy(), sampler, subset, nullptr, caps);
return std::unique_ptr<GrFragmentProcessor>(
new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
}
@ -209,7 +223,7 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyV
const SkRect& subset,
const SkRect& domain,
const GrCaps& caps) {
Sampling sampling(*view.proxy(), sampler, subset, false, &domain, caps);
Sampling sampling(*view.proxy(), sampler, subset, &domain, caps);
return std::unique_ptr<GrFragmentProcessor>(
new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
}

View File

@ -61,9 +61,6 @@ public:
/**
* This is similar to MakeTexelSubset but takes a float rather than integer subset rect.
* No adjustment is done for filtering, the texture coordinates are limited to the
* unmodified subset. The subset should be unnormalized. The effect will apply texture
* coordinate normalization after subset restriction (logically).
*/
static std::unique_ptr<GrFragmentProcessor> MakeSubset(GrSurfaceProxyView,
SkAlphaType,
@ -108,7 +105,6 @@ private:
Sampling(const GrSurfaceProxy& proxy,
GrSamplerState sampler,
const SkRect&,
bool,
const SkRect*,
const GrCaps&);
inline bool usesDecal() const;

View File

@ -70,10 +70,6 @@ std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const sk_sp<GrTextur
GrSurfaceProxyView view(proxies[i], origin, swizzle);
if (domain) {
SkASSERT(planeFilter != GrSamplerState::Filter::kMipMap);
if (planeFilter != GrSamplerState::Filter::kNearest) {
// Inset by half a pixel for bilerp, after scaling to the size of the plane
planeDomain.inset(0.5f, 0.5f);
}
planeFPs[i] = GrTextureEffect::MakeSubset(std::move(view), kUnknown_SkAlphaType,
*planeMatrix, planeFilter, planeDomain, caps);
} else {

View File

@ -1071,17 +1071,14 @@ std::unique_ptr<GrDrawOp> GrTextureOp::Make(GrRecordingContext* context,
std::unique_ptr<GrFragmentProcessor> fp;
if (domain) {
// Update domain to match what GrTextureOp would do for bilerp, but don't do any
// normalization since GrTextureEffect handles that and the origin.
SkRect correctedDomain = normalize_domain(filter, {1.f, 1.f, 0.f}, domain);
const auto& caps = *context->priv().caps();
SkRect localRect;
if (localQuad.asRect(&localRect)) {
fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(),
filter, correctedDomain, localRect, caps);
fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(), filter,
*domain, localRect, caps);
} else {
fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(),
filter, correctedDomain, caps);
fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(), filter,
*domain, caps);
}
} else {
fp = GrTextureEffect::Make(std::move(proxyView), alphaType, SkMatrix::I(), filter);