Fix downsampled blur with clamp mode.
Ensure the extra border added to the downsampled image doesn't filter in values from outside the source bounds. Bug: chromium:1174354 Change-Id: I6c62eeaa57ce4e5341eab24985553f87ab0df666 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/378322 Reviewed-by: Michael Ludwig <michaelludwig@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
e1a728416c
commit
6d25f9d068
57
gm/crbug_1174354.cpp
Normal file
57
gm/crbug_1174354.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm/gm.h"
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "include/core/SkRect.h"
|
||||
#include "include/effects/SkGradientShader.h"
|
||||
#include "include/effects/SkImageFilters.h"
|
||||
|
||||
static void draw_bg_blur(SkCanvas* canvas, SkIRect rect, float sigma) {
|
||||
// First create an intermediate layer that has an opaque area that we blur with transparency
|
||||
// all around it. We want to make sure the transparency doesn't affect the blur of the opaque
|
||||
// content.
|
||||
auto outsetRect = SkRect::Make(rect).makeOutset(10, 10);
|
||||
canvas->saveLayer(outsetRect, nullptr);
|
||||
SkColor colors[] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN};
|
||||
float cx = (rect.left() + rect.right() )/2.f;
|
||||
float cy = (rect.top() + rect.bottom())/2.f;
|
||||
auto g = SkGradientShader::MakeSweep(cx,
|
||||
cy,
|
||||
colors,
|
||||
nullptr,
|
||||
3,
|
||||
SkTileMode::kMirror,
|
||||
0,
|
||||
45,
|
||||
0,
|
||||
nullptr);
|
||||
SkPaint paint;
|
||||
paint.setShader(std::move(g));
|
||||
canvas->drawRect(SkRect::Make(rect), paint);
|
||||
// Now do the bg-blur into another save-layer that should only read the opaque region.
|
||||
SkCanvas::SaveLayerRec rec;
|
||||
auto blur = SkImageFilters::Blur(sigma, sigma, SkTileMode::kClamp, nullptr, rect);
|
||||
rec.fBounds = &outsetRect;
|
||||
rec.fBackdrop = blur.get();
|
||||
canvas->saveLayer(rec);
|
||||
canvas->restore();
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(crbug_1174354, canvas, 70, 250) {
|
||||
// The initial fix for crbug.com/1156805 had an issue where the border added to the downsampled
|
||||
// texture that was used as input to the blur could actually bring in transparency when there
|
||||
// wasn't any within the original src bounds. It was populated the border using a filtering draw
|
||||
// from the full res source that could read from outside the pixels surrounding the original src
|
||||
// bounds.
|
||||
draw_bg_blur(canvas, SkIRect::MakeXYWH(10, 10, 50, 50), 5);
|
||||
draw_bg_blur(canvas, SkIRect::MakeXYWH(10, 70, 50, 50), 15);
|
||||
draw_bg_blur(canvas, SkIRect::MakeXYWH(10, 130, 50, 50), 30);
|
||||
draw_bg_blur(canvas, SkIRect::MakeXYWH(10, 190, 50, 50), 70);
|
||||
}
|
@ -118,6 +118,7 @@ gm_sources = [
|
||||
"$_gm/crbug_1162942.cpp",
|
||||
"$_gm/crbug_1167277.cpp",
|
||||
"$_gm/crbug_1174186.cpp",
|
||||
"$_gm/crbug_1174354.cpp",
|
||||
"$_gm/crbug_1177833.cpp",
|
||||
"$_gm/crbug_224618.cpp",
|
||||
"$_gm/crbug_691386.cpp",
|
||||
|
@ -594,48 +594,56 @@ std::unique_ptr<GrSurfaceDrawContext> GaussianBlur(GrRecordingContext* context,
|
||||
// Rather than run a potentially multi-pass rescaler on single rows/columns we just do a
|
||||
// single bilerp draw. If we find this quality unacceptable we should think more about how
|
||||
// to rescale these with better quality but without 4 separate multi-pass downscales.
|
||||
// These all batch together into a single draw (and with the above rescaling when there
|
||||
// is only one pass in the interior rescale).
|
||||
auto cheapDownscale = [&](SkIRect dstRect, SkRect srcRect) {
|
||||
rescaledSDC->drawTexture(nullptr,
|
||||
srcCtx->readSurfaceView(),
|
||||
srcAlphaType,
|
||||
GrSamplerState::Filter::kLinear,
|
||||
GrSamplerState::MipmapMode::kNone,
|
||||
SkBlendMode::kSrc,
|
||||
SK_PMColor4fWHITE,
|
||||
srcRect,
|
||||
SkRect::Make(dstRect),
|
||||
GrAA::kNo,
|
||||
GrQuadAAFlags::kNone,
|
||||
SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint,
|
||||
SkMatrix::I(),
|
||||
nullptr);
|
||||
auto cheapDownscale = [&](SkIRect dstRect, SkIRect srcRect) {
|
||||
rescaledSDC->drawTexture(nullptr,
|
||||
srcCtx->readSurfaceView(),
|
||||
srcAlphaType,
|
||||
GrSamplerState::Filter::kLinear,
|
||||
GrSamplerState::MipmapMode::kNone,
|
||||
SkBlendMode::kSrc,
|
||||
SK_PMColor4fWHITE,
|
||||
SkRect::Make(srcRect),
|
||||
SkRect::Make(dstRect),
|
||||
GrAA::kNo,
|
||||
GrQuadAAFlags::kNone,
|
||||
SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint,
|
||||
SkMatrix::I(),
|
||||
nullptr);
|
||||
};
|
||||
auto [dw, dh] = rescaledSDC->dimensions();
|
||||
auto [dw, dh] = rescaledSize;
|
||||
// The are the src rows and columns from the source that we will scale into the dst padding.
|
||||
float sLCol = srcBounds.left();
|
||||
float sTRow = srcBounds.top();
|
||||
float sRCol = srcBounds.right() - 1;
|
||||
float sBRow = srcBounds.bottom() - 1;
|
||||
|
||||
// Calculate src offsets and lengths for y when copying a col and x when copying a row.
|
||||
float isx = 1.f/scaleX;
|
||||
float isy = 1.f/scaleY;
|
||||
float sx = srcBounds.left() - isx;
|
||||
float sy = srcBounds.top() - isy;
|
||||
float sw = srcBounds.width() + 2*isx;
|
||||
float sh = srcBounds.height() + 2*isy;
|
||||
int sx = srcBounds.left();
|
||||
int sy = srcBounds.top();
|
||||
int sw = srcBounds.width();
|
||||
int sh = srcBounds.height();
|
||||
|
||||
// We double hit the four corners (last hit wins) rather than complicate the rects here.
|
||||
cheapDownscale(SkIRect::MakeXYWH( 0, 0, 1, dh),
|
||||
SkRect::MakeXYWH( sLCol, sy, 1, sh));
|
||||
cheapDownscale(SkIRect::MakeXYWH( 0, 0, dw, 1),
|
||||
SkRect::MakeXYWH( sx, sTRow, sw, 1));
|
||||
cheapDownscale(SkIRect::MakeXYWH(dw - 1, 0, 1, dh),
|
||||
SkRect::MakeXYWH( sRCol, sy, 1, sh));
|
||||
cheapDownscale(SkIRect::MakeXYWH( 0, dh - 1, dw, 1),
|
||||
SkRect::MakeXYWH( sx, sBRow, sw, 1));
|
||||
// Downscale the edges from the original source. These draws should batch together (and with
|
||||
// the above interior rescaling when it is a single pass).
|
||||
cheapDownscale(SkIRect::MakeXYWH( 0, 1, 1, dh),
|
||||
SkIRect::MakeXYWH( sLCol, sy, 1, sh));
|
||||
cheapDownscale(SkIRect::MakeXYWH( 1, 0, dw, 1),
|
||||
SkIRect::MakeXYWH( sx, sTRow, sw, 1));
|
||||
cheapDownscale(SkIRect::MakeXYWH(dw + 1, 1, 1, dh),
|
||||
SkIRect::MakeXYWH( sRCol, sy, 1, sh));
|
||||
cheapDownscale(SkIRect::MakeXYWH( 1, dh + 1, dw, 1),
|
||||
SkIRect::MakeXYWH( sx, sBRow, sw, 1));
|
||||
|
||||
// Copy the corners from the original source. These would batch with the edges except that
|
||||
// at time of writing we recognize these can use kNearest and downgrade the filter. So they
|
||||
// batch with each other but not the edge draws.
|
||||
cheapDownscale(SkIRect::MakeXYWH( 0, 0, 1, 1),
|
||||
SkIRect::MakeXYWH(sLCol, sTRow, 1, 1));
|
||||
cheapDownscale(SkIRect::MakeXYWH(dw + 1, 0, 1, 1),
|
||||
SkIRect::MakeXYWH(sRCol, sTRow, 1, 1));
|
||||
cheapDownscale(SkIRect::MakeXYWH(dw + 1,dh + 1, 1, 1),
|
||||
SkIRect::MakeXYWH(sRCol, sBRow, 1, 1));
|
||||
cheapDownscale(SkIRect::MakeXYWH( 0, dh + 1, 1, 1),
|
||||
SkIRect::MakeXYWH(sLCol, sBRow, 1, 1));
|
||||
}
|
||||
srcView = rescaledSDC->readSurfaceView();
|
||||
// Drop the contexts so we don't hold the proxies longer than necessary.
|
||||
|
Loading…
Reference in New Issue
Block a user