Revert[7] "replace SkXfermode obj with SkBlendMode enum in paints"

This reverts commit Ib4a154cdd5f5d1dcac921ef50d53b79a2d6a1be8.

Reason for revert: new assert from 100K bot

Original change's description:
> Revert[6] "replace SkXfermode obj with SkBlendMode enum in paints"
> 
> - perform version check in CreateProc for XfermodeImageFilter and ArithmeticImageFilter
> This reverts commit 3ed485f4249e17abb4b11f5018d03175fd1afb44.
> 
> BUG=skia:
> 
> GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2992
> 
> Change-Id: Ib4a154cdd5f5d1dcac921ef50d53b79a2d6a1be8
> Reviewed-on: https://skia-review.googlesource.com/2992
> Reviewed-by: Mike Reed <reed@google.com>
> Commit-Queue: Mike Reed <reed@google.com>
> 

TBR=reed@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Change-Id: I848e5a69c5cd67f2c14889f4f0a346652578c4ff
Reviewed-on: https://skia-review.googlesource.com/3023
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/src/c/sk_paint.cpp b/src/c/sk_paint.cpp
index 126170c..f82cd81 100644
--- a/src/c/sk_paint.cpp
+++ b/src/c/sk_paint.cpp
@@ -5,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-#include "SkBlendMode.h"
 #include "SkMaskFilter.h"
 #include "SkPaint.h"
 #include "SkShader.h"
@@ -133,41 +132,41 @@
 
 void sk_paint_set_xfermode_mode(sk_paint_t* paint, sk_xfermode_mode_t mode) {
     SkASSERT(paint);
-    SkBlendMode skmode;
+    SkXfermode::Mode skmode;
     switch (mode) {
         #define MAP(X, Y) case (X): skmode = (Y); break
-        MAP( CLEAR_SK_XFERMODE_MODE,      SkBlendMode::kClear      );
-        MAP( SRC_SK_XFERMODE_MODE,        SkBlendMode::kSrc        );
-        MAP( DST_SK_XFERMODE_MODE,        SkBlendMode::kDst        );
-        MAP( SRCOVER_SK_XFERMODE_MODE,    SkBlendMode::kSrcOver    );
-        MAP( DSTOVER_SK_XFERMODE_MODE,    SkBlendMode::kDstOver    );
-        MAP( SRCIN_SK_XFERMODE_MODE,      SkBlendMode::kSrcIn      );
-        MAP( DSTIN_SK_XFERMODE_MODE,      SkBlendMode::kDstIn      );
-        MAP( SRCOUT_SK_XFERMODE_MODE,     SkBlendMode::kSrcOut     );
-        MAP( DSTOUT_SK_XFERMODE_MODE,     SkBlendMode::kDstOut     );
-        MAP( SRCATOP_SK_XFERMODE_MODE,    SkBlendMode::kSrcATop    );
-        MAP( DSTATOP_SK_XFERMODE_MODE,    SkBlendMode::kDstATop    );
-        MAP( XOR_SK_XFERMODE_MODE,        SkBlendMode::kXor        );
-        MAP( PLUS_SK_XFERMODE_MODE,       SkBlendMode::kPlus       );
-        MAP( MODULATE_SK_XFERMODE_MODE,   SkBlendMode::kModulate   );
-        MAP( SCREEN_SK_XFERMODE_MODE,     SkBlendMode::kScreen     );
-        MAP( OVERLAY_SK_XFERMODE_MODE,    SkBlendMode::kOverlay    );
-        MAP( DARKEN_SK_XFERMODE_MODE,     SkBlendMode::kDarken     );
-        MAP( LIGHTEN_SK_XFERMODE_MODE,    SkBlendMode::kLighten    );
-        MAP( COLORDODGE_SK_XFERMODE_MODE, SkBlendMode::kColorDodge );
-        MAP( COLORBURN_SK_XFERMODE_MODE,  SkBlendMode::kColorBurn  );
-        MAP( HARDLIGHT_SK_XFERMODE_MODE,  SkBlendMode::kHardLight  );
-        MAP( SOFTLIGHT_SK_XFERMODE_MODE,  SkBlendMode::kSoftLight  );
-        MAP( DIFFERENCE_SK_XFERMODE_MODE, SkBlendMode::kDifference );
-        MAP( EXCLUSION_SK_XFERMODE_MODE,  SkBlendMode::kExclusion  );
-        MAP( MULTIPLY_SK_XFERMODE_MODE,   SkBlendMode::kMultiply   );
-        MAP( HUE_SK_XFERMODE_MODE,        SkBlendMode::kHue        );
-        MAP( SATURATION_SK_XFERMODE_MODE, SkBlendMode::kSaturation );
-        MAP( COLOR_SK_XFERMODE_MODE,      SkBlendMode::kColor      );
-        MAP( LUMINOSITY_SK_XFERMODE_MODE, SkBlendMode::kLuminosity );
+        MAP( CLEAR_SK_XFERMODE_MODE,      SkXfermode::kClear_Mode      );
+        MAP( SRC_SK_XFERMODE_MODE,        SkXfermode::kSrc_Mode        );
+        MAP( DST_SK_XFERMODE_MODE,        SkXfermode::kDst_Mode        );
+        MAP( SRCOVER_SK_XFERMODE_MODE,    SkXfermode::kSrcOver_Mode    );
+        MAP( DSTOVER_SK_XFERMODE_MODE,    SkXfermode::kDstOver_Mode    );
+        MAP( SRCIN_SK_XFERMODE_MODE,      SkXfermode::kSrcIn_Mode      );
+        MAP( DSTIN_SK_XFERMODE_MODE,      SkXfermode::kDstIn_Mode      );
+        MAP( SRCOUT_SK_XFERMODE_MODE,     SkXfermode::kSrcOut_Mode     );
+        MAP( DSTOUT_SK_XFERMODE_MODE,     SkXfermode::kDstOut_Mode     );
+        MAP( SRCATOP_SK_XFERMODE_MODE,    SkXfermode::kSrcATop_Mode    );
+        MAP( DSTATOP_SK_XFERMODE_MODE,    SkXfermode::kDstATop_Mode    );
+        MAP( XOR_SK_XFERMODE_MODE,        SkXfermode::kXor_Mode        );
+        MAP( PLUS_SK_XFERMODE_MODE,       SkXfermode::kPlus_Mode       );
+        MAP( MODULATE_SK_XFERMODE_MODE,   SkXfermode::kModulate_Mode   );
+        MAP( SCREEN_SK_XFERMODE_MODE,     SkXfermode::kScreen_Mode     );
+        MAP( OVERLAY_SK_XFERMODE_MODE,    SkXfermode::kOverlay_Mode    );
+        MAP( DARKEN_SK_XFERMODE_MODE,     SkXfermode::kDarken_Mode     );
+        MAP( LIGHTEN_SK_XFERMODE_MODE,    SkXfermode::kLighten_Mode    );
+        MAP( COLORDODGE_SK_XFERMODE_MODE, SkXfermode::kColorDodge_Mode );
+        MAP( COLORBURN_SK_XFERMODE_MODE,  SkXfermode::kColorBurn_Mode  );
+        MAP( HARDLIGHT_SK_XFERMODE_MODE,  SkXfermode::kHardLight_Mode  );
+        MAP( SOFTLIGHT_SK_XFERMODE_MODE,  SkXfermode::kSoftLight_Mode  );
+        MAP( DIFFERENCE_SK_XFERMODE_MODE, SkXfermode::kDifference_Mode );
+        MAP( EXCLUSION_SK_XFERMODE_MODE,  SkXfermode::kExclusion_Mode  );
+        MAP( MULTIPLY_SK_XFERMODE_MODE,   SkXfermode::kMultiply_Mode   );
+        MAP( HUE_SK_XFERMODE_MODE,        SkXfermode::kHue_Mode        );
+        MAP( SATURATION_SK_XFERMODE_MODE, SkXfermode::kSaturation_Mode );
+        MAP( COLOR_SK_XFERMODE_MODE,      SkXfermode::kColor_Mode      );
+        MAP( LUMINOSITY_SK_XFERMODE_MODE, SkXfermode::kLuminosity_Mode );
         #undef MAP
         default:
             return;
     }
-    AsPaint(paint)->setBlendMode(skmode);
+    AsPaint(paint)->setXfermodeMode(skmode);
 }
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 26d253c..440de68 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -446,7 +446,7 @@
         paint.getPathEffect() ||
         paint.isFakeBoldText() ||
         paint.getStyle() != SkPaint::kFill_Style ||
-        !paint.isSrcOver())
+        !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode))
     {
         return true;
     }
diff --git a/src/core/SkBlendModePriv.h b/src/core/SkBlendModePriv.h
deleted file mode 100644
index b5d9e75..0000000
--- a/src/core/SkBlendModePriv.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkBlendModePriv_DEFINED
-#define SkBlendModePriv_DEFINED
-
-#include "SkBlendMode.h"
-
-bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode);
-
-#if SK_SUPPORT_GPU
-sk_sp<GrXPFactory> SkBlendMode_AsXPFactory(SkBlendMode);
-#endif
-
-#endif
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index 3643e66..b57bd43 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -815,7 +815,7 @@
 
     SkShader* shader = origPaint.getShader();
     SkColorFilter* cf = origPaint.getColorFilter();
-    SkBlendMode mode = origPaint.getBlendMode();
+    SkXfermode* mode = origPaint.getXfermode();
     sk_sp<Sk3DShader> shader3D;
 
     SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
@@ -828,12 +828,12 @@
         shader = shader3D.get();
     }
 
