Create fragment processor for performing input color blend with child processor

The new FP is used to implement SkXM::Mode color filters and SkXM::Mode image filters. Also, these now support all advanced SkXM::Mode xfermodes.

Review URL: https://codereview.chromium.org/1334293003
diff --git a/src/gpu/GrBlend.cpp b/src/gpu/GrBlend.cpp
new file mode 100644
index 0000000..72a89d8
--- /dev/null
+++ b/src/gpu/GrBlend.cpp
@@ -0,0 +1,124 @@
+/*
+* Copyright 2015 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "GrBlend.h"
+
+/**
+ * MaskedColor is used to evaluate the color and valid color component flags through the
+ * blending equation. Could possibly extend this to be used more broadly.
+ */
+class MaskedColor {
+public:
+    MaskedColor(GrColor color, GrColorComponentFlags flags)
+        : fColor(color)
+        , fFlags(flags) {}
+
+    MaskedColor() {}
+
+    void set(GrColor color, GrColorComponentFlags flags) {
+        fColor = color;
+        fFlags = flags;
+    }
+
+    static MaskedColor Invert(const MaskedColor& in) {
+        return MaskedColor(GrInvertColor(in.fColor), in.fFlags);
+    }
+
+    static MaskedColor ExtractAlpha(const MaskedColor& in) {
+        GrColorComponentFlags flags = (in.fFlags & kA_GrColorComponentFlag) ?
+            kRGBA_GrColorComponentFlags : kNone_GrColorComponentFlags;
+        return MaskedColor(GrColorPackA4(GrColorUnpackA(in.fColor)), flags);
+    }
+
+    static MaskedColor ExtractInverseAlpha(const MaskedColor& in) {
+        GrColorComponentFlags flags = (in.fFlags & kA_GrColorComponentFlag) ?
+            kRGBA_GrColorComponentFlags : kNone_GrColorComponentFlags;
+        return MaskedColor(GrColorPackA4(0xFF - GrColorUnpackA(in.fColor)), flags);
+    }
+
+    static MaskedColor Mul(const MaskedColor& a, const MaskedColor& b) {
+        GrColorComponentFlags outFlags = (a.fFlags & b.fFlags) | a.componentsWithValue(0) |
+                                         b.componentsWithValue(0);
+        return MaskedColor(GrColorMul(a.fColor, b.fColor), outFlags);
+    }
+
+    static MaskedColor SatAdd(const MaskedColor& a, const MaskedColor& b) {
+        GrColorComponentFlags outFlags = (a.fFlags & b.fFlags) | a.componentsWithValue(0xFF) |
+                                         b.componentsWithValue(0xFF);
+        return MaskedColor(GrColorSatAdd(a.fColor, b.fColor), outFlags);
+    }
+
+    GrColor color() const { return fColor; }
+
+    GrColorComponentFlags validFlags () const { return fFlags; }
+
+private:
+    GrColorComponentFlags componentsWithValue(unsigned value) const {
+        GrColorComponentFlags flags = kNone_GrColorComponentFlags;
+        if ((kR_GrColorComponentFlag & fFlags) && value == GrColorUnpackR(fColor)) {
+            flags |= kR_GrColorComponentFlag;
+        }
+        if ((kG_GrColorComponentFlag & fFlags) && value == GrColorUnpackG(fColor)) {
+            flags |= kG_GrColorComponentFlag;
+        }
+        if ((kB_GrColorComponentFlag & fFlags) && value == GrColorUnpackB(fColor)) {
+            flags |= kB_GrColorComponentFlag;
+        }
+        if ((kA_GrColorComponentFlag & fFlags) && value == GrColorUnpackA(fColor)) {
+            flags |= kA_GrColorComponentFlag;
+        }
+        return flags;
+    }
+
+    GrColor                 fColor;
+    GrColorComponentFlags   fFlags;
+};
+
+static MaskedColor get_term(GrBlendCoeff coeff, const MaskedColor& src, const MaskedColor& dst,
+                            const MaskedColor& value) {
+    switch (coeff) {
+        case kZero_GrBlendCoeff:
+            return MaskedColor(0, kRGBA_GrColorComponentFlags);
+        case kOne_GrBlendCoeff:
+            return value;
+        case kDC_GrBlendCoeff:
+            return MaskedColor::Mul(dst, value);
+        case kIDC_GrBlendCoeff:
+            return MaskedColor::Mul(MaskedColor::Invert(dst), value);
+        case kDA_GrBlendCoeff:
+            return MaskedColor::Mul(MaskedColor::ExtractAlpha(dst), value);
+        case kIDA_GrBlendCoeff:
+            return MaskedColor::Mul(MaskedColor::ExtractInverseAlpha(dst), value);
+        case kSC_GrBlendCoeff:
+            return MaskedColor::Mul(src, value);
+        case kISC_GrBlendCoeff:
+            return MaskedColor::Mul(MaskedColor::Invert(src), value);
+        case kSA_GrBlendCoeff:
+            return MaskedColor::Mul(MaskedColor::ExtractAlpha(src), value);
+        case kISA_GrBlendCoeff:
+            return MaskedColor::Mul(MaskedColor::ExtractInverseAlpha(src), value);
+        default:
+            SkFAIL("Illegal coefficient");
+            return MaskedColor();
+    }
+}
+
+void GrGetCoeffBlendKnownComponents(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff,
+                                    GrColor srcColor, GrColorComponentFlags srcColorFlags,
+                                    GrColor dstColor, GrColorComponentFlags dstColorFlags,
+                                    GrColor* outColor,
+                                    GrColorComponentFlags* outFlags) {
+    MaskedColor src(srcColor, srcColorFlags);
+    MaskedColor dst(dstColor, dstColorFlags);
+
+    MaskedColor srcTerm = get_term(srcCoeff, src, dst, src);
+    MaskedColor dstTerm = get_term(dstCoeff, src, dst, dst);
+
+    MaskedColor output = MaskedColor::SatAdd(srcTerm, dstTerm);
+    *outColor = output.color();
+    *outFlags = output.validFlags();
+}
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index b3544b0..782b82b 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -50,7 +50,7 @@
  * we verify the count is as expected.  If a new factory is added, then these numbers must be
  * manually adjusted.
  */
