Generalize composing imagefilters and shaders to blenders

The preexisting enum versions now are just shallow factories for
the new blender versions, though internally we've kept the
specializations on impl.

Change-Id: I3449682bb443a4ff9f53cc7b0860343c56e377e4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/424436
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/arithmode.cpp b/gm/arithmode.cpp
index 8f68c74..3e8a8a8 100644
--- a/gm/arithmode.cpp
+++ b/gm/arithmode.cpp
@@ -195,7 +195,7 @@
         fSrc = make_src(W, H);
         fDst = make_dst(W, H);
 
-        fChecker = ToolUtils::create_checkerboard_image(W, H, 0xFF999999, 0xFFCCCCCC, 8);
+        fChecker = ToolUtils::create_checkerboard_image(W, H, 0xFFBBBBBB, 0xFFEEEEEE, 8);
     }
 
     bool onAnimate(double nanos) override {
@@ -213,16 +213,30 @@
         canvas->drawImage(fSrc, 10, 10);
         canvas->drawImage(fDst, 10, 10 + fSrc->height() + 10);
 
+        auto sampling = SkSamplingOptions();
+        auto blender = SkBlenders::Arithmetic(fK1, fK2, fK3, fK4, true);
+
+        SkPaint paint;
+
         canvas->translate(10 + fSrc->width() + 10, 10);
         canvas->drawImage(fChecker, 0, 0);
 
-        SkPaint paint;
-        paint.setBlender(SkBlenders::Arithmetic(fK1, fK2, fK3, fK4, true));
-
+        // Draw via blend step
         canvas->saveLayer(&rect, nullptr);
         canvas->drawImage(fDst, 0, 0);
-        canvas->drawImage(fSrc, 0, 0, SkSamplingOptions(), &paint);
+        paint.setBlender(blender);
+        canvas->drawImage(fSrc, 0, 0, sampling, &paint);
         canvas->restore();
+
+        canvas->translate(0, 10 + fSrc->height());
+        canvas->drawImage(fChecker, 0, 0);
+
+        // Draw via imagefilter (should appear the same as above)
+        paint.setBlender(nullptr);
+        paint.setImageFilter(SkImageFilters::Blend(blender,
+                                                   /* dst imagefilter */nullptr,
+                                                   SkImageFilters::Image(fSrc, sampling)));
+        canvas->drawImage(fDst, 0, 0, sampling, &paint);
     }
 
 private:
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index eccb545..fba17f8 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -17,6 +17,7 @@
 
 class SkArenaAlloc;
 class SkBitmap;
+class SkBlender;
 class SkColorFilter;
 class SkColorSpace;
 class SkImage;
@@ -138,6 +139,7 @@
     static sk_sp<SkShader> Color(SkColor);
     static sk_sp<SkShader> Color(const SkColor4f&, sk_sp<SkColorSpace>);
     static sk_sp<SkShader> Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src);
+    static sk_sp<SkShader> Blend(sk_sp<SkBlender>, sk_sp<SkShader> dst, sk_sp<SkShader> src);
 
 private:
     SkShaders() = delete;
diff --git a/include/effects/SkImageFilters.h b/include/effects/SkImageFilters.h
index 9e30b82..bc79cf9 100644
--- a/include/effects/SkImageFilters.h
+++ b/include/effects/SkImageFilters.h
@@ -18,6 +18,7 @@
 
 #include <cstddef>
 
+class SkBlender;
 class SkColorFilter;
 class SkPaint;
 class SkRegion;