-    if (mode != SkBlendMode::kSrcOver) {
+    if (mode) {
         bool deviceIsOpaque = kRGB_565_SkColorType == device.colorType();
         switch (SkInterpretXfermode(*paint, deviceIsOpaque)) {
             case kSrcOver_SkXfermodeInterpretation:
-                mode = SkBlendMode::kSrcOver;
-                paint.writable()->setBlendMode(mode);
+                mode = nullptr;
+                paint.writable()->setXfermode(nullptr);
                 break;
             case kSkipDrawing_SkXfermodeInterpretation:{
                 return allocator->createT<SkNullBlitter>();
@@ -848,13 +848,13 @@
      *  color/shader/colorfilter, and just pretend we're SRC + color==0. This
      *  will fall into our optimizations for SRC mode.
      */
-    if (mode == SkBlendMode::kClear) {
+    if (SkXfermode::IsMode(mode, SkXfermode::kClear_Mode)) {
         SkPaint* p = paint.writable();
         p->setShader(nullptr);
         shader = nullptr;
         p->setColorFilter(nullptr);
         cf = nullptr;
-        p->setBlendMode(mode = SkBlendMode::kSrc);
+        mode = p->setXfermodeMode(SkXfermode::kSrc_Mode);
         p->setColor(0);
     }
 
@@ -863,7 +863,7 @@
     }
 
     if (nullptr == shader) {
-        if (mode != SkBlendMode::kSrcOver) {
+        if (mode) {
             // xfermodes (and filters) require shaders for our current blitters
             paint.writable()->setShader(SkShader::MakeColorShader(paint->getColor()));
             paint.writable()->setAlpha(0xFF);
@@ -914,7 +914,7 @@
         case kAlpha_8_SkColorType:
             if (drawCoverage) {
                 SkASSERT(nullptr == shader);
-                SkASSERT(paint->isSrcOver());
+                SkASSERT(nullptr == paint->getXfermode());
                 blitter = allocator->createT<SkA8_Coverage_Blitter>(device, *paint);
             } else if (shader) {
                 blitter = allocator->createT<SkA8_Shader_Blitter>(device, *paint, shaderContext);
diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
index cb7d718..6697614 100644
--- a/src/core/SkBlitter_A8.cpp
+++ b/src/core/SkBlitter_A8.cpp
@@ -230,8 +230,10 @@
                                          SkShader::Context* shaderContext)
     : INHERITED(device, paint, shaderContext)
 {
-    fXfermode = SkXfermode::Peek(paint.getBlendMode());
-    SkASSERT(!fXfermode || fShaderContext);
+    if ((fXfermode = paint.getXfermode()) != nullptr) {
+        fXfermode->ref();
+        SkASSERT(fShaderContext);
+    }
 
     int width = device.width();
     fBuffer = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * (width + (SkAlign4(width) >> 2)));
@@ -239,6 +241,7 @@
 }
 
 SkA8_Shader_Blitter::~SkA8_Shader_Blitter() {
+    if (fXfermode) SkSafeUnref(fXfermode);
     sk_free(fBuffer);
 }
 
@@ -352,7 +355,7 @@
 SkA8_Coverage_Blitter::SkA8_Coverage_Blitter(const SkPixmap& device,
                              const SkPaint& paint) : SkRasterBlitter(device) {
     SkASSERT(nullptr == paint.getShader());
-    SkASSERT(paint.isSrcOver());
+    SkASSERT(nullptr == paint.getXfermode());
     SkASSERT(nullptr == paint.getColorFilter());
 }
 
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
index ea0554d..aada058 100644
--- a/src/core/SkBlitter_ARGB32.cpp
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -339,7 +339,8 @@
 {
     fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor)));
 
-    fXfermode = SkXfermode::Peek(paint.getBlendMode());
+    fXfermode = paint.getXfermode();
+    SkSafeRef(fXfermode);
 
     int flags = 0;
     if (!(shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
@@ -369,6 +370,7 @@
 }
 
 SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() {
+    SkSafeUnref(fXfermode);
     sk_free(fBuffer);
 }
 
diff --git a/src/core/SkBlitter_PM4f.cpp b/src/core/SkBlitter_PM4f.cpp
index d63e924..455a97b 100644
--- a/src/core/SkBlitter_PM4f.cpp
+++ b/src/core/SkBlitter_PM4f.cpp
@@ -325,7 +325,7 @@
 
 struct State4f {
     State4f(const SkImageInfo& info, const SkPaint& paint, const SkShader::Context* shaderContext) {
-        fXfer = SkXfermode::Peek(paint.getBlendMode());
+        fXfer = paint.getXfermode();
         if (shaderContext) {
             fBuffer.reset(info.width());
         } else {
@@ -410,7 +410,7 @@
         SkShader::Context::BlitState bstate;
         sk_bzero(&bstate, sizeof(bstate));
         bstate.fCtx = shaderContext;
-        bstate.fXfer = SkXfermode::Peek(paint.getBlendMode());
+        bstate.fXfer = paint.getXfermode();
 
         (void)shaderContext->chooseBlitProcs(device.info(), &bstate);
         return allocator->createT<SkState_Shader_Blitter<State>>(device, paint, bstate);
diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
index 7860b7c..066ec61 100644
--- a/src/core/SkBlitter_RGB16.cpp
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -160,7 +160,7 @@
     : INHERITED(device, paint) {
     SkASSERT(paint.getShader() == nullptr);
     SkASSERT(paint.getColorFilter() == nullptr);
-    SkASSERT(paint.isSrcOver());
+    SkASSERT(paint.getXfermode() == nullptr);
     SkASSERT(paint.getColor() == SK_ColorBLACK);
 }
 
@@ -683,7 +683,7 @@
                                                SkShader::Context* shaderContext)
     : INHERITED(device, paint, shaderContext)
 {
-    SkASSERT(paint.isSrcOver());
+    SkASSERT(paint.getXfermode() == nullptr);
 
     fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor));
 
@@ -809,8 +809,9 @@
                                 SkShader::Context* shaderContext)
     : INHERITED(device, paint, shaderContext)
 {
-    fXfermode = SkXfermode::Peek(paint.getBlendMode());
+    fXfermode = paint.getXfermode();
     SkASSERT(fXfermode);
+    fXfermode->ref();
 
     int width = device.width();
     fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor));
@@ -818,6 +819,7 @@
 }
 
 SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() {
+    fXfermode->unref();
     sk_free(fBuffer);
 }
 
@@ -895,14 +897,14 @@
 
     SkBlitter* blitter;
     SkShader* shader = paint.getShader();
-    bool is_srcover = paint.isSrcOver();
+    SkXfermode* mode = paint.getXfermode();
 
     // we require a shader if there is an xfermode, handled by our caller
-    SkASSERT(is_srcover || shader);
+    SkASSERT(nullptr == mode || shader);
 
     if (shader) {
         SkASSERT(shaderContext != nullptr);
-        if (!is_srcover) {
+        if (mode) {
             blitter = allocator->createT<SkRGB16_Shader_Xfermode_Blitter>(device, paint,
                                                                           shaderContext);
         } else {
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
index cef4cfa..950f187 100644
--- a/src/core/SkBlitter_Sprite.cpp
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -68,11 +68,14 @@
         if (0xFF != paint.getAlpha()) {
             return false;
         }
-        SkBlendMode mode = paint.getBlendMode();
-        if (SkBlendMode::kSrc == mode) {
+        SkXfermode::Mode mode;
+        if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) {
+            return false;
+        }
+        if (SkXfermode::kSrc_Mode == mode) {
             return true;
         }
-        if (SkBlendMode::kSrcOver == mode && src.isOpaque()) {
+        if (SkXfermode::kSrcOver_Mode == mode && src.isOpaque()) {
             return true;
         }
 
@@ -82,7 +85,7 @@
             return false;
         }
 
-        return SkBlendMode::kSrcOver == mode;
+        return SkXfermode::kSrcOver_Mode == mode;
     }
 
     SkSpriteBlitter_Src_SrcOver(const SkPixmap& src)
@@ -91,11 +94,14 @@
     void setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override {
         SkASSERT(Supports(dst, fSource, paint));
         this->INHERITED::setup(dst, left, top, paint);
-        SkBlendMode mode = paint.getBlendMode();
+        SkXfermode::Mode mode;
+        if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) {
+            SkFAIL("Should never happen.");
+        }
 
-        SkASSERT(mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc);
+        SkASSERT(mode == SkXfermode::kSrcOver_Mode || mode == SkXfermode::kSrc_Mode);
 
-        if (mode == SkBlendMode::kSrcOver && !fSource.isOpaque()) {
+        if (mode == SkXfermode::kSrcOver_Mode && !fSource.isOpaque()) {
             fUseMemcpy = false;
         }
     }
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index cf3b530..7db8601 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -484,7 +484,7 @@
              */
             SkPaint tmp;
             tmp.setImageFilter(fPaint->getImageFilter());
-            tmp.setBlendMode(fPaint->getBlendMode());
+            tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
             SkRect storage;
             if (rawBounds) {
                 // Make rawBounds include all paint outsets except for those due to image filters.
@@ -558,7 +558,7 @@
 
     if (fTempLayerForImageFilter) {
         paint->setImageFilter(nullptr);
-        paint->setBlendMode(SkBlendMode::kSrcOver);
+        paint->setXfermode(nullptr);
     }
 
     if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
@@ -2634,7 +2634,7 @@
     // nothing to draw
     if (text == nullptr || byteLength == 0 ||
         draw.fRC->isEmpty() ||
-        (paint.getAlpha() == 0 && paint.isSrcOver())) {
+        (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
         return;
     }
 
@@ -2938,21 +2938,26 @@
 // methods, rather than actually drawing themselves.
 //////////////////////////////////////////////////////////////////////////////
 
-void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
+void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+                        SkXfermode::Mode mode) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
     SkPaint paint;
 
     paint.setARGB(a, r, g, b);
-    paint.setBlendMode(mode);
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        paint.setXfermodeMode(mode);
+    }
     this->drawPaint(paint);
 }
 
-void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
+void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
     SkPaint paint;
 
     paint.setColor(c);
-    paint.setBlendMode(mode);
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        paint.setXfermodeMode(mode);
+    }
     this->drawPaint(paint);
 }
 
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 2a88781..53c8e5f 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -7,7 +7,6 @@
 #define __STDC_LIMIT_MACROS
 
 #include "SkDraw.h"
-#include "SkBlendModePriv.h"
 #include "SkBlitter.h"
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
@@ -164,27 +163,31 @@
         return nullptr;
     }
 
-    SkBlendMode mode = paint.getBlendMode();
+    SkXfermode::Mode mode;
+    if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) {
+        return nullptr;
+    }
+
     SkColor color = paint.getColor();
 
     // collaps modes based on color...
-    if (SkBlendMode::kSrcOver == mode) {
+    if (SkXfermode::kSrcOver_Mode == mode) {
         unsigned alpha = SkColorGetA(color);
         if (0 == alpha) {
-            mode = SkBlendMode::kDst;
+            mode = SkXfermode::kDst_Mode;
         } else if (0xFF == alpha) {
-            mode = SkBlendMode::kSrc;
+            mode = SkXfermode::kSrc_Mode;
         }
     }
 
     switch (mode) {
-        case SkBlendMode::kClear:
+        case SkXfermode::kClear_Mode:
 //            SkDebugf("--- D_Clear_BitmapXferProc\n");
             return D_Clear_BitmapXferProc;  // ignore data
-        case SkBlendMode::kDst:
+        case SkXfermode::kDst_Mode:
 //            SkDebugf("--- D_Dst_BitmapXferProc\n");
             return D_Dst_BitmapXferProc;    // ignore data
-        case SkBlendMode::kSrc: {
+        case SkXfermode::kSrc_Mode: {
             /*
                 should I worry about dithering for the lower depths?
             */
@@ -1140,7 +1143,7 @@
         if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
             if (SK_Scalar1 == coverage) {
                 paint.writable()->setStrokeWidth(0);
-            } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
+            } else if (SkXfermode::SupportsCoverageAsAlpha(origPaint.getXfermode())) {
                 U8CPU newAlpha;
 #if 0
                 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index ec3b0a9..5b29884 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -80,7 +80,7 @@
     sk_sp<GrFragmentProcessor> conv(GrConvolutionEffect::MakeGaussian(
         texture, direction, radius, sigma, useBounds, bounds));
     paint.addColorFragmentProcessor(std::move(conv));
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
                                                -SkIntToScalar(srcOffset.y()));
     drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(),
@@ -110,7 +110,7 @@
             srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_Mode,
             true, sigmaX, sigmaY));
     paint.addColorFragmentProcessor(std::move(conv));
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), 
                                          SkRect::Make(dstRect), localMatrix);
 }
@@ -285,7 +285,7 @@
             GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
             paint.addColorTextureProcessor(srcTexture.get(), nullptr, matrix, params);
         }
-        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
         shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY);
 
         dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(),
@@ -361,7 +361,7 @@
         GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
         sk_sp<GrTexture> tex(srcDrawContext->asTexture());
         paint.addColorTextureProcessor(tex.get(), nullptr, matrix, params);
-        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
         SkIRect dstRect(srcRect);
         scale_irect(&dstRect, scaleFactorX, scaleFactorY);
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 68183cc..63095bc 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -281,7 +281,7 @@
                                                 const OutputProperties& outputProperties) {
     GrPaint paint;
     paint.addColorFragmentProcessor(std::move(fp));
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
     sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace());
     GrPixelConfig config = GrRenderableConfigForColorSpace(colorSpace.get());
diff --git a/src/core/SkMatrixImageFilter.cpp b/src/core/SkMatrixImageFilter.cpp
index 0a33280..12efc64 100644
--- a/src/core/SkMatrixImageFilter.cpp
+++ b/src/core/SkMatrixImageFilter.cpp
@@ -85,7 +85,7 @@
 
     SkPaint paint;
     paint.setAntiAlias(true);
-    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     paint.setFilterQuality(fFilterQuality);
 
     input->draw(canvas, srcRect.x(), srcRect.y(), &paint);
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 1784349..9d76a16 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -52,7 +52,6 @@
     fColor      = SK_ColorBLACK;
     fWidth      = 0;
     fMiterLimit = SkPaintDefaults_MiterLimit;
-    fBlendMode  = (unsigned)SkBlendMode::kSrcOver;
 
     // Zero all bitfields, then set some non-zero defaults.
     fBitfieldsUInt           = 0;
@@ -70,6 +69,7 @@
     : COPY(fTypeface)
     , COPY(fPathEffect)
     , COPY(fShader)
+    , COPY(fXfermode)
     , COPY(fMaskFilter)
     , COPY(fColorFilter)
     , COPY(fRasterizer)
@@ -81,7 +81,6 @@
     , COPY(fColor)
     , COPY(fWidth)
     , COPY(fMiterLimit)
-    , COPY(fBlendMode)
     , COPY(fBitfields)
 #undef COPY
 {}
@@ -91,6 +90,7 @@
     MOVE(fTypeface);
     MOVE(fPathEffect);
     MOVE(fShader);
+    MOVE(fXfermode);
     MOVE(fMaskFilter);
     MOVE(fColorFilter);
     MOVE(fRasterizer);
@@ -102,7 +102,6 @@
     MOVE(fColor);
     MOVE(fWidth);
     MOVE(fMiterLimit);
-    MOVE(fBlendMode);
     MOVE(fBitfields);
 #undef MOVE
 }
@@ -118,6 +117,7 @@
     ASSIGN(fTypeface);
     ASSIGN(fPathEffect);
     ASSIGN(fShader);
+    ASSIGN(fXfermode);
     ASSIGN(fMaskFilter);
     ASSIGN(fColorFilter);
     ASSIGN(fRasterizer);
@@ -129,7 +129,6 @@
     ASSIGN(fColor);
     ASSIGN(fWidth);
     ASSIGN(fMiterLimit);
