allow imagefilter to manage CTM decomposition

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

Review URL: https://codereview.chromium.org/1908173006
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 4db9196..7aefd48 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -1706,3 +1706,31 @@
     test_large_blur_input(reporter, surface->getCanvas());
 }
 #endif
+
+/*
+ *  Test that colorfilterimagefilter does not require its CTM to be decomposed when it has more
+ *  than just scale/translate, but that other filters do.
+ */
+DEF_TEST(ImageFilterDecomposeCTM, reporter) {
+    // just need a colorfilter to exercise the corresponding imagefilter
+    sk_sp<SkColorFilter> cf = SkColorFilter::MakeModeFilter(SK_ColorRED, SkXfermode::kSrcATop_Mode);
+    sk_sp<SkImageFilter> cfif = SkColorFilterImageFilter::Make(cf, nullptr);
+    sk_sp<SkImageFilter> blif = SkBlurImageFilter::Make(3, 3, nullptr);
+
+    struct {
+        sk_sp<SkImageFilter> fFilter;
+        bool                 fExpectCanHandle;
+    } recs[] = {
+        { cfif,                                     true  },
+        { SkColorFilterImageFilter::Make(cf, cfif), true  },
+        { SkMergeImageFilter::Make(cfif, cfif),     true  },
+        { blif,                                     false },
+        { SkMergeImageFilter::Make(cfif, blif),     false },
+        { SkColorFilterImageFilter::Make(cf, blif), false },
+    };
+    
+    for (const auto& rec : recs) {
+        const bool canHandle = rec.fFilter->canHandleAffine();
+        REPORTER_ASSERT(reporter, canHandle == rec.fExpectCanHandle);
+    }
+}