@@ -97,6 +98,17 @@
                                       const CropRect& cropRect = {});
 
     /**
+     *  This filter takes an SkBlendMode and uses it to composite the two filters together.
+     *  @param blender       The blender that defines the compositing operation
+     *  @param background The Dst pixels used in blending, if null the source bitmap is used.
+     *  @param foreground The Src pixels used in blending, if null the source bitmap is used.
+     *  @cropRect         Optional rectangle to crop input and output.
+     */
+    static sk_sp<SkImageFilter> Blend(sk_sp<SkBlender> blender, sk_sp<SkImageFilter> background,
+                                      sk_sp<SkImageFilter> foreground = nullptr,
+                                      const CropRect& cropRect = {});
+
+    /**
      *  Create a filter that blurs its input by the separate X and Y sigmas. The provided tile mode
      *  is used when the blur kernel goes outside the input image.
      *  @param sigmaX   The Gaussian sigma value for blurring along the X axis.
diff --git a/src/core/SkBlendModePriv.h b/src/core/SkBlendModePriv.h
index b19e565..f24c8ca 100644
--- a/src/core/SkBlendModePriv.h
+++ b/src/core/SkBlendModePriv.h
@@ -14,6 +14,13 @@
 
 class SkRasterPipeline;
 
+/**
+ *  Sentinel value for SkBlendMode enum.
+ *
+ *  Will never be a valid enum value, but will be storable in a byte.
+ */
+constexpr uint8_t kCustom_SkBlendMode = 0xFF;
+
 bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode);
 
 static inline bool SkBlendMode_CaresAboutRBOrder(SkBlendMode mode) {
diff --git a/src/core/SkPicturePriv.h b/src/core/SkPicturePriv.h
index 871943f..657f2fe 100644
--- a/src/core/SkPicturePriv.h
+++ b/src/core/SkPicturePriv.h
@@ -97,6 +97,7 @@
     // V85: Remove legacy support for inheriting sampling from the paint.
     // V86: Remove support for custom data inside SkVertices
     // V87: SkPaint now holds a user-defined blend function (SkBlender), no longer has DrawLooper
+    // V88: Add blender to ComposeShader and BlendImageFilter
 
     enum Version {
         kPictureShaderFilterParam_Version   = 82,
@@ -105,10 +106,11 @@
         kNoFilterQualityShaders_Version     = 85,
         kVerticesRemoveCustomData_Version   = 86,
         kSkBlenderInSkPaint                 = 87,
+        kBlenderInEffects                   = 88,
 
         // Only SKPs within the min/current picture version range (inclusive) can be read.
         kMin_Version     = kPictureShaderFilterParam_Version,
-        kCurrent_Version = kSkBlenderInSkPaint
+        kCurrent_Version = kBlenderInEffects
     };
 };
 
diff --git a/src/effects/imagefilters/SkBlendImageFilter.cpp b/src/effects/imagefilters/SkBlendImageFilter.cpp
index a5a40f3..321387f 100644
--- a/src/effects/imagefilters/SkBlendImageFilter.cpp
+++ b/src/effects/imagefilters/SkBlendImageFilter.cpp
@@ -8,7 +8,10 @@
 #include "include/core/SkCanvas.h"
 #include "include/effects/SkImageFilters.h"
 #include "include/private/SkColorData.h"
+#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkBlenderBase.h"
 #include "src/core/SkImageFilter_Base.h"
+#include "src/core/SkMatrixProvider.h"
 #include "src/core/SkReadBuffer.h"
 #include "src/core/SkSpecialImage.h"
 #include "src/core/SkSpecialSurface.h"
@@ -30,10 +33,13 @@
 
 class SkBlendImageFilter : public SkImageFilter_Base {
 public:
-    SkBlendImageFilter(SkBlendMode mode, sk_sp<SkImageFilter> inputs[2],
+    SkBlendImageFilter(sk_sp<SkBlender> blender, sk_sp<SkImageFilter> inputs[2],
                        const SkRect* cropRect)
           : INHERITED(inputs, 2, cropRect)
-          , fMode(mode) {}
+          , fBlender(std::move(blender))
+    {
+        SkASSERT(fBlender);
+    }
 
 protected:
     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
@@ -58,7 +64,7 @@
     friend void ::SkRegisterBlendImageFilterFlattenable();
     SK_FLATTENABLE_HOOKS(SkBlendImageFilter)
 
-    SkBlendMode fMode;
+    sk_sp<SkBlender> fBlender;
 
     using INHERITED = SkImageFilter_Base;
 };
@@ -70,7 +76,18 @@
                                            sk_sp<SkImageFilter> foreground,
                                            const CropRect& cropRect) {
     sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
-    return sk_sp<SkImageFilter>(new SkBlendImageFilter(mode, inputs, cropRect));
+    return sk_sp<SkImageFilter>(new SkBlendImageFilter(SkBlender::Mode(mode), inputs, cropRect));
+}
+
+sk_sp<SkImageFilter> SkImageFilters::Blend(sk_sp<SkBlender> blender,
+                                           sk_sp<SkImageFilter> background,
+                                           sk_sp<SkImageFilter> foreground,
+                                           const CropRect& cropRect) {
+    if (!blender) {
+        blender = SkBlender::Mode(SkBlendMode::kSrcOver);
+    }
+    sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
+    return sk_sp<SkImageFilter>(new SkBlendImageFilter(blender, inputs, cropRect));
 }
 
 void SkRegisterBlendImageFilterFlattenable() {
@@ -80,25 +97,32 @@
     SkFlattenable::Register("SkXfermodeImageFilterImpl", SkBlendImageFilter::CreateProc);
 }
 