-    ASSIGN(fBlendMode);
     ASSIGN(fBitfields);
 #undef ASSIGN
 
@@ -145,6 +144,7 @@
     MOVE(fTypeface);
     MOVE(fPathEffect);
     MOVE(fShader);
+    MOVE(fXfermode);
     MOVE(fMaskFilter);
     MOVE(fColorFilter);
     MOVE(fRasterizer);
@@ -156,7 +156,6 @@
     MOVE(fColor);
     MOVE(fWidth);
     MOVE(fMiterLimit);
-    MOVE(fBlendMode);
     MOVE(fBitfields);
 #undef MOVE
 
@@ -168,6 +167,7 @@
     return EQUAL(fTypeface)
         && EQUAL(fPathEffect)
         && EQUAL(fShader)
+        && EQUAL(fXfermode)
         && EQUAL(fMaskFilter)
         && EQUAL(fColorFilter)
         && EQUAL(fRasterizer)
@@ -179,7 +179,6 @@
         && EQUAL(fColor)
         && EQUAL(fWidth)
         && EQUAL(fMiterLimit)
-        && EQUAL(fBlendMode)
         && EQUAL(fBitfieldsUInt)
         ;
 #undef EQUAL
@@ -361,6 +360,7 @@
 MOVE_FIELD(ImageFilter)
 MOVE_FIELD(Shader)
 MOVE_FIELD(ColorFilter)
+MOVE_FIELD(Xfermode)
 MOVE_FIELD(PathEffect)
 MOVE_FIELD(MaskFilter)
 MOVE_FIELD(DrawLooper)
@@ -386,10 +386,7 @@
 SET_PTR(ColorFilter)
 #endif
 #ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR
-SkXfermode* SkPaint::setXfermode(SkXfermode* xfer) {
-    this->setBlendMode(xfer ? xfer->blend() : SkBlendMode::kSrcOver);
-    return this->getXfermode();
-}
+SET_PTR(Xfermode)
 #endif
 #ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR
 SET_PTR(PathEffect)
@@ -406,18 +403,10 @@
 }
 #endif
 
-#ifdef SK_SUPPORT_LEGACY_XFERMODE_OBJECT
-void SkPaint::setXfermode(sk_sp<SkXfermode> mode) {
-    this->setBlendMode(mode ? mode->blend() : SkBlendMode::kSrcOver);
-}
-SkXfermode* SkPaint::getXfermode() const {
-    return SkXfermode::Peek((SkBlendMode)fBlendMode);
-}
 SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
-    this->setBlendMode((SkBlendMode)mode);
-    return SkXfermode::Peek((SkBlendMode)mode);
+    fXfermode = SkXfermode::Make(mode);
+    return fXfermode.get(); // can/should we change this API to be void, like the other setters?
 }
-#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1915,6 +1904,7 @@
     }
     if (asint(this->getPathEffect()) |
         asint(this->getShader()) |
+        asint(this->getXfermode()) |
         asint(this->getMaskFilter()) |
         asint(this->getColorFilter()) |
         asint(this->getRasterizer()) |
@@ -1933,8 +1923,7 @@
     buffer.writeUInt(pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
                                       this->getFilterQuality(), flatFlags));
     buffer.writeUInt(pack_4(this->getStrokeCap(), this->getStrokeJoin(),
-                            (this->getStyle() << 4) | this->getTextEncoding(),
-                            fBlendMode));
+                            this->getStyle(), this->getTextEncoding()));
 
     // now we're done with ptr and the (pre)reserved space. If we need to write
     // additional fields, use the buffer directly
@@ -1944,6 +1933,7 @@
     if (flatFlags & kHasEffects_FlatFlag) {
         buffer.writeFlattenable(this->getPathEffect());
         buffer.writeFlattenable(this->getShader());
+        buffer.writeFlattenable(this->getXfermode());
         buffer.writeFlattenable(this->getMaskFilter());
         buffer.writeFlattenable(this->getColorFilter());
         buffer.writeFlattenable(this->getRasterizer());
@@ -1965,14 +1955,8 @@
     uint32_t tmp = buffer.readUInt();
     this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
     this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
-    if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode_Version)) {
-        this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
-        this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
-    } else {
-        this->setStyle(static_cast<Style>((tmp >> 12) & 0xF));
-        this->setTextEncoding(static_cast<TextEncoding>((tmp >> 8) & 0xF));
-        this->setBlendMode((SkBlendMode)(tmp & 0xFF));
-    }
+    this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
+    this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
 
     if (flatFlags & kHasTypeface_FlatFlag) {
         this->setTypeface(buffer.readTypeface());
@@ -1983,10 +1967,7 @@
     if (flatFlags & kHasEffects_FlatFlag) {
         this->setPathEffect(buffer.readPathEffect());
         this->setShader(buffer.readShader());
-        if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode_Version)) {
-            sk_sp<SkXfermode> xfer = buffer.readXfermode();
-            this->setBlendMode(xfer ? xfer->blend() : SkBlendMode::kSrcOver);
-        }
+        this->setXfermode(buffer.readXfermode());
         this->setMaskFilter(buffer.readMaskFilter());
         this->setColorFilter(buffer.readColorFilter());
         this->setRasterizer(buffer.readRasterizer());
@@ -2005,6 +1986,7 @@
     } else {
         this->setPathEffect(nullptr);
         this->setShader(nullptr);
+        this->setXfermode(nullptr);
         this->setMaskFilter(nullptr);
         this->setColorFilter(nullptr);
         this->setRasterizer(nullptr);
@@ -2134,8 +2116,11 @@
         str->append("</dd>");
     }
 
-    if (!this->isSrcOver()) {
-        str->appendf("<dt>Xfermode:</dt><dd>%d</dd>", fBlendMode);
+    SkXfermode* xfer = this->getXfermode();
+    if (xfer) {
+        str->append("<dt>Xfermode:</dt><dd>");
+        xfer->toString(str);
+        str->append("</dd>");
     }
 
     SkMaskFilter* maskFilter = this->getMaskFilter();
@@ -2378,20 +2363,23 @@
     if (fDrawLooper) {
         return false;
     }
-    switch ((SkBlendMode)fBlendMode) {
-        case SkBlendMode::kSrcOver:
-        case SkBlendMode::kSrcATop:
-        case SkBlendMode::kDstOut:
-        case SkBlendMode::kDstOver:
-        case SkBlendMode::kPlus:
-            if (0 == this->getAlpha()) {
-                return !affects_alpha(fColorFilter.get()) && !affects_alpha(fImageFilter.get());
-            }
-            break;
-        case SkBlendMode::kDst:
-            return true;
-        default:
-            break;
+    SkXfermode::Mode mode;
+    if (SkXfermode::AsMode(fXfermode.get(), &mode)) {
+        switch (mode) {
+            case SkXfermode::kSrcOver_Mode:
+            case SkXfermode::kSrcATop_Mode:
+            case SkXfermode::kDstOut_Mode:
+            case SkXfermode::kDstOver_Mode:
+            case SkXfermode::kPlus_Mode:
+                if (0 == this->getAlpha()) {
+                    return !affects_alpha(fColorFilter.get()) && !affects_alpha(fImageFilter.get());
+                }
+                break;
+            case SkXfermode::kDst_Mode:
+                return true;
+            default:
+                break;
+        }
     }
     return false;
 }
@@ -2399,7 +2387,7 @@
 uint32_t SkPaint::getHash() const {
     // We're going to hash 10 pointers and 7 32-bit values, finishing up with fBitfields,
     // so fBitfields should be 10 pointers and 6 32-bit values from the start.
-    static_assert(offsetof(SkPaint, fBitfields) == 8 * sizeof(void*) + 7 * sizeof(uint32_t),
+    static_assert(offsetof(SkPaint, fBitfields) == 9 * sizeof(void*) + 6 * sizeof(uint32_t),
                   "SkPaint_notPackedTightly");
     return SkOpts::hash(reinterpret_cast<const uint32_t*>(this),
                         offsetof(SkPaint, fBitfields) + sizeof(fBitfields));
diff --git a/src/core/SkPaintPriv.cpp b/src/core/SkPaintPriv.cpp
index cbe2558..6725cb4 100644
--- a/src/core/SkPaintPriv.cpp
+++ b/src/core/SkPaintPriv.cpp
@@ -41,7 +41,7 @@
         }
     }
 
-    return SkXfermode::IsOpaque(paint->getBlendMode(), opacityType);
+    return SkXfermode::IsOpaque(paint->getXfermode(), opacityType);
 }
 
 bool SkPaintPriv::Overwrites(const SkBitmap& bitmap, const SkPaint* paint) {
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 108c877..7749839 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -267,7 +267,7 @@
 
     SkPaint paint;
     paint.setFilterQuality(quality);
-    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     surface->getCanvas()->drawBitmapRect(bitmap, SkRect::MakeIWH(dst.width(), dst.height()),
                                          &paint);
     return true;
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index 91d60be..1e8dcf5 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -82,8 +82,8 @@
     }
 
     SkRasterPipeline shader, colorFilter, xfermode;
