Fix SkXfermodeImageFilter GPU fast path for differing sizes.

The GPU fast path was not doing the correct thing for input bitmaps of
differing sizes. This change brings the fast path in line with the
slow path: use the union of foreground and background bounds as bounds,
offset the draw context by the bounds translation, and translate the
foreground and background independently by their respective offsets.

Finally, we add a texture domain for the background fragment
processor, since we may access texels outside its domain.

Note: this adds two new test cases to the xfermodeimagefilter GM, so
those will need to be rebaselined.

BUG=568196
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1588633002

Review URL: https://codereview.chromium.org/1588633002
This commit is contained in:
senorblanco 2016-01-13 13:48:54 -08:00 committed by Commit bot
parent f8aec588bf
commit 06d54ad09a
2 changed files with 52 additions and 19 deletions

View File

@ -14,7 +14,7 @@
#include "SkXfermodeImageFilter.h"
#define WIDTH 600
#define HEIGHT 600
#define HEIGHT 700
#define MARGIN 12
namespace skiagm {
@ -169,6 +169,28 @@ protected:
y += fBitmap.height() + MARGIN;
}
}
// Test small bg, large fg with Screen (uses shader blend)
mode.reset(SkXfermode::Create(SkXfermode::kScreen_Mode));
SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(10, 10, 60, 60));
SkAutoTUnref<SkImageFilter> cropped(
SkOffsetImageFilter::Create(0, 0, foreground, &cropRect));
filter.reset(SkXfermodeImageFilter::Create(mode, cropped, background));
paint.setImageFilter(filter);
DrawClippedPaint(canvas, clipRect, paint, x, y);
x += fBitmap.width() + MARGIN;
if (x + fBitmap.width() > WIDTH) {
x = 0;
y += fBitmap.height() + MARGIN;
}
// Test small fg, large bg with Screen (uses shader blend)
filter.reset(SkXfermodeImageFilter::Create(mode, background, cropped));
paint.setImageFilter(filter);
DrawClippedPaint(canvas, clipRect, paint, x, y);
x += fBitmap.width() + MARGIN;
if (x + fBitmap.width() > WIDTH) {
x = 0;
y += fBitmap.height() + MARGIN;
}
}
private:

View File

@ -147,13 +147,18 @@ bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
}
GrTexture* foregroundTex = foreground.getTexture();
GrContext* context = foregroundTex->getContext();
SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y());
bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y()));
if (bounds.isEmpty()) {
return false;
}
const GrFragmentProcessor* xferFP = nullptr;
GrSurfaceDesc desc;
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fWidth = src.width();
desc.fHeight = src.height();
desc.fWidth = bounds.width();
desc.fHeight = bounds.height();
desc.fConfig = kSkia8888_GrPixelConfig;
SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc));
if (!dst) {
@ -161,32 +166,36 @@ bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
}
GrPaint paint;
SkMatrix bgMatrix;
bgMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
SkAutoTUnref<const GrFragmentProcessor> bgFP(
GrSimpleTextureEffect::Create(backgroundTex, bgMatrix));
SkMatrix backgroundMatrix;
backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX),
SkIntToScalar(-backgroundOffset.fY));
SkAutoTUnref<const GrFragmentProcessor> bgFP(GrTextureDomainEffect::Create(
backgroundTex, backgroundMatrix,
GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()),
GrTextureDomain::kDecal_Mode,
GrTextureParams::kNone_FilterMode)
);
if (!fMode || !fMode->asFragmentProcessor(&xferFP, bgFP)) {
// canFilterImageGPU() should've taken care of this
SkASSERT(false);
return false;
}
SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex);
foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX),
SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY));
SkMatrix foregroundMatrix;
foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height());
foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX),
SkIntToScalar(-foregroundOffset.fY));
SkRect srcRect;
src.getBounds(&srcRect);
SkAutoTUnref<const GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create(
SkAutoTUnref<const GrFragmentProcessor> foregroundFP(GrTextureDomainEffect::Create(
foregroundTex, foregroundMatrix,
GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
GrTextureDomain::kDecal_Mode,
GrTextureParams::kNone_FilterMode)
);
paint.addColorFragmentProcessor(foregroundDomain.get());
paint.addColorFragmentProcessor(foregroundFP.get());
if (xferFP) {
paint.addColorFragmentProcessor(xferFP)->unref();
}
@ -197,11 +206,13 @@ bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
return false;
}
drawContext->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), srcRect);
SkMatrix matrix;
matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds));
offset->fX = backgroundOffset.fX;
offset->fY = backgroundOffset.fY;
WrapTexture(dst, src.width(), src.height(), result);
offset->fX = bounds.left();
offset->fY = bounds.top();
WrapTexture(dst, bounds.width(), bounds.height(), result);
return true;
}