Don't draw as sprite when alpha, CF, or MF are present

This makes SkCanvas::drawImage() consistent with the AutoLayerForImageFilter
that applies when the draw isn't a sprite, or if drawImageRect or drawRect
with an image-shader was used instead.

Bug: skia:9561
Change-Id: Ic395f580b06753706ddcaed832535ec4edb3bb5a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/274507
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2020-03-02 16:27:03 -05:00 committed by Skia Commit-Bot
parent b58098f34c
commit 1e58c1ab3a
2 changed files with 95 additions and 0 deletions

View File

@ -14,6 +14,7 @@
#include "include/core/SkImage.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMaskFilter.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRRect.h"
@ -23,7 +24,10 @@
#include "include/core/SkSurface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkColorMatrix.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkHighContrastFilter.h"
#include "include/effects/SkImageFilters.h"
#include "include/effects/SkShaderMaskFilter.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
@ -189,3 +193,89 @@ protected:
};
DEF_GM(return new SaveLayerWithBackdropGM();)
///////////////////////////////////////////////////////////////////////////////////////////////////
// Test that color filters and mask filters are applied before the image filter, even if it would
// normally be a sprite draw that could avoid an auto-saveLayer.
DEF_SIMPLE_GM(imagefilters_effect_order, canvas, 512, 512) {
sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_256.png"));
if (canvas->getGrContext()) {
image = image->makeTextureImage(canvas->getGrContext());
}
SkISize kernelSize = SkISize::Make(3, 3);
SkIPoint kernelOffset = SkIPoint::Make(1, 1);
// A Laplacian edge detector, ie https://en.wikipedia.org/wiki/Kernel_(image_processing)
SkScalar kernel[9] = {-1.f, -1.f, -1.f,
-1.f, 8.f, -1.f,
-1.f, -1.f, -1.f};
auto edgeDetector = SkImageFilters::MatrixConvolution(
kernelSize, kernel, 1.f, 0.f, kernelOffset, SkTileMode::kClamp, false, nullptr);
// This uses the high contrast filter because it resembles a pre-processing step you may perform
// prior to edge detection. The specifics of the high contrast algorithm don't matter for the GM
auto edgeAmplify = SkHighContrastFilter::Make(
{false, SkHighContrastConfig::InvertStyle::kNoInvert, 0.5f});
SkPaint testCFPaint;
testCFPaint.setColorFilter(edgeAmplify);
testCFPaint.setImageFilter(edgeDetector);
// The expected result is color filter then image filter, so represent this explicitly in the
// image filter graph.
SkPaint expectedCFPaint;
expectedCFPaint.setImageFilter(SkImageFilters::Compose(edgeDetector,
SkImageFilters::ColorFilter(edgeAmplify, nullptr)));
// Draw the image twice (expected on the left, test on the right that should match)
SkRect crop = SkRect::Make(image->bounds());
canvas->save();
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &expectedCFPaint); // Filter applied by draw's SkPaint
canvas->restore();
canvas->save();
canvas->translate(image->width(), 0);
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &testCFPaint);
canvas->restore();
// Now test mask filters. These should be run before the image filter, and thus have the same
// effect as multiplying by an alpha mask.
// This mask filter pokes a hole in the center of the image
static constexpr SkColor kAlphas[] = { SK_ColorBLACK, SK_ColorTRANSPARENT };
static constexpr SkScalar kPos[] = { 0.4f, 0.9f };
sk_sp<SkShader> alphaMaskShader = SkGradientShader::MakeRadial(
{128.f, 128.f}, 128.f, kAlphas, kPos, 2, SkTileMode::kClamp);
sk_sp<SkMaskFilter> maskFilter = SkShaderMaskFilter::Make(alphaMaskShader);
// If edge detector sees the mask filter, it'll have alpha and then blend with the original
// image; otherwise the mask filter will apply late (incorrectly) and none of the original
// image will be visible.
sk_sp<SkImageFilter> edgeBlend = SkImageFilters::Xfermode(SkBlendMode::kSrcOver,
SkImageFilters::Image(image), edgeDetector);
SkPaint testMaskPaint;
testMaskPaint.setMaskFilter(maskFilter);
testMaskPaint.setImageFilter(edgeBlend);
SkPaint alphaPaint;
alphaPaint.setShader(alphaMaskShader);
SkPaint expectedMaskPaint;
expectedMaskPaint.setImageFilter(SkImageFilters::Compose(edgeBlend,
SkImageFilters::Xfermode(SkBlendMode::kSrcIn,
SkImageFilters::Paint(alphaPaint))));
canvas->save();
canvas->translate(0, image->height());
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &expectedMaskPaint);
canvas->restore();
canvas->save();
canvas->translate(image->width(), image->height());
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &testMaskPaint);
canvas->restore();
}

View File

@ -2467,6 +2467,11 @@ bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const
return false;
}
// The other paint effects need to be applied before the image filter, but the sprite draw
// applies the filter explicitly first.
if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
return false;
}
// Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
// Once we can filter and the filter will return a result larger than itself, we should be
// able to remove this constraint.