-    if (!append_effect_stages(paint.getColorFilter(),                 &colorFilter) ||
-        !append_effect_stages(SkXfermode::Peek(paint.getBlendMode()), &xfermode   )) {
+    if (!append_effect_stages(paint.getColorFilter(), &colorFilter) ||
+        !append_effect_stages(paint.getXfermode(),    &xfermode   )) {
         return nullptr;
     }
 
@@ -104,7 +104,7 @@
     if (!paint.getShader()) {
         blitter->fShader.append(SkRasterPipeline::constant_color, &blitter->fPaintColor);
     }
-    if (paint.isSrcOver()) {
+    if (!paint.getXfermode()) {
         blitter->fXfermode.append(SkRasterPipeline::srcover);
     }
 
diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h
index 6b0d332..8a3b7ba 100644
--- a/src/core/SkReadBuffer.h
+++ b/src/core/SkReadBuffer.h
@@ -69,7 +69,6 @@
         kLightingShaderWritesInvNormRotation = 45,
         kBlurMaskFilterWritesOccluder      = 47,
         kGradientShaderFloatColor_Version  = 49,
-        kXfermodeToBlendMode_Version       = 50,
     };
 
     /**
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index dca19df..ca9c1b6 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -341,27 +341,34 @@
                 return true;
             }
 
-            // Unusual blendmodes require us to process a saved layer
+            // Unusual Xfermodes require us to process a saved layer
             // even with operations outisde the clip.
             // For example, DstIn is used by masking layers.
             // https://code.google.com/p/skia/issues/detail?id=1291
             // https://crbug.com/401593
-            switch (paint->getBlendMode()) {
-                // For each of the following transfer modes, if the source
-                // alpha is zero (our transparent black), the resulting
-                // blended alpha is not necessarily equal to the original
-                // destination alpha.
-                case SkBlendMode::kClear:
-                case SkBlendMode::kSrc:
-                case SkBlendMode::kSrcIn:
-                case SkBlendMode::kDstIn:
-                case SkBlendMode::kSrcOut:
-                case SkBlendMode::kDstATop:
-                case SkBlendMode::kModulate:
-                    return true;
-                    break;
-                default:
-                    break;
+            SkXfermode* xfermode = paint->getXfermode();
+            SkXfermode::Mode mode;
+            // SrcOver is ok, and is also the common case with a nullptr xfermode.
+            // So we should make that the fast path and bypass the mode extraction
+            // and test.
+            if (xfermode && xfermode->asMode(&mode)) {
+                switch (mode) {
+                    // For each of the following transfer modes, if the source
+                    // alpha is zero (our transparent black), the resulting
+                    // blended alpha is not necessarily equal to the original
+                    // destination alpha.
+                    case SkXfermode::kClear_Mode:
+                    case SkXfermode::kSrc_Mode:
+                    case SkXfermode::kSrcIn_Mode:
+                    case SkXfermode::kDstIn_Mode:
+                    case SkXfermode::kSrcOut_Mode:
+                    case SkXfermode::kDstATop_Mode:
+                    case SkXfermode::kModulate_Mode:
+                        return true;
+                        break;
+                    default:
+                        break;
+                }
             }
         }
         return false;
diff --git a/src/core/SkRecordOpts.cpp b/src/core/SkRecordOpts.cpp
index a7feec1..d46a657 100644
--- a/src/core/SkRecordOpts.cpp
+++ b/src/core/SkRecordOpts.cpp
@@ -98,7 +98,7 @@
     // looper drawing unmodulated filter layer twice and then modulating the result produces
     // different image to drawing modulated filter layer twice.
     // TODO: most likely the looper and only some xfer modes are the hard constraints
-    if (!paint->isSrcOver() || paint->getLooper()) {
+    if (paint->getXfermode() || paint->getLooper()) {
         return false;
     }
 
@@ -129,9 +129,9 @@
         }
 
         // The layer paint can not have any effects.
-        if (layerPaint->getPathEffect()  ||
+        if (layerPaint->getPathEffect() ||
             layerPaint->getShader()      ||
-            !layerPaint->isSrcOver()     ||
+            layerPaint->getXfermode()    ||
             layerPaint->getMaskFilter()  ||
             layerPaint->getColorFilter() ||
             layerPaint->getRasterizer()  ||
@@ -174,12 +174,16 @@
 }
 
 static bool effectively_srcover(const SkPaint* paint) {
-    if (!paint || paint->isSrcOver()) {
+    if (!paint) {
+        return true;
+    }
+    SkXfermode* mode = paint->getXfermode();
+    if (SkXfermode::IsMode(mode, SkXfermode::kSrcOver_Mode)) {
         return true;
     }
     // src-mode with opaque and no effects (which might change opaqueness) is ok too.
     return !paint->getShader() && !paint->getColorFilter() && !paint->getImageFilter() &&
-           0xFF == paint->getAlpha() && paint->getBlendMode() == SkBlendMode::kSrc;
+           0xFF == paint->getAlpha() && SkXfermode::IsMode(mode, SkXfermode::kSrc_Mode);
 }
 
 // For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
diff --git a/src/core/SkSpriteBlitter4f.cpp b/src/core/SkSpriteBlitter4f.cpp
index 38ec739..a13edd9 100644
--- a/src/core/SkSpriteBlitter4f.cpp
+++ b/src/core/SkSpriteBlitter4f.cpp
@@ -13,7 +13,7 @@
 class Sprite_4f : public SkSpriteBlitter {
 public:
     Sprite_4f(const SkPixmap& src, const SkPaint& paint) : INHERITED(src) {
-        fXfer = SkXfermode::Peek(paint.getBlendMode());
+        fXfer = paint.getXfermode();
         fLoader = SkLoadSpanProc_Choose(src.info());
         fFilter = SkFilterSpanProc_Choose(paint);
         fBuffer.reset(src.width());
diff --git a/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
index 1a76b1b..9388599 100644
--- a/src/core/SkSpriteBlitter_ARGB32.cpp
+++ b/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -63,7 +63,8 @@
         fColorFilter = paint.getColorFilter();
         SkSafeRef(fColorFilter);
 
-        fXfermode = SkXfermode::Peek(paint.getBlendMode());
+        fXfermode = paint.getXfermode();
+        SkSafeRef(fXfermode);
 
         fBufferSize = 0;
         fBuffer = nullptr;
@@ -82,6 +83,7 @@
 
     virtual ~Sprite_D32_XferFilter() {
         delete[] fBuffer;
+        SkSafeUnref(fXfermode);
         SkSafeUnref(fColorFilter);
     }
 
@@ -261,7 +263,7 @@
     }
 
     U8CPU       alpha = paint.getAlpha();
-    bool isSrcOver = paint.isSrcOver();
+    SkXfermode* xfermode = paint.getXfermode();
     SkColorFilter* filter = paint.getColorFilter();
     SkSpriteBlitter* blitter = nullptr;
 
@@ -270,7 +272,7 @@
             if (alpha != 0xFF) {
                 return nullptr;    // we only have opaque sprites
             }
-            if (!isSrcOver || filter) {
+            if (xfermode || filter) {
                 blitter = allocator->createT<Sprite_D32_S4444_XferFilter>(source, paint);
             } else if (source.isOpaque()) {
                 blitter = allocator->createT<Sprite_D32_S4444_Opaque>(source);
@@ -279,7 +281,7 @@
             }
             break;
         case kN32_SkColorType:
-            if (!isSrcOver || filter) {
+            if (xfermode || filter) {
                 if (255 == alpha) {
                     // this can handle xfermode or filter, but not alpha
                     blitter = allocator->createT<Sprite_D32_S32A_XferFilter>(source, paint);
diff --git a/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
index 9df7dab..6c5a7cb 100644
--- a/src/core/SkSpriteBlitter_RGB16.cpp
+++ b/src/core/SkSpriteBlitter_RGB16.cpp
@@ -307,7 +307,7 @@
     if (paint.getMaskFilter() != nullptr) { // may add cases for this
         return nullptr;
     }
-    if (!paint.isSrcOver()) { // may add cases for this
+    if (paint.getXfermode() != nullptr) { // may add cases for this
         return nullptr;
     }
     if (paint.getColorFilter() != nullptr) { // may add cases for this
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 38a1608..58c5578 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -1472,64 +1472,3 @@
     }
     return false;
 }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode mode) {
-    switch (mode) {
-        case SkBlendMode::kDst:
-        case SkBlendMode::kSrcOver:
-        case SkBlendMode::kDstOver:
-        case SkBlendMode::kDstOut:
-        case SkBlendMode::kSrcATop:
-        case SkBlendMode::kXor:
-        case SkBlendMode::kPlus:
-            return true;
-        default:
-            break;
-    }
-    return false;
-}
-
-bool SkXfermode::IsOpaque(SkBlendMode mode, SrcColorOpacity opacityType) {
-    const ProcCoeff rec = gProcCoeffs[(int)mode];
-
-    switch (rec.fSC) {
-        case kDA_Coeff:
-        case kDC_Coeff:
-        case kIDA_Coeff:
-        case kIDC_Coeff:
-            return false;
-        default:
-            break;
-    }
-
-    switch (rec.fDC) {
-        case kZero_Coeff:
-            return true;
-        case kISA_Coeff:
-            return kOpaque_SrcColorOpacity == opacityType;
-        case kSA_Coeff:
-            return kTransparentBlack_SrcColorOpacity == opacityType ||
-            kTransparentAlpha_SrcColorOpacity == opacityType;
-        case kSC_Coeff:
-            return kTransparentBlack_SrcColorOpacity == opacityType;
-        default:
-            return false;
-    }
-    return false;
-}
-
-#if SK_SUPPORT_GPU
-sk_sp<GrXPFactory> SkBlendMode_AsXPFactory(SkBlendMode mode) {
-    const ProcCoeff rec = gProcCoeffs[(int)mode];
-    if (CANNOT_USE_COEFF != rec.fSC) {
-        sk_sp<GrXPFactory> result(GrPorterDuffXPFactory::Make(mode));
-        SkASSERT(result);
-        return result;
-    }
-
-    SkASSERT(GrCustomXfermode::IsSupportedMode((SkXfermode::Mode)mode));
-    return GrCustomXfermode::MakeXPFactory((SkXfermode::Mode)mode);
-}
-#endif
diff --git a/src/core/SkXfermodeInterpretation.cpp b/src/core/SkXfermodeInterpretation.cpp
index 3a1da36..1b2c8e3 100644
--- a/src/core/SkXfermodeInterpretation.cpp
+++ b/src/core/SkXfermodeInterpretation.cpp
@@ -9,31 +9,38 @@
 #include "SkPaint.h"
 
 static bool just_solid_color(const SkPaint& p) {
-    return SK_AlphaOPAQUE == p.getAlpha() && !p.getColorFilter() && !p.getShader();
+    return SK_AlphaOPAQUE == p.getAlpha()
+        && !p.getColorFilter() && !p.getShader();
 }
 
-SkXfermodeInterpretation SkInterpretXfermode(const SkPaint& paint, bool dstIsOpaque) {
-    switch (paint.getBlendMode()) {
-        case SkBlendMode::kSrcOver:
+SkXfermodeInterpretation SkInterpretXfermode(const SkPaint& paint,
+                                             bool dstIsOpaque) {
+    const SkXfermode* xfer = paint.getXfermode();
+    SkXfermode::Mode mode;
+    if (!SkXfermode::AsMode(xfer, &mode)) {
+        return kNormal_SkXfermodeInterpretation;
+    }
+    switch (mode) {
+        case SkXfermode::kSrcOver_Mode:
             return kSrcOver_SkXfermodeInterpretation;
-        case SkBlendMode::kSrc:
+        case SkXfermode::kSrc_Mode:
             if (just_solid_color(paint)) {
                 return kSrcOver_SkXfermodeInterpretation;
             }
             return kNormal_SkXfermodeInterpretation;
-        case SkBlendMode::kDst:
+        case SkXfermode::kDst_Mode:
             return kSkipDrawing_SkXfermodeInterpretation;
-        case SkBlendMode::kDstOver:
+        case SkXfermode::kDstOver_Mode:
             if (dstIsOpaque) {
                 return kSkipDrawing_SkXfermodeInterpretation;
             }
             return kNormal_SkXfermodeInterpretation;
-        case SkBlendMode::kSrcIn:
+        case SkXfermode::kSrcIn_Mode:
             if (dstIsOpaque && just_solid_color(paint)) {
                 return kSrcOver_SkXfermodeInterpretation;
             }
             return kNormal_SkXfermodeInterpretation;
-        case SkBlendMode::kDstIn:
+        case SkXfermode::kDstIn_Mode:
             if (just_solid_color(paint)) {
                 return kSkipDrawing_SkXfermodeInterpretation;
             }
diff --git a/src/effects/SkAlphaThresholdFilter.cpp b/src/effects/SkAlphaThresholdFilter.cpp
index bbae2e1..515e05b 100644
--- a/src/effects/SkAlphaThresholdFilter.cpp
+++ b/src/effects/SkAlphaThresholdFilter.cpp
@@ -107,7 +107,7 @@
     }
 
     GrPaint grPaint;
-    grPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    grPaint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     SkRegion::Iterator iter(fRegion);
     drawContext->clear(nullptr, 0x0, true);
 
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
index 507a805..d6b23d5 100644
--- a/src/effects/SkColorFilterImageFilter.cpp
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -89,7 +89,7 @@
 
     SkPaint paint;
 
-    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     paint.setColorFilter(fColorFilter);
 
     // TODO: it may not be necessary to clear or drawPaint inside the input bounds
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 88f5026..283372f 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -343,7 +343,7 @@
                                           offsetMatrix,
                                           colorTexture.get(),
                                           SkISize::Make(color->width(), color->height())));
-        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
         SkMatrix matrix;
         matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y()));
 
diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp
index cc43db7..b4b8cac 100644
--- a/src/effects/SkDropShadowImageFilter.cpp
+++ b/src/effects/SkDropShadowImageFilter.cpp
@@ -95,6 +95,7 @@
     SkPaint paint;
     paint.setImageFilter(SkBlurImageFilter::Make(sigma.fX, sigma.fY, nullptr));
     paint.setColorFilter(SkColorFilter::MakeModeFilter(fColor, SkXfermode::kSrcIn_Mode));
+    paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
 
     SkVector offsetVec = SkVector::Make(fDx, fDy);
     ctx.ctm().mapVectors(&offsetVec, 1);
diff --git a/src/effects/SkImageSource.cpp b/src/effects/SkImageSource.cpp
index f96a4a1..f434de4 100644
--- a/src/effects/SkImageSource.cpp
+++ b/src/effects/SkImageSource.cpp
@@ -108,7 +108,7 @@
 
     // Subtract off the integer component of the translation (will be applied in offset, below).
     dstRect.offset(-SkIntToScalar(dstIRect.fLeft), -SkIntToScalar(dstIRect.fTop));
-    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     // FIXME: this probably shouldn't be necessary, but drawImageRect asserts
     // None filtering when it's translate-only
     paint.setFilterQuality(
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index 784228f..d8f7744 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -104,7 +104,7 @@
         dst->setColorFilter(sk_ref_sp(src.getColorFilter()));
     }
     if (bits & kXfermode_Bit) {
-        dst->setBlendMode(src.getBlendMode());
+        dst->setXfermode(sk_ref_sp(src.getXfermode()));
     }
 
     // we don't override these
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 057ef24..5627574 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -396,7 +396,7 @@
     sk_sp<GrFragmentProcessor> fp(this->makeFragmentProcessor(src, matrix, srcBounds,
                                                               boundaryMode));
     paint.addColorFragmentProcessor(std::move(fp));
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect);
 }
 
diff --git a/src/effects/SkMergeImageFilter.cpp b/src/effects/SkMergeImageFilter.cpp
index 9830669..cc7e336 100755
--- a/src/effects/SkMergeImageFilter.cpp
+++ b/src/effects/SkMergeImageFilter.cpp
@@ -131,7 +131,7 @@
 
         SkPaint paint;
         if (fModes) {
-            paint.setBlendMode((SkBlendMode)fModes[i]);
+            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
         }
 
         inputs[i]->draw(canvas,
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index 2bd7928..82e47c5 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -401,7 +401,7 @@
                                                              radius,
                                                              morphType,
                                                              bounds));
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     drawContext->fillRectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect),
                                      SkRect::Make(srcRect));
 }
@@ -418,7 +418,7 @@
     paint.setGammaCorrect(drawContext->isGammaCorrect());
     paint.addColorFragmentProcessor(GrMorphologyEffect::Make(texture, direction, radius,
                                                              morphType));
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     drawContext->fillRectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect),
                                 SkRect::Make(srcRect));
 }
diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp
index 2e8b0d9..1c99154 100644
--- a/src/effects/SkOffsetImageFilter.cpp
+++ b/src/effects/SkOffsetImageFilter.cpp
@@ -61,7 +61,7 @@
         canvas->clear(0x0);
 
         SkPaint paint;
-        paint.setBlendMode(SkBlendMode::kSrc);
+        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
         canvas->translate(SkIntToScalar(srcOffset.fX - bounds.fLeft),
                           SkIntToScalar(srcOffset.fY - bounds.fTop));
 
diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp
index a140db2..46c4d9a 100644
--- a/src/effects/SkTileImageFilter.cpp
+++ b/src/effects/SkTileImageFilter.cpp
@@ -87,7 +87,7 @@
         SkASSERT(canvas);
 
         SkPaint paint;
-        paint.setBlendMode(SkBlendMode::kSrc);
+        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
 
         input->draw(canvas, 
                     SkIntToScalar(inputOffset.x()), SkIntToScalar(inputOffset.y()),
@@ -107,7 +107,7 @@
     SkASSERT(canvas);
 
     SkPaint paint;
-    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     paint.setShader(subset->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
     canvas->translate(-dstRect.fLeft, -dstRect.fTop);
     canvas->drawRect(dstRect, paint);
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index 344c750..952ce97 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -27,7 +27,7 @@
 
 class SkXfermodeImageFilter_Base : public SkImageFilter {
 public:
-    SkXfermodeImageFilter_Base(SkBlendMode mode, sk_sp<SkImageFilter> inputs[2],
+    SkXfermodeImageFilter_Base(sk_sp<SkXfermode> mode, sk_sp<SkImageFilter> inputs[2],
                                const CropRect* cropRect);
 
     SK_TO_STRING_OVERRIDE()
@@ -55,7 +55,7 @@
 #endif
 
 private:
-    SkBlendMode fMode;
+    sk_sp<SkXfermode> fMode;
 
     friend class SkXfermodeImageFilter;
 
@@ -64,7 +64,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-sk_sp<SkImageFilter> SkXfermodeImageFilter::Make(SkBlendMode mode,
+sk_sp<SkImageFilter> SkXfermodeImageFilter::Make(sk_sp<SkXfermode> mode,
                                                  sk_sp<SkImageFilter> background,
                                                  sk_sp<SkImageFilter> foreground,
                                                  const SkImageFilter::CropRect* cropRect) {
@@ -72,44 +72,23 @@
     return sk_sp<SkImageFilter>(new SkXfermodeImageFilter_Base(mode, inputs, cropRect));
 }
 
-#ifdef SK_SUPPORT_LEGACY_XFERMODE_OBJECT
-sk_sp<SkImageFilter> SkXfermodeImageFilter::Make(sk_sp<SkXfermode> mode,
-                                                 sk_sp<SkImageFilter> background,
-                                                 sk_sp<SkImageFilter> foreground,
-                                                 const SkImageFilter::CropRect* cropRect) {
-    return Make(mode ? mode->blend() : SkBlendMode::kSrcOver,
-                std::move(background), std::move(foreground), cropRect);
-}
-#endif
-
-SkXfermodeImageFilter_Base::SkXfermodeImageFilter_Base(SkBlendMode mode,
-                                                       sk_sp<SkImageFilter> inputs[2],
-                                                       const CropRect* cropRect)
+SkXfermodeImageFilter_Base::SkXfermodeImageFilter_Base(sk_sp<SkXfermode> mode,
+                                             sk_sp<SkImageFilter> inputs[2],
+                                             const CropRect* cropRect)
     : INHERITED(inputs, 2, cropRect)
-    , fMode(mode)
-{}
-
-static SkBlendMode unflatten_blendmode_version_check(SkReadBuffer& buffer) {
-    if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode_Version)) {
-        sk_sp<SkXfermode> xfer = buffer.readXfermode();
-        return xfer ? xfer->blend() : SkBlendMode::kSrcOver;
-    } else {
-        uint32_t mode = buffer.read32();
-        (void)buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode);
-        return (SkBlendMode)mode;
-    }
+    , fMode(std::move(mode)) {
 }
 
 sk_sp<SkFlattenable> SkXfermodeImageFilter_Base::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
-    SkBlendMode mode = unflatten_blendmode_version_check(buffer);
-    return SkXfermodeImageFilter::Make(mode, common.getInput(0), common.getInput(1),
+    sk_sp<SkXfermode> mode(buffer.readXfermode());
+    return SkXfermodeImageFilter::Make(std::move(mode), common.getInput(0), common.getInput(1),
                                        &common.cropRect());
 }
 
 void SkXfermodeImageFilter_Base::flatten(SkWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    buffer.write32((unsigned)fMode);
+    buffer.writeFlattenable(fMode.get());
 }
 
 sk_sp<SkSpecialImage> SkXfermodeImageFilter_Base::onFilterImage(SkSpecialImage* source,
@@ -168,7 +147,7 @@
 
     if (background) {
         SkPaint paint;
-        paint.setBlendMode(SkBlendMode::kSrc);
+        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
         background->draw(canvas,
                          SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY),
                          &paint);
@@ -182,7 +161,7 @@
 void SkXfermodeImageFilter_Base::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
                                                 const SkIRect& fgBounds) const {
     SkPaint paint;
-    paint.setBlendMode(fMode);
+    paint.setXfermode(fMode);
     if (img) {
         img->draw(canvas, SkIntToScalar(fgBounds.fLeft), SkIntToScalar(fgBounds.fTop), &paint);
     }
@@ -196,7 +175,11 @@
 #ifndef SK_IGNORE_TO_STRING
 void SkXfermodeImageFilter_Base::toString(SkString* str) const {
     str->appendf("SkXfermodeImageFilter: (");
-    str->appendf("blendmode: (%d)", fMode);
+    str->appendf("xfermode: (");
+    if (fMode) {
+        fMode->toString(str);
+    }
+    str->append(")");
     if (this->getInput(0)) {
         str->appendf("foreground: (");
         this->getInput(0)->toString(str);
@@ -283,7 +266,7 @@
         paint.addColorFragmentProcessor(std::move(bgFP));
     }
 
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
     sk_sp<GrDrawContext> drawContext(
         context->makeDrawContext(SkBackingFit::kApprox, bounds.width(), bounds.height(),
@@ -307,9 +290,8 @@
 sk_sp<GrFragmentProcessor>
 SkXfermodeImageFilter_Base::makeFGFrag(sk_sp<GrFragmentProcessor> bgFP) const {
     // A null fMode is interpreted to mean kSrcOver_Mode (to match raster).
-    SkXfermode* xfer = SkXfermode::Peek(fMode);
-    sk_sp<SkXfermode> srcover;
-    if (!xfer) {
+    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.
@@ -317,11 +299,9 @@
         rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode);
         SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC);
 
-        srcover.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode));
-        xfer = srcover.get();
-
+        mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode));
     }
-    return xfer->makeFragmentProcessorForImageFilter(std::move(bgFP));
+    return mode->makeFragmentProcessorForImageFilter(std::move(bgFP));
 }
 
 #endif
@@ -332,8 +312,7 @@
 public:
     SkArithmeticImageFilter(float k1, float k2, float k3, float k4, bool enforcePMColor,
                             sk_sp<SkImageFilter> inputs[2], const CropRect* cropRect)
-        // need to pass a blendmode to our inherited constructor, but we ignore it
-        : SkXfermodeImageFilter_Base(SkBlendMode::kSrcOver, inputs, cropRect)
+        : SkXfermodeImageFilter_Base(nullptr, inputs, cropRect)
         , fK{ k1, k2, k3, k4 }
         , fEnforcePMColor(enforcePMColor)
     {}
@@ -368,11 +347,8 @@
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
 
     // skip the mode (srcover) our parent-class wrote
-    SkDEBUGCODE(SkBlendMode mode =) unflatten_blendmode_version_check(buffer);
-    if (!buffer.isValid()) {
-        return nullptr;
-    }
-    SkASSERT(SkBlendMode::kSrcOver == mode);
+    sk_sp<SkXfermode> mode(buffer.readXfermode());
+    SkASSERT(nullptr == mode);
 
     float k[4];
     for (int i = 0; i < 4; ++i) {
@@ -499,16 +475,16 @@
     int mode = -1;  // illegal mode
     if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) &&
         SkScalarNearlyZero(k3) && SkScalarNearlyZero(k4)) {
-        mode = (int)SkBlendMode::kSrc;
+        mode = SkXfermode::kSrc_Mode;
     } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) &&
                SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) {
-        mode = (int)SkBlendMode::kDst;
+        mode = SkXfermode::kDst_Mode;
     } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) &&
                SkScalarNearlyZero(k3) && SkScalarNearlyZero(k4)) {
-        mode = (int)SkBlendMode::kClear;
+        mode = SkXfermode::kClear_Mode;
     }
     if (mode >= 0) {
-        return SkXfermodeImageFilter::Make((SkBlendMode)mode,
+        return SkXfermodeImageFilter::Make(SkXfermode::Make((SkXfermode::Mode)mode),
                                            std::move(background), std::move(foreground), crop);
     }
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 176d5da..6cec1da 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -356,7 +356,7 @@
             }
             GrPaint paint;
             paint.addColorFragmentProcessor(std::move(fp));
-            paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+            paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
             paint.setAllowSRGBInputs(true);
             SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
             drawContext->drawRect(GrNoClip(), paint, matrix, rect, nullptr);
@@ -471,7 +471,7 @@
             if (fp) {
                 GrPaint paint;
                 paint.addColorFragmentProcessor(std::move(fp));
-                paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+                paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
                 paint.setAllowSRGBInputs(true);
                 SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
                 tempDC->drawRect(GrNoClip(), paint, SkMatrix::I(), rect, nullptr);
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 42d6795..e9a9619 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -241,7 +241,7 @@
 
         GrPaint paint;
         paint.setColor4f(GrColor4f::FromGrColor(color));
-        paint.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::Mode::kSrc_Mode));
+        paint.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::kSrc_Mode));
 
         this->drawRect(clip, paint, SkMatrix::I(), clearRect);
     } else if (isFull) {
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 7bc26af..8bb3fed 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -21,15 +21,15 @@
 /*
  * Convert a boolean operation into a transfer mode code
  */
-static SkBlendMode op_to_mode(SkRegion::Op op) {
+static SkXfermode::Mode op_to_mode(SkRegion::Op op) {
 
-    static const SkBlendMode modeMap[] = {
-        SkBlendMode::kDstOut,   // kDifference_Op
-        SkBlendMode::kModulate, // kIntersect_Op
-        SkBlendMode::kSrcOver,  // kUnion_Op
-        SkBlendMode::kXor,      // kXOR_Op
-        SkBlendMode::kClear,    // kReverseDifference_Op
-        SkBlendMode::kSrc,      // kReplace_Op
+    static const SkXfermode::Mode modeMap[] = {
+        SkXfermode::kDstOut_Mode,   // kDifference_Op
+        SkXfermode::kModulate_Mode, // kIntersect_Op
+        SkXfermode::kSrcOver_Mode,  // kUnion_Op
+        SkXfermode::kXor_Mode,      // kXOR_Op
+        SkXfermode::kClear_Mode,    // kReverseDifference_Op
+        SkXfermode::kSrc_Mode,      // kReplace_Op
     };
 
     return modeMap[op];
@@ -42,7 +42,7 @@
                               bool antiAlias, uint8_t alpha) {
     SkPaint paint;
 
-    paint.setBlendMode(op_to_mode(op));
+    paint.setXfermode(SkXfermode::Make(op_to_mode(op)));
     paint.setAntiAlias(antiAlias);
     paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
 
@@ -65,7 +65,7 @@
         SkASSERT(0xFF == paint.getAlpha());
         fDraw.drawPathCoverage(path, paint);
     } else {
-        paint.setBlendMode(op_to_mode(op));
+        paint.setXfermodeMode(op_to_mode(op));
         paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
         fDraw.drawPath(path, paint);
     }
diff --git a/src/gpu/GrTextureParamsAdjuster.cpp b/src/gpu/GrTextureParamsAdjuster.cpp
index f51cc54..3ca90f5 100644
--- a/src/gpu/GrTextureParamsAdjuster.cpp
+++ b/src/gpu/GrTextureParamsAdjuster.cpp
@@ -72,7 +72,7 @@
         GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter);
         paint.addColorTextureProcessor(inputTexture, nullptr, SkMatrix::I(), params);
     }
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
     SkRect localRect;
     if (subset) {
diff --git a/src/gpu/GrTextureToYUVPlanes.cpp b/src/gpu/GrTextureToYUVPlanes.cpp
index 93a62d2..5e7dafe 100644
--- a/src/gpu/GrTextureToYUVPlanes.cpp
+++ b/src/gpu/GrTextureToYUVPlanes.cpp
@@ -41,7 +41,7 @@
         return false;
     }
     GrPaint paint;
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     paint.addColorFragmentProcessor(std::move(fp));
     dst->drawRect(GrNoClip(), paint, SkMatrix::I(), SkRect::MakeIWH(dstW, dstH));
     return true;
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index db58e0a..b187ec3 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -142,7 +142,7 @@
         }
     }
 
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth,
             yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index ee4e40a..d4db461 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -20,7 +20,6 @@
 #include "GrXferProcessor.h"
 #include "GrYUVProvider.h"
 