-static unsigned unflatten_blendmode(SkReadBuffer& buffer) {
-    unsigned mode = buffer.read32();
-    (void)buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode);
-    return mode;
-}
-
 sk_sp<SkFlattenable> SkBlendImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
-    unsigned mode = unflatten_blendmode(buffer);
-    if (!buffer.isValid()) {
-        return nullptr;
+
+    sk_sp<SkBlender> blender;
+    const uint32_t mode = buffer.read32();
+    if (mode == kCustom_SkBlendMode) {
+        blender = buffer.readBlender();
+    } else {
+        if (mode > (unsigned)SkBlendMode::kLastMode) {
+            buffer.validate(false);
+            return nullptr;
+        }
+        blender = SkBlender::Mode((SkBlendMode)mode);
     }
-    return SkImageFilters::Blend((SkBlendMode)mode, common.getInput(0), common.getInput(1),
+    return SkImageFilters::Blend(std::move(blender), common.getInput(0), common.getInput(1),
                                  common.cropRect());
 }
 
 void SkBlendImageFilter::flatten(SkWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    buffer.write32((unsigned)fMode);
+    if (auto bm = as_BB(fBlender)->asBlendMode()) {
+        buffer.write32((unsigned)bm.value());
+    } else {
+        buffer.write32(kCustom_SkBlendMode);
+        buffer.writeFlattenable(fBlender.get());
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -183,39 +207,39 @@
     auto getForeground = [&]() {
         return this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, inputRect) : src;
     };
-    switch (fMode) {
-        case SkBlendMode::kClear:
-            return SkIRect::MakeEmpty();
-
-        case SkBlendMode::kSrc:
-        case SkBlendMode::kDstATop:
-            return getForeground();
-
-        case SkBlendMode::kDst:
-        case SkBlendMode::kSrcATop:
-            return getBackground();
-
-        case SkBlendMode::kSrcIn:
-        case SkBlendMode::kDstIn: {
-            auto result = getBackground();
-            if (!result.intersect(getForeground())) {
+    if (auto bm = as_BB(fBlender)->asBlendMode()) {
+        switch (bm.value()) {
+            case SkBlendMode::kClear:
                 return SkIRect::MakeEmpty();
-            }
-            return result;
-        }
 
-        default: {
-            auto result = getBackground();
-            result.join(getForeground());
-            return result;
+            case SkBlendMode::kSrc:
+            case SkBlendMode::kDstATop:
+                return getForeground();
+
+            case SkBlendMode::kDst:
+            case SkBlendMode::kSrcATop:
+                return getBackground();
+
+            case SkBlendMode::kSrcIn:
+            case SkBlendMode::kDstIn: {
+                auto result = getBackground();
+                if (!result.intersect(getForeground())) {
+                    return SkIRect::MakeEmpty();
+                }
+                return result;
+            }
+            default: break;
         }
     }
+    auto result = getBackground();
+    result.join(getForeground());
+    return result;
 }
 
 void SkBlendImageFilter::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
                                         const SkIRect& fgBounds) const {
     SkPaint paint;
-    paint.setBlendMode(fMode);
+    paint.setBlender(fBlender);
     if (img) {
         img->draw(canvas, SkIntToScalar(fgBounds.fLeft), SkIntToScalar(fgBounds.fTop),
                   SkSamplingOptions(), &paint);
@@ -270,6 +294,8 @@
         fp = GrFragmentProcessor::MakeColor(SK_PMColor4fTRANSPARENT);
     }
 
+    GrImageInfo info(ctx.grColorType(), kPremul_SkAlphaType, ctx.refColorSpace(), bounds.size());
+
     if (foregroundView.asTextureProxy()) {
         SkRect fgSubset = SkRect::Make(foreground->subset());
         SkMatrix fgMatrix = SkMatrix::Translate(
@@ -280,10 +306,12 @@
         fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP), foreground->getColorSpace(),
                                              foreground->alphaType(), ctx.colorSpace(),
                                              kPremul_SkAlphaType);
-        fp = GrBlendFragmentProcessor::Make(std::move(fgFP), std::move(fp), fMode);
+
+        GrFPArgs args(rContext, SkSimpleMatrixProvider(SkMatrix::I()), &info.colorInfo());
+
+        fp = as_BB(fBlender)->asFragmentProcessor(std::move(fgFP), std::move(fp), args);
     }
 
-    GrImageInfo info(ctx.grColorType(), kPremul_SkAlphaType, ctx.refColorSpace(), bounds.size());
     auto sfc = GrSurfaceFillContext::Make(rContext, info, SkBackingFit::kApprox);
     if (!sfc) {
         return nullptr;
diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp
index f9944d9..e4dce34 100644
--- a/src/shaders/SkComposeShader.cpp
+++ b/src/shaders/SkComposeShader.cpp
@@ -10,6 +10,7 @@
 #include "include/private/SkColorData.h"
 #include "src/core/SkArenaAlloc.h"
 #include "src/core/SkBlendModePriv.h"
+#include "src/core/SkBlenderBase.h"
 #include "src/core/SkRasterPipeline.h"
 #include "src/core/SkReadBuffer.h"
 #include "src/core/SkRuntimeEffectPriv.h"
@@ -54,8 +55,55 @@
     return sk_sp<SkShader>(new SkShader_Blend(mode, std::move(dst), std::move(src)));
 }
 
+sk_sp<SkShader> SkShaders::Blend(sk_sp<SkBlender> blender, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
+    if (!src || !dst) {
+        return nullptr;
+    }
+    if (!blender) {
+        return SkShaders::Blend(SkBlendMode::kSrcOver, std::move(dst), std::move(src));
+    }
+    if (auto bm = as_BB(blender)->asBlendMode()) {
+        return SkShaders::Blend(bm.value(), std::move(dst), std::move(src));
+    }
+    return sk_sp<SkShader>(new SkShader_Blend(std::move(blender), std::move(dst), std::move(src)));
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
+sk_sp<SkFlattenable> SkShader_Blend::CreateProc(SkReadBuffer& buffer) {
+    sk_sp<SkShader> dst(buffer.readShader());
+    sk_sp<SkShader> src(buffer.readShader());
+    if (!buffer.validate(dst && src)) {
+        return nullptr;
+    }
+
+    sk_sp<SkBlender> blender(nullptr);
+    unsigned        mode = buffer.read32();
+
+    if (mode == kCustom_SkBlendMode) {
+        blender = buffer.readBlender();
+        if (buffer.validate(blender != nullptr)) {
+            return SkShaders::Blend(std::move(blender), std::move(dst), std::move(src));
+        }
+    } else {
+        if (buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
+            return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
+        }
+    }
+    return nullptr;
+}
+
+void SkShader_Blend::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeFlattenable(fDst.get());
+    buffer.writeFlattenable(fSrc.get());
+    if (fBlender) {
+        buffer.write32(kCustom_SkBlendMode);
+        buffer.writeFlattenable(fBlender.get());
+    } else {
+        buffer.write32((int)fMode);
+    }
+}
+
 // Returns the output of e0, and leaves the output of e1 in r,g,b,a
 static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader* s1) {
     struct Storage {
@@ -74,27 +122,11 @@
     return storage->fRes0;
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkFlattenable> SkShader_Blend::CreateProc(SkReadBuffer& buffer) {
-    sk_sp<SkShader> dst(buffer.readShader());
-    sk_sp<SkShader> src(buffer.readShader());
-    unsigned        mode = buffer.read32();
-
-    // check for valid mode and children before we cast to the enum type and make the shader.
-    if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode && dst && src)) {
-        return nullptr;
-    }
-    return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
-}
-
-void SkShader_Blend::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeFlattenable(fDst.get());
-    buffer.writeFlattenable(fSrc.get());
-    buffer.write32((int)fMode);
-}
-
 bool SkShader_Blend::onAppendStages(const SkStageRec& orig_rec) const {
+    if (fBlender) {
+        return false;
+    }
+
     const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix());
 
     float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get());
