Update SkModeColorFilter to support an input FP.

This change relies on the Xfermode updates in the prior CL
(http://review.skia.org/299703) to render properly, and requires
slightly different blending behavior in the compose FP which
necessitated a new ComposeBehavior enum.

Eventually we would like to settle on a universal ComposeBehavior which
works well for all call sites, but that will be its own fairly
disruptive change. This work will be tracked at skia:10457.

Change-Id: I3cc0ea5e016fbef82bc63d653d60d0505efaa66f
Bug: skia:10217
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/298821
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/tablecolorfilter.cpp b/gm/tablecolorfilter.cpp
index b4bbd12..5b6fa86 100644
--- a/gm/tablecolorfilter.cpp
+++ b/gm/tablecolorfilter.cpp
@@ -131,7 +131,6 @@
         canvas->drawColor(0xFFDDDDDD);
         canvas->translate(20, 20);
 
-
         static sk_sp<SkColorFilter> (*gColorFilterMakers[])() = {
             make_null_cf, make_cf0, make_cf1, make_cf2, make_cf3
         };
@@ -142,7 +141,7 @@
         //  - A first line with the original bitmap, followed by the image drawn once
         //  with each of the N color filters
         //  - N lines of the bitmap drawn N times, this will cover all N*N combinations of
-        //  pair of color filters in order to test the collpsing of consecutive table
+        //  pair of color filters in order to test the collapsing of consecutive table
         //  color filters.
         //
         //  Here is a graphical representation of the result for 2 bitmaps and 2 filters
diff --git a/gn/core.gni b/gn/core.gni
index fab22d3..56769b4 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -269,6 +269,7 @@
   "$_src/core/SkMipMap.cpp",
   "$_src/core/SkMipMap.h",
   "$_src/core/SkModeColorFilter.cpp",
+  "$_src/core/SkModeColorFilter.h",
   "$_src/core/SkNextID.h",
   "$_src/core/SkOSFile.h",
   "$_src/core/SkOpts.cpp",
diff --git a/src/core/SkModeColorFilter.cpp b/src/core/SkModeColorFilter.cpp
index 6394e65..445d9c3 100644
--- a/src/core/SkModeColorFilter.cpp
+++ b/src/core/SkModeColorFilter.cpp
@@ -88,27 +88,39 @@
 #include "src/gpu/effects/GrXfermodeFragmentProcessor.h"
 #include "src/gpu/effects/generated/GrConstColorProcessor.h"
 
-std::unique_ptr<GrFragmentProcessor> SkModeColorFilter::asFragmentProcessor(
-        GrRecordingContext*, const GrColorInfo& dstColorInfo) const {
-    if (SkBlendMode::kDst == fMode) {
-        return nullptr;
+GrFPResult SkModeColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
+                                                  GrRecordingContext*,
+                                                  const GrColorInfo& dstColorInfo) const {
+    if (fMode == SkBlendMode::kDst) {
+        // If the blend mode is "dest," the blend color won't factor into it at all.
+        // We can return the input FP as-is.
+        return GrFPSuccess(std::move(inputFP));
     }
 
-    auto constFP = GrConstColorProcessor::Make(/*inputFP=*/nullptr,
-                                               SkColorToPMColor4f(fColor, dstColorInfo),
-                                               GrConstColorProcessor::InputMode::kIgnore);
-    auto fp = GrXfermodeFragmentProcessor::Make(std::move(constFP), /*dst=*/nullptr, fMode);
-    if (!fp) {
-        return nullptr;
+    SkDEBUGCODE(const bool fpHasConstIO = !inputFP || inputFP->hasConstantOutputForConstantInput();)
+
+    auto colorFP = GrConstColorProcessor::Make(
+            /*inputFP=*/nullptr, SkColorToPMColor4f(fColor, dstColorInfo),
+            GrConstColorProcessor::InputMode::kIgnore);
+    auto xferFP = GrXfermodeFragmentProcessor::Make(
+            std::move(colorFP), std::move(inputFP), fMode,
+            GrXfermodeFragmentProcessor::ComposeBehavior::kSkModeBehavior);
+
+    if (xferFP == nullptr) {
+        // This is only expected to happen if the blend mode is "dest" and the input FP is null.
+        // Since we already did an early-out in the "dest" blend mode case, we shouldn't get here.
+        SkDEBUGFAIL("GrXfermodeFragmentProcessor::Make returned null unexpectedly");
+        return GrFPFailure(nullptr);
     }
-#ifdef SK_DEBUG
+
     // With a solid color input this should always be able to compute the blended color
-    // (at least for coeff modes)
-    if ((unsigned)fMode <= (unsigned)SkBlendMode::kLastCoeffMode) {
-        SkASSERT(fp->hasConstantOutputForConstantInput());
-    }
-#endif
-    return fp;
+    // (at least for coeff modes).
+    // Occasionally, we even do better than we started; specifically, in "src" blend mode, we end up
+    // ditching the input FP entirely, which turns a non-constant operation into a constant one.
+    SkASSERT(fMode > SkBlendMode::kLastCoeffMode ||
+             xferFP->hasConstantOutputForConstantInput() >= fpHasConstIO);
+
+    return GrFPSuccess(std::move(xferFP));
 }
 
 #endif
diff --git a/src/core/SkModeColorFilter.h b/src/core/SkModeColorFilter.h
index e2aa599..dc3ce44 100644
--- a/src/core/SkModeColorFilter.h
+++ b/src/core/SkModeColorFilter.h
@@ -19,8 +19,9 @@
     uint32_t onGetFlags() const override;
 
 #if SK_SUPPORT_GPU
-    std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(GrRecordingContext*,
-                                                             const GrColorInfo&) const override;
+    bool colorFilterAcceptsInputFP() const override { return true; }
+    GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
+                                   GrRecordingContext*, const GrColorInfo&) const override;
 #endif
 
     SK_FLATTENABLE_HOOKS(SkModeColorFilter)
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
index cf42a9f..f994107 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
@@ -15,6 +15,8 @@
 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 
+using GrXfermodeFragmentProcessor::ComposeBehavior;
+
 // Some of the cpu implementations of blend modes differ too much from the GPU enough that
 // we can't use the cpu implementation to implement constantOutputForConstantInput.
 static inline bool does_cpu_blend_impl_match_gpu(SkBlendMode mode) {
@@ -25,15 +27,27 @@
            mode != SkBlendMode::kColorBurn;
 }
 
+static const char* ComposeBehavior_Name(ComposeBehavior behavior) {
+    SkASSERT(unsigned(behavior) <= unsigned(ComposeBehavior::kLastComposeBehavior));
+    static constexpr const char* gStrings[] = {
+        "Default",
+        "Compose-One",
+        "Compose-Two",
+        "SkMode",
+    };
+    static_assert(SK_ARRAY_COUNT(gStrings) == size_t(ComposeBehavior::kLastComposeBehavior) + 1);
+    return gStrings[int(behavior)];
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
 class ComposeFragmentProcessor : public GrFragmentProcessor {
 public:
     static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
                                                      std::unique_ptr<GrFragmentProcessor> dst,
-                                                     SkBlendMode mode) {
+                                                     SkBlendMode mode, ComposeBehavior behavior) {
         return std::unique_ptr<GrFragmentProcessor>(
-                new ComposeFragmentProcessor(std::move(src), std::move(dst), mode));
+                new ComposeFragmentProcessor(std::move(src), std::move(dst), mode, behavior));
     }
 
     const char* name() const override { return "Compose"; }
@@ -55,15 +69,21 @@
     std::unique_ptr<GrFragmentProcessor> clone() const override;
 
     SkBlendMode getMode() const { return fMode; }
+    ComposeBehavior composeBehavior() const { return fComposeBehavior; }
     int srcFPIndex() const { return fSrcFPIndex; }
     int dstFPIndex() const { return fDstFPIndex; }
 
 private:
     ComposeFragmentProcessor(std::unique_ptr<GrFragmentProcessor> src,
                              std::unique_ptr<GrFragmentProcessor> dst,
-                             SkBlendMode mode)
+                             SkBlendMode mode, ComposeBehavior behavior)
             : INHERITED(kComposeFragmentProcessor_ClassID, OptFlags(src.get(), dst.get(), mode))
-            , fMode(mode) {
+            , fMode(mode)
+            , fComposeBehavior(behavior) {
+        if (fComposeBehavior == ComposeBehavior::kDefault) {
+            fComposeBehavior = (src && dst) ? ComposeBehavior::kComposeTwoBehavior
+                                            : ComposeBehavior::kComposeOneBehavior;
+        }
         if (src != nullptr) {
             fSrcFPIndex = this->registerChild(std::move(src));
         }
@@ -75,6 +95,7 @@
     ComposeFragmentProcessor(const ComposeFragmentProcessor& that)
             : INHERITED(kComposeFragmentProcessor_ClassID, ProcessorOptimizationFlags(&that))
             , fMode(that.fMode)
+            , fComposeBehavior(that.fComposeBehavior)
             , fSrcFPIndex(that.fSrcFPIndex)
             , fDstFPIndex(that.fDstFPIndex) {
         this->cloneAndRegisterAllChildProcessors(that);
@@ -176,26 +197,41 @@
         const auto* src = (fSrcFPIndex >= 0) ? &this->childProcessor(fSrcFPIndex) : nullptr;
         const auto* dst = (fDstFPIndex >= 0) ? &this->childProcessor(fDstFPIndex) : nullptr;
 
-        if (src && dst) {
-            SkPMColor4f opaqueInput = { input.fR, input.fG, input.fB, 1 };
-            SkPMColor4f srcColor = ConstantOutputForConstantInput(*src, opaqueInput);
-            SkPMColor4f dstColor = ConstantOutputForConstantInput(*dst, opaqueInput);
-            SkPMColor4f result = SkBlendMode_Apply(fMode, srcColor, dstColor);
-            return result * input.fA;
-        } else if (src) {
-            SkPMColor4f srcColor = ConstantOutputForConstantInput(*src, SK_PMColor4fWHITE);
-            return SkBlendMode_Apply(fMode, srcColor, input);
-        } else if (dst) {
-            SkPMColor4f dstColor = ConstantOutputForConstantInput(*dst, SK_PMColor4fWHITE);
-            return SkBlendMode_Apply(fMode, input, dstColor);
-        } else {
-            return input;
+        switch (fComposeBehavior) {
+            case ComposeBehavior::kComposeOneBehavior: {
+                SkPMColor4f srcColor = src ? ConstantOutputForConstantInput(*src, SK_PMColor4fWHITE)
+                                           : input;
+                SkPMColor4f dstColor = dst ? ConstantOutputForConstantInput(*dst, SK_PMColor4fWHITE)
+                                           : input;
+                return SkBlendMode_Apply(fMode, srcColor, dstColor);
+            }
+
+            case ComposeBehavior::kComposeTwoBehavior: {
+                SkPMColor4f opaqueInput = { input.fR, input.fG, input.fB, 1 };
+                SkPMColor4f srcColor = ConstantOutputForConstantInput(*src, opaqueInput);
+                SkPMColor4f dstColor = ConstantOutputForConstantInput(*dst, opaqueInput);
+                SkPMColor4f result = SkBlendMode_Apply(fMode, srcColor, dstColor);
+                return result * input.fA;
+            }
+
+            case ComposeBehavior::kSkModeBehavior: {
+                SkPMColor4f srcColor = src ? ConstantOutputForConstantInput(*src, SK_PMColor4fWHITE)
+                                           : input;
+                SkPMColor4f dstColor = dst ? ConstantOutputForConstantInput(*dst, input)
+                                           : input;
+                return SkBlendMode_Apply(fMode, srcColor, dstColor);
+            }
+
+            default:
+                SK_ABORT("unrecognized compose behavior");
+                return input;
         }
     }
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
 
     SkBlendMode fMode;
+    ComposeBehavior fComposeBehavior;
     int fSrcFPIndex = -1;
     int fDstFPIndex = -1;
 
@@ -225,11 +261,14 @@
     std::unique_ptr<GrFragmentProcessor> fpB(GrProcessorUnitTest::MakeChildFP(d));
 
     SkBlendMode mode;
+    ComposeBehavior behavior;
     do {
         mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
+        behavior = static_cast<ComposeBehavior>(
+                       d->fRandom->nextRangeU(0, (int)ComposeBehavior::kLastComposeBehavior));
     } while (SkBlendMode::kClear == mode || SkBlendMode::kSrc == mode || SkBlendMode::kDst == mode);
     return std::unique_ptr<GrFragmentProcessor>(
-            new ComposeFragmentProcessor(std::move(fpA), std::move(fpB), mode));
+            new ComposeFragmentProcessor(std::move(fpA), std::move(fpB), mode, behavior));
 }
 #endif
 
@@ -248,24 +287,42 @@
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     const ComposeFragmentProcessor& cs = args.fFp.cast<ComposeFragmentProcessor>();
     SkBlendMode mode = cs.getMode();
+    ComposeBehavior behavior = cs.composeBehavior();
     int srcFPIndex = cs.srcFPIndex();
     int dstFPIndex = cs.dstFPIndex();
 
     // Load the input color and make an opaque copy if needed.
-    fragBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkBlendMode_Name(mode));
+    fragBuilder->codeAppendf("// %s Xfer Mode: %s\n",
+                             ComposeBehavior_Name(behavior), SkBlendMode_Name(mode));
 
     SkString srcColor, dstColor;
-    if (srcFPIndex >= 0 && dstFPIndex >= 0) {
-        // Compose-two operations historically have forced the input color to opaque.
-        fragBuilder->codeAppendf("half4 inputOpaque = %s.rgb1;\n", args.fInputColor);
-        srcColor = this->invokeChild(srcFPIndex, "inputOpaque", args);
-        dstColor = this->invokeChild(dstFPIndex, "inputOpaque", args);
-    } else {
-        // Compose-one operations historically leave the alpha on the input color.
-        srcColor = (srcFPIndex >= 0) ? this->invokeChild(srcFPIndex, args)
-                                     : SkString(args.fInputColor);
-        dstColor = (dstFPIndex >= 0) ? this->invokeChild(dstFPIndex, args)
-                                     : SkString(args.fInputColor);
+    switch (behavior) {
+        case ComposeBehavior::kComposeOneBehavior:
+            // Compose-one operations historically leave the alpha on the input color.
+            srcColor = (srcFPIndex >= 0) ? this->invokeChild(srcFPIndex, args)
+                                         : SkString(args.fInputColor);
+            dstColor = (dstFPIndex >= 0) ? this->invokeChild(dstFPIndex, args)
+                                         : SkString(args.fInputColor);
+            break;
+
+        case ComposeBehavior::kComposeTwoBehavior:
+            // Compose-two operations historically have forced the input color to opaque.
+            fragBuilder->codeAppendf("half4 inputOpaque = %s.rgb1;\n", args.fInputColor);
+            srcColor = this->invokeChild(srcFPIndex, "inputOpaque", args);
+            dstColor = this->invokeChild(dstFPIndex, "inputOpaque", args);
+            break;
+
+        case ComposeBehavior::kSkModeBehavior:
+            // SkModeColorFilter operations act like ComposeOne, but pass the input color to dst.
+            srcColor = (srcFPIndex >= 0) ? this->invokeChild(srcFPIndex, args)
+                                         : SkString(args.fInputColor);
+            dstColor = (dstFPIndex >= 0) ? this->invokeChild(dstFPIndex, args.fInputColor, args)
+                                         : SkString(args.fInputColor);
+            break;
+
+        default:
+            SK_ABORT("unrecognized compose behavior");
+            break;
     }
 
     // Blend src and dst colors together.
@@ -273,7 +330,7 @@
                             args.fOutputColor, mode);
 
     // Reapply alpha from input color if we are doing a compose-two.
-    if (srcFPIndex >= 0 && dstFPIndex >= 0) {
+    if (behavior == ComposeBehavior::kComposeTwoBehavior) {
         fragBuilder->codeAppendf("%s *= %s.a;\n", args.fOutputColor, args.fInputColor);
     }
 }
@@ -283,7 +340,7 @@
 std::unique_ptr<GrFragmentProcessor> GrXfermodeFragmentProcessor::Make(
         std::unique_ptr<GrFragmentProcessor> src,
         std::unique_ptr<GrFragmentProcessor> dst,
-        SkBlendMode mode) {
+        SkBlendMode mode, ComposeBehavior behavior) {
     switch (mode) {
         case SkBlendMode::kClear:
             return GrConstColorProcessor::Make(/*inputFP=*/nullptr, SK_PMColor4fTRANSPARENT,
@@ -295,6 +352,6 @@
             return GrFragmentProcessor::OverrideInput(std::move(dst), SK_PMColor4fWHITE,
                                                       /*useUniform=*/false);
         default:
-            return ComposeFragmentProcessor::Make(std::move(src), std::move(dst), mode);
+            return ComposeFragmentProcessor::Make(std::move(src), std::move(dst), mode, behavior);
     }
 }
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.h b/src/gpu/effects/GrXfermodeFragmentProcessor.h
index 7493fb1..5801163 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.h
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.h
@@ -15,12 +15,29 @@
 
 namespace GrXfermodeFragmentProcessor {
 
+enum class ComposeBehavior {
+    // Picks "ComposeOne" or "ComposeTwo" automatically depending on presence of src/dst FPs.
+    kDefault = 0,
+
+    // half(1) is passed as the input color to child FPs. No alpha channel trickery.
+    kComposeOneBehavior,
+
+    // sk_InColor.rgb1 is passed as the input color to child FPs. Alpha is manually blended.
+    kComposeTwoBehavior,
+
+    // half(1) is passed to src; sk_InColor.rgba is passed to dst. No alpha channel trickery.
+    kSkModeBehavior,
+
+    kLastComposeBehavior = kSkModeBehavior,
+};
+
 /** Blends src and dst inputs according to the blend mode.
  *  If either input is null, the input color (sk_InColor) is used instead.
  */
 std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
                                           std::unique_ptr<GrFragmentProcessor> dst,
-                                          SkBlendMode mode);
+                                          SkBlendMode mode,
+                                          ComposeBehavior behavior = ComposeBehavior::kDefault);
 
 };
 
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
index 7a0858c..9b3b7b7 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
@@ -17,6 +17,7 @@
 }
 
 void GrGLSLFragmentProcessor::emitChildFunction(int childIndex, EmitArgs& args) {
+    SkASSERT(childIndex >= 0);
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     while (childIndex >= (int) fFunctionNames.size()) {
         fFunctionNames.emplace_back();
@@ -43,6 +44,7 @@
 
 SkString GrGLSLFragmentProcessor::invokeChild(int childIndex, const char* inputColor,
                                               EmitArgs& args, SkSL::String skslCoords) {
+    SkASSERT(childIndex >= 0);
     this->emitChildFunction(childIndex, args);
 
     if (skslCoords.empty()) {
@@ -72,6 +74,7 @@
 SkString GrGLSLFragmentProcessor::invokeChildWithMatrix(int childIndex, const char* inputColor,
                                                         EmitArgs& args,
                                                         SkSL::String skslMatrix) {
+    SkASSERT(childIndex >= 0);
     this->emitChildFunction(childIndex, args);
 
     const GrFragmentProcessor& childProc = args.fFp.childProcessor(childIndex);