-#include "SkBlendModePriv.h"
 #include "SkColorFilter.h"
 #include "SkConfig8888.h"
 #include "SkCanvas.h"
@@ -681,8 +680,9 @@
     // When the xfermode is null on the SkPaint (meaning kSrcOver) we need the XPFactory field on
     // the GrPaint to also be null (also kSrcOver).
     SkASSERT(!grPaint->getXPFactory());
-    if (!skPaint.isSrcOver()) {
-        grPaint->setXPFactory(SkBlendMode_AsXPFactory(skPaint.getBlendMode()));
+    SkXfermode* xfermode = skPaint.getXfermode();
+    if (xfermode) {
+        grPaint->setXPFactory(xfermode->asXPFactory());
     }
 
 #ifndef SK_IGNORE_GPU_DITHER
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 80a0314..4eb7a11 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -229,19 +229,19 @@
                 tempDC->asTexture().get(), GrSwizzle::RGBA(), *pmToUPMRule, SkMatrix::I()));
 
         paint1.addColorFragmentProcessor(std::move(pmToUPM1));
-        paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint1.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
         readDC->fillRectToRect(GrNoClip(), paint1, SkMatrix::I(), kDstRect, kSrcRect);
 
         readDC->asTexture()->readPixels(0, 0, kSize, kSize, kConfig, firstRead);
 
         paint2.addColorFragmentProcessor(std::move(upmToPM));
