Improve clamp mode in GPU blur with downsampling.
This ensures that if the original src had transparent-black in the
border pixels that our downsampled image does as well. Otherwise,
clamp mode causes these nonzero border pixels to produce vertical
/horizontal smears at the edges of the result.
Bug: chromium:1156804
Change-Id: Id2c3a66de29724db2fcc7954abf7f14937cfb76d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/343111
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index 1eb5186..1a8ca89 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -14,6 +14,7 @@
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkYUVMath.h"
#include "src/gpu/GrAuditTrail.h"
+#include "src/gpu/GrColorSpaceXform.h"
#include "src/gpu/GrDataUtils.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrDrawingManager.h"
@@ -1079,18 +1080,46 @@
SkIRect srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality) {
+ // We rescale by drawing and don't currently only support drawing to premul.
+ if (info.alphaType() != kPremul_SkAlphaType) {
+ return nullptr;
+ }
+ auto sdc = GrSurfaceDrawContext::MakeWithFallback(fContext,
+ info.colorType(),
+ info.refColorSpace(),
+ SkBackingFit::kExact,
+ info.dimensions(),
+ 1,
+ GrMipmapped::kNo,
+ this->asSurfaceProxy()->isProtected(),
+ origin);
+ if (!sdc || !this->rescaleInto(sdc.get(),
+ SkIRect::MakeSize(sdc->dimensions()),
+ srcRect,
+ rescaleGamma,
+ rescaleQuality)) {
+ return nullptr;
+ }
+ return sdc;
+}
+
+bool GrSurfaceContext::rescaleInto(GrSurfaceDrawContext* dst,
+ SkIRect dstRect,
+ SkIRect srcRect,
+ RescaleGamma rescaleGamma,
+ SkFilterQuality rescaleQuality) {
+ SkASSERT(dst);
+ if (!SkIRect::MakeSize(dst->dimensions()).contains((dstRect))) {
+ return false;
+ }
+
auto rtProxy = this->asRenderTargetProxy();
if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
- return nullptr;
+ return false;
}
if (this->asSurfaceProxy()->framebufferOnly()) {
- return nullptr;
- }
-
- // We rescale by drawing and don't currently support drawing to a kUnpremul destination.
- if (info.alphaType() == kUnpremul_SkAlphaType) {
- return nullptr;
+ return false;
}
GrSurfaceProxyView texView = this->readSurfaceView();
@@ -1099,12 +1128,14 @@
texView = GrSurfaceProxyView::Copy(fContext, std::move(texView), GrMipmapped::kNo, srcRect,
SkBackingFit::kApprox, SkBudgeted::kNo);
if (!texView) {
- return nullptr;
+ return false;
}
SkASSERT(texView.asTextureProxy());
srcRect = SkIRect::MakeSize(srcRect.size());
}
+ SkISize finalSize = dstRect.size();
+
// Within a rescaling pass A is the input (if not null) and B is the output. At the end of the
// pass B is moved to A. If 'this' is the input on the first pass then tempA is null.
std::unique_ptr<GrSurfaceDrawContext> tempA;
@@ -1120,9 +1151,9 @@
// We'll fall back to kRGBA_8888 if half float not supported.
auto linearRTC = GrSurfaceDrawContext::MakeWithFallback(
fContext, GrColorType::kRGBA_F16, cs, SkBackingFit::kApprox, srcRect.size(), 1,
- GrMipmapped::kNo, GrProtected::kNo, origin);
+ GrMipmapped::kNo, GrProtected::kNo, dst->origin());
if (!linearRTC) {
- return nullptr;
+ return false;
}
// 1-to-1 draw can always be kFast.
linearRTC->drawTexture(nullptr,
@@ -1145,39 +1176,48 @@
srcRect = SkIRect::MakeSize(srcRect.size());
}
- while (srcRect.size() != info.dimensions()) {
- SkISize nextDims = info.dimensions();
+ while (srcRect.size() != finalSize) {
+ SkISize nextDims = finalSize;
if (rescaleQuality != kNone_SkFilterQuality) {
- if (srcRect.width() > info.width()) {
- nextDims.fWidth = std::max((srcRect.width() + 1)/2, info.width());
- } else if (srcRect.width() < info.width()) {
- nextDims.fWidth = std::min(srcRect.width()*2, info.width());
+ if (srcRect.width() > finalSize.width()) {
+ nextDims.fWidth = std::max((srcRect.width() + 1)/2, finalSize.width());
+ } else if (srcRect.width() < finalSize.width()) {
+ nextDims.fWidth = std::min(srcRect.width()*2, finalSize.width());
}
- if (srcRect.height() > info.height()) {
- nextDims.fHeight = std::max((srcRect.height() + 1)/2, info.height());
- } else if (srcRect.height() < info.height()) {
- nextDims.fHeight = std::min(srcRect.height()*2, info.height());
+ if (srcRect.height() > finalSize.height()) {
+ nextDims.fHeight = std::max((srcRect.height() + 1)/2, finalSize.height());
+ } else if (srcRect.height() < finalSize.height()) {
+ nextDims.fHeight = std::min(srcRect.height()*2, finalSize.height());
}
}
auto input = tempA ? tempA.get() : this;
- GrColorType colorType = input->colorInfo().colorType();
- auto cs = input->colorInfo().refColorSpace();
sk_sp<GrColorSpaceXform> xform;
- auto prevAlphaType = input->colorInfo().alphaType();
- if (nextDims == info.dimensions()) {
+ GrSurfaceDrawContext* tempBPtr;
+ SkIRect tempRect;
+ if (nextDims == finalSize) {
// Might as well fold conversion to final info in the last step.
- cs = info.refColorSpace();
xform = GrColorSpaceXform::Make(input->colorInfo().colorSpace(),
- input->colorInfo().alphaType(), cs.get(),
- info.alphaType());
+ input->colorInfo().alphaType(),
+ dst->colorInfo().colorSpace(),
+ dst->colorInfo().alphaType());
+ tempBPtr = dst;
+ tempRect = dstRect;
+ } else {
+ tempB = GrSurfaceDrawContext::MakeWithFallback(fContext,
+ input->colorInfo().colorType(),
+ input->colorInfo().refColorSpace(),
+ SkBackingFit::kApprox,
+ nextDims,
+ 1,
+ GrMipmapped::kNo,
+ GrProtected::kNo,
+ dst->origin());
+ if (!tempB) {
+ return false;
+ }
+ tempBPtr = tempB.get();
+ tempRect = SkIRect::MakeSize(tempB->dimensions());
}
- tempB = GrSurfaceDrawContext::MakeWithFallback(fContext, colorType, std::move(cs),
- SkBackingFit::kApprox, nextDims, 1,
- GrMipmapped::kNo, GrProtected::kNo, origin);
- if (!tempB) {
- return nullptr;
- }
- auto dstRect = SkRect::Make(nextDims);
if (rescaleQuality == kHigh_SkFilterQuality) {
SkMatrix matrix;
matrix.setScaleTranslate((float)srcRect.width()/nextDims.width(),
@@ -1193,16 +1233,27 @@
}
static constexpr auto kWM = GrSamplerState::WrapMode::kClamp;
static constexpr auto kKernel = GrBicubicEffect::gCatmullRom;
- fp = GrBicubicEffect::MakeSubset(std::move(texView), prevAlphaType, matrix, kWM, kWM,
- SkRect::Make(srcRect), kKernel, dir, *this->caps());
+ fp = GrBicubicEffect::MakeSubset(std::move(texView),
+ input->colorInfo().alphaType(),
+ matrix,
+ kWM,
+ kWM,
+ SkRect::Make(srcRect),
+ kKernel,
+ dir,
+ *this->caps());
if (xform) {
fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform));
}
GrPaint paint;
paint.setColorFragmentProcessor(std::move(fp));
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
- tempB->fillRectToRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
- dstRect);
+ tempBPtr->fillRectToRect(nullptr,
+ std::move(paint),
+ GrAA::kNo,
+ SkMatrix::I(),
+ SkRect::Make(tempRect),
+ SkRect::Make(tempRect));
} else {
auto filter = rescaleQuality == kNone_SkFilterQuality ? GrSamplerState::Filter::kNearest
: GrSamplerState::Filter::kLinear;
@@ -1211,27 +1262,26 @@
if (nextDims.width() <= srcRect.width() && nextDims.height() <= srcRect.height()) {
constraint = SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint;
}
- tempB->drawTexture(nullptr,
- std::move(texView),
- srcAlphaType,
- filter,
- GrSamplerState::MipmapMode::kNone,
- SkBlendMode::kSrc,
- SK_PMColor4fWHITE,
- SkRect::Make(srcRect),
- dstRect,
- GrAA::kNo,
- GrQuadAAFlags::kNone,
- constraint,
- SkMatrix::I(),
- std::move(xform));
+ tempBPtr->drawTexture(nullptr,
+ std::move(texView),
+ srcAlphaType,
+ filter,
+ GrSamplerState::MipmapMode::kNone,
+ SkBlendMode::kSrc,
+ SK_PMColor4fWHITE,
+ SkRect::Make(srcRect),
+ SkRect::Make(tempRect),
+ GrAA::kNo,
+ GrQuadAAFlags::kNone,
+ constraint,
+ SkMatrix::I(),
+ std::move(xform));
}
- texView = tempB->readSurfaceView();
+ texView = tempBPtr->readSurfaceView();
tempA = std::move(tempB);
srcRect = SkIRect::MakeSize(nextDims);
}
- SkASSERT(tempA);
- return tempA;
+ return true;
}
GrSurfaceContext::PixelTransferResult GrSurfaceContext::transferPixels(GrColorType dstCT,