diff options
author | senorblanco <senorblanco@chromium.org> | 2016-04-05 04:43:45 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-04-05 04:43:45 -0700 |
commit | 6a93fa1a4526ba14782f7f17a73479ca5a4a8e3a (patch) | |
tree | f5d771e8c0cae2aef3ff50e07fd5e69ad6351bd0 | |
parent | 3b37545bc594a96de45eba62dea0ce478750f2a9 (diff) |
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
-rw-r--r-- | src/effects/SkColorFilterImageFilter.cpp | 35 | ||||
-rw-r--r-- | tests/ImageFilterTest.cpp | 64 |
2 files changed, 89 insertions, 10 deletions
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp index c1637d8128..1fdc668f9b 100644 --- a/src/effects/SkColorFilterImageFilter.cpp +++ b/src/effects/SkColorFilterImageFilter.cpp @@ -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; diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp index e6430b4d87..21ddc162d2 100644 --- a/tests/ImageFilterTest.cpp +++ b/tests/ImageFilterTest.cpp @@ -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. |