@@ -110,13 +142,17 @@
 skvm::Color SkShader_Blend::onProgram(skvm::Builder* p,
                                       skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                       const SkMatrixProvider& mats, const SkMatrix* localM,
-                                      const SkColorInfo& dst,
+                                      const SkColorInfo& cinfo,
                                       skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
     skvm::Color d,s;
-    if ((d = as_SB(fDst)->program(p, device,local, paint, mats,localM, dst, uniforms,alloc)) &&
-        (s = as_SB(fSrc)->program(p, device,local, paint, mats,localM, dst, uniforms,alloc)))
+    if ((d = as_SB(fDst)->program(p, device,local, paint, mats,localM, cinfo, uniforms,alloc)) &&
+        (s = as_SB(fSrc)->program(p, device,local, paint, mats,localM, cinfo, uniforms,alloc)))
     {
-        return p->blend(fMode, s,d);
+        if (fBlender) {
+            return as_BB(fBlender)->program(p, s,d, cinfo, uniforms,alloc);
+        } else {
+            return p->blend(fMode, s,d);
+        }
     }
     return {};
 }
@@ -137,7 +173,11 @@
         // This is unexpected. Both src and dst shaders should be valid. Just fail.
         return nullptr;
     }
-    auto blend = GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), fMode);
-    return GrFragmentProcessor::MakeInputOpaqueAndPostApplyAlpha(std::move(blend));
+    if (fBlender) {
+        return as_BB(fBlender)->asFragmentProcessor(std::move(fpB), std::move(fpA), orig_args);
+    } else {
+        auto blend = GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), fMode);
+        return GrFragmentProcessor::MakeInputOpaqueAndPostApplyAlpha(std::move(blend));
+    }
 }
 #endif
