Fix failed filter followed by an affects-transparent-black filter.

When an upstream filter returns null, either through failure or clipping, a downstream affects-transparent-black
filter should still produce non-transparent pixels.

This patch fixes SkColorFilterImageFilter.

Note: this will affect the results of the imagefilterscropexpand GM.

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

Review URL: https://codereview.chromium.org/1844593002
This commit is contained in:
senorblanco 2016-04-05 04:43:45 -07:00 committed by Commit bot
parent 3b37545bc5
commit 6a93fa1a45
2 changed files with 89 additions and 10 deletions

View File

@ -57,13 +57,19 @@ sk_sp<SkSpecialImage> SkColorFilterImageFilter::onFilterImage(SkSpecialImage* so
SkIPoint* offset) const {
SkIPoint inputOffset = SkIPoint::Make(0, 0);
sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
if (!input) {
SkIRect inputBounds;
if (fColorFilter->affectsTransparentBlack()) {
// If the color filter affects transparent black, the bounds are the entire clip.
inputBounds = ctx.clipBounds();
} else if (!input) {
return nullptr;
} else {
inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
input->width(), input->height());
}
SkIRect bounds;
const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
input->width(), input->height());
if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
return nullptr;
}
@ -77,18 +83,27 @@ sk_sp<SkSpecialImage> SkColorFilterImageFilter::onFilterImage(SkSpecialImage* so
SkCanvas* canvas = surf->getCanvas();
SkASSERT(canvas);
// TODO: it seems like this clear shouldn't be necessary (see skbug.com/5075)
canvas->clear(0x0);
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setColorFilter(fColorFilter);
input->draw(canvas,
SkIntToScalar(inputOffset.fX - bounds.fLeft),
SkIntToScalar(inputOffset.fY - bounds.fTop),
&paint);
// TODO: it may not be necessary to clear or drawPaint inside the input bounds
// (see skbug.com/5075)
if (fColorFilter->affectsTransparentBlack()) {
// The subsequent input->draw() call may not fill the entire canvas. For filters which
// affect transparent black, ensure that the filter is applied everywhere.
canvas->drawPaint(paint);
} else {
canvas->clear(0x0);
}
if (input) {
input->draw(canvas,
SkIntToScalar(inputOffset.fX - bounds.fLeft),
SkIntToScalar(inputOffset.fY - bounds.fTop),
&paint);
}
offset->fX = bounds.fLeft;
offset->fY = bounds.fTop;

View File

@ -86,6 +86,36 @@ private:
typedef SkImageFilter INHERITED;
};
class FailImageFilter : public SkImageFilter {
public:
FailImageFilter() : SkImageFilter(0, nullptr) {
}
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
const Context& ctx,
SkIPoint* offset) const override {
return nullptr;
}
SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
private:
typedef SkImageFilter INHERITED;
};
sk_sp<SkFlattenable> FailImageFilter::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
return sk_sp<SkFlattenable>(new FailImageFilter());
}
#ifndef SK_IGNORE_TO_STRING
void FailImageFilter::toString(SkString* str) const {
str->appendf("FailImageFilter: (");
str->append(")");
}
#endif
void draw_gradient_circle(SkCanvas* canvas, int width, int height) {
SkScalar x = SkIntToScalar(width / 2);
SkScalar y = SkIntToScalar(height / 2);
@ -673,6 +703,40 @@ DEF_GPUTEST_FOR_NATIVE_CONTEXT(TestZeroBlurSigma_Gpu, reporter, context) {
}
#endif
// Tests that, even when an upstream filter has returned null (due to failure or clipping), a
// downstream filter that affects transparent black still does so even with a nullptr input.
static void test_fail_affects_transparent_black(SkImageFilter::Proxy* proxy,
skiatest::Reporter* reporter,
GrContext* context) {
sk_sp<FailImageFilter> failFilter(new FailImageFilter());
sk_sp<SkSpecialImage> source(create_empty_special_image(context, proxy, 5));
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 1, 1), nullptr);
sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
SkASSERT(green->affectsTransparentBlack());
sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Create(green.get(),
failFilter.get()));
SkIPoint offset;
sk_sp<SkSpecialImage> result(greenFilter->filterImage(source.get(), ctx, &offset));
REPORTER_ASSERT(reporter, nullptr != result.get());
if (result.get()) {
SkBitmap resultBM;
TestingSpecialImageAccess::GetROPixels(result.get(), &resultBM);
SkAutoLockPixels lock(resultBM);
REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN);
}
}
DEF_TEST(ImageFilterFailAffectsTransparentBlack, reporter) {
run_raster_test(reporter, 100, test_fail_affects_transparent_black);
}
#if SK_SUPPORT_GPU
DEF_GPUTEST_FOR_NATIVE_CONTEXT(ImageFilterFailAffectsTransparentBlack_Gpu, reporter, context) {
run_gpu_test(reporter, context, 100, test_fail_affects_transparent_black);
}
#endif
DEF_TEST(ImageFilterDrawTiled, reporter) {
// Check that all filters when drawn tiled (with subsequent clip rects) exactly
// match the same filters drawn with a single full-canvas bitmap draw.