-        paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint2.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
         tempDC->fillRectToRect(GrNoClip(), paint2, SkMatrix::I(), kDstRect, kSrcRect);
 
         paint3.addColorFragmentProcessor(std::move(pmToUPM2));
-        paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint3.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
 
         readDC->fillRectToRect(GrNoClip(), paint3, SkMatrix::I(), kDstRect, kSrcRect);
 
diff --git a/src/gpu/text/GrTextUtils.cpp b/src/gpu/text/GrTextUtils.cpp
index a5685f0..56d4ec1 100644
--- a/src/gpu/text/GrTextUtils.cpp
+++ b/src/gpu/text/GrTextUtils.cpp
@@ -547,7 +547,8 @@
 }
 
 bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
-    return paint.getMaskFilter() ||
+    return !SkXfermode::AsMode(paint.getXfermode(), nullptr) ||
+           paint.getMaskFilter() ||
            paint.getRasterizer() ||
            paint.getPathEffect() ||
            paint.isFakeBoldText() ||
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 67779ba..2870f31 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -259,7 +259,7 @@
     SkCanvas canvas(bm);
 
     SkPaint paint;
-    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     canvas.drawImage(this, -SkIntToScalar(srcX), -SkIntToScalar(srcY), &paint);
 
     return true;
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 217dd3f..f13b4bc 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -47,11 +47,12 @@
 
 // Utility functions
 
-// If the paint will definitely draw opaquely, replace kSrc with
-// kSrcOver.  http://crbug.com/473572
+// If the paint will definitely draw opaquely, replace kSrc_Mode with
+// kSrcOver_Mode.  http://crbug.com/473572
 static void replace_srcmode_on_opaque_paint(SkPaint* paint) {
-    if (kSrcOver_SkXfermodeInterpretation == SkInterpretXfermode(*paint, false)) {
-        paint->setBlendMode(SkBlendMode::kSrcOver);
+    if (kSrcOver_SkXfermodeInterpretation
+        == SkInterpretXfermode(*paint, false)) {
+        paint->setXfermode(nullptr);
     }
 }
 
@@ -391,7 +392,7 @@
                        const SkPaint& paint, bool hasText = false)
         : fDevice(device),
           fContentEntry(nullptr),
-          fBlendMode(SkBlendMode::kSrcOver),
+          fXfermode(SkXfermode::kSrcOver_Mode),
           fDstFormXObject(nullptr) {
         init(draw.fClipStack, draw.fRC->bwRgn(), *draw.fMatrix, paint, hasText);
     }
@@ -400,7 +401,7 @@
                        const SkPaint& paint, bool hasText = false)
         : fDevice(device),
           fContentEntry(nullptr),
-          fBlendMode(SkBlendMode::kSrcOver),
+          fXfermode(SkXfermode::kSrcOver_Mode),
           fDstFormXObject(nullptr) {
         init(clipStack, clipRegion, matrix, paint, hasText);
     }
@@ -411,7 +412,7 @@
             if (shape->isEmpty()) {
                 shape = nullptr;
             }
-            fDevice->finishContentEntry(fBlendMode, std::move(fDstFormXObject), shape);
+            fDevice->finishContentEntry(fXfermode, std::move(fDstFormXObject), shape);
         }
     }
 
