Implement SkBlender support in SkVM.

A lot of the SkVM-related setup was already hooked up in
http://review.skia.org/416916. This CL finishes the job by hooking up
the SkPaint's SkBlender field to SkVM, and adds various checks
throughout Skia's software drawing code that can detect the presence of
an SkBlender.

Since only SkVM knows how to render an SkBlender correctly, we now need
to avoid the legacy blitter and RasterPipeline blitter whenever an
SkBlender is present on the paint. This CL fixes the cases that I've
found so far, while testing rendering with ovals and images. I'm sure
there are more cases lurking; these will be uncovered and dealt with in
future CLs.

Change-Id: I1a7b3d6625352b3cba8e4f8d4c61129d08ac6ae7
Bug: skia:12080
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419576
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 63e8541..677485f 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -467,6 +467,10 @@
         }
     }
 
+    if (paint.getBlender()) {
+        goto USE_SHADER;
+    }
+
     if (src && !src->contains(bitmapBounds) &&
         SkCanvas::kFast_SrcRectConstraint == constraint &&
         sampling != SkSamplingOptions()) {
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index 73e2d26..b997d3c 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -662,6 +662,7 @@
 
     // The legacy blitters cannot handle any of these complex features (anymore).
     if (device.alphaType() == kUnpremul_SkAlphaType        ||
+        paint.getBlender()                                 ||
         paint.getBlendMode() > SkBlendMode::kLastCoeffMode ||
         (mf && mf->getFormat() == SkMask::k3D_Format)) {
         return false;
@@ -703,26 +704,28 @@
     // We may tweak the original paint as we go.
     SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
 
-    // We have the most fast-paths for SrcOver, so see if we can act like SrcOver.
-    if (paint->getBlendMode() != SkBlendMode::kSrcOver) {
-        switch (SkInterpretXfermode(*paint, SkColorTypeIsAlwaysOpaque(device.colorType()))) {
-            case kSrcOver_SkXfermodeInterpretation:
-                paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
-                break;
-            case kSkipDrawing_SkXfermodeInterpretation:
-                return alloc->make<SkNullBlitter>();
-            default:
-                break;
+    if (!paint->getBlender()) {
+        // We have the most fast-paths for SrcOver, so see if we can act like SrcOver.
+        if (paint->getBlendMode() != SkBlendMode::kSrcOver) {
+            switch (SkInterpretXfermode(*paint, SkColorTypeIsAlwaysOpaque(device.colorType()))) {
+                case kSrcOver_SkXfermodeInterpretation:
+                    paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
+                    break;
+                case kSkipDrawing_SkXfermodeInterpretation:
+                    return alloc->make<SkNullBlitter>();
+                default:
+                    break;
+            }
         }
-    }
 
-    // A Clear blend mode will ignore the entire color pipeline, as if Src mode with 0x00000000.
-    if (paint->getBlendMode() == SkBlendMode::kClear) {
-        SkPaint* p = paint.writable();
-        p->setShader(nullptr);
-        p->setColorFilter(nullptr);
-        p->setBlendMode(SkBlendMode::kSrc);
-        p->setColor(0x00000000);
+        // A Clear blend mode will ignore the entire color pipeline, as if Src mode with 0x00000000.
+        if (paint->getBlendMode() == SkBlendMode::kClear) {
+            SkPaint* p = paint.writable();
+            p->setShader(nullptr);
+            p->setColorFilter(nullptr);
+            p->setBlendMode(SkBlendMode::kSrc);
+            p->setColor(0x00000000);
+        }
     }
 
     if (paint->getColorFilter()) {
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 2329b09..53a7ccc 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2131,7 +2131,7 @@
     }
 
     if (realPaint.getImageFilter() &&
-        this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), sampling, realPaint)  &&
+        this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), sampling, realPaint) &&
         !image_to_color_filter(&realPaint)) {
         // Evaluate the image filter directly on the input image and then draw the result, instead
         // of first drawing the image to a temporary layer and filtering.
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 33ead63..f42490c 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -1127,7 +1127,8 @@
     SkDraw draw(*this);
     draw.fMatrixProvider = &matrixProvider;
 
-    if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
+    if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter() &&
+            !paint->getBlender()) {
         draw.drawBitmapAsMask(bitmap, sampling, *paint);
     } else {
         SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index f7fd9a5..83ae3c8 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -90,6 +90,11 @@
                                          const SkMatrixProvider& matrixProvider,
                                          SkArenaAlloc* alloc,
                                          sk_sp<SkShader> clipShader) {
+    if (paint.getBlender()) {
+        // The raster pipeline doesn't support SkBlender.
+        return nullptr;
+    }
+
     SkColorSpace* dstCS = dst.colorSpace();
     SkColorType dstCT = dst.colorType();
     SkColor4f paintColor = paint.getColor4f();
diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp
index d15332b..0b3b9b3 100644
--- a/src/core/SkVMBlitter.cpp
+++ b/src/core/SkVMBlitter.cpp
@@ -357,7 +357,7 @@
         }
 
         // Clamp to fit destination color format if needed.
-        if (src_in_gamut) {
+        if (!params.blender && src_in_gamut) {
             // An in-gamut src blended with an in-gamut dst should stay in gamut.
             // Being in-gamut implies all channels are in [0,1], so no need to clamp.
             // We allow one ulp error above 1.0f, and about that much (~1.2e-7) below 0.
@@ -556,6 +556,9 @@
             shader = sk_make_sp<DitherShader>(std::move(shader));
         }
 
+        // Add the user blend function.
+        sk_sp<SkBlender> blender = paint.refBlender();
+
         // The most common blend mode is SrcOver, and it can be strength-reduced
         // _greatly_ to Src mode when the shader is opaque.
         //
@@ -570,8 +573,8 @@
         // not just a property of the uniforms.  The shader program hash includes
         // this information, making it safe to use anywhere in the blitter codegen.
         SkBlendMode blendMode = paint.getBlendMode();
-        if (blendMode == SkBlendMode::kSrcOver && shader->isOpaque()) {
-            blendMode =  SkBlendMode::kSrc;
+        if ((blendMode == SkBlendMode::kSrcOver && shader->isOpaque()) || blender) {
+            blendMode = SkBlendMode::kSrc;
         }
 
         SkColor4f paintColor = paint.getColor4f();
@@ -582,7 +585,7 @@
         return {
             std::move(shader),
             std::move(clip),
-            /*blender=*/nullptr,
+            std::move(blender),
             { device.colorType(), device.alphaType(), device.refColorSpace() },
             blendMode,
             Coverage::Full,  // Placeholder... withCoverage() will change as needed.
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index d0a6bb8..278bbde 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -604,8 +604,7 @@
 }
 
 DEF_TEST(SkRuntimeEffect_Blender_CPU, r) {
-    // TODO(skia:12080): add CPU support for SkBlender
-//  test_RuntimeEffect_Blenders(r, /*rContext=*/nullptr);
+    test_RuntimeEffect_Blenders(r, /*rContext=*/nullptr);
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffect_Blender_GPU, r, ctxInfo) {