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
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
index c1637d8..1fdc668 100644
--- a/src/effects/SkColorFilterImageFilter.cpp
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -57,13 +57,19 @@
                                                               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 @@
     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 e6430b4..21ddc16 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -86,6 +86,36 @@
     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 @@
 }
 #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.