-static const int kFPFactoryCount = 40;
+static const int kFPFactoryCount = 39;
 static const int kGPFactoryCount = 14;
 static const int kXPFactoryCount = 5;
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 471c972..ee06f3e 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -734,7 +734,7 @@
             SkColor filtered = colorFilter->filterColor(skPaint.getColor());
             grPaint->setColor(SkColor2GrColor(filtered));
         } else {
-            SkTDArray<GrFragmentProcessor*> array;
+            SkTDArray<const GrFragmentProcessor*> array;
             // return false if failed?
             if (colorFilter->asFragmentProcessors(context, grPaint->getProcessorDataManager(),
                                                   &array)) {
diff --git a/src/gpu/effects/GrCustomXfermode.cpp b/src/gpu/effects/GrCustomXfermode.cpp
index f626e7a..0d31034 100644
--- a/src/gpu/effects/GrCustomXfermode.cpp
+++ b/src/gpu/effects/GrCustomXfermode.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "effects/GrCustomXfermode.h"
-#include "effects/GrCustomXfermodePriv.h"
 
 #include "GrCoordTransform.h"
 #include "GrContext.h"
@@ -18,6 +17,7 @@
 #include "SkXfermode.h"
 #include "gl/GrGLCaps.h"
 #include "gl/GrGLGpu.h"
+#include "gl/GrGLSLBlend.h"
 #include "gl/GrGLFragmentProcessor.h"
 #include "gl/GrGLProgramDataManager.h"
 #include "gl/builders/GrGLProgramBuilder.h"
@@ -67,452 +67,6 @@
     return true;
 }
 
-static void hard_light(GrGLFragmentBuilder* fsBuilder,
-                       const char* final,
-                       const char* src,
-                       const char* dst) {
-    static const char kComponents[] = {'r', 'g', 'b'};
-    for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
-        char component = kComponents[i];
-        fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
-        fsBuilder->codeAppendf("%s.%c = 2.0 * %s.%c * %s.%c;",
-                               final, component, src, component, dst, component);
-        fsBuilder->codeAppend("} else {");
-        fsBuilder->codeAppendf("%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);",
-                               final, component, src, dst, dst, dst, component, src, src,
-                               component);
-        fsBuilder->codeAppend("}");
-    }
-    fsBuilder->codeAppendf("%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);",
-                           final, src, dst, dst, src);
-}
-
-// Does one component of color-dodge
-static void color_dodge_component(GrGLFragmentBuilder* fsBuilder,
-                                  const char* final,
-                                  const char* src,
-                                  const char* dst,
-                                  const char component) {
-    fsBuilder->codeAppendf("if (0.0 == %s.%c) {", dst, component);
-    fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
-                           final, component, src, component, dst);
-    fsBuilder->codeAppend("} else {");
-    fsBuilder->codeAppendf("float d = %s.a - %s.%c;", src, src, component);
-    fsBuilder->codeAppend("if (0.0 == d) {");
-    fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
-                           final, component, src, dst, src, component, dst, dst, component,
-                           src);
-    fsBuilder->codeAppend("} else {");
-    fsBuilder->codeAppendf("d = min(%s.a, %s.%c * %s.a / d);",
-                           dst, dst, component, src);
-    fsBuilder->codeAppendf("%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
-                           final, component, src, src, component, dst, dst, component, src);
-    fsBuilder->codeAppend("}");
-    fsBuilder->codeAppend("}");
-}
-
-// Does one component of color-burn
-static void color_burn_component(GrGLFragmentBuilder* fsBuilder,
-                                 const char* final,
-                                 const char* src,
-                                 const char* dst,
-                                 const char component) {
-    fsBuilder->codeAppendf("if (%s.a == %s.%c) {", dst, dst, component);
-    fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
-                           final, component, src, dst, src, component, dst, dst, component,
-                           src);
-    fsBuilder->codeAppendf("} else if (0.0 == %s.%c) {", src, component);
-    fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
-                           final, component, dst, component, src);
-    fsBuilder->codeAppend("} else {");
-    fsBuilder->codeAppendf("float d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);",
-                           dst, dst, dst, component, src, src, component);
-    fsBuilder->codeAppendf("%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
-                           final, component, src, src, component, dst, dst, component, src);
-    fsBuilder->codeAppend("}");
-}
-
-// Does one component of soft-light. Caller should have already checked that dst alpha > 0.
-static void soft_light_component_pos_dst_alpha(GrGLFragmentBuilder* fsBuilder,
-                                               const char* final,
-                                               const char* src,
-                                               const char* dst,
-                                               const char component) {
-    // if (2S < Sa)
-    fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
-    // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
-    fsBuilder->codeAppendf("%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a +"
-                                   "(1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s.%c + 1.0);",
-                           final, component, dst, component, dst, component, src, src,
-                           component, dst, dst, src, component, dst, component, src, src,
-                           component);
-    // else if (4D < Da)
-    fsBuilder->codeAppendf("} else if (4.0 * %s.%c <= %s.a) {",
-                           dst, component, dst);
-    fsBuilder->codeAppendf("float DSqd = %s.%c * %s.%c;",
-                           dst, component, dst, component);
-    fsBuilder->codeAppendf("float DCub = DSqd * %s.%c;", dst, component);
-    fsBuilder->codeAppendf("float DaSqd = %s.a * %s.a;", dst, dst);
-    fsBuilder->codeAppendf("float DaCub = DaSqd * %s.a;", dst);
-    // (Da^3 (-S)+Da^2 (S-D (3 Sa-6 S-1))+12 Da D^2 (Sa-2 S)-16 D^3 (Sa-2 S))/Da^2
-    fsBuilder->codeAppendf("%s.%c ="
-                           "(DaSqd*(%s.%c - %s.%c * (3.0*%s.a - 6.0*%s.%c - 1.0)) +"
-                           " 12.0*%s.a*DSqd*(%s.a - 2.0*%s.%c) - 16.0*DCub * (%s.a - 2.0*%s.%c) -"
-                           " DaCub*%s.%c) / DaSqd;",
-                           final, component, src, component, dst, component,
-                           src, src, component, dst, src, src, component, src, src,
-                           component, src, component);
-    fsBuilder->codeAppendf("} else {");
-    // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
-    fsBuilder->codeAppendf("%s.%c = %s.%c*(%s.a - 2.0*%s.%c + 1.0) + %s.%c -"
-                           " sqrt(%s.a*%s.%c)*(%s.a - 2.0*%s.%c) - %s.a*%s.%c;",
-                           final, component, dst, component, src, src, component, src, component,
-                           dst, dst, component, src, src, component, dst, src, component);
-    fsBuilder->codeAppendf("}");
-}
-
-// Adds a function that takes two colors and an alpha as input. It produces a color with the
-// hue and saturation of the first color, the luminosity of the second color, and the input
-// alpha. It has this signature:
-//      vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
-static void add_lum_function(GrGLFragmentBuilder* fsBuilder, SkString* setLumFunction) {
-    // Emit a helper that gets the luminance of a color.
-    SkString getFunction;
-    GrGLShaderVar getLumArgs[] = {
-        GrGLShaderVar("color", kVec3f_GrSLType),
-    };
-    SkString getLumBody("return dot(vec3(0.3, 0.59, 0.11), color);");
-    fsBuilder->emitFunction(kFloat_GrSLType,
-                            "luminance",
-                            SK_ARRAY_COUNT(getLumArgs), getLumArgs,
-                            getLumBody.c_str(),
-                            &getFunction);
-
-    // Emit the set luminance function.
-    GrGLShaderVar setLumArgs[] = {
-        GrGLShaderVar("hueSat", kVec3f_GrSLType),
-        GrGLShaderVar("alpha", kFloat_GrSLType),
-        GrGLShaderVar("lumColor", kVec3f_GrSLType),
-    };
-    SkString setLumBody;
-    setLumBody.printf("float diff = %s(lumColor - hueSat);", getFunction.c_str());
-    setLumBody.append("vec3 outColor = hueSat + diff;");
-    setLumBody.appendf("float outLum = %s(outColor);", getFunction.c_str());
-    setLumBody.append("float minComp = min(min(outColor.r, outColor.g), outColor.b);"
-                      "float maxComp = max(max(outColor.r, outColor.g), outColor.b);"
-                      "if (minComp < 0.0 && outLum != minComp) {"
-                      "outColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) /"
-                                          "(outLum - minComp);"
-                      "}"
-                      "if (maxComp > alpha && maxComp != outLum) {"
-                      "outColor = outLum +"
-                                 "((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) /"
-                                 "(maxComp - outLum);"
-                      "}"
-                      "return outColor;");
-    fsBuilder->emitFunction(kVec3f_GrSLType,
-                            "set_luminance",
-                            SK_ARRAY_COUNT(setLumArgs), setLumArgs,
-                            setLumBody.c_str(),
-                            setLumFunction);
-}
-
-// Adds a function that creates a color with the hue and luminosity of one input color and
-// the saturation of another color. It will have this signature:
-//      float set_saturation(vec3 hueLumColor, vec3 satColor)
-static void add_sat_function(GrGLFragmentBuilder* fsBuilder, SkString* setSatFunction) {
-    // Emit a helper that gets the saturation of a color
-    SkString getFunction;
-    GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
-    SkString getSatBody;
-    getSatBody.printf("return max(max(color.r, color.g), color.b) - "
-                      "min(min(color.r, color.g), color.b);");
-    fsBuilder->emitFunction(kFloat_GrSLType,
-                            "saturation",
-                            SK_ARRAY_COUNT(getSatArgs), getSatArgs,
-                            getSatBody.c_str(),
-                            &getFunction);
-
-    // Emit a helper that sets the saturation given sorted input channels. This used
-    // to use inout params for min, mid, and max components but that seems to cause
-    // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
-    // adjusted min, mid, and max inputs, respectively.
-    SkString helperFunction;
-    GrGLShaderVar helperArgs[] = {
-        GrGLShaderVar("minComp", kFloat_GrSLType),
-        GrGLShaderVar("midComp", kFloat_GrSLType),
-        GrGLShaderVar("maxComp", kFloat_GrSLType),
-        GrGLShaderVar("sat", kFloat_GrSLType),
-    };
-    static const char kHelperBody[] = "if (minComp < maxComp) {"
-        "vec3 result;"
-        "result.r = 0.0;"
-        "result.g = sat * (midComp - minComp) / (maxComp - minComp);"
-        "result.b = sat;"
-        "return result;"
-        "} else {"
-        "return vec3(0, 0, 0);"
-        "}";
-    fsBuilder->emitFunction(kVec3f_GrSLType,
-                            "set_saturation_helper",
-                            SK_ARRAY_COUNT(helperArgs), helperArgs,
-                            kHelperBody,
-                            &helperFunction);
-
-    GrGLShaderVar setSatArgs[] = {
-        GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
-        GrGLShaderVar("satColor", kVec3f_GrSLType),
-    };
-    const char* helpFunc = helperFunction.c_str();
-    SkString setSatBody;
-    setSatBody.appendf("float sat = %s(satColor);"
-                       "if (hueLumColor.r <= hueLumColor.g) {"
-                       "if (hueLumColor.g <= hueLumColor.b) {"
-                       "hueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);"
-                       "} else if (hueLumColor.r <= hueLumColor.b) {"
-                       "hueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);"
-                       "} else {"
-                       "hueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);"
-                       "}"
-                       "} else if (hueLumColor.r <= hueLumColor.b) {"
-                       "hueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);"
-                       "} else if (hueLumColor.g <= hueLumColor.b) {"
-                       "hueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);"
-                       "} else {"
-                       "hueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);"
-                       "}"
-                       "return hueLumColor;",
-                       getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
-                       helpFunc, helpFunc);
-    fsBuilder->emitFunction(kVec3f_GrSLType,
-                            "set_saturation",
-                            SK_ARRAY_COUNT(setSatArgs), setSatArgs,
-                            setSatBody.c_str(),
-                            setSatFunction);
-
-}
-
-static void emit_custom_xfermode_code(SkXfermode::Mode mode,
-                                      GrGLFragmentBuilder* fsBuilder,
-                                      const char* outputColor,
-                                      const char* inputColor,
-                                      const char* dstColor) {
-    // We don't try to optimize for this case at all
-    if (nullptr == inputColor) {
-        fsBuilder->codeAppendf("const vec4 ones = vec4(1);");
-        inputColor = "ones";
-    }
-    fsBuilder->codeAppendf("// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
-
-    // These all perform src-over on the alpha channel.
-    fsBuilder->codeAppendf("%s.a = %s.a + (1.0 - %s.a) * %s.a;",
-                           outputColor, inputColor, inputColor, dstColor);
-
-    switch (mode) {
-        case SkXfermode::kOverlay_Mode:
-            // Overlay is Hard-Light with the src and dst reversed
-            hard_light(fsBuilder, outputColor, dstColor, inputColor);
-            break;
-        case SkXfermode::kDarken_Mode:
-            fsBuilder->codeAppendf("%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
-                                   "(1.0 - %s.a) * %s.rgb + %s.rgb);",
-                                   outputColor,
-                                   inputColor, dstColor, inputColor,
-                                   dstColor, inputColor, dstColor);
-            break;
-        case SkXfermode::kLighten_Mode:
-            fsBuilder->codeAppendf("%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
-                                   "(1.0 - %s.a) * %s.rgb + %s.rgb);",
-                                   outputColor,
-                                   inputColor, dstColor, inputColor,
-                                   dstColor, inputColor, dstColor);
-            break;
-        case SkXfermode::kColorDodge_Mode:
-            color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'r');
-            color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'g');
-            color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'b');
-            break;
-        case SkXfermode::kColorBurn_Mode:
-            color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'r');
-            color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'g');
-            color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'b');
-            break;
-        case SkXfermode::kHardLight_Mode:
-            hard_light(fsBuilder, outputColor, inputColor, dstColor);
-            break;
-        case SkXfermode::kSoftLight_Mode:
-            fsBuilder->codeAppendf("if (0.0 == %s.a) {", dstColor);
-            fsBuilder->codeAppendf("%s.rgba = %s;", outputColor, inputColor);
-            fsBuilder->codeAppendf("} else {");
-            soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'r');
-            soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'g');
-            soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'b');
-            fsBuilder->codeAppendf("}");
-            break;
-        case SkXfermode::kDifference_Mode:
-            fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb -"
-                                   "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);",
-                                   outputColor, inputColor, dstColor, inputColor, dstColor,
-                                   dstColor, inputColor);
-            break;
-        case SkXfermode::kExclusion_Mode:
-            fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb - "
-                                   "2.0 * %s.rgb * %s.rgb;",
-                                   outputColor, dstColor, inputColor, dstColor, inputColor);
-            break;
-        case SkXfermode::kMultiply_Mode:
-            fsBuilder->codeAppendf("%s.rgb = (1.0 - %s.a) * %s.rgb + "
-                                   "(1.0 - %s.a) * %s.rgb + "
-                                   "%s.rgb * %s.rgb;",
-                                   outputColor, inputColor, dstColor, dstColor, inputColor,
-                                   inputColor, dstColor);
-            break;
-        case SkXfermode::kHue_Mode: {
-            //  SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
-            SkString setSat, setLum;
-            add_sat_function(fsBuilder, &setSat);
-            add_lum_function(fsBuilder, &setLum);
-            fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
-                                   dstColor, inputColor);
-            fsBuilder->codeAppendf("%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb),"
-                                               "dstSrcAlpha.a, dstSrcAlpha.rgb);",
-                                   outputColor, setLum.c_str(), setSat.c_str(), inputColor,
-                                   dstColor);
-            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
-                                   outputColor, inputColor, dstColor, dstColor, inputColor);
-            break;
-        }
-        case SkXfermode::kSaturation_Mode: {
-            // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
-            SkString setSat, setLum;
-            add_sat_function(fsBuilder, &setSat);
-            add_lum_function(fsBuilder, &setLum);
-            fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
-                                   dstColor, inputColor);
-            fsBuilder->codeAppendf("%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a),"
-                                               "dstSrcAlpha.a, dstSrcAlpha.rgb);",
-                                   outputColor, setLum.c_str(), setSat.c_str(), inputColor,
-                                   dstColor);
-            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
-                                   outputColor, inputColor, dstColor, dstColor, inputColor);
-            break;
-        }
-        case SkXfermode::kColor_Mode: {
-            //  SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
-            SkString setLum;
-            add_lum_function(fsBuilder, &setLum);
-            fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
-                                   inputColor, dstColor);
-            fsBuilder->codeAppendf("%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);",
-                                   outputColor, setLum.c_str(), dstColor, inputColor);
-            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
-                                   outputColor, inputColor, dstColor, dstColor, inputColor);
-            break;
-        }
-        case SkXfermode::kLuminosity_Mode: {
-            //  SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
-            SkString setLum;
-            add_lum_function(fsBuilder, &setLum);
-            fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
-                                   inputColor, dstColor);
-            fsBuilder->codeAppendf("%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);",
-                                   outputColor, setLum.c_str(), dstColor, inputColor);
-            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
-                                   outputColor, inputColor, dstColor, dstColor, inputColor);
-            break;
-        }
-        default:
-            SkFAIL("Unknown Custom Xfer mode.");
-            break;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Fragment Processor
-///////////////////////////////////////////////////////////////////////////////
-
-GrFragmentProcessor* GrCustomXfermode::CreateFP(GrProcessorDataManager* procDataManager,
-                                                SkXfermode::Mode mode, GrTexture* background) {
-    if (!GrCustomXfermode::IsSupportedMode(mode)) {
-        return nullptr;
-    } else {
-        return new GrCustomXferFP(procDataManager, mode, background);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class GLCustomXferFP : public GrGLFragmentProcessor {
-public:
-    GLCustomXferFP(const GrFragmentProcessor&) {}
-    ~GLCustomXferFP() override {};
-
-    void emitCode(EmitArgs& args) override {
-        SkXfermode::Mode mode = args.fFp.cast<GrCustomXferFP>().mode();
-        GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
-        const char* dstColor = "bgColor";
-        fsBuilder->codeAppendf("vec4 %s = ", dstColor);
-        fsBuilder->appendTextureLookup(args.fSamplers[0], args.fCoords[0].c_str(),
-                                       args.fCoords[0].getType());
-        fsBuilder->codeAppendf(";");
-
-        emit_custom_xfermode_code(mode, fsBuilder, args.fOutputColor, args.fInputColor, dstColor);
-    }
-
-    static void GenKey(const GrFragmentProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
-        // The background may come from the dst or from a texture.
-        uint32_t key = proc.numTextures();
-        SkASSERT(key <= 1);
-        key |= proc.cast<GrCustomXferFP>().mode() << 1;
-        b->add32(key);
-    }
-
-protected:
-    void onSetData(const GrGLProgramDataManager&, const GrProcessor&) override {}
-
-private:
-    typedef GrGLFragmentProcessor INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrCustomXferFP::GrCustomXferFP(GrProcessorDataManager*, SkXfermode::Mode mode, GrTexture* background)
-    : fMode(mode) {
-    this->initClassID<GrCustomXferFP>();
-
-    SkASSERT(background);
-    fBackgroundTransform.reset(kLocal_GrCoordSet, background, 
-                               GrTextureParams::kNone_FilterMode);
-    this->addCoordTransform(&fBackgroundTransform);
-    fBackgroundAccess.reset(background);
-    this->addTextureAccess(&fBackgroundAccess);
-}
-
-void GrCustomXferFP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
-    GLCustomXferFP::GenKey(*this, caps, b);
-}
-
-GrGLFragmentProcessor* GrCustomXferFP::onCreateGLInstance() const {
-    return new GLCustomXferFP(*this);
-}
-
-bool GrCustomXferFP::onIsEqual(const GrFragmentProcessor& other) const {
-    const GrCustomXferFP& s = other.cast<GrCustomXferFP>();
-    return fMode == s.fMode;
-}
-
-void GrCustomXferFP::onComputeInvariantOutput(GrInvariantOutput* inout) const {
-    inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCustomXferFP);
-const GrFragmentProcessor* GrCustomXferFP::TestCreate(GrProcessorTestData* d) {
-    int mode = d->fRandom->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
-
-    return new GrCustomXferFP(d->fProcDataManager, static_cast<SkXfermode::Mode>(mode),
-                              d->fTextures[0]);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Xfer Processor
 ///////////////////////////////////////////////////////////////////////////////
@@ -567,16 +121,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) {
-    if (!GrCustomXfermode::IsSupportedMode(mode)) {
-        return nullptr;
-    } else {
-        return new GrCustomXPFactory(mode);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
 class GLCustomXP : public GrGLXferProcessor {
 public:
     GLCustomXP(const GrXferProcessor&) {}
@@ -621,7 +165,7 @@
         SkASSERT(!xp.hasHWBlendEquation());
 
         GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
-        emit_custom_xfermode_code(xp.mode(), fsBuilder, outColor, srcColor, dstColor);
+        GrGLSLBlend::AppendMode(fsBuilder, srcColor, dstColor, outColor, xp.mode());
     }
 
     void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
@@ -770,20 +314,54 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+class CustomXPFactory : public GrXPFactory {
+public:
+    CustomXPFactory(SkXfermode::Mode mode);
 
-GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
+    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const override {
+        return true;
+    }
+
+    void getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
+                                  GrXPFactory::InvariantBlendedColor*) const override;
+
+private:
+    GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
+                                           const GrProcOptInfo& colorPOI,
+                                           const GrProcOptInfo& coveragePOI,
+                                           bool hasMixedSamples,
+                                           const DstTexture*) const override;
+
+    bool willReadDstColor(const GrCaps& caps,
+                          const GrProcOptInfo& colorPOI,
+                          const GrProcOptInfo& coveragePOI,
+                          bool hasMixedSamples) const override;
+
+    bool onIsEqual(const GrXPFactory& xpfBase) const override {
+        const CustomXPFactory& xpf = xpfBase.cast<CustomXPFactory>();
+        return fMode == xpf.fMode;
+    }
+
+    GR_DECLARE_XP_FACTORY_TEST;
+
+    SkXfermode::Mode fMode;
+    GrBlendEquation  fHWBlendEquation;
+
+    typedef GrXPFactory INHERITED;
+};
+
+CustomXPFactory::CustomXPFactory(SkXfermode::Mode mode)
     : fMode(mode),
       fHWBlendEquation(hw_blend_equation(mode)) {
     SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
-    this->initClassID<GrCustomXPFactory>();
+    this->initClassID<CustomXPFactory>();
 }
 
-GrXferProcessor*
-GrCustomXPFactory::onCreateXferProcessor(const GrCaps& caps,
-                                         const GrProcOptInfo& colorPOI,
-                                         const GrProcOptInfo& coveragePOI,
-                                         bool hasMixedSamples,
-                                         const DstTexture* dstTexture) const {
+GrXferProcessor* CustomXPFactory::onCreateXferProcessor(const GrCaps& caps,
+                                                        const GrProcOptInfo& colorPOI,
+                                                        const GrProcOptInfo& coveragePOI,
+                                                        bool hasMixedSamples,
+                                                        const DstTexture* dstTexture) const {
     if (can_use_hw_blend_equation(fHWBlendEquation, coveragePOI, caps)) {
         SkASSERT(!dstTexture || !dstTexture->texture());
         return new CustomXP(fMode, fHWBlendEquation);
@@ -791,24 +369,33 @@
     return new CustomXP(dstTexture, hasMixedSamples, fMode);
 }
 
-bool GrCustomXPFactory::willReadDstColor(const GrCaps& caps,
-                                         const GrProcOptInfo& colorPOI,
-                                         const GrProcOptInfo& coveragePOI,
-                                         bool hasMixedSamples) const {
+bool CustomXPFactory::willReadDstColor(const GrCaps& caps,
+                                       const GrProcOptInfo& colorPOI,
+                                       const GrProcOptInfo& coveragePOI,
+                                       bool hasMixedSamples) const {
     return !can_use_hw_blend_equation(fHWBlendEquation, coveragePOI, caps);
 }
 
-void GrCustomXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
-                                                 InvariantBlendedColor* blendedColor) const {
+void CustomXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
+                                               InvariantBlendedColor* blendedColor) const {
     blendedColor->fWillBlendWithDst = true;
     blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags;
 }
 
-GR_DEFINE_XP_FACTORY_TEST(GrCustomXPFactory);
-const GrXPFactory* GrCustomXPFactory::TestCreate(GrProcessorTestData* d) {
+GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory);
+const GrXPFactory* CustomXPFactory::TestCreate(GrProcessorTestData* d) {
     int mode = d->fRandom->nextRangeU(SkXfermode::kLastCoeffMode + 1,
                                       SkXfermode::kLastSeparableMode);
 
-    return new GrCustomXPFactory(static_cast<SkXfermode::Mode>(mode));
+    return new CustomXPFactory(static_cast<SkXfermode::Mode>(mode));
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) {
+    if (!GrCustomXfermode::IsSupportedMode(mode)) {
+        return nullptr;
+    } else {
+        return new CustomXPFactory(mode);
+    }
+}
diff --git a/src/gpu/effects/GrCustomXfermodePriv.h b/src/gpu/effects/GrCustomXfermodePriv.h
deleted file mode 100644
index 05e07b6..0000000
--- a/src/gpu/effects/GrCustomXfermodePriv.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCustomXfermodePriv_DEFINED
-#define GrCustomXfermodePriv_DEFINED
-
-#include "GrCaps.h"
-#include "GrCoordTransform.h"
-#include "GrFragmentProcessor.h"
-#include "GrTextureAccess.h"
-#include "GrXferProcessor.h"
-#include "SkXfermode.h"
-
-class GrGLCaps;
-class GrGLFragmentProcessor;
-class GrInvariantOutput;
-class GrProcessorKeyBuilder;
-class GrTexture;
-
-///////////////////////////////////////////////////////////////////////////////
-// Fragment Processor
-///////////////////////////////////////////////////////////////////////////////
-
-class GrCustomXferFP : public GrFragmentProcessor {
-public:
-    GrCustomXferFP(GrProcessorDataManager*, SkXfermode::Mode mode, GrTexture* background);
-
-    const char* name() const override { return "Custom Xfermode"; }
-
-    SkXfermode::Mode mode() const { return fMode; }
-    const GrTextureAccess&  backgroundAccess() const { return fBackgroundAccess; }
-
-private:
-    GrGLFragmentProcessor* onCreateGLInstance() const override;
-
-    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
-
-    bool onIsEqual(const GrFragmentProcessor& other) const override; 
-
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    SkXfermode::Mode fMode;
-    GrCoordTransform fBackgroundTransform;
-    GrTextureAccess  fBackgroundAccess;
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Xfer Processor
-///////////////////////////////////////////////////////////////////////////////
-
-class GrCustomXPFactory : public GrXPFactory {
-public:
-    GrCustomXPFactory(SkXfermode::Mode mode); 
-
-    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const override {
-        return true;
-    }
-
-    void getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
-                                  GrXPFactory::InvariantBlendedColor*) const override;
-
-private:
-    GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
-                                           const GrProcOptInfo& colorPOI,
-                                           const GrProcOptInfo& coveragePOI,
-                                           bool hasMixedSamples,
-                                           const DstTexture*) const override; 
-
-    bool willReadDstColor(const GrCaps& caps,
-                          const GrProcOptInfo& colorPOI,
-                          const GrProcOptInfo& coveragePOI,
-                          bool hasMixedSamples) const override;
-
-    bool onIsEqual(const GrXPFactory& xpfBase) const override {
-        const GrCustomXPFactory& xpf = xpfBase.cast<GrCustomXPFactory>();
-        return fMode == xpf.fMode;
-    }
-
-    GR_DECLARE_XP_FACTORY_TEST;
-
-    SkXfermode::Mode fMode;
-    GrBlendEquation  fHWBlendEquation;
-
-    typedef GrXPFactory INHERITED;
-};
-#endif
-
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
index 4c50e47..1312d35 100644
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
@@ -13,7 +13,7 @@
 #include "GrProcOptInfo.h"
 #include "GrTypes.h"
 #include "GrXferProcessor.h"
-#include "gl/GrGLBlend.h"
+#include "gl/GrGLSLBlend.h"
 #include "gl/GrGLXferProcessor.h"
 #include "gl/builders/GrGLFragmentShaderBuilder.h"
 #include "gl/builders/GrGLProgramBuilder.h"
@@ -547,7 +547,7 @@
         const ShaderPDXferProcessor& xp = proc.cast<ShaderPDXferProcessor>();
         GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
 
-        GrGLBlend::AppendPorterDuffBlend(fsBuilder, srcColor, dstColor, outColor, xp.getXfermode());
+        GrGLSLBlend::AppendMode(fsBuilder, srcColor, dstColor, outColor, xp.getXfermode());
     }
 
     void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
@@ -651,7 +651,7 @@
         return nullptr;
     }
 
-    GrColor blendConstant = GrUnPreMulColor(colorPOI.color());
+    GrColor blendConstant = GrUnpremulColor(colorPOI.color());
     uint8_t alpha = GrColorUnpackA(blendConstant);
     blendConstant |= (0xff << GrColor_SHIFT_A);
 
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
index 7f66f90..d99c30e 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.h
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -15,18 +15,16 @@
 /**
  * The output color of this effect is a modulation of the input color and a sample from a texture.
  * It allows explicit specification of the filtering and wrap modes (GrTextureParams). It can use
- * local coords, positions, or a custom vertex attribute as input texture coords. The input coords
- * can have a matrix applied in the VS in both the local and position cases but not with a custom
- * attribute coords at this time. It will add a varying to input interpolate texture coords to the
- * FS.
+ * local coords or device space coords as input texture coords. The input coords may be transformed
+ * by a matrix.
  */
 class GrSimpleTextureEffect : public GrSingleTextureEffect {
 public:
     /* unfiltered, clamp mode */
-    static GrFragmentProcessor* Create(GrProcessorDataManager* procDataManager,
-                                       GrTexture* tex,
-                                       const SkMatrix& matrix,
-                                       GrCoordSet coordSet = kLocal_GrCoordSet) {
+    static const GrFragmentProcessor* Create(GrProcessorDataManager* procDataManager,
+                                             GrTexture* tex,
+                                             const SkMatrix& matrix,
+                                             GrCoordSet coordSet = kLocal_GrCoordSet) {
         return new GrSimpleTextureEffect(procDataManager, tex, matrix,
                                          GrTextureParams::kNone_FilterMode, coordSet);
     }
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
index ab944d8..942251c 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
@@ -9,25 +9,23 @@
 
 #include "GrFragmentProcessor.h"
 #include "effects/GrConstColorProcessor.h"
-#include "gl/GrGLBlend.h"
+#include "gl/GrGLSLBlend.h"
 #include "gl/builders/GrGLProgramBuilder.h"
+#include "SkGr.h"
 
-
-class GrComposeTwoFragmentProcessor : public GrFragmentProcessor {
+class ComposeTwoFragmentProcessor : public GrFragmentProcessor {
 public:
-    GrComposeTwoFragmentProcessor(const GrFragmentProcessor* src, const GrFragmentProcessor* dst,
+    ComposeTwoFragmentProcessor(const GrFragmentProcessor* src, const GrFragmentProcessor* dst,
                     SkXfermode::Mode mode)
         : fMode(mode) {
-        // Only coefficient xfer modes are supported
-        SkASSERT(SkXfermode::kLastCoeffMode >= mode);
-        this->initClassID<GrComposeTwoFragmentProcessor>();
+        this->initClassID<ComposeTwoFragmentProcessor>();
         SkDEBUGCODE(int shaderAChildIndex = )this->registerChildProcessor(src);
         SkDEBUGCODE(int shaderBChildIndex = )this->registerChildProcessor(dst);
         SkASSERT(0 == shaderAChildIndex);
         SkASSERT(1 == shaderBChildIndex);
     }
 
-    const char* name() const override { return "ComposeShader"; }
+    const char* name() const override { return "ComposeTwo"; }
 
     void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
         b->add32(fMode);
@@ -37,7 +35,7 @@
 
 protected:
     bool onIsEqual(const GrFragmentProcessor& other) const override {
-        const GrComposeTwoFragmentProcessor& cs = other.cast<GrComposeTwoFragmentProcessor>();
+        const ComposeTwoFragmentProcessor& cs = other.cast<ComposeTwoFragmentProcessor>();
         return fMode == cs.fMode;
     }
 
@@ -57,9 +55,9 @@
 
 /////////////////////////////////////////////////////////////////////
 
-class GrGLComposeTwoFragmentProcessor : public GrGLFragmentProcessor {
+class GLComposeTwoFragmentProcessor : public GrGLFragmentProcessor {
 public:
-    GrGLComposeTwoFragmentProcessor(const GrProcessor& processor) {}
+    GLComposeTwoFragmentProcessor(const GrProcessor& processor) {}
 
     void emitCode(EmitArgs&) override;
 
@@ -69,28 +67,28 @@
 
 /////////////////////////////////////////////////////////////////////
 
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrComposeTwoFragmentProcessor);
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ComposeTwoFragmentProcessor);
 
-const GrFragmentProcessor* GrComposeTwoFragmentProcessor::TestCreate(GrProcessorTestData* d) {
+const GrFragmentProcessor* ComposeTwoFragmentProcessor::TestCreate(GrProcessorTestData* d) {
     // Create two random frag procs.
     SkAutoTUnref<const GrFragmentProcessor> fpA(GrProcessorUnitTest::CreateChildFP(d));
     SkAutoTUnref<const GrFragmentProcessor> fpB(GrProcessorUnitTest::CreateChildFP(d));
 
     SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(
-            d->fRandom->nextRangeU(0, SkXfermode::kLastCoeffMode));
-    return SkNEW_ARGS(GrComposeTwoFragmentProcessor, (fpA, fpB, mode));
+        d->fRandom->nextRangeU(0, SkXfermode::kLastMode));
+    return new ComposeTwoFragmentProcessor(fpA, fpB, mode);
 }
 
-GrGLFragmentProcessor* GrComposeTwoFragmentProcessor::onCreateGLInstance() const{
-    return SkNEW_ARGS(GrGLComposeTwoFragmentProcessor, (*this));
+GrGLFragmentProcessor* ComposeTwoFragmentProcessor::onCreateGLInstance() const{
+    return new GLComposeTwoFragmentProcessor(*this);
 }
 
 /////////////////////////////////////////////////////////////////////
 
-void GrGLComposeTwoFragmentProcessor::emitCode(EmitArgs& args) {
+void GLComposeTwoFragmentProcessor::emitCode(EmitArgs& args) {
 
     GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
-    const GrComposeTwoFragmentProcessor& cs = args.fFp.cast<GrComposeTwoFragmentProcessor>();
+    const ComposeTwoFragmentProcessor& cs = args.fFp.cast<ComposeTwoFragmentProcessor>();
 
     // Store alpha of input color and un-premultiply the input color by its alpha. We will
     // re-multiply by this alpha after blending the output colors of the two child procs.
@@ -122,8 +120,8 @@
     SkXfermode::Mode mode = cs.getMode();
     fsBuilder->codeAppend("{");
     fsBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkXfermode::ModeName(mode));
-    GrGLBlend::AppendPorterDuffBlend(fsBuilder, outputColorSrc.c_str(),
-                                     outputColorDst.c_str(), args.fOutputColor, mode);
+    GrGLSLBlend::AppendMode(fsBuilder, outputColorSrc.c_str(),
+                             outputColorDst.c_str(), args.fOutputColor, mode);
     fsBuilder->codeAppend("}");
 
     // re-multiply the output color by the input color's alpha
@@ -134,21 +132,184 @@
 
 const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromTwoProcessors(
          const GrFragmentProcessor* src, const GrFragmentProcessor* dst, SkXfermode::Mode mode) {
-    if (SkXfermode::kLastCoeffMode < mode) {
-        return nullptr;
-    }
     switch (mode) {
         case SkXfermode::kClear_Mode:
-            return GrConstColorProcessor::Create(GrColor_TRANS_BLACK,
+            return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK,
                                                  GrConstColorProcessor::kIgnore_InputMode);
-            break;
         case SkXfermode::kSrc_Mode:
             return SkRef(src);
-            break;
         case SkXfermode::kDst_Mode:
             return SkRef(dst);
-            break;
         default:
-            return new GrComposeTwoFragmentProcessor(src, dst, mode);
+            return new ComposeTwoFragmentProcessor(src, dst, mode);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ComposeOneFragmentProcessor : public GrFragmentProcessor {
+public:
+    enum Child {
+        kDst_Child,
+        kSrc_Child,
+    };
+
+    ComposeOneFragmentProcessor(const GrFragmentProcessor* dst, SkXfermode::Mode mode, Child child)
+        : fMode(mode)
+        , fChild(child) {
+        this->initClassID<ComposeOneFragmentProcessor>();
+        SkDEBUGCODE(int dstIndex = )this->registerChildProcessor(dst);
+        SkASSERT(0 == dstIndex);
+    }
+
+    const char* name() const override { return "ComposeOne"; }
+
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
+        GR_STATIC_ASSERT((SkXfermode::kLastMode & SK_MaxU16) == SkXfermode::kLastMode);
+        b->add32(fMode | (fChild << 16));
+    }
+
+    SkXfermode::Mode mode() const { return fMode; }
+
+    Child child() const { return fChild; }
+
+protected:
+    bool onIsEqual(const GrFragmentProcessor& that) const override {
+        return fMode == that.cast<ComposeOneFragmentProcessor>().fMode;
+    }
+
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
+        SkXfermode::Coeff skSrcCoeff, skDstCoeff;
+        if (SkXfermode::ModeAsCoeff(fMode, &skSrcCoeff, &skDstCoeff)) {
+            GrBlendCoeff srcCoeff = SkXfermodeCoeffToGrBlendCoeff(skSrcCoeff);
+            GrBlendCoeff dstCoeff = SkXfermodeCoeffToGrBlendCoeff(skDstCoeff);
+            GrInvariantOutput childOutput(0xFFFFFFFF, kRGBA_GrColorComponentFlags, false);
+            this->childProcessor(0).computeInvariantOutput(&childOutput);
+            GrColor blendColor;
+            GrColorComponentFlags blendFlags;
+            if (kDst_Child == fChild) {
+                GrGetCoeffBlendKnownComponents(srcCoeff, dstCoeff,
+                                               inout->color(), inout->validFlags(),
+                                               childOutput.color(), childOutput.validFlags(),
+                                               &blendColor, &blendFlags);
+            } else {
+                GrGetCoeffBlendKnownComponents(srcCoeff, dstCoeff,
+                                               childOutput.color(), childOutput.validFlags(),
+                                               inout->color(), inout->validFlags(),
+                                               &blendColor, &blendFlags);
+            }
+            // will the shader code reference the input color?
+            GrInvariantOutput::ReadInput readsInput = GrInvariantOutput::kWillNot_ReadInput;
+            if (kDst_Child == fChild) {
+                if (kZero_GrBlendCoeff != srcCoeff || GrBlendCoeffRefsSrc(dstCoeff)) {
+                    readsInput = GrInvariantOutput::kWill_ReadInput;
+                }
+            } else {
+                if (kZero_GrBlendCoeff != dstCoeff || GrBlendCoeffRefsDst(srcCoeff)) {
+                    readsInput = GrInvariantOutput::kWill_ReadInput;
+                }
+            }
+            inout->setToOther(blendFlags, blendColor, readsInput);
+        } else {
+            inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
+        }
+    }
+
+private:
+    GrGLFragmentProcessor* onCreateGLInstance() const override;
+
+    SkXfermode::Mode    fMode;
+    Child               fChild;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GLComposeOneFragmentProcessor : public GrGLFragmentProcessor {
+public:
+    GLComposeOneFragmentProcessor(const GrProcessor& processor) {}
+
+    void emitCode(EmitArgs& args) override {
+        GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
+        SkXfermode::Mode mode = args.fFp.cast<ComposeOneFragmentProcessor>().mode();
+        ComposeOneFragmentProcessor::Child child =
+            args.fFp.cast<ComposeOneFragmentProcessor>().child();
+        // declare _dstColor and emit the code for the two child
+        fsBuilder->codeAppendf("vec4 _child;");
+        this->emitChild(0, nullptr, "_child", args);
+
+        const char* inputColor = args.fInputColor;
+        // We don't try to optimize for this case at all
+        if (!inputColor) {
+            fsBuilder->codeAppendf("const vec4 ones = vec4(1);");
+            inputColor = "ones";
+        }
+
+        // emit blend code
+        fsBuilder->codeAppend("{");
+        fsBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkXfermode::ModeName(mode));
+        if (ComposeOneFragmentProcessor::kDst_Child == child) {
+            GrGLSLBlend::AppendMode(fsBuilder, inputColor, "_child", args.fOutputColor, mode);
+        } else {
+            GrGLSLBlend::AppendMode(fsBuilder, "_child", inputColor, args.fOutputColor, mode);
+        }
+        fsBuilder->codeAppend("}");
+    }
+
+private:
+    typedef GrGLFragmentProcessor INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ComposeOneFragmentProcessor);
+
+const GrFragmentProcessor* ComposeOneFragmentProcessor::TestCreate(GrProcessorTestData* d) {
+    // Create one random frag procs.
+    // For now, we'll prevent either children from being a shader with children to prevent the
+    // possibility of an arbitrarily large tree of procs.
+    SkAutoTUnref<const GrFragmentProcessor> dst(GrProcessorUnitTest::CreateChildFP(d));
+    SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(
+        d->fRandom->nextRangeU(0, SkXfermode::kLastMode));
+    ComposeOneFragmentProcessor::Child child = d->fRandom->nextBool() ?
+        ComposeOneFragmentProcessor::kDst_Child :
+        ComposeOneFragmentProcessor::kSrc_Child;
+    return new ComposeOneFragmentProcessor(dst, mode, child);
+}
+
+GrGLFragmentProcessor* ComposeOneFragmentProcessor::onCreateGLInstance() const {
+    return new GLComposeOneFragmentProcessor(*this);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromDstProcessor(
+    const GrFragmentProcessor* dst, SkXfermode::Mode mode) {
+    switch (mode) {
+        case SkXfermode::kClear_Mode:
+            return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK,
+                                                 GrConstColorProcessor::kIgnore_InputMode);
+        case SkXfermode::kSrc_Mode:
+            return nullptr;
+        default:
+            return new ComposeOneFragmentProcessor(dst, mode,
+                                                   ComposeOneFragmentProcessor::kDst_Child);
+    }
+}
+
+const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromSrcProcessor(
+    const GrFragmentProcessor* src, SkXfermode::Mode mode) {
+    switch (mode) {
+        case SkXfermode::kClear_Mode:
+            return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK,
+                                                 GrConstColorProcessor::kIgnore_InputMode);
+        case SkXfermode::kDst_Mode:
+            return nullptr;
+        default:
+            return new ComposeOneFragmentProcessor(src, mode,
+                                                   ComposeOneFragmentProcessor::kSrc_Child);
     }
 }
diff --git a/src/gpu/gl/GrGLBlend.cpp b/src/gpu/gl/GrGLBlend.cpp
deleted file mode 100644
index 4179ec0..0000000
--- a/src/gpu/gl/GrGLBlend.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "GrGLBlend.h"
-#include "gl/builders/GrGLFragmentShaderBuilder.h"
-
-static bool append_porterduff_term(GrGLFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff,
-                                   const char* colorName, const char* srcColorName,
-                                   const char* dstColorName, bool hasPrevious) {
-    if (SkXfermode::kZero_Coeff == coeff) {
-        return hasPrevious;
-    } else {
-        if (hasPrevious) {
-            fsBuilder->codeAppend(" + ");
-        }
-        fsBuilder->codeAppendf("%s", colorName);
-        switch (coeff) {
-            case SkXfermode::kOne_Coeff:
-                break;
-            case SkXfermode::kSC_Coeff:
-                fsBuilder->codeAppendf(" * %s", srcColorName);
-                break;
-            case SkXfermode::kISC_Coeff:
-                fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName);
-                break;
-            case SkXfermode::kDC_Coeff:
-                fsBuilder->codeAppendf(" * %s", dstColorName);
-                break;
-            case SkXfermode::kIDC_Coeff:
-                fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName);
-                break;
-            case SkXfermode::kSA_Coeff:
-                fsBuilder->codeAppendf(" * %s.a", srcColorName);
-                break;
-            case SkXfermode::kISA_Coeff:
-                fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName);
-                break;
-            case SkXfermode::kDA_Coeff:
-                fsBuilder->codeAppendf(" * %s.a", dstColorName);
-                break;
-            case SkXfermode::kIDA_Coeff:
-                fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName);
-                break;
-            default:
-                SkFAIL("Unsupported Blend Coeff");
-        }
-        return true;
-    }
-}
-
-void GrGLBlend::AppendPorterDuffBlend(GrGLFragmentBuilder* fsBuilder, const char* srcColor,
-                                      const char* dstColor, const char* outColor,
-                                      SkXfermode::Mode mode) {
-
-    SkXfermode::Coeff srcCoeff, dstCoeff;
-    SkXfermode::ModeAsCoeff(mode, &srcCoeff, &dstCoeff);
-
-    fsBuilder->codeAppendf("%s = ", outColor);
-    // append src blend
-    bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, srcColor, srcColor, dstColor,
-                                            false);
-    // append dst blend
-    if(!append_porterduff_term(fsBuilder, dstCoeff, dstColor, srcColor, dstColor, didAppend)) {
-        fsBuilder->codeAppend("vec4(0, 0, 0, 0)");
-    }
-    fsBuilder->codeAppend(";");
-}
diff --git a/src/gpu/gl/GrGLSLBlend.cpp b/src/gpu/gl/GrGLSLBlend.cpp
new file mode 100644
index 0000000..b6224e8
--- /dev/null
+++ b/src/gpu/gl/GrGLSLBlend.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "GrGLSLBlend.h"
+#include "gl/builders/GrGLFragmentShaderBuilder.h"
+
+//////////////////////////////////////////////////////////////////////////////
+//  Advanced (non-coeff) blend helpers
+//////////////////////////////////////////////////////////////////////////////
+
+static void hard_light(GrGLFragmentBuilder* fsBuilder,
+                       const char* final,
+                       const char* src,
+                       const char* dst) {
+    static const char kComponents[] = { 'r', 'g', 'b' };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
+        char component = kComponents[i];
+        fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
+        fsBuilder->codeAppendf("%s.%c = 2.0 * %s.%c * %s.%c;",
+                               final, component, src, component, dst, component);
+        fsBuilder->codeAppend("} else {");
+        fsBuilder->codeAppendf("%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);",
+                               final, component, src, dst, dst, dst, component, src, src,
+                               component);
+        fsBuilder->codeAppend("}");
+    }
+    fsBuilder->codeAppendf("%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);",
+                           final, src, dst, dst, src);
+}
+
+// Does one component of color-dodge
+static void color_dodge_component(GrGLFragmentBuilder* fsBuilder,
+                                  const char* final,
+                                  const char* src,
+                                  const char* dst,
+                                  const char component) {
+    fsBuilder->codeAppendf("if (0.0 == %s.%c) {", dst, component);
+    fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
+                           final, component, src, component, dst);
+    fsBuilder->codeAppend("} else {");
+    fsBuilder->codeAppendf("float d = %s.a - %s.%c;", src, src, component);
+    fsBuilder->codeAppend("if (0.0 == d) {");
+    fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
+                           final, component, src, dst, src, component, dst, dst, component,
+                           src);
+    fsBuilder->codeAppend("} else {");
+    fsBuilder->codeAppendf("d = min(%s.a, %s.%c * %s.a / d);",
+                           dst, dst, component, src);
+    fsBuilder->codeAppendf("%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
+                           final, component, src, src, component, dst, dst, component, src);
+    fsBuilder->codeAppend("}");
+    fsBuilder->codeAppend("}");
+}
+
+// Does one component of color-burn
+static void color_burn_component(GrGLFragmentBuilder* fsBuilder,
+                                 const char* final,
+                                 const char* src,
+                                 const char* dst,
+                                 const char component) {
+    fsBuilder->codeAppendf("if (%s.a == %s.%c) {", dst, dst, component);
+    fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
+                           final, component, src, dst, src, component, dst, dst, component,
+                           src);
+    fsBuilder->codeAppendf("} else if (0.0 == %s.%c) {", src, component);
+    fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
+                           final, component, dst, component, src);
+    fsBuilder->codeAppend("} else {");
+    fsBuilder->codeAppendf("float d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);",
+                           dst, dst, dst, component, src, src, component);
+    fsBuilder->codeAppendf("%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
+                           final, component, src, src, component, dst, dst, component, src);
+    fsBuilder->codeAppend("}");
+}
+
+// Does one component of soft-light. Caller should have already checked that dst alpha > 0.
+static void soft_light_component_pos_dst_alpha(GrGLFragmentBuilder* fsBuilder,
+                                               const char* final,
+                                               const char* src,
+                                               const char* dst,
+                                               const char component) {
+    // if (2S < Sa)
+    fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
+    // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
+    fsBuilder->codeAppendf("%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a +"
+                           "(1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s.%c + 1.0);",
+                           final, component, dst, component, dst, component, src, src,
+                           component, dst, dst, src, component, dst, component, src, src,
+                           component);
+    // else if (4D < Da)
+    fsBuilder->codeAppendf("} else if (4.0 * %s.%c <= %s.a) {",
+                           dst, component, dst);
+    fsBuilder->codeAppendf("float DSqd = %s.%c * %s.%c;",
+                           dst, component, dst, component);
+    fsBuilder->codeAppendf("float DCub = DSqd * %s.%c;", dst, component);
+    fsBuilder->codeAppendf("float DaSqd = %s.a * %s.a;", dst, dst);
+    fsBuilder->codeAppendf("float DaCub = DaSqd * %s.a;", dst);
+    // (Da^3 (-S)+Da^2 (S-D (3 Sa-6 S-1))+12 Da D^2 (Sa-2 S)-16 D^3 (Sa-2 S))/Da^2
+    fsBuilder->codeAppendf("%s.%c ="
+                           "(DaSqd*(%s.%c - %s.%c * (3.0*%s.a - 6.0*%s.%c - 1.0)) +"
+                           " 12.0*%s.a*DSqd*(%s.a - 2.0*%s.%c) - 16.0*DCub * (%s.a - 2.0*%s.%c) -"
+                           " DaCub*%s.%c) / DaSqd;",
+                           final, component, src, component, dst, component,
+                           src, src, component, dst, src, src, component, src, src,
+                           component, src, component);
+    fsBuilder->codeAppendf("} else {");
+    // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
+    fsBuilder->codeAppendf("%s.%c = %s.%c*(%s.a - 2.0*%s.%c + 1.0) + %s.%c -"
+                           " sqrt(%s.a*%s.%c)*(%s.a - 2.0*%s.%c) - %s.a*%s.%c;",
+                           final, component, dst, component, src, src, component, src, component,
+                           dst, dst, component, src, src, component, dst, src, component);
+    fsBuilder->codeAppendf("}");
+}
+
+// Adds a function that takes two colors and an alpha as input. It produces a color with the
+// hue and saturation of the first color, the luminosity of the second color, and the input
+// alpha. It has this signature:
+//      vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
+static void add_lum_function(GrGLFragmentBuilder* fsBuilder, SkString* setLumFunction) {
+    // Emit a helper that gets the luminance of a color.
+    SkString getFunction;
+    GrGLShaderVar getLumArgs[] = {
+        GrGLShaderVar("color", kVec3f_GrSLType),
+    };
+    SkString getLumBody("return dot(vec3(0.3, 0.59, 0.11), color);");
+    fsBuilder->emitFunction(kFloat_GrSLType,
+                            "luminance",
+                            SK_ARRAY_COUNT(getLumArgs), getLumArgs,
+                            getLumBody.c_str(),
+                            &getFunction);
+
+    // Emit the set luminance function.
+    GrGLShaderVar setLumArgs[] = {
+        GrGLShaderVar("hueSat", kVec3f_GrSLType),
+        GrGLShaderVar("alpha", kFloat_GrSLType),
+        GrGLShaderVar("lumColor", kVec3f_GrSLType),
+    };
+    SkString setLumBody;
+    setLumBody.printf("float diff = %s(lumColor - hueSat);", getFunction.c_str());
+    setLumBody.append("vec3 outColor = hueSat + diff;");
+    setLumBody.appendf("float outLum = %s(outColor);", getFunction.c_str());
+    setLumBody.append("float minComp = min(min(outColor.r, outColor.g), outColor.b);"
+                      "float maxComp = max(max(outColor.r, outColor.g), outColor.b);"
+                      "if (minComp < 0.0 && outLum != minComp) {"
+                      "outColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) /"
+                      "(outLum - minComp);"
+                      "}"
+                      "if (maxComp > alpha && maxComp != outLum) {"
+                      "outColor = outLum +"
+                      "((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) /"
+                      "(maxComp - outLum);"
+                      "}"
+                      "return outColor;");
+    fsBuilder->emitFunction(kVec3f_GrSLType,
+                            "set_luminance",
+                            SK_ARRAY_COUNT(setLumArgs), setLumArgs,
+                            setLumBody.c_str(),
+                            setLumFunction);
+}
+
+// Adds a function that creates a color with the hue and luminosity of one input color and
+// the saturation of another color. It will have this signature:
+//      float set_saturation(vec3 hueLumColor, vec3 satColor)
+static void add_sat_function(GrGLFragmentBuilder* fsBuilder, SkString* setSatFunction) {
+    // Emit a helper that gets the saturation of a color
+    SkString getFunction;
+    GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
+    SkString getSatBody;
+    getSatBody.printf("return max(max(color.r, color.g), color.b) - "
+                      "min(min(color.r, color.g), color.b);");
+    fsBuilder->emitFunction(kFloat_GrSLType,
+                            "saturation",
+                            SK_ARRAY_COUNT(getSatArgs), getSatArgs,
+                            getSatBody.c_str(),
+                            &getFunction);
+
+    // Emit a helper that sets the saturation given sorted input channels. This used
+    // to use inout params for min, mid, and max components but that seems to cause
+    // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
+    // adjusted min, mid, and max inputs, respectively.
+    SkString helperFunction;
+    GrGLShaderVar helperArgs[] = {
+        GrGLShaderVar("minComp", kFloat_GrSLType),
+        GrGLShaderVar("midComp", kFloat_GrSLType),
+        GrGLShaderVar("maxComp", kFloat_GrSLType),
+        GrGLShaderVar("sat", kFloat_GrSLType),
+    };
+    static const char kHelperBody[] = "if (minComp < maxComp) {"
+        "vec3 result;"
+        "result.r = 0.0;"
+        "result.g = sat * (midComp - minComp) / (maxComp - minComp);"
+        "result.b = sat;"
+        "return result;"
+        "} else {"
+        "return vec3(0, 0, 0);"
+        "}";
+    fsBuilder->emitFunction(kVec3f_GrSLType,
+                            "set_saturation_helper",
+                            SK_ARRAY_COUNT(helperArgs), helperArgs,
+                            kHelperBody,
+                            &helperFunction);
+
+    GrGLShaderVar setSatArgs[] = {
+        GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
+        GrGLShaderVar("satColor", kVec3f_GrSLType),
+    };
+    const char* helpFunc = helperFunction.c_str();
+    SkString setSatBody;
+    setSatBody.appendf("float sat = %s(satColor);"
+                       "if (hueLumColor.r <= hueLumColor.g) {"
+                       "if (hueLumColor.g <= hueLumColor.b) {"
+                       "hueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);"
+                       "} else if (hueLumColor.r <= hueLumColor.b) {"
+                       "hueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);"
+                       "} else {"
+                       "hueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);"
+                       "}"
+                       "} else if (hueLumColor.r <= hueLumColor.b) {"
+                       "hueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);"
+                       "} else if (hueLumColor.g <= hueLumColor.b) {"
+                       "hueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);"
+                       "} else {"
+                       "hueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);"
+                       "}"
+                       "return hueLumColor;",
+                       getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
+                       helpFunc, helpFunc);
+    fsBuilder->emitFunction(kVec3f_GrSLType,
+                            "set_saturation",
+                            SK_ARRAY_COUNT(setSatArgs), setSatArgs,
+                            setSatBody.c_str(),
+                            setSatFunction);
+}
+
+static void emit_advanced_xfermode_code(GrGLFragmentBuilder* fsBuilder, const char* srcColor,
+                                        const char* dstColor, const char* outputColor,
+                                        SkXfermode::Mode mode) {
+    SkASSERT(srcColor);
+    SkASSERT(dstColor);
+    SkASSERT(outputColor);
+    // These all perform src-over on the alpha channel.
+    fsBuilder->codeAppendf("%s.a = %s.a + (1.0 - %s.a) * %s.a;",
+                           outputColor, srcColor, srcColor, dstColor);
+
+    switch (mode) {
+        case SkXfermode::kOverlay_Mode:
+            // Overlay is Hard-Light with the src and dst reversed
+            hard_light(fsBuilder, outputColor, dstColor, srcColor);
+            break;
+        case SkXfermode::kDarken_Mode:
+            fsBuilder->codeAppendf("%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
+                                   "(1.0 - %s.a) * %s.rgb + %s.rgb);",
+                                   outputColor,
+                                   srcColor, dstColor, srcColor,
+                                   dstColor, srcColor, dstColor);
+            break;
+        case SkXfermode::kLighten_Mode:
+            fsBuilder->codeAppendf("%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
+                                   "(1.0 - %s.a) * %s.rgb + %s.rgb);",
+                                   outputColor,
+                                   srcColor, dstColor, srcColor,
+                                   dstColor, srcColor, dstColor);
+            break;
+        case SkXfermode::kColorDodge_Mode:
+            color_dodge_component(fsBuilder, outputColor, srcColor, dstColor, 'r');
+            color_dodge_component(fsBuilder, outputColor, srcColor, dstColor, 'g');
+            color_dodge_component(fsBuilder, outputColor, srcColor, dstColor, 'b');
+            break;
+        case SkXfermode::kColorBurn_Mode:
+            color_burn_component(fsBuilder, outputColor, srcColor, dstColor, 'r');
+            color_burn_component(fsBuilder, outputColor, srcColor, dstColor, 'g');
+            color_burn_component(fsBuilder, outputColor, srcColor, dstColor, 'b');
+            break;
+        case SkXfermode::kHardLight_Mode:
+            hard_light(fsBuilder, outputColor, srcColor, dstColor);
+            break;
+        case SkXfermode::kSoftLight_Mode:
+            fsBuilder->codeAppendf("if (0.0 == %s.a) {", dstColor);
+            fsBuilder->codeAppendf("%s.rgba = %s;", outputColor, srcColor);
+            fsBuilder->codeAppendf("} else {");
+            soft_light_component_pos_dst_alpha(fsBuilder, outputColor, srcColor, dstColor, 'r');
+            soft_light_component_pos_dst_alpha(fsBuilder, outputColor, srcColor, dstColor, 'g');
+            soft_light_component_pos_dst_alpha(fsBuilder, outputColor, srcColor, dstColor, 'b');
+            fsBuilder->codeAppendf("}");
+            break;
+        case SkXfermode::kDifference_Mode:
+            fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb -"
+                                   "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);",
+                                   outputColor, srcColor, dstColor, srcColor, dstColor,
+                                   dstColor, srcColor);
+            break;
+        case SkXfermode::kExclusion_Mode:
+            fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb - "
+                                   "2.0 * %s.rgb * %s.rgb;",
+                                   outputColor, dstColor, srcColor, dstColor, srcColor);
+            break;
+        case SkXfermode::kMultiply_Mode:
+            fsBuilder->codeAppendf("%s.rgb = (1.0 - %s.a) * %s.rgb + "
+                                   "(1.0 - %s.a) * %s.rgb + "
+                                   "%s.rgb * %s.rgb;",
+                                   outputColor, srcColor, dstColor, dstColor, srcColor,
+                                   srcColor, dstColor);
+            break;
+        case SkXfermode::kHue_Mode: {
+            //  SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
+            SkString setSat, setLum;
+            add_sat_function(fsBuilder, &setSat);
+            add_lum_function(fsBuilder, &setLum);
+            fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
+                                   dstColor, srcColor);
+            fsBuilder->codeAppendf("%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb),"
+                                   "dstSrcAlpha.a, dstSrcAlpha.rgb);",
+                                   outputColor, setLum.c_str(), setSat.c_str(), srcColor,
+                                   dstColor);
+            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
+                                   outputColor, srcColor, dstColor, dstColor, srcColor);
+            break;
+        }
+        case SkXfermode::kSaturation_Mode: {
+            // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
+            SkString setSat, setLum;
+            add_sat_function(fsBuilder, &setSat);
+            add_lum_function(fsBuilder, &setLum);
+            fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
+                                   dstColor, srcColor);
+            fsBuilder->codeAppendf("%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a),"
+                                   "dstSrcAlpha.a, dstSrcAlpha.rgb);",
+                                   outputColor, setLum.c_str(), setSat.c_str(), srcColor,
+                                   dstColor);
+            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
+                                   outputColor, srcColor, dstColor, dstColor, srcColor);
+            break;
+        }
+        case SkXfermode::kColor_Mode: {
+            //  SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
+            SkString setLum;
+            add_lum_function(fsBuilder, &setLum);
+            fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
+                                   srcColor, dstColor);
+            fsBuilder->codeAppendf("%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);",
+                                   outputColor, setLum.c_str(), dstColor, srcColor);
+            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
+                                   outputColor, srcColor, dstColor, dstColor, srcColor);
+            break;
+        }
+        case SkXfermode::kLuminosity_Mode: {
+            //  SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
+            SkString setLum;
+            add_lum_function(fsBuilder, &setLum);
+            fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
+                                   srcColor, dstColor);
+            fsBuilder->codeAppendf("%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);",
+                                   outputColor, setLum.c_str(), dstColor, srcColor);
+            fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
+                                   outputColor, srcColor, dstColor, dstColor, srcColor);
+            break;
+        }
+        default:
+            SkFAIL("Unknown Custom Xfer mode.");
+            break;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//  Porter-Duff blend helper
+//////////////////////////////////////////////////////////////////////////////
+
+static bool append_porterduff_term(GrGLFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff,
+                                   const char* colorName, const char* srcColorName,
+                                   const char* dstColorName, bool hasPrevious) {
+    if (SkXfermode::kZero_Coeff == coeff) {
+        return hasPrevious;
+    } else {
+        if (hasPrevious) {
+            fsBuilder->codeAppend(" + ");
+        }
+        fsBuilder->codeAppendf("%s", colorName);
+        switch (coeff) {
+            case SkXfermode::kOne_Coeff:
+                break;
+            case SkXfermode::kSC_Coeff:
+                fsBuilder->codeAppendf(" * %s", srcColorName);
+                break;
+            case SkXfermode::kISC_Coeff:
+                fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName);
+                break;
+            case SkXfermode::kDC_Coeff:
+                fsBuilder->codeAppendf(" * %s", dstColorName);
+                break;
+            case SkXfermode::kIDC_Coeff:
+                fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName);
+                break;
+            case SkXfermode::kSA_Coeff:
+                fsBuilder->codeAppendf(" * %s.a", srcColorName);
+                break;
+            case SkXfermode::kISA_Coeff:
+                fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName);
+                break;
+            case SkXfermode::kDA_Coeff:
+                fsBuilder->codeAppendf(" * %s.a", dstColorName);
+                break;
+            case SkXfermode::kIDA_Coeff:
+                fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName);
+                break;
+            default:
+                SkFAIL("Unsupported Blend Coeff");
+        }
+        return true;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void GrGLSLBlend::AppendMode(GrGLFragmentBuilder* fsBuilder, const char* srcColor,
+                             const char* dstColor, const char* outColor,
+                             SkXfermode::Mode mode) {
+
+    SkXfermode::Coeff srcCoeff, dstCoeff;
+    if (SkXfermode::ModeAsCoeff(mode, &srcCoeff, &dstCoeff)) {
+        fsBuilder->codeAppendf("%s = ", outColor);
+        // append src blend
+        bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, srcColor, srcColor, dstColor,
+                                                false);
+        // append dst blend
+        if(!append_porterduff_term(fsBuilder, dstCoeff, dstColor, srcColor, dstColor, didAppend)) {
+            fsBuilder->codeAppend("vec4(0, 0, 0, 0)");
+        }
+        fsBuilder->codeAppend(";");
+    } else {
+        emit_advanced_xfermode_code(fsBuilder, srcColor, dstColor, outColor, mode);
+    }
+}
diff --git a/src/gpu/gl/GrGLBlend.h b/src/gpu/gl/GrGLSLBlend.h
similarity index 67%
rename from src/gpu/gl/GrGLBlend.h
rename to src/gpu/gl/GrGLSLBlend.h
index af447b4..b763f45 100644
--- a/src/gpu/gl/GrGLBlend.h
+++ b/src/gpu/gl/GrGLSLBlend.h
@@ -12,13 +12,13 @@
 
 class GrGLFragmentBuilder;
 
-namespace GrGLBlend {
+namespace GrGLSLBlend {
     /*
      * Appends GLSL code to fsBuilder that assigns a specified blend of the srcColor and dstColor
      * variables to the outColor variable.
      */
-    void AppendPorterDuffBlend(GrGLFragmentBuilder* fsBuilder, const char* srcColor,
-                               const char* dstColor, const char* outColor, SkXfermode::Mode mode);
+    void AppendMode(GrGLFragmentBuilder* fsBuilder, const char* srcColor,
+                    const char* dstColor, const char* outColor, SkXfermode::Mode mode);
 };
 
 #endif