diff --git a/src/shaders/SkComposeShader.h b/src/shaders/SkComposeShader.h
index ee776ba..8770844 100644
--- a/src/shaders/SkComposeShader.h
+++ b/src/shaders/SkComposeShader.h
@@ -9,6 +9,8 @@
 #define SkComposeShader_DEFINED
 
 #include "include/core/SkBlendMode.h"
+#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkBlenderBase.h"
 #include "src/shaders/SkShaderBase.h"
 
 class SkShader_Blend final : public SkShaderBase {
@@ -16,9 +18,17 @@
     SkShader_Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src)
         : fDst(std::move(dst))
         , fSrc(std::move(src))
+        , fBlender(nullptr)
         , fMode(mode)
     {}
 
+    SkShader_Blend(sk_sp<SkBlender> blender, sk_sp<SkShader> dst, sk_sp<SkShader> src)
+        : fDst(std::move(dst))
+        , fSrc(std::move(src))
+        , fBlender(std::move(blender))
+        , fMode((SkBlendMode)kCustom_SkBlendMode)
+    {}
+
 #if SK_SUPPORT_GPU
     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
 #endif
@@ -36,7 +46,8 @@
 
     sk_sp<SkShader>     fDst;
     sk_sp<SkShader>     fSrc;
-    const SkBlendMode   fMode;
+    sk_sp<SkBlender>    fBlender;   // if null, use fMode
+    const SkBlendMode   fMode;      // only use if fBlender is null
 
     using INHERITED = SkShaderBase;
 };