From 06d54ad09a03edd62d597b2ba67ad3c05944fd92 Mon Sep 17 00:00:00 2001 From: senorblanco Date: Wed, 13 Jan 2016 13:48:54 -0800 Subject: [PATCH] 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 --- gm/xfermodeimagefilter.cpp | 24 +++++++++++++- src/effects/SkXfermodeImageFilter.cpp | 47 +++++++++++++++++---------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/gm/xfermodeimagefilter.cpp b/gm/xfermodeimagefilter.cpp index bb86ceffa8..85bbf44765 100644 --- a/gm/xfermodeimagefilter.cpp +++ b/gm/xfermodeimagefilter.cpp @@ -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 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: diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp index 3ff6c491b5..c2013fdb86 100644 --- a/src/effects/SkXfermodeImageFilter.cpp +++ b/src/effects/SkXfermodeImageFilter.cpp @@ -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 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 bgFP( - GrSimpleTextureEffect::Create(backgroundTex, bgMatrix)); + SkMatrix backgroundMatrix; + backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); + backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), + SkIntToScalar(-backgroundOffset.fY)); + SkAutoTUnref 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 foregroundDomain(GrTextureDomainEffect::Create( + SkAutoTUnref 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; }