@@ -419,16 +420,16 @@
 
     /* Returns true when we explicitly need the shape of the drawing. */
     bool needShape() {
-        switch (fBlendMode) {
-            case SkBlendMode::kClear:
-            case SkBlendMode::kSrc:
-            case SkBlendMode::kSrcIn:
-            case SkBlendMode::kSrcOut:
-            case SkBlendMode::kDstIn:
-            case SkBlendMode::kDstOut:
-            case SkBlendMode::kSrcATop:
-            case SkBlendMode::kDstATop:
-            case SkBlendMode::kModulate:
+        switch (fXfermode) {
+            case SkXfermode::kClear_Mode:
+            case SkXfermode::kSrc_Mode:
+            case SkXfermode::kSrcIn_Mode:
+            case SkXfermode::kSrcOut_Mode:
+            case SkXfermode::kDstIn_Mode:
+            case SkXfermode::kDstOut_Mode:
+            case SkXfermode::kSrcATop_Mode:
+            case SkXfermode::kDstATop_Mode:
+            case SkXfermode::kModulate_Mode:
                 return true;
             default:
                 return false;
@@ -437,7 +438,7 @@
 
     /* Returns true unless we only need the shape of the drawing. */
     bool needSource() {
-        if (fBlendMode == SkBlendMode::kClear) {
+        if (fXfermode == SkXfermode::kClear_Mode) {
             return false;
         }
         return true;
@@ -454,7 +455,7 @@
 private:
     SkPDFDevice* fDevice;
     SkPDFDevice::ContentEntry* fContentEntry;
-    SkBlendMode fBlendMode;
+    SkXfermode::Mode fXfermode;
     sk_sp<SkPDFObject> fDstFormXObject;
     SkPath fShape;
 
@@ -465,7 +466,9 @@
             NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
             return;
         }
-        fBlendMode = paint.getBlendMode();
+        if (paint.getXfermode()) {
+            paint.getXfermode()->asMode(&fXfermode);
+        }
         fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion,
                                                    matrix, paint, hasText,
                                                    &fDstFormXObject);
@@ -1704,7 +1707,7 @@
                                           sk_sp<SkPDFObject> mask,
                                           const SkClipStack* clipStack,
                                           const SkRegion& clipRegion,
-                                          SkBlendMode mode,
+                                          SkXfermode::Mode mode,
                                           bool invertClip) {
     if (clipRegion.isEmpty() && !invertClip) {
         return;
@@ -1717,7 +1720,7 @@
     SkMatrix identity;
     identity.reset();
     SkPaint paint;
-    paint.setBlendMode(mode);
+    paint.setXfermodeMode(mode);
     ScopedContentEntry content(this, clipStack, clipRegion, identity, paint);
     if (!content.entry()) {
         return;
@@ -1762,24 +1765,27 @@
         }
     }
 
-    SkBlendMode blendMode = paint.getBlendMode();
+    SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+    if (paint.getXfermode()) {
+        paint.getXfermode()->asMode(&xfermode);
+    }
 
     // For the following modes, we want to handle source and destination
     // separately, so make an object of what's already there.
-    if (blendMode == SkBlendMode::kClear       ||
-            blendMode == SkBlendMode::kSrc     ||
-            blendMode == SkBlendMode::kSrcIn   ||
-            blendMode == SkBlendMode::kDstIn   ||
-            blendMode == SkBlendMode::kSrcOut  ||
-            blendMode == SkBlendMode::kDstOut  ||
-            blendMode == SkBlendMode::kSrcATop ||
-            blendMode == SkBlendMode::kDstATop ||
-            blendMode == SkBlendMode::kModulate) {
+    if (xfermode == SkXfermode::kClear_Mode       ||
+            xfermode == SkXfermode::kSrc_Mode     ||
+            xfermode == SkXfermode::kSrcIn_Mode   ||
+            xfermode == SkXfermode::kDstIn_Mode   ||
+            xfermode == SkXfermode::kSrcOut_Mode  ||
+            xfermode == SkXfermode::kDstOut_Mode  ||
+            xfermode == SkXfermode::kSrcATop_Mode ||
+            xfermode == SkXfermode::kDstATop_Mode ||
+            xfermode == SkXfermode::kModulate_Mode) {
         if (!isContentEmpty()) {
             *dst = this->makeFormXObjectFromDevice();
             SkASSERT(isContentEmpty());
-        } else if (blendMode != SkBlendMode::kSrc &&
-                   blendMode != SkBlendMode::kSrcOut) {
+        } else if (xfermode != SkXfermode::kSrc_Mode &&
+                   xfermode != SkXfermode::kSrcOut_Mode) {
             // Except for Src and SrcOut, if there isn't anything already there,
             // then we're done.
             return nullptr;
@@ -1789,14 +1795,14 @@
     // Xor, Plus.
 
     // Dst xfer mode doesn't draw source at all.
-    if (blendMode == SkBlendMode::kDst) {
+    if (xfermode == SkXfermode::kDst_Mode) {
         return nullptr;
     }
 
     SkPDFDevice::ContentEntry* entry;
     if (fContentEntries.back() && fContentEntries.back()->fContent.getOffset() == 0) {
         entry = fContentEntries.back();
-    } else if (blendMode != SkBlendMode::kDstOver) {
+    } else if (xfermode != SkXfermode::kDstOver_Mode) {
         entry = fContentEntries.emplace_back();
     } else {
         entry = fContentEntries.emplace_front();
@@ -1806,23 +1812,23 @@
     return entry;
 }
 
-void SkPDFDevice::finishContentEntry(SkBlendMode blendMode,
+void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
                                      sk_sp<SkPDFObject> dst,
                                      SkPath* shape) {
-    if (blendMode != SkBlendMode::kClear       &&
-            blendMode != SkBlendMode::kSrc     &&
-            blendMode != SkBlendMode::kDstOver &&
-            blendMode != SkBlendMode::kSrcIn   &&
-            blendMode != SkBlendMode::kDstIn   &&
-            blendMode != SkBlendMode::kSrcOut  &&
-            blendMode != SkBlendMode::kDstOut  &&
-            blendMode != SkBlendMode::kSrcATop &&
-            blendMode != SkBlendMode::kDstATop &&
-            blendMode != SkBlendMode::kModulate) {
+    if (xfermode != SkXfermode::kClear_Mode       &&
+            xfermode != SkXfermode::kSrc_Mode     &&
+            xfermode != SkXfermode::kDstOver_Mode &&
+            xfermode != SkXfermode::kSrcIn_Mode   &&
+            xfermode != SkXfermode::kDstIn_Mode   &&
+            xfermode != SkXfermode::kSrcOut_Mode  &&
+            xfermode != SkXfermode::kDstOut_Mode  &&
+            xfermode != SkXfermode::kSrcATop_Mode &&
+            xfermode != SkXfermode::kDstATop_Mode &&
+            xfermode != SkXfermode::kModulate_Mode) {
         SkASSERT(!dst);
         return;
     }
-    if (blendMode == SkBlendMode::kDstOver) {
+    if (xfermode == SkXfermode::kDstOver_Mode) {
         SkASSERT(!dst);
         if (fContentEntries.front()->fContent.getOffset() == 0) {
             // For DstOver, an empty content entry was inserted before the rest
@@ -1833,8 +1839,8 @@
         return;
     }
     if (!dst) {
-        SkASSERT(blendMode == SkBlendMode::kSrc ||
-                 blendMode == SkBlendMode::kSrcOut);
+        SkASSERT(xfermode == SkXfermode::kSrc_Mode ||
+                 xfermode == SkXfermode::kSrcOut_Mode);
         return;
     }
 
@@ -1859,8 +1865,8 @@
         // If there is shape, then an empty source with Src, SrcIn, SrcOut,
         // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
         // reduces to Dst.
-        if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
-                blendMode == SkBlendMode::kSrcATop) {
+        if (shape == nullptr || xfermode == SkXfermode::kDstOut_Mode ||
+                xfermode == SkXfermode::kSrcATop_Mode) {
             ScopedContentEntry content(this, &fExistingClipStack,
                                        fExistingClipRegion, identity,
                                        stockPaint);
@@ -1869,7 +1875,7 @@
                                         &content.entry()->fContent);
             return;
         } else {
-            blendMode = SkBlendMode::kClear;
+            xfermode = SkXfermode::kClear_Mode;
         }
     } else {
         SkASSERT(fContentEntries.count() == 1);
@@ -1878,14 +1884,14 @@
 
     // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
     // without alpha.
-    if (blendMode == SkBlendMode::kSrcATop) {
+    if (xfermode == SkXfermode::kSrcATop_Mode) {
         // TODO(vandebo): In order to properly support SrcATop we have to track
         // the shape of what's been drawn at all times. It's the intersection of
         // the non-transparent parts of the device and the outlines (shape) of
         // all images and devices drawn.
         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
                                 &fExistingClipStack, fExistingClipRegion,
-                                SkBlendMode::kSrcOver, true);
+                                SkXfermode::kSrcOver_Mode, true);
     } else {
         if (shape != nullptr) {
             // Draw shape into a form-xobject.
@@ -1901,19 +1907,19 @@
             drawFormXObjectWithMask(addXObjectResource(dst.get()),
                                     this->makeFormXObjectFromDevice(),
                                     &fExistingClipStack, fExistingClipRegion,
-                                    SkBlendMode::kSrcOver, true);
+                                    SkXfermode::kSrcOver_Mode, true);
 
         } else {
             drawFormXObjectWithMask(addXObjectResource(dst.get()), srcFormXObject,
                                     &fExistingClipStack, fExistingClipRegion,
-                                    SkBlendMode::kSrcOver, true);
+                                    SkXfermode::kSrcOver_Mode, true);
         }
     }
 
-    if (blendMode == SkBlendMode::kClear) {
+    if (xfermode == SkXfermode::kClear_Mode) {
         return;
-    } else if (blendMode == SkBlendMode::kSrc ||
-            blendMode == SkBlendMode::kDstATop) {
+    } else if (xfermode == SkXfermode::kSrc_Mode ||
+            xfermode == SkXfermode::kDstATop_Mode) {
         ScopedContentEntry content(this, &fExistingClipStack,
                                    fExistingClipRegion, identity, stockPaint);
         if (content.entry()) {
@@ -1921,10 +1927,10 @@
                     this->addXObjectResource(srcFormXObject.get()),
                     &content.entry()->fContent);
         }
-        if (blendMode == SkBlendMode::kSrc) {
+        if (xfermode == SkXfermode::kSrc_Mode) {
             return;
         }
-    } else if (blendMode == SkBlendMode::kSrcATop) {
+    } else if (xfermode == SkXfermode::kSrcATop_Mode) {
         ScopedContentEntry content(this, &fExistingClipStack,
                                    fExistingClipRegion, identity, stockPaint);
         if (content.entry()) {
@@ -1933,36 +1939,36 @@
         }
     }
 
-    SkASSERT(blendMode == SkBlendMode::kSrcIn   ||
-             blendMode == SkBlendMode::kDstIn   ||
-             blendMode == SkBlendMode::kSrcOut  ||
-             blendMode == SkBlendMode::kDstOut  ||
-             blendMode == SkBlendMode::kSrcATop ||
-             blendMode == SkBlendMode::kDstATop ||
-             blendMode == SkBlendMode::kModulate);
+    SkASSERT(xfermode == SkXfermode::kSrcIn_Mode   ||
+             xfermode == SkXfermode::kDstIn_Mode   ||
+             xfermode == SkXfermode::kSrcOut_Mode  ||
+             xfermode == SkXfermode::kDstOut_Mode  ||
+             xfermode == SkXfermode::kSrcATop_Mode ||
+             xfermode == SkXfermode::kDstATop_Mode ||
+             xfermode == SkXfermode::kModulate_Mode);
 
-    if (blendMode == SkBlendMode::kSrcIn ||
-            blendMode == SkBlendMode::kSrcOut ||
-            blendMode == SkBlendMode::kSrcATop) {
+    if (xfermode == SkXfermode::kSrcIn_Mode ||
+            xfermode == SkXfermode::kSrcOut_Mode ||
+            xfermode == SkXfermode::kSrcATop_Mode) {
         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
                                 std::move(dst),
                                 &fExistingClipStack, fExistingClipRegion,
-                                SkBlendMode::kSrcOver,
-                                blendMode == SkBlendMode::kSrcOut);
+                                SkXfermode::kSrcOver_Mode,
+                                xfermode == SkXfermode::kSrcOut_Mode);
         return;
     } else {
-        SkBlendMode mode = SkBlendMode::kSrcOver;
+        SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
         int resourceID = addXObjectResource(dst.get());
-        if (blendMode == SkBlendMode::kModulate) {
+        if (xfermode == SkXfermode::kModulate_Mode) {
             drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
                                     std::move(dst), &fExistingClipStack,
                                     fExistingClipRegion,
-                                    SkBlendMode::kSrcOver, false);
-            mode = SkBlendMode::kMultiply;
+                                    SkXfermode::kSrcOver_Mode, false);
+            mode = SkXfermode::kMultiply_Mode;
         }
         drawFormXObjectWithMask(resourceID, std::move(srcFormXObject),
                                 &fExistingClipStack, fExistingClipRegion, mode,
-                                blendMode == SkBlendMode::kDstOut);
+                                xfermode == SkXfermode::kDstOut_Mode);
         return;
     }
 }
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 7d207e7..be0a277 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -246,7 +246,7 @@
                                  sk_sp<SkPDFObject> mask,
                                  const SkClipStack* clipStack,
                                  const SkRegion& clipRegion,
-                                 SkBlendMode,
+                                 SkXfermode::Mode mode,
                                  bool invertClip);
 
     // If the paint or clip is such that we shouldn't draw anything, this
@@ -259,7 +259,9 @@
                                     const SkPaint& paint,
                                     bool hasText,
                                     sk_sp<SkPDFObject>* dst);
-    void finishContentEntry(SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
+    void finishContentEntry(SkXfermode::Mode xfermode,
+                            sk_sp<SkPDFObject> dst,
+                            SkPath* shape);
     bool isContentEmpty();
 
     void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index d60526c..a78c4c5 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -12,58 +12,58 @@
 #include "SkPDFGraphicState.h"
 #include "SkPDFUtils.h"
 
-static const char* as_blend_mode(SkBlendMode mode) {
+static const char* as_blend_mode(SkXfermode::Mode mode) {
     switch (mode) {
-        case SkBlendMode::kSrcOver:
+        case SkXfermode::kSrcOver_Mode:
             return "Normal";
-        case SkBlendMode::kMultiply:
+        case SkXfermode::kMultiply_Mode:
             return "Multiply";
-        case SkBlendMode::kScreen:
+        case SkXfermode::kScreen_Mode:
             return "Screen";
-        case SkBlendMode::kOverlay:
+        case SkXfermode::kOverlay_Mode:
             return "Overlay";
-        case SkBlendMode::kDarken:
+        case SkXfermode::kDarken_Mode:
             return "Darken";
-        case SkBlendMode::kLighten:
+        case SkXfermode::kLighten_Mode:
             return "Lighten";
-        case SkBlendMode::kColorDodge:
+        case SkXfermode::kColorDodge_Mode:
             return "ColorDodge";
-        case SkBlendMode::kColorBurn:
+        case SkXfermode::kColorBurn_Mode:
             return "ColorBurn";
-        case SkBlendMode::kHardLight:
+        case SkXfermode::kHardLight_Mode:
             return "HardLight";
-        case SkBlendMode::kSoftLight:
+        case SkXfermode::kSoftLight_Mode:
             return "SoftLight";
-        case SkBlendMode::kDifference:
+        case SkXfermode::kDifference_Mode:
             return "Difference";
-        case SkBlendMode::kExclusion:
+        case SkXfermode::kExclusion_Mode:
             return "Exclusion";
-        case SkBlendMode::kHue:
+        case SkXfermode::kHue_Mode:
             return "Hue";
-        case SkBlendMode::kSaturation:
+        case SkXfermode::kSaturation_Mode:
             return "Saturation";
-        case SkBlendMode::kColor:
+        case SkXfermode::kColor_Mode:
             return "Color";
-        case SkBlendMode::kLuminosity:
+        case SkXfermode::kLuminosity_Mode:
             return "Luminosity";
 
         // These are handled in SkPDFDevice::setUpContentEntry.
-        case SkBlendMode::kClear:
-        case SkBlendMode::kSrc:
-        case SkBlendMode::kDst:
-        case SkBlendMode::kDstOver:
-        case SkBlendMode::kSrcIn:
-        case SkBlendMode::kDstIn:
-        case SkBlendMode::kSrcOut:
-        case SkBlendMode::kDstOut:
-        case SkBlendMode::kSrcATop:
-        case SkBlendMode::kDstATop:
-        case SkBlendMode::kModulate:
+        case SkXfermode::kClear_Mode:
+        case SkXfermode::kSrc_Mode:
+        case SkXfermode::kDst_Mode:
+        case SkXfermode::kDstOver_Mode:
+        case SkXfermode::kSrcIn_Mode:
+        case SkXfermode::kDstIn_Mode:
+        case SkXfermode::kSrcOut_Mode:
+        case SkXfermode::kDstOut_Mode:
+        case SkXfermode::kSrcATop_Mode:
+        case SkXfermode::kDstATop_Mode:
+        case SkXfermode::kModulate_Mode:
             return "Normal";
 
         // TODO(vandebo): Figure out if we can support more of these modes.
-        case SkBlendMode::kXor:
-        case SkBlendMode::kPlus:
+        case SkXfermode::kXor_Mode:
+        case SkXfermode::kPlus_Mode:
             return nullptr;
     }
     return nullptr;
@@ -71,28 +71,32 @@
 
 // If a SkXfermode is unsupported in PDF, this function returns
 // SrcOver, otherwise, it returns that Xfermode as a Mode.
-static SkBlendMode mode_for_pdf(SkBlendMode mode) {
+static SkXfermode::Mode mode_for_pdf(const SkXfermode* xfermode) {
+    SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
+    if (xfermode) {
+        xfermode->asMode(&mode);
+    }
     switch (mode) {
-        case SkBlendMode::kSrcOver:
-        case SkBlendMode::kMultiply:
-        case SkBlendMode::kScreen:
-        case SkBlendMode::kOverlay:
-        case SkBlendMode::kDarken:
-        case SkBlendMode::kLighten:
-        case SkBlendMode::kColorDodge:
-        case SkBlendMode::kColorBurn:
-        case SkBlendMode::kHardLight:
-        case SkBlendMode::kSoftLight:
-        case SkBlendMode::kDifference:
-        case SkBlendMode::kExclusion:
-        case SkBlendMode::kHue:
-        case SkBlendMode::kSaturation:
-        case SkBlendMode::kColor:
-        case SkBlendMode::kLuminosity:
+        case SkXfermode::kSrcOver_Mode:
+        case SkXfermode::kMultiply_Mode:
+        case SkXfermode::kScreen_Mode:
+        case SkXfermode::kOverlay_Mode:
+        case SkXfermode::kDarken_Mode:
+        case SkXfermode::kLighten_Mode:
+        case SkXfermode::kColorDodge_Mode:
+        case SkXfermode::kColorBurn_Mode:
+        case SkXfermode::kHardLight_Mode:
+        case SkXfermode::kSoftLight_Mode:
+        case SkXfermode::kDifference_Mode:
+        case SkXfermode::kExclusion_Mode:
+        case SkXfermode::kHue_Mode:
+        case SkXfermode::kSaturation_Mode:
+        case SkXfermode::kColor_Mode:
+        case SkXfermode::kLuminosity_Mode:
             // Mode is suppported and handled by pdf graphics state.
             return mode;
         default:
-            return SkBlendMode::kSrcOver;  // Default mode.
+            return SkXfermode::kSrcOver_Mode;  // Default mode.
     }
 }
 
@@ -102,7 +106,7 @@
     , fAlpha(p.getAlpha())
     , fStrokeCap(SkToU8(p.getStrokeCap()))
     , fStrokeJoin(SkToU8(p.getStrokeJoin()))
-    , fMode(SkToU8((unsigned)mode_for_pdf(p.getBlendMode()))) {}
+    , fMode(SkToU8(mode_for_pdf(p.getXfermode()))) {}
 
 // static
 SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
@@ -182,6 +186,7 @@
 
     SkPaint::Cap strokeCap = (SkPaint::Cap)fStrokeCap;
     SkPaint::Join strokeJoin = (SkPaint::Join)fStrokeJoin;
+    SkXfermode::Mode xferMode = (SkXfermode::Mode)fMode;
 
     static_assert(SkPaint::kButt_Cap == 0, "paint_cap_mismatch");
     static_assert(SkPaint::kRound_Cap == 1, "paint_cap_mismatch");
@@ -200,6 +205,6 @@
     dict->insertScalar("LW", fStrokeWidth);
     dict->insertScalar("ML", fStrokeMiter);
     dict->insertBool("SA", true);  // SA = Auto stroke adjustment.
-    dict->insertName("BM", as_blend_mode((SkBlendMode)fMode));
+    dict->insertName("BM", as_blend_mode(xferMode));
     dict->emitObject(stream, objNumMap);
 }
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index 8ee6728..0c2e4a0 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -70,7 +70,7 @@
     const uint8_t fAlpha;
     const uint8_t fStrokeCap;   // SkPaint::Cap
     const uint8_t fStrokeJoin;  // SkPaint::Join
-    const uint8_t fMode;        // SkBlendMode
+    const uint8_t fMode;        // SkXfermode::Mode
 
     SkPDFGraphicState(const SkPaint&);
 
diff --git a/src/pipe/SkPipeCanvas.cpp b/src/pipe/SkPipeCanvas.cpp
index 3b636a2..a01237f 100644
--- a/src/pipe/SkPipeCanvas.cpp
+++ b/src/pipe/SkPipeCanvas.cpp
@@ -81,6 +81,7 @@
         bits |= (paint.getMaskFilter()  ? kMaskFilter_NonDef : 0);
     }
 
+    bits |= (paint.getXfermode()    ? kXfermode_NonDef : 0);
     bits |= (paint.getColorFilter() ? kColorFilter_NonDef : 0);
     bits |= (paint.getImageFilter() ? kImageFilter_NonDef : 0);
     bits |= (paint.getDrawLooper()  ? kDrawLooper_NonDef : 0);
@@ -149,8 +150,15 @@
     writer.write32(packedFlags);
 
     unsigned nondef = compute_nondef(paint, (PaintUsage)usage);
+    SkXfermode::Mode mode;
+    if (SkXfermode::AsMode(paint.getXfermode(), &mode)) {
+        nondef &= ~kXfermode_NonDef;    // don't need to store a pointer since we have an enum
+    } else {
+        SkASSERT(nondef & kXfermode_NonDef);
+        mode = (SkXfermode::Mode)0;
+    }
     const uint8_t pad = 0;
-    writer.write32((nondef << 16) | ((unsigned)paint.getBlendMode() << 8) | pad);
+    writer.write32((nondef << 16) | ((unsigned)mode << 8) | pad);
 
     CHECK_WRITE_SCALAR(writer, nondef, paint, TextSize);
     CHECK_WRITE_SCALAR(writer, nondef, paint, TextScaleX);
@@ -171,6 +179,7 @@
 
     CHECK_WRITE_FLATTENABLE(writer, nondef, paint, PathEffect);
     CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Shader);
+    CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Xfermode);
     CHECK_WRITE_FLATTENABLE(writer, nondef, paint, MaskFilter);
     CHECK_WRITE_FLATTENABLE(writer, nondef, paint, ColorFilter);
     CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Rasterizer);
diff --git a/src/pipe/SkPipeFormat.h b/src/pipe/SkPipeFormat.h
index 9a1d30c..8f5c828 100644
--- a/src/pipe/SkPipeFormat.h
+++ b/src/pipe/SkPipeFormat.h
@@ -94,11 +94,12 @@
     kTypeface_NonDef    = 1 << 6,
     kPathEffect_NonDef  = 1 << 7,
     kShader_NonDef      = 1 << 8,
-    kMaskFilter_NonDef  = 1 << 9,
-    kColorFilter_NonDef = 1 << 10,
-    kRasterizer_NonDef  = 1 << 11,
-    kImageFilter_NonDef = 1 << 12,
-    kDrawLooper_NonDef  = 1 << 13,
+    kXfermode_NonDef    = 1 << 9,
+    kMaskFilter_NonDef  = 1 << 10,
+    kColorFilter_NonDef = 1 << 11,
+    kRasterizer_NonDef  = 1 << 12,
+    kImageFilter_NonDef = 1 << 13,
+    kDrawLooper_NonDef  = 1 << 14,
 };
 
 enum {
diff --git a/src/pipe/SkPipeReader.cpp b/src/pipe/SkPipeReader.cpp
index 47d4072..8840da1 100644
--- a/src/pipe/SkPipeReader.cpp
+++ b/src/pipe/SkPipeReader.cpp
@@ -149,13 +149,13 @@
  *      pad zeros       : 8
  */
 static SkPaint read_paint(SkReadBuffer& reader) {
-    SkPaint paint;
-
     uint32_t packedFlags = reader.read32();
     uint32_t extra = reader.read32();
     unsigned nondef = extra >> 16;
-    paint.setBlendMode(SkBlendMode((extra >> 8) & 0xFF));
-    SkASSERT((extra & 0xFF) == 0);  // zero pad byte
+    SkXfermode::Mode mode = (SkXfermode::Mode)((extra >> 8) & 0xFF);
+    SkASSERT((extra & 0xFF) == 0);
+
+    SkPaint paint;
 
     packedFlags >>= 2;  // currently unused
     paint.setTextEncoding((SkPaint::TextEncoding)(packedFlags & 3));    packedFlags >>= 2;
@@ -180,12 +180,17 @@
     CHECK_SET_FLATTENABLE(Typeface);
     CHECK_SET_FLATTENABLE(PathEffect);
     CHECK_SET_FLATTENABLE(Shader);
+    CHECK_SET_FLATTENABLE(Xfermode);
     CHECK_SET_FLATTENABLE(MaskFilter);
     CHECK_SET_FLATTENABLE(ColorFilter);
     CHECK_SET_FLATTENABLE(Rasterizer);
     CHECK_SET_FLATTENABLE(ImageFilter);
     CHECK_SET_FLATTENABLE(DrawLooper);
 
+    if (!(nondef & kXfermode_NonDef)) {
+        paint.setXfermodeMode(mode);
+    }
+
     return paint;
 }
 
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
index 68bd13e..fcb24d2 100644
--- a/src/utils/SkDumpCanvas.cpp
+++ b/src/utils/SkDumpCanvas.cpp
@@ -209,8 +209,8 @@
         if (paint->getAlpha() != 0xFF) {
             str.appendf(" alpha:0x%02X", paint->getAlpha());
         }
-        if (!paint->isSrcOver()) {
-            str.appendf(" blendmode:%d", paint->getBlendMode());
+        if (paint->getXfermode()) {
+            str.appendf(" xfermode:%p", paint->getXfermode());
         }
     }
     this->dump(kSave_Verb, paint, str.c_str());
@@ -540,10 +540,8 @@
 
     if (p) {
         msg.appendf(" color:0x%08X flags:%X", p->getColor(), p->getFlags());
-        if (!p->isSrcOver()) {
-            msg.appendf(" blendmode:%d", p->getBlendMode());
-        }
         appendFlattenable(&msg, p->getShader(), "shader");
+        appendFlattenable(&msg, p->getXfermode(), "xfermode");
         appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
         appendFlattenable(&msg, p->getMaskFilter(), "maskFilter");
         appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp
index ba311af..e80708c 100644
--- a/src/utils/SkLua.cpp
+++ b/src/utils/SkLua.cpp
@@ -28,6 +28,7 @@
 #include "SkSurface.h"
 #include "SkTextBlob.h"
 #include "SkTypeface.h"
+#include "SkXfermode.h"
 
 extern "C" {
     #include "lua.h"
@@ -58,6 +59,7 @@
 DEF_MTNAME(SkSurface)
 DEF_MTNAME(SkTextBlob)
 DEF_MTNAME(SkTypeface)
+DEF_MTNAME(SkXfermode)
 
 template <typename T> T* push_new(lua_State* L) {
     T* addr = (T*)lua_newuserdata(L, sizeof(T));
@@ -1071,9 +1073,26 @@
     setfield_bool_if(L, "shader",      !!paint->getShader());
     setfield_bool_if(L, "colorFilter", !!paint->getColorFilter());
     setfield_bool_if(L, "imageFilter", !!paint->getImageFilter());
+    setfield_bool_if(L, "xfermode",    !!paint->getXfermode());
     return 1;
 }
 
+static int lpaint_getXfermode(lua_State* L) {
+    const SkPaint* paint = get_obj<SkPaint>(L, 1);
+    SkXfermode* xfermode = paint->getXfermode();
+    if (xfermode) {
+        push_ref(L, xfermode);
+        return 1;
+    }
+    return 0;
+}
+
+static int lpaint_setXfermode(lua_State* L) {
+    SkPaint* paint = get_obj<SkPaint>(L, 1);
+    paint->setXfermode(sk_ref_sp(get_ref<SkXfermode>(L, 2)));
+    return 0;
+}
+
 static int lpaint_getColorFilter(lua_State* L) {
     const SkPaint* paint = get_obj<SkPaint>(L, 1);
     SkColorFilter* cf = paint->getColorFilter();
@@ -1198,6 +1217,8 @@
     { "setColorFilter", lpaint_setColorFilter },
     { "getImageFilter", lpaint_getImageFilter },
     { "setImageFilter", lpaint_setImageFilter },
+    { "getXfermode", lpaint_getXfermode },
+    { "setXfermode", lpaint_setXfermode },
     { "getShader", lpaint_getShader },
     { "setShader", lpaint_setShader },
     { "getPathEffect", lpaint_getPathEffect },
@@ -1320,6 +1341,24 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static int lpxfermode_getTypeName(lua_State* L) {
+    lua_pushstring(L, get_ref<SkXfermode>(L, 1)->getTypeName());
+    return 1;
+}
+
+static int lpxfermode_gc(lua_State* L) {
+    get_ref<SkXfermode>(L, 1)->unref();
+    return 0;
+}
+
+static const struct luaL_Reg gSkXfermode_Methods[] = {
+    { "getTypeName",    lpxfermode_getTypeName },
+    { "__gc",           lpxfermode_gc },
+    { nullptr, nullptr }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 static int lpcolorfilter_gc(lua_State* L) {
     get_ref<SkColorFilter>(L, 1)->unref();
     return 0;
@@ -2139,6 +2178,7 @@
     REG_CLASS(L, SkSurface);
     REG_CLASS(L, SkTextBlob);
     REG_CLASS(L, SkTypeface);
+    REG_CLASS(L, SkXfermode);
 }
 
 extern "C" int luaopen_skia(lua_State* L);
diff --git a/src/utils/SkRGBAToYUV.cpp b/src/utils/SkRGBAToYUV.cpp
index 0528b14..63d9152 100644
--- a/src/utils/SkRGBAToYUV.cpp
+++ b/src/utils/SkRGBAToYUV.cpp
@@ -45,7 +45,7 @@
         }
         SkPaint paint;
         paint.setFilterQuality(kLow_SkFilterQuality);
-        paint.setBlendMode(SkBlendMode::kSrc);
+        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
         int rowStartIdx = 5 * i;
         const SkScalar* row = kYUVColorSpaceInvMatrices[colorSpace] + rowStartIdx;
         paint.setColorFilter(
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index 5db644c..7757cb8 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -1220,7 +1220,7 @@
                                    const SkPaint& paint) {
     //Exit early if there is nothing to draw.
     if (d.fRC->isEmpty() ||
-        (paint.getAlpha() == 0 && paint.isSrcOver())) {
+        (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
         return;
     }
 
@@ -1536,7 +1536,7 @@
 
     // nothing to draw
     if (d.fRC->isEmpty() ||
-        (paint->getAlpha() == 0 && paint->isSrcOver())) {
+        (paint->getAlpha() == 0 && paint->getXfermode() == nullptr)) {
         return;
     }