Alter SkXfermode's asFragmentProcessor & asXPFactory contracts

TBR=bsalomon@google.com

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

Review URL: https://codereview.chromium.org/1674673002
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index 5f88240..fbceedb 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -15,6 +15,7 @@
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
 #include "GrDrawContext.h"
+#include "effects/GrConstColorProcessor.h"
 #include "effects/GrTextureDomain.h"
 #include "effects/GrSimpleTextureEffect.h"
 #include "SkGr.h"
@@ -25,12 +26,8 @@
 SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
                                              SkImageFilter* inputs[2],
                                              const CropRect* cropRect)
-  : INHERITED(2, inputs, cropRect), fMode(mode) {
-    SkSafeRef(fMode);
-}
-
-SkXfermodeImageFilter::~SkXfermodeImageFilter() {
-    SkSafeUnref(fMode);
+    : INHERITED(2, inputs, cropRect)
+    , fMode(SkSafeRef(mode)) {
 }
 
 SkFlattenable* SkXfermodeImageFilter::CreateProc(SkReadBuffer& buffer) {
@@ -45,10 +42,10 @@
 }
 
 bool SkXfermodeImageFilter::onFilterImage(Proxy* proxy,
-                                            const SkBitmap& src,
-                                            const Context& ctx,
-                                            SkBitmap* dst,
-                                            SkIPoint* offset) const {
+                                          const SkBitmap& src,
+                                          const Context& ctx,
+                                          SkBitmap* dst,
+                                          SkIPoint* offset) const {
     SkBitmap background = src, foreground = src;
     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
     if (!this->filterInput(0, proxy, src, ctx, &background, &backgroundOffset)) {
@@ -120,41 +117,47 @@
 #if SK_SUPPORT_GPU
 
 bool SkXfermodeImageFilter::canFilterImageGPU() const {
-    return fMode && fMode->asFragmentProcessor(nullptr, nullptr) && !cropRectIsSet();
+    return !this->cropRectIsSet();
 }
 
+#include "SkXfermode_proccoeff.h"
+
 bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
                                            const SkBitmap& src,
                                            const Context& ctx,
                                            SkBitmap* result,
                                            SkIPoint* offset) const {
+    GrContext* context = nullptr;
     SkBitmap background = src;
     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
     if (!this->filterInputGPU(0, proxy, src, ctx, &background, &backgroundOffset)) {
-        return false;
+        background.reset();
     }
-
     GrTexture* backgroundTex = background.getTexture();
-    if (nullptr == backgroundTex) {
-        SkASSERT(false);
-        return false;
+    if (backgroundTex) {
+        context = backgroundTex->getContext();
     }
 
     SkBitmap foreground = src;
     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
     if (!this->filterInputGPU(1, proxy, src, ctx, &foreground, &foregroundOffset)) {
-        return false;
+        foreground.reset();
     }
     GrTexture* foregroundTex = foreground.getTexture();
-    GrContext* context = foregroundTex->getContext();
+    if (foregroundTex) {
+        context = foregroundTex->getContext();
+    }
+
+    if (!context) {
+        return false;
+    }
+
     SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y());
     bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y()));
     if (bounds.isEmpty()) {
         return false;
     }
 
-    const GrFragmentProcessor* xferFP = nullptr;
-
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fWidth = bounds.width();
@@ -166,39 +169,62 @@
     }
 
     GrPaint paint;
-    SkMatrix backgroundMatrix;
-    backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
-    backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX),
-                                  SkIntToScalar(-backgroundOffset.fY));
-    SkAutoTUnref<const GrFragmentProcessor> bgFP(GrTextureDomainEffect::Create(
-        backgroundTex, backgroundMatrix,
-        GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()),
-        GrTextureDomain::kDecal_Mode,
-        GrTextureParams::kNone_FilterMode)
-    );
-    if (!fMode || !fMode->asFragmentProcessor(&xferFP, bgFP)) {
-        // canFilterImageGPU() should've taken care of this
-        SkASSERT(false);
-        return false;
+    SkAutoTUnref<const GrFragmentProcessor> bgFP;
+
+    if (backgroundTex) {
+        SkMatrix backgroundMatrix;
+        backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
+        backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX),
+                                      SkIntToScalar(-backgroundOffset.fY));
+        bgFP.reset(GrTextureDomainEffect::Create(
+                            backgroundTex, backgroundMatrix,
+                            GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()),
+                            GrTextureDomain::kDecal_Mode,
+                            GrTextureParams::kNone_FilterMode));
+    } else {
+        bgFP.reset(GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK,
+                                                 GrConstColorProcessor::kIgnore_InputMode));
     }
 
-    SkMatrix foregroundMatrix;
-    foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height());
-    foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX),
-                                  SkIntToScalar(-foregroundOffset.fY));
+    if (foregroundTex) {
+        SkMatrix foregroundMatrix;
+        foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height());
+        foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX),
+                                      SkIntToScalar(-foregroundOffset.fY));
 
+        SkAutoTUnref<const GrFragmentProcessor> foregroundFP;
 
-    SkAutoTUnref<const GrFragmentProcessor> foregroundFP(GrTextureDomainEffect::Create(
-        foregroundTex, foregroundMatrix,
-        GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
-        GrTextureDomain::kDecal_Mode,
-        GrTextureParams::kNone_FilterMode)
-    );
+        foregroundFP.reset(GrTextureDomainEffect::Create(
+                            foregroundTex, foregroundMatrix,
+                            GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
+                            GrTextureDomain::kDecal_Mode,
+                            GrTextureParams::kNone_FilterMode));
 
-    paint.addColorFragmentProcessor(foregroundFP.get());
-    if (xferFP) {
-        paint.addColorFragmentProcessor(xferFP)->unref();
+        paint.addColorFragmentProcessor(foregroundFP.get());
+
+        // A null fMode is interpreted to mean kSrcOver_Mode (to match raster).
+        SkAutoTUnref<SkXfermode> mode(SkSafeRef(fMode.get()));
+        if (!mode) {
+            // It would be awesome to use SkXfermode::Create here but it knows better
+            // than us and won't return a kSrcOver_Mode SkXfermode. That means we
+            // have to get one the hard way.
+            struct ProcCoeff rec;
+            rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode);
+            SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC);
+
+            mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode));
+        }
+
+        SkAutoTUnref<const GrFragmentProcessor> xferFP(mode->getFragmentProcessorForImageFilter(bgFP));
+
+        // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed
+        if (xferFP) {
+            paint.addColorFragmentProcessor(xferFP);
+        }
+    } else {
+        paint.addColorFragmentProcessor(bgFP);
     }
+
     paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
     SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget()));