Implement SkColorFilter as a GrGLEffect

Adds GrEffect::willUseInputColor() which indicates whether or not the
input color affects the output of the effect. This is needed for
certain Xfermodes, such as kSrc_Mode. For these modes the color filter
will not use the input color.

An effect with GrEffect::willUseInputColor() true will cause all color
or coverage effects before it to be discarded, as their computations
cannot affect the output. In these cases program is marked as having
white input color.

This fixes an assert when Skia is compiled in a mode that prefers
using uniforms instead of attributes for constants. (Flags
GR_GL_USE_NV_PATH_RENDERING or GR_GL_NO_CONSTANT_ATTRIBUTES). Using
attributes hides the problem where the fragment shader does not need
input color for color filters that ignore DST part of the filter. The
assert would be hit when uniform manager tries to bind an uniform which
has been optimized away by the shader compiler.

Adds specific GrGLSLExpr4 and GrGLSLExpr1 classes. This way the GLSL
expressions like "(v - src.a)" can remain somewhat readable in form of
"(v - src.a())". The GrGLSLExpr<typename> template implements the
generic functionality, GrGLSLExprX is the specialization that exposes
the type-safe interface to this functionality.

Also adds operators so that GLSL binary operators of the form
"(float * vecX)" can be expressed in C++. Before only the equivalent
"(vecX * float)" was possible. This reverts the common blending
calculations to more conventional order, such as "(1-a) * c" instead of
"c * (1-a)".

Changes GrGLSLExpr1::OnesStr from 1 to 1.0 in order to preserve the
color filter blending formula string the same (with the exception of
variable name change).

Shaders change in case of input color being needed:
 -   vec4 filteredColor;
 -   filteredColor = (((1.0 - uFilterColor.a) * output_Stage0) + uFilterColor);
 -   fsColorOut = filteredColor;
 +   vec4 output_Stage1;
 +   { // Stage 1: ModeColorFilterEffect
 +   output_Stage1 = (((1.0 - uFilterColor_Stage1.a) * output_Stage0) + uFilterColor_Stage1);
 +   }
 +   fsColorOut = output_Stage1;

Shaders change in case of input color being not needed:
 -uniform vec4 uFilterColor;
 -in vec4 vColor;
 +uniform vec4 uFilterColor_Stage0;
  out vec4 fsColorOut;
  void main() {
 -   vec4 filteredColor;
 -   filteredColor = uFilterColor;
 -   fsColorOut = filteredColor;
 +   vec4 output_Stage0;
 +   { // Stage 0: ModeColorFilterEffect
 +   output_Stage0 = uFilterColor_Stage0;
 +   }
 +   fsColorOut = output_Stage0;
  }

R=bsalomon@google.com, robertphillips@google.com, jvanverth@google.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/25023003

git-svn-id: http://skia.googlecode.com/svn/trunk/src@11912 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/effects/SkBitmapAlphaThresholdShader.cpp b/effects/SkBitmapAlphaThresholdShader.cpp
index 6f1c94a..7fb09fd 100644
--- a/effects/SkBitmapAlphaThresholdShader.cpp
+++ b/effects/SkBitmapAlphaThresholdShader.cpp
@@ -150,7 +150,7 @@
                                     "\t\t}\n");
 
             builder->fsCodeAppendf("color = %s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<4>("color")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr4("color")).c_str());
         }
 
         virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& e) SK_OVERRIDE {
diff --git a/effects/SkColorFilters.cpp b/effects/SkColorFilters.cpp
index a821129..66ddb02 100644
--- a/effects/SkColorFilters.cpp
+++ b/effects/SkColorFilters.cpp
@@ -85,6 +85,9 @@
     }
 #endif
 
+#if SK_SUPPORT_GPU
+    virtual GrEffectRef* asNewEffect(GrContext*) const SK_OVERRIDE;
+#endif
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkModeColorFilter)
 
 protected:
@@ -117,6 +120,293 @@
     typedef SkColorFilter INHERITED;
 };
 
+///////////////////////////////////////////////////////////////////////////////
+#if SK_SUPPORT_GPU
+#include "GrBlend.h"
+#include "GrEffect.h"
+#include "GrEffectUnitTest.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+#include "SkGr.h"
+
+namespace {
+/**
+ * A definition of blend equation for one coefficient. Generates a
+ * blend_coeff * value "expression".
+ */
+template<typename ColorExpr>
+static inline ColorExpr blend_term(SkXfermode::Coeff coeff,
+                                   const ColorExpr& src,
+                                   const ColorExpr& dst,
+                                   const ColorExpr& value) {
+    switch (coeff) {
+    default:
+        GrCrash("Unexpected xfer coeff.");
+    case SkXfermode::kZero_Coeff:    /** 0 */
+        return ColorExpr(0);
+    case SkXfermode::kOne_Coeff:     /** 1 */
+        return value;
+    case SkXfermode::kSC_Coeff:
+        return src * value;
+    case SkXfermode::kISC_Coeff:
+        return (ColorExpr(1) - src) * dst;
+    case SkXfermode::kDC_Coeff:
+        return dst * value;
+    case SkXfermode::kIDC_Coeff:
+        return (ColorExpr(1) - dst) * value;
+    case SkXfermode::kSA_Coeff:      /** src alpha */
+        return src.a() * value;
+    case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
+        return (typename ColorExpr::AExpr(1) - src.a()) * value;
+    case SkXfermode::kDA_Coeff:      /** dst alpha */
+        return dst.a() * value;
+    case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
+        return (typename ColorExpr::AExpr(1) - dst.a()) *  value;
+    }
+}
+/**
+ * Creates a color filter expression which modifies the color by
+ * the specified color filter.
+ */
+template <typename ColorExpr>
+static inline ColorExpr color_filter_expression(const SkXfermode::Mode& mode,
+                                                const ColorExpr& filterColor,
+                                                const ColorExpr& inColor) {
+    SkXfermode::Coeff colorCoeff;
+    SkXfermode::Coeff filterColorCoeff;
+    SkAssertResult(SkXfermode::ModeAsCoeff(mode, &filterColorCoeff, &colorCoeff));
+    return blend_term(colorCoeff, filterColor, inColor, inColor) +
+        blend_term(filterColorCoeff, filterColor, inColor, filterColor);
+}
+
+}
+
+class ModeColorFilterEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(const GrColor& c, SkXfermode::Mode mode) {
+        AutoEffectUnref effect(SkNEW_ARGS(ModeColorFilterEffect, (c, mode)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    bool willUseFilterColor() const {
+        SkXfermode::Coeff dstCoeff;
+        SkXfermode::Coeff srcCoeff;
+        SkAssertResult(SkXfermode::ModeAsCoeff(fMode, &srcCoeff, &dstCoeff));
+        if (SkXfermode::kZero_Coeff == srcCoeff) {
+            return GrBlendCoeffRefsSrc(sk_blend_to_grblend(dstCoeff));
+        }
+        return true;
+    }
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<ModeColorFilterEffect>::getInstance();
+    }
+
+    static const char* Name() { return "ModeColorFilterEffect"; }
+
+    SkXfermode::Mode mode() const { return fMode; }
+    GrColor color() const { return fColor; }
+
+    class GLEffect : public GrGLEffect {
+    public:
+        GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+            : INHERITED(factory) {
+        }
+
+        virtual void emitCode(GrGLShaderBuilder* builder,
+                              const GrDrawEffect& drawEffect,
+                              EffectKey key,
+                              const char* outputColor,
+                              const char* inputColor,
+                              const TransformedCoordsArray& coords,
+                              const TextureSamplerArray& samplers) SK_OVERRIDE {
+            SkXfermode::Mode mode = drawEffect.castEffect<ModeColorFilterEffect>().mode();
+
+            SkASSERT(SkXfermode::kDst_Mode != mode);
+            const char* colorFilterColorUniName = NULL;
+            if (drawEffect.castEffect<ModeColorFilterEffect>().willUseFilterColor()) {
+                fFilterColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                                      kVec4f_GrSLType, "FilterColor",
+                                                      &colorFilterColorUniName);
+            }
+
+            GrGLSLExpr4 filter =
+                color_filter_expression(mode, GrGLSLExpr4(colorFilterColorUniName), GrGLSLExpr4(inputColor));
+
+            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, filter.c_str());
+        }
+
+        static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
+            const ModeColorFilterEffect& colorModeFilter = drawEffect.castEffect<ModeColorFilterEffect>();
+            // The SL code does not depend on filter color at the moment, so no need to represent it
+            // in the key.
+            EffectKey modeKey = colorModeFilter.mode();
+            return modeKey;
+        }
+
+        virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) SK_OVERRIDE {
+            if (fFilterColorUni.isValid()) {
+                const ModeColorFilterEffect& colorModeFilter = drawEffect.castEffect<ModeColorFilterEffect>();
+                GrGLfloat c[4];
+                GrColorToRGBAFloat(colorModeFilter.color(), c);
+                uman.set4fv(fFilterColorUni, 0, 1, c);
+            }
+        }
+
+    private:
+
+        GrGLUniformManager::UniformHandle fFilterColorUni;
+        typedef GrGLEffect INHERITED;
+    };
+
+    GR_DECLARE_EFFECT_TEST;
+
+private:
+    ModeColorFilterEffect(GrColor color, SkXfermode::Mode mode)
+        : fMode(mode),
+          fColor(color) {
+
+        SkXfermode::Coeff dstCoeff;
+        SkXfermode::Coeff srcCoeff;
+        SkAssertResult(SkXfermode::ModeAsCoeff(fMode, &srcCoeff, &dstCoeff));
+        // These could be calculated from the blend equation with template trickery..
+        if (SkXfermode::kZero_Coeff == dstCoeff && !GrBlendCoeffRefsDst(sk_blend_to_grblend(srcCoeff))) {
+            this->setWillNotUseInputColor();
+        }
+    }
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        const ModeColorFilterEffect& s = CastEffect<ModeColorFilterEffect>(other);
+        return fMode == s.fMode && fColor == s.fColor;
+    }
+
+    SkXfermode::Mode fMode;
+    GrColor fColor;
+
+    typedef GrEffect INHERITED;
+};
+
+namespace {
+
+/** Function color_component_to_int tries to reproduce the GLSL rounding. The spec doesn't specify
+ * to which direction the 0.5 goes.
+ */
+static inline int color_component_to_int(float value) {
+    return sk_float_round2int(GrMax(0.f, GrMin(1.f, value)) * 255.f);
+}
+
+/** MaskedColorExpr is used to evaluate the color and valid color component flags through the
+ * blending equation. It has members similar to GrGLSLExpr so that it can be used with the
+ * templated helpers above.
+ */
+class MaskedColorExpr {
+public:
+    MaskedColorExpr(const float color[], uint32_t flags)
+        : fFlags(flags) {
+        fColor[0] = color[0];
+        fColor[1] = color[1];
+        fColor[2] = color[2];
+        fColor[3] = color[3];
+    }
+
+    MaskedColorExpr(float v, uint32_t flags = kRGBA_GrColorComponentFlags)
+        : fFlags(flags) {
+        fColor[0] = v;
+        fColor[1] = v;
+        fColor[2] = v;
+        fColor[3] = v;
+    }
+
+    MaskedColorExpr operator*(const MaskedColorExpr& other) const {
+        float tmp[4];
+        tmp[0] = fColor[0] * other.fColor[0];
+        tmp[1] = fColor[1] * other.fColor[1];
+        tmp[2] = fColor[2] * other.fColor[2];
+        tmp[3] = fColor[3] * other.fColor[3];
+
+        return MaskedColorExpr(tmp, fFlags & other.fFlags);
+    }
+
+    MaskedColorExpr operator+(const MaskedColorExpr& other) const {
+        float tmp[4];
+        tmp[0] = fColor[0] + other.fColor[0];
+        tmp[1] = fColor[1] + other.fColor[1];
+        tmp[2] = fColor[2] + other.fColor[2];
+        tmp[3] = fColor[3] + other.fColor[3];
+
+        return MaskedColorExpr(tmp, fFlags & other.fFlags);
+    }
+
+    MaskedColorExpr operator-(const MaskedColorExpr& other) const {
+        float tmp[4];
+        tmp[0] = fColor[0] - other.fColor[0];
+        tmp[1] = fColor[1] - other.fColor[1];
+        tmp[2] = fColor[2] - other.fColor[2];
+        tmp[3] = fColor[3] - other.fColor[3];
+
+        return MaskedColorExpr(tmp, fFlags & other.fFlags);
+    }
+
+    MaskedColorExpr a() const {
+        uint32_t flags = (fFlags & kA_GrColorComponentFlag) ? kRGBA_GrColorComponentFlags : 0;
+        return MaskedColorExpr(fColor[3], flags);
+    }
+
+    GrColor getColor() const {
+        return GrColorPackRGBA(color_component_to_int(fColor[0]),
+                               color_component_to_int(fColor[1]),
+                               color_component_to_int(fColor[2]),
+                               color_component_to_int(fColor[3]));
+    }
+
+    uint32_t getValidComponents() const  { return fFlags; }
+
+    typedef MaskedColorExpr AExpr;
+private:
+    float fColor[4];
+    uint32_t fFlags;
+};
+
+}
+
+void ModeColorFilterEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    float inputColor[4];
+    GrColorToRGBAFloat(*color, inputColor);
+    float filterColor[4];
+    GrColorToRGBAFloat(fColor, filterColor);
+    MaskedColorExpr result =
+        color_filter_expression(fMode,
+                                MaskedColorExpr(filterColor, kRGBA_GrColorComponentFlags),
+                                MaskedColorExpr(inputColor, *validFlags));
+
+    *color = result.getColor();
+    *validFlags = result.getValidComponents();
+}
+
+GR_DEFINE_EFFECT_TEST(ModeColorFilterEffect);
+GrEffectRef* ModeColorFilterEffect::TestCreate(SkRandom* rand,
+                                    GrContext*,
+                                    const GrDrawTargetCaps&,
+                                    GrTexture*[]) {
+    int mode = rand->nextRangeU(0, SkXfermode::kLastCoeffMode);
+    GrColor color = rand->nextU();
+    static AutoEffectUnref gEffect(SkNEW_ARGS(ModeColorFilterEffect, (color, static_cast<SkXfermode::Mode>(mode))));
+    return CreateEffectRef(gEffect);
+}
+
+GrEffectRef* SkModeColorFilter::asNewEffect(GrContext*) const {
+    if (SkXfermode::kDst_Mode != fMode) {
+        return ModeColorFilterEffect::Create(SkColor2GrColor(fColor), fMode);
+    }
+    return NULL;
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
 class Src_SkModeColorFilter : public SkModeColorFilter {
 public:
     Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {}
diff --git a/effects/SkDisplacementMapEffect.cpp b/effects/SkDisplacementMapEffect.cpp
index 3722178..ff2e837 100644
--- a/effects/SkDisplacementMapEffect.cpp
+++ b/effects/SkDisplacementMapEffect.cpp
@@ -348,6 +348,7 @@
     this->addTextureAccess(&fDisplacementAccess);
     this->addCoordTransform(&fColorTransform);
     this->addTextureAccess(&fColorAccess);
+    this->setWillNotUseInputColor();
 }
 
 GrDisplacementMapEffect::~GrDisplacementMapEffect() {
diff --git a/effects/SkMatrixConvolutionImageFilter.cpp b/effects/SkMatrixConvolutionImageFilter.cpp
index a9dfecc..c89b9dc 100644
--- a/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/effects/SkMatrixConvolutionImageFilter.cpp
@@ -423,6 +423,7 @@
                                            const char* inputColor,
                                            const TransformedCoordsArray& coords,
                                            const TextureSamplerArray& samplers) {
+    sk_ignore_unused_variable(inputColor);
     SkString coords2D = builder->ensureFSCoords2D(coords, 0);
     fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                      kVec4f_GrSLType, "Bounds");
@@ -548,6 +549,7 @@
     }
     fTarget[0] = static_cast<float>(target.x());
     fTarget[1] = static_cast<float>(target.y());
+    this->setWillNotUseInputColor();
 }
 
 GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
diff --git a/effects/SkPerlinNoiseShader.cpp b/effects/SkPerlinNoiseShader.cpp
index df6b78e..af51afc 100644
--- a/effects/SkPerlinNoiseShader.cpp
+++ b/effects/SkPerlinNoiseShader.cpp
@@ -610,6 +610,7 @@
         m.postTranslate(SK_Scalar1, SK_Scalar1);
         fCoordTransform.reset(kLocal_GrCoordSet, m);
         this->addCoordTransform(&fCoordTransform);
+        this->setWillNotUseInputColor();
     }
 
     SkPerlinNoiseShader::Type       fType;
diff --git a/effects/gradients/SkGradientShader.cpp b/effects/gradients/SkGradientShader.cpp
index fb8bbac..c90adfe 100644
--- a/effects/gradients/SkGradientShader.cpp
+++ b/effects/gradients/SkGradientShader.cpp
@@ -951,7 +951,7 @@
         }
 
         builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                               (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<4>("colorTemp")).c_str());
+                               (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
     } else if (GrGradientEffect::kThree_ColorType == ColorTypeFromKey(key)){
         builder->fsCodeAppendf("\tfloat oneMinus2t = 1.0 - (2.0 * (%s));\n",
                                gradientTValue);
@@ -975,7 +975,7 @@
         }
 
         builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                               (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<4>("colorTemp")).c_str());
+                               (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
     } else {
         builder->fsCodeAppendf("\tvec2 coord = vec2(%s, %s);\n",
                                gradientTValue,
diff --git a/gpu/GrAAConvexPathRenderer.cpp b/gpu/GrAAConvexPathRenderer.cpp
index 2292929..a8af559 100644
--- a/gpu/GrAAConvexPathRenderer.cpp
+++ b/gpu/GrAAConvexPathRenderer.cpp
@@ -564,7 +564,7 @@
 
 
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("edgeAlpha")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
 
             builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
         }
diff --git a/gpu/GrAARectRenderer.cpp b/gpu/GrAARectRenderer.cpp
index a6b8d97..ee7c024 100644
--- a/gpu/GrAARectRenderer.cpp
+++ b/gpu/GrAARectRenderer.cpp
@@ -86,7 +86,7 @@
 
 
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("coverage")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr1("coverage")).c_str());
         }
 
         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -219,7 +219,7 @@
 
 
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("coverage")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr1("coverage")).c_str());
         }
 
         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
diff --git a/gpu/GrDrawState.cpp b/gpu/GrDrawState.cpp
index d1d6c58..1743604 100644
--- a/gpu/GrDrawState.cpp
+++ b/gpu/GrDrawState.cpp
@@ -59,7 +59,6 @@
     this->setState(GrDrawState::kHWAntialias_StateBit, paint.isAntiAlias());
 
     this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff());
-    this->setColorFilter(paint.getColorFilterColor(), paint.getColorFilterMode());
     this->setCoverage(paint.getCoverage());
 }
 
@@ -219,13 +218,6 @@
         (*effect)->getConstantColorComponents(&color, &validComponentFlags);
     }
 
-    // Check if the color filter could introduce an alpha.
-    // We could skip the above work when this is true, but it is rare and the right fix is to make
-    // the color filter a GrEffect and implement getConstantColorComponents() for it.
-    if (SkXfermode::kDst_Mode != this->getColorFilterMode()) {
-        validComponentFlags = 0;
-    }
-
     // Check whether coverage is treated as color. If so we run through the coverage computation.
     if (this->isCoverageDrawing()) {
         GrColor coverageColor = this->getCoverage();
diff --git a/gpu/GrDrawState.h b/gpu/GrDrawState.h
index 889dec9..2e4d7f8 100644
--- a/gpu/GrDrawState.h
+++ b/gpu/GrDrawState.h
@@ -265,18 +265,6 @@
     }
 
     /**
-     * Add a color filter that can be represented by a color and a mode. Applied
-     * after color-computing effect stages.
-     */
-    void setColorFilter(GrColor c, SkXfermode::Mode mode) {
-        fCommon.fColorFilterColor = c;
-        fCommon.fColorFilterMode = mode;
-    }
-
-    GrColor getColorFilterColor() const { return fCommon.fColorFilterColor; }
-    SkXfermode::Mode getColorFilterMode() const { return fCommon.fColorFilterMode; }
-
-    /**
      * Constructor sets the color to be 'color' which is undone by the destructor.
      */
     class AutoColorRestore : public ::SkNoncopyable {
@@ -914,8 +902,6 @@
         fCommon.fFlagBits = 0x0;
         fCommon.fStencilSettings.setDisabled();
         fCommon.fCoverage = 0xffffffff;
-        fCommon.fColorFilterMode = SkXfermode::kDst_Mode;
-        fCommon.fColorFilterColor = 0x0;
         fCommon.fDrawFace = kBoth_DrawFace;
     }
 
@@ -932,8 +918,6 @@
         int                   fVACount;
         GrStencilSettings     fStencilSettings;
         GrColor               fCoverage;
-        SkXfermode::Mode      fColorFilterMode;
-        GrColor               fColorFilterColor;
         DrawFace              fDrawFace;
 
         // This is simply a different representation of info in fVertexAttribs and thus does
@@ -951,8 +935,6 @@
                           !memcmp(fVAPtr, other.fVAPtr, fVACount * sizeof(GrVertexAttrib)) &&
                           fStencilSettings == other.fStencilSettings &&
                           fCoverage == other.fCoverage &&
-                          fColorFilterMode == other.fColorFilterMode &&
-                          fColorFilterColor == other.fColorFilterColor &&
                           fDrawFace == other.fDrawFace;
             SkASSERT(!result || 0 == memcmp(fFixedFunctionVertexAttribIndices,
                                             other.fFixedFunctionVertexAttribIndices,
diff --git a/gpu/GrOvalRenderer.cpp b/gpu/GrOvalRenderer.cpp
index 91e3997..27bd74a 100644
--- a/gpu/GrOvalRenderer.cpp
+++ b/gpu/GrOvalRenderer.cpp
@@ -117,7 +117,7 @@
             }
 
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("edgeAlpha")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
         }
 
         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -249,7 +249,7 @@
             }
 
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("edgeAlpha")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
         }
 
         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -407,7 +407,7 @@
             }
 
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("edgeAlpha")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
         }
 
         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
diff --git a/gpu/GrPaint.cpp b/gpu/GrPaint.cpp
index fec62bb..7499cd0 100644
--- a/gpu/GrPaint.cpp
+++ b/gpu/GrPaint.cpp
@@ -56,12 +56,6 @@
 
     // TODO: Share this implementation with GrDrawState
 
-    // Since fColorFilterXfermode is going away soon, we aren't attempting to handle anything but
-    // the default setting.
-    if (SkXfermode::kDst_Mode != fColorFilterXfermode) {
-        return false;
-    }
-
     GrColor coverage = GrColorPackRGBA(fCoverage, fCoverage, fCoverage, fCoverage);
     uint32_t coverageComps = kRGBA_GrColorComponentFlags;
     int count = fCoverageStages.count();
diff --git a/gpu/SkGpuDevice.cpp b/gpu/SkGpuDevice.cpp
index 1498fc4..a70a0f5 100644
--- a/gpu/SkGpuDevice.cpp
+++ b/gpu/SkGpuDevice.cpp
@@ -475,13 +475,6 @@
             SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));
             if (NULL != effect.get()) {
                 grPaint->addColorEffect(effect);
-            } else {
-                // TODO: rewrite this using asNewEffect()
-                SkColor color;
-                SkXfermode::Mode filterMode;
-                if (colorFilter->asColorMode(&color, &filterMode)) {
-                    grPaint->setXfermodeColorFilter(filterMode, SkColor2GrColor(color));
-                }
             }
         }
     }
diff --git a/gpu/effects/GrBezierEffect.cpp b/gpu/effects/GrBezierEffect.cpp
index 9cca054..c9a2bea 100644
--- a/gpu/effects/GrBezierEffect.cpp
+++ b/gpu/effects/GrBezierEffect.cpp
@@ -111,7 +111,7 @@
     }
 
     builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                           (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("edgeAlpha")).c_str());
+                           (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
 }
 
 GrGLEffect::EffectKey GrGLConicEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -239,7 +239,7 @@
     }
 
     builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                           (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("edgeAlpha")).c_str());
+                           (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
 
 
     builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
@@ -383,7 +383,7 @@
     }
 
     builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                           (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<1>("edgeAlpha")).c_str());
+                           (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
 }
 
 GrGLEffect::EffectKey GrGLCubicEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
diff --git a/gpu/effects/GrBicubicEffect.cpp b/gpu/effects/GrBicubicEffect.cpp
index ccbf788..25a1591 100644
--- a/gpu/effects/GrBicubicEffect.cpp
+++ b/gpu/effects/GrBicubicEffect.cpp
@@ -44,6 +44,8 @@
                                  const char* inputColor,
                                  const TransformedCoordsArray& coords,
                                  const TextureSamplerArray& samplers) {
+    sk_ignore_unused_variable(inputColor);
+
     SkString coords2D = builder->ensureFSCoords2D(coords, 0);
     fCoefficientsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                            kMat44f_GrSLType, "Coefficients");
@@ -106,6 +108,7 @@
             fCoefficients[x * 4 + y] = SkScalarToFloat(coefficients[y * 4 + x]);
         }
     }
+    this->setWillNotUseInputColor();
 }
 
 GrBicubicEffect::GrBicubicEffect(GrTexture* texture,
@@ -120,6 +123,7 @@
             fCoefficients[x * 4 + y] = SkScalarToFloat(coefficients[y * 4 + x]);
         }
     }
+    this->setWillNotUseInputColor();
 }
 
 GrBicubicEffect::~GrBicubicEffect() {
diff --git a/gpu/gl/GrGLProgram.cpp b/gpu/gl/GrGLProgram.cpp
index c1b3a31..7ddd861 100644
--- a/gpu/gl/GrGLProgram.cpp
+++ b/gpu/gl/GrGLProgram.cpp
@@ -48,7 +48,6 @@
     fDstCopyTexUnit = -1;
 
     fColor = GrColor_ILLEGAL;
-    fColorFilterColor = GrColor_ILLEGAL;
 
     if (fDesc.getHeader().fHasVertexCode ||
         !fGpu->shouldUseFixedFunctionTexturing()) {
@@ -97,117 +96,6 @@
     }
 }
 
-namespace {
-// given two blend coefficients determine whether the src
-// and/or dst computation can be omitted.
-inline void need_blend_inputs(SkXfermode::Coeff srcCoeff,
-                              SkXfermode::Coeff dstCoeff,
-                              bool* needSrcValue,
-                              bool* needDstValue) {
-    if (SkXfermode::kZero_Coeff == srcCoeff) {
-        switch (dstCoeff) {
-            // these all read the src
-            case SkXfermode::kSC_Coeff:
-            case SkXfermode::kISC_Coeff:
-            case SkXfermode::kSA_Coeff:
-            case SkXfermode::kISA_Coeff:
-                *needSrcValue = true;
-                break;
-            default:
-                *needSrcValue = false;
-                break;
-        }
-    } else {
-        *needSrcValue = true;
-    }
-    if (SkXfermode::kZero_Coeff == dstCoeff) {
-        switch (srcCoeff) {
-            // these all read the dst
-            case SkXfermode::kDC_Coeff:
-            case SkXfermode::kIDC_Coeff:
-            case SkXfermode::kDA_Coeff:
-            case SkXfermode::kIDA_Coeff:
-                *needDstValue = true;
-                break;
-            default:
-                *needDstValue = false;
-                break;
-        }
-    } else {
-        *needDstValue = true;
-    }
-}
-
-/**
- * Create a blend_coeff * value string to be used in shader code. Sets empty
- * string if result is trivially zero.
- */
-inline void blend_term_string(SkString* str, SkXfermode::Coeff coeff,
-                       const char* src, const char* dst,
-                       const char* value) {
-    switch (coeff) {
-    case SkXfermode::kZero_Coeff:    /** 0 */
-        *str = "";
-        break;
-    case SkXfermode::kOne_Coeff:     /** 1 */
-        *str = value;
-        break;
-    case SkXfermode::kSC_Coeff:
-        str->printf("(%s * %s)", src, value);
-        break;
-    case SkXfermode::kISC_Coeff:
-        str->printf("((vec4(1) - %s) * %s)", src, value);
-        break;
-    case SkXfermode::kDC_Coeff:
-        str->printf("(%s * %s)", dst, value);
-        break;
-    case SkXfermode::kIDC_Coeff:
-        str->printf("((vec4(1) - %s) * %s)", dst, value);
-        break;
-    case SkXfermode::kSA_Coeff:      /** src alpha */
-        str->printf("(%s.a * %s)", src, value);
-        break;
-    case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
-        str->printf("((1.0 - %s.a) * %s)", src, value);
-        break;
-    case SkXfermode::kDA_Coeff:      /** dst alpha */
-        str->printf("(%s.a * %s)", dst, value);
-        break;
-    case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
-        str->printf("((1.0 - %s.a) * %s)", dst, value);
-        break;
-    default:
-        GrCrash("Unexpected xfer coeff.");
-        break;
-    }
-}
-/**
- * Adds a line to the fragment shader code which modifies the color by
- * the specified color filter.
- */
-void add_color_filter(GrGLShaderBuilder* builder,
-                      const char * outputVar,
-                      SkXfermode::Coeff uniformCoeff,
-                      SkXfermode::Coeff colorCoeff,
-                      const char* filterColor,
-                      const char* inColor) {
-    SkString colorStr, constStr;
-    blend_term_string(&colorStr, colorCoeff, filterColor, inColor, inColor);
-    blend_term_string(&constStr, uniformCoeff, filterColor, inColor, filterColor);
-    GrGLSLExpr<4> sum;
-    if (colorStr.isEmpty() && constStr.isEmpty()) {
-        sum = GrGLSLExpr<4>(0);
-    } else if (colorStr.isEmpty()) {
-        sum = constStr;
-    } else if (constStr.isEmpty()) {
-        sum = colorStr;
-    } else {
-        sum = GrGLSLExpr<4>(colorStr) + GrGLSLExpr<4>(constStr);
-    }
-    builder->fsCodeAppendf("\t%s = %s;\n", outputVar, sum.c_str());
-}
-}
-
 bool GrGLProgram::genProgram(GrGLShaderBuilder* builder,
                              const GrEffectStage* colorStages[],
                              const GrEffectStage* coverageStages[]) {
@@ -216,40 +104,17 @@
     const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
 
     // incoming color to current stage being processed.
-    GrGLSLExpr<4> inColor = builder->getInputColor();
-
-    // Get the coeffs for the Mode-based color filter, determine if color is needed.
-    SkXfermode::Coeff colorCoeff;
-    SkXfermode::Coeff filterColorCoeff;
-    SkAssertResult(
-        SkXfermode::ModeAsCoeff(header.fColorFilterXfermode,
-                                &filterColorCoeff,
-                                &colorCoeff));
-    bool needColor, needFilterColor;
-    need_blend_inputs(filterColorCoeff, colorCoeff, &needFilterColor, &needColor);
+    GrGLSLExpr4 inColor = builder->getInputColor();
 
     fColorEffects.reset(
         builder->createAndEmitEffects(colorStages,
                                       fDesc.effectKeys(),
-                                      needColor ? fDesc.numColorEffects() : 0,
+                                      fDesc.numColorEffects(),
                                       &inColor));
 
-    // Insert the color filter. This will soon be replaced by a color effect.
-    if (SkXfermode::kDst_Mode != header.fColorFilterXfermode) {
-        const char* colorFilterColorUniName = NULL;
-        fUniformHandles.fColorFilterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
-                                                              kVec4f_GrSLType, "FilterColor",
-                                                              &colorFilterColorUniName);
-
-        builder->fsCodeAppend("\tvec4 filteredColor;\n");
-        add_color_filter(builder, "filteredColor", filterColorCoeff,
-                         colorCoeff, colorFilterColorUniName, inColor.c_str());
-        inColor = "filteredColor";
-    }
-
     ///////////////////////////////////////////////////////////////////////////
     // compute the partial coverage
-    GrGLSLExpr<4> inCoverage = builder->getInputCoverage();
+    GrGLSLExpr4 inCoverage = builder->getInputCoverage();
 
     fCoverageEffects.reset(
         builder->createAndEmitEffects(coverageStages,
@@ -272,13 +137,13 @@
         const char* secondaryOutputName = builder->enableSecondaryOutput();
 
         // default coeff to ones for kCoverage_DualSrcOutput
-        GrGLSLExpr<4> coeff(1);
+        GrGLSLExpr4 coeff(1);
         if (GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput == header.fCoverageOutput) {
             // Get (1-A) into coeff
-            coeff = GrGLSLExprCast4(GrGLSLExpr<1>(1) - GrGLSLExprExtractAlpha(inColor));
+            coeff = GrGLSLExpr4::VectorCast(GrGLSLExpr1(1) - inColor.a());
         } else if (GrGLProgramDesc::kSecondaryCoverageISC_CoverageOutput == header.fCoverageOutput) {
             // Get (1-RGBA) into coeff
-            coeff = GrGLSLExpr<4>(1) - inColor;
+            coeff = GrGLSLExpr4(1) - inColor;
         }
         // Get coeff * coverage into modulate and then write that to the dual source output.
         builder->fsCodeAppendf("\t%s = %s;\n", secondaryOutputName, (coeff * inCoverage).c_str());
@@ -288,12 +153,12 @@
     // combine color and coverage as frag color
 
     // Get "color * coverage" into fragColor
-    GrGLSLExpr<4> fragColor = inColor * inCoverage;
+    GrGLSLExpr4 fragColor = inColor * inCoverage;
     // Now tack on "+(1-coverage)dst onto the frag color if we were asked to do so.
     if (GrGLProgramDesc::kCombineWithDst_CoverageOutput == header.fCoverageOutput) {
-        GrGLSLExpr<4> dstCoeff = GrGLSLExpr<4>(1) - inCoverage;
+        GrGLSLExpr4 dstCoeff = GrGLSLExpr4(1) - inCoverage;
 
-        GrGLSLExpr<4> dstContribution = dstCoeff * GrGLSLExpr<4>(builder->dstColor());
+        GrGLSLExpr4 dstContribution = dstCoeff * GrGLSLExpr4(builder->dstColor());
 
         fragColor = fragColor + dstContribution;
     }
@@ -352,15 +217,6 @@
     this->setCoverage(drawState, coverage, sharedState);
     this->setMatrixAndRenderTargetHeight(drawState);
 
-    // Setup the SkXfermode::Mode-based colorfilter uniform if necessary
-    if (fUniformHandles.fColorFilterUni.isValid() &&
-        fColorFilterColor != drawState.getColorFilterColor()) {
-        GrGLfloat c[4];
-        GrColorToRGBAFloat(drawState.getColorFilterColor(), c);
-        fUniformManager.set4fv(fUniformHandles.fColorFilterUni, 0, 1, c);
-        fColorFilterColor = drawState.getColorFilterColor();
-    }
-
     if (NULL != dstCopy) {
         if (fUniformHandles.fDstCopyTopLeftUni.isValid()) {
             fUniformManager.set2f(fUniformHandles.fDstCopyTopLeftUni,
@@ -409,7 +265,7 @@
                 }
                 break;
             case GrGLProgramDesc::kUniform_ColorInput:
-                if (fColor != color) {
+                if (fColor != color && fUniformHandles.fColorUni.isValid()) {
                     // OpenGL ES doesn't support unsigned byte varieties of glUniform
                     GrGLfloat c[4];
                     GrColorToRGBAFloat(color, c);
diff --git a/gpu/gl/GrGLProgram.h b/gpu/gl/GrGLProgram.h
index 23ed456..8e2ae74 100644
--- a/gpu/gl/GrGLProgram.h
+++ b/gpu/gl/GrGLProgram.h
@@ -139,7 +139,6 @@
         UniformHandle       fViewMatrixUni;
         UniformHandle       fColorUni;
         UniformHandle       fCoverageUni;
-        UniformHandle       fColorFilterUni;
 
         // We use the render target height to provide a y-down frag coord when specifying
         // origin_upper_left is not supported.
@@ -187,7 +186,6 @@
     MatrixState                       fMatrixState;
     GrColor                           fColor;
     GrColor                           fCoverage;
-    GrColor                           fColorFilterColor;
     int                               fDstCopyTexUnit;
 
     SkAutoTDelete<GrGLProgramEffects> fColorEffects;
diff --git a/gpu/gl/GrGLProgramDesc.cpp b/gpu/gl/GrGLProgramDesc.cpp
index f9728ff..d958742 100644
--- a/gpu/gl/GrGLProgramDesc.cpp
+++ b/gpu/gl/GrGLProgramDesc.cpp
@@ -56,6 +56,27 @@
 
     bool skipColor = SkToBool(blendOpts & (GrDrawState::kEmitTransBlack_BlendOptFlag |
                                            GrDrawState::kEmitCoverage_BlendOptFlag));
+    int firstEffectiveColorStage = 0;
+    bool inputColorIsUsed = true;
+    if (!skipColor) {
+        firstEffectiveColorStage = drawState.numColorStages();
+        while (firstEffectiveColorStage > 0 && inputColorIsUsed) {
+            --firstEffectiveColorStage;
+            const GrEffect* effect = drawState.getColorStage(firstEffectiveColorStage).getEffect()->get();
+            inputColorIsUsed = effect->willUseInputColor();
+        }
+    }
+
+    int firstEffectiveCoverageStage = 0;
+    bool inputCoverageIsUsed = true;
+    if (!skipCoverage) {
+        firstEffectiveCoverageStage = drawState.numCoverageStages();
+        while (firstEffectiveCoverageStage > 0 && inputCoverageIsUsed) {
+            --firstEffectiveCoverageStage;
+            const GrEffect* effect = drawState.getCoverageStage(firstEffectiveCoverageStage).getEffect()->get();
+            inputCoverageIsUsed = effect->willUseInputColor();
+        }
+    }
 
     // The descriptor is used as a cache key. Thus when a field of the
     // descriptor will not affect program generation (because of the attribute
@@ -70,10 +91,11 @@
 
     bool colorIsTransBlack = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag);
     bool colorIsSolidWhite = (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) ||
-                             (!requiresColorAttrib && 0xffffffff == drawState.getColor());
+                             (!requiresColorAttrib && 0xffffffff == drawState.getColor()) ||
+                             (!inputColorIsUsed);
 
-    int numEffects = (skipColor ? 0 : drawState.numColorStages()) +
-                     (skipCoverage ? 0 : drawState.numCoverageStages());
+    int numEffects = (skipColor ? 0 : (drawState.numColorStages() - firstEffectiveColorStage)) +
+                     (skipCoverage ? 0 : (drawState.numCoverageStages() - firstEffectiveCoverageStage));
 
     size_t newKeyLength = KeyLength(numEffects);
     bool allocChanged;
@@ -93,7 +115,7 @@
     bool readFragPosition = false;
     bool hasVertexCode = false;
     if (!skipColor) {
-        for (int s = 0; s < drawState.numColorStages(); ++s) {
+        for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) {
             effectKeys[currEffectKey++] =
                 get_key_and_update_stats(drawState.getColorStage(s), gpu->glCaps(),
                                          requiresLocalCoordAttrib, &readsDst, &readFragPosition,
@@ -101,7 +123,7 @@
         }
     }
     if (!skipCoverage) {
-        for (int s = 0; s < drawState.numCoverageStages(); ++s) {
+        for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) {
             effectKeys[currEffectKey++] =
                 get_key_and_update_stats(drawState.getCoverageStage(s), gpu->glCaps(),
                                          requiresLocalCoordAttrib, &readsDst, &readFragPosition,
@@ -111,7 +133,6 @@
 
     header->fHasVertexCode = hasVertexCode || requiresLocalCoordAttrib;
     header->fEmitsPointSize = isPoints;
-    header->fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode();
 
     // Currently the experimental GS will only work with triangle prims (and it doesn't do anything
     // other than pass through values from the VS to the FS anyway).
@@ -139,7 +160,7 @@
 
     if (skipCoverage) {
         header->fCoverageInput = kTransBlack_ColorInput;
-    } else if (covIsSolidWhite) {
+    } else if (covIsSolidWhite || !inputCoverageIsUsed) {
         header->fCoverageInput = kSolidWhite_ColorInput;
     } else if (defaultToUniformInputs && !requiresCoverageAttrib) {
         header->fCoverageInput = kUniform_ColorInput;
@@ -202,10 +223,6 @@
     bool separateCoverageFromColor = false;
     if (!drawState.isCoverageDrawing() && !skipCoverage &&
         (drawState.numCoverageStages() > 0 || requiresCoverageAttrib)) {
-        // color filter is applied between color/coverage computation
-        if (SkXfermode::kDst_Mode != header->fColorFilterXfermode) {
-            separateCoverageFromColor = true;
-        }
 
         // If we're stenciling then we want to discard samples that have zero coverage
         if (drawState.getStencil().doesWrite()) {
@@ -237,24 +254,23 @@
         }
     }
     if (!skipColor) {
-        for (int s = 0; s < drawState.numColorStages(); ++s) {
+        for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) {
             colorStages->push_back(&drawState.getColorStage(s));
         }
-        header->fColorEffectCnt = drawState.numColorStages();
     }
     if (!skipCoverage) {
         SkTArray<const GrEffectStage*, true>* array;
         if (separateCoverageFromColor) {
             array = coverageStages;
-            header->fCoverageEffectCnt = drawState.numCoverageStages();
         } else {
             array = colorStages;
-            header->fColorEffectCnt += drawState.numCoverageStages();
         }
-        for (int s = 0; s < drawState.numCoverageStages(); ++s) {
+        for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) {
             array->push_back(&drawState.getCoverageStage(s));
         }
     }
+    header->fColorEffectCnt = colorStages->count();
+    header->fCoverageEffectCnt = coverageStages->count();
 
     *desc->checksum() = 0;
     *desc->checksum() = SkChecksum::Compute(reinterpret_cast<uint32_t*>(desc->fKey.get()),
diff --git a/gpu/gl/GrGLProgramDesc.h b/gpu/gl/GrGLProgramDesc.h
index c8ad77c..ec3f097 100644
--- a/gpu/gl/GrGLProgramDesc.h
+++ b/gpu/gl/GrGLProgramDesc.h
@@ -160,7 +160,6 @@
 
         SkBool8                     fHasVertexCode;
         SkBool8                     fEmitsPointSize;
-        SkXfermode::Mode            fColorFilterXfermode : 8;
 
         // To enable experimental geometry shader code (not for use in
         // production)
diff --git a/gpu/gl/GrGLSL.cpp b/gpu/gl/GrGLSL.cpp
index f0088fc..0f740d9 100644
--- a/gpu/gl/GrGLSL.cpp
+++ b/gpu/gl/GrGLSL.cpp
@@ -77,7 +77,7 @@
 void GrGLSLMulVarBy4f(SkString* outAppend,
                       unsigned tabCnt,
                       const char* vec4VarName,
-                      const GrGLSLExpr<4>& mulFactor) {
+                      const GrGLSLExpr4& mulFactor) {
     if (mulFactor.isOnes()) {
         *outAppend = SkString();
     }
diff --git a/gpu/gl/GrGLSL.h b/gpu/gl/GrGLSL.h
index 9387d2b..8c5da51 100644
--- a/gpu/gl/GrGLSL.h
+++ b/gpu/gl/GrGLSL.h
@@ -76,34 +76,48 @@
     }
 }
 
-/** A class representing a GLSL expression.
+/** A generic base-class representing a GLSL expression.
  * The instance can be a variable name, expression or vecN(0) or vecN(1). Does simple constant
  * folding with help of 1 and 0.
- * Complex expressions can be constructed with operators *, +, -
+ *
+ * Clients should not use this class, rather the specific instantiations defined
+ * later, for example GrGLSLExpr4.
  */
-template <int N>
+template <typename Self>
 class GrGLSLExpr {
 public:
+    bool isOnes() const { return kOnes_ExprType == fType; }
+    bool isZeros() const { return kZeros_ExprType == fType; }
+
+    const char* c_str() const {
+        if (kZeros_ExprType == fType) {
+            return Self::ZerosStr();
+        } else if (kOnes_ExprType == fType) {
+            return Self::OnesStr();
+        }
+        SkASSERT(!fExpr.isEmpty()); // Empty expressions should not be used.
+        return fExpr.c_str();
+    }
+
+protected:
     /** Constructs an invalid expression.
      * Useful only as a return value from functions that never actually return
      * this and instances that will be assigned to later. */
     GrGLSLExpr()
         : fType(kFullExpr_ExprType) {
-        SK_COMPILE_ASSERT(N > 0 && N <= 4, dimensions_not_in_range);
         // The only constructor that is allowed to build an empty expression.
         SkASSERT(!this->isValid());
     }
 
     /** Constructs an expression with all components as value v */
     explicit GrGLSLExpr(int v) {
-        SK_COMPILE_ASSERT(N > 0 && N <= 4, dimensions_not_in_range);
         if (v == 0) {
             fType = kZeros_ExprType;
         } else if (v == 1) {
             fType = kOnes_ExprType;
         } else {
             fType = kFullExpr_ExprType;
-            fExpr.appendf(CastIntStr(), v);
+            fExpr.appendf(Self::CastIntStr(), v);
         }
     }
 
@@ -111,7 +125,6 @@
      * Argument expr is a simple expression or a parenthesized expression. */
     // TODO: make explicit once effects input Exprs.
     GrGLSLExpr(const char expr[]) {
-        SK_COMPILE_ASSERT(N > 0 && N <= 4, dimensions_not_in_range);
         if (NULL == expr) {  // TODO: remove this once effects input Exprs.
             fType = kOnes_ExprType;
         } else {
@@ -125,7 +138,6 @@
      * Argument expr is a simple expression or a parenthesized expression. */
     // TODO: make explicit once effects input Exprs.
     GrGLSLExpr(const SkString& expr) {
-        SK_COMPILE_ASSERT(N > 0 && N <= 4, dimensions_not_in_range);
         if (expr.isEmpty()) {  // TODO: remove this once effects input Exprs.
             fType = kOnes_ExprType;
         } else {
@@ -135,77 +147,61 @@
         SkASSERT(this->isValid());
     }
 
-    bool isOnes() const { return kOnes_ExprType == fType; }
-    bool isZeros() const { return kZeros_ExprType == fType; }
-
-    const char* c_str() const {
-        if (kZeros_ExprType == fType) {
-            return ZerosStr();
-        } else if (kOnes_ExprType == fType) {
-            return OnesStr();
-        }
-        SkASSERT(!fExpr.isEmpty()); // Empty expressions should not be used.
-        return fExpr.c_str();
-    }
-
-private:
+    /** Constructs an expression from a string with one substitution. */
     GrGLSLExpr(const char format[], const char in0[])
         : fType(kFullExpr_ExprType) {
         fExpr.appendf(format, in0);
     }
 
+    /** Constructs an expression from a string with two substitutions. */
     GrGLSLExpr(const char format[], const char in0[], const char in1[])
         : fType(kFullExpr_ExprType) {
         fExpr.appendf(format, in0, in1);
     }
 
-    GrGLSLExpr(const char format[], const char in0[], char in1)
-        : fType(kFullExpr_ExprType) {
-        fExpr.appendf(format, in0, in1);
-    }
-
     bool isValid() const {
         return kFullExpr_ExprType != fType || !fExpr.isEmpty();
     }
 
-    static const char* ZerosStr();
-    static const char* OnesStr();
-    static const char* ExtractAlphaStr();
-    static const char* CastStr();
-    static const char* CastIntStr();
+    /** Returns expression casted to another type.
+     * Generic implementation that is called for non-trivial cases of casts. */
+    template <typename T>
+    static Self VectorCastImpl(const T& other);
 
-    /** Casts the expression expr into smaller or bigger expression.
-     * Casting is done with GLSL rules:
-     * M==3, N==4 vec3(a, b, c) -> vec4(a, b, c, 0)
-     * N==4, M==3 vec4(a, b, c, d) -> vec3(a, b, c)
+    /** Returns a GLSL multiplication: component-wise or component-by-scalar.
+     * The multiplication will be component-wise or multiply each component by a scalar.
+     *
+     * The returned expression will compute the value of:
+     *    vecN(in0.x * in1.x, ...) if dim(T0) == dim(T1) (component-wise)
+     *    vecN(in0.x * in1, ...) if dim(T1) == 1 (vector by scalar)
+     *    vecN(in0 * in1.x, ...) if dim(T0) == 1 (scalar by vector)
      */
-    template <int M>
-    static GrGLSLExpr<N> VectorCast(const GrGLSLExpr<M>& expr);
+    template <typename T0, typename T1>
+    static Self Mul(T0 in0, T1 in1);
 
-    /** GLSL multiplication: component-wise or multiply each component by a scalar.
-     * M == N --> vecN(in0.x * in1.x, ...)
-     * M == 1 --> vecN(in0.x * in1, ...)
-     * otherwise --> compile-time error
+    /** Returns a GLSL addition: component-wise or add a scalar to each component.
+     * Return value computes:
+     *   vecN(in0.x + in1.x, ...) or vecN(in0.x + in1, ...) or vecN(in0 + in1.x, ...).
      */
-    template <int M>
-    static GrGLSLExpr<N> Mul(const GrGLSLExpr<N>& in0, const GrGLSLExpr<M>& in1);
+    template <typename T0, typename T1>
+    static Self Add(T0 in0, T1 in1);
 
-    /** GLSL addition: component-wise or add a scalar to each compoment.
-     * M == N --> vecN(in0.x + in1.x, ...)
-     * M == 1 --> vecN(in0.x + in1, ...)
-     * otherwise --> compile-time error
+    /** Returns a GLSL subtraction: component-wise or subtract compoments by a scalar.
+     * Return value computes
+     *   vecN(in0.x - in1.x, ...) or vecN(in0.x - in1, ...) or vecN(in0 - in1.x, ...).
      */
-    template <int M>
-    static GrGLSLExpr<N> Add(const GrGLSLExpr<N>& in0, const GrGLSLExpr<M>& in1);
+    template <typename T0, typename T1>
+    static Self Sub(T0 in0, T1 in1);
 
-    /** GLSL subtraction: component-wise or subtract compoments by a scalar.
-     * M == N --> vecN(in0.x - in1.x, ...)
-     * M == 1 --> vecN(in0.x - in1, ...)
-     * otherwise --> compile-time error
+    /** Returns expression that accesses component(s) of the expression.
+     * format should be the form "%s.x" where 'x' is the component(s) to access.
+     * Caller is responsible for making sure the amount of components in the
+     * format string is equal to dim(T).
      */
-    template <int M>
-    static GrGLSLExpr<N> Sub(const GrGLSLExpr<N>& in0, const GrGLSLExpr<M>& in1);
+    template <typename T>
+    T extractComponents(const char format[]) const;
 
+private:
     enum ExprType {
         kZeros_ExprType,
         kOnes_ExprType,
@@ -213,66 +209,112 @@
     };
     ExprType fType;
     SkString fExpr;
-
-    template <int> friend class GrGLSLExpr;
-
-    /** Multiplies two expressions component-wise. */
-    template <int M> friend GrGLSLExpr<M> operator*(const GrGLSLExpr<M>&, const GrGLSLExpr<M>&);
-    /** Adds two expressions component-wise. */
-    template <int M> friend GrGLSLExpr<M> operator+(const GrGLSLExpr<M>&, const GrGLSLExpr<M>&);
-    /** Subtracts two expressions component-wise. */
-    template <int M> friend GrGLSLExpr<M> operator-(const GrGLSLExpr<M>&, const GrGLSLExpr<M>&);
-    /** Multiplies every component of an expression with a scalar expression. */
-    friend GrGLSLExpr<4> operator*(const GrGLSLExpr<4>&, const GrGLSLExpr<1>&);
-    /** Adds a scalar expression to every component of an expression. */
-    friend GrGLSLExpr<4> operator+(const GrGLSLExpr<4>&, const GrGLSLExpr<1>&);
-    /** Subtracts a scalar expression from every component of an expression. */
-    friend GrGLSLExpr<4> operator-(const GrGLSLExpr<4>&, const GrGLSLExpr<1>&);
-
-    friend GrGLSLExpr<1> GrGLSLExprExtractAlpha(const GrGLSLExpr<4>& expr);
-    friend GrGLSLExpr<4> GrGLSLExprCast4(const GrGLSLExpr<1>& expr);
 };
 
+class GrGLSLExpr1;
+class GrGLSLExpr4;
 
-template <int N>
-inline GrGLSLExpr<N> operator*(const GrGLSLExpr<N>& in0, const GrGLSLExpr<N>&in1) {
-    return GrGLSLExpr<N>::Mul(in0, in1);
-}
+/** Class representing a float GLSL expression. */
+class GrGLSLExpr1 : public GrGLSLExpr<GrGLSLExpr1> {
+public:
+    GrGLSLExpr1()
+        : INHERITED() {
+    }
+    explicit GrGLSLExpr1(int v)
+        : INHERITED(v) {
+    }
+    GrGLSLExpr1(const char* expr)
+        : INHERITED(expr) {
+    }
+    GrGLSLExpr1(const SkString& expr)
+        : INHERITED(expr) {
+    }
 
-template <int N>
-inline GrGLSLExpr<N> operator+(const GrGLSLExpr<N>& in0, const GrGLSLExpr<N>&in1) {
-    return GrGLSLExpr<N>::Add(in0, in1);
-}
+    static GrGLSLExpr1 VectorCast(const GrGLSLExpr1& expr);
 
-template <int N>
-inline GrGLSLExpr<N> operator-(const GrGLSLExpr<N>& in0, const GrGLSLExpr<N>&in1) {
-    return GrGLSLExpr<N>::Sub(in0, in1);
-}
+private:
+    GrGLSLExpr1(const char format[], const char in0[])
+        : INHERITED(format, in0) {
+    }
+    GrGLSLExpr1(const char format[], const char in0[], const char in1[])
+        : INHERITED(format, in0, in1) {
+    }
 
-inline GrGLSLExpr<4> operator*(const GrGLSLExpr<4>& in0, const GrGLSLExpr<1>& in1) {
-    return GrGLSLExpr<4>::Mul(in0, in1);
-}
+    static const char* ZerosStr();
+    static const char* OnesStr();
+    static const char* CastStr();
+    static const char* CastIntStr();
 
-inline GrGLSLExpr<4> operator+(const GrGLSLExpr<4>& in0, const GrGLSLExpr<1>& in1) {
-    return GrGLSLExpr<4>::Add(in0, in1);
-}
+    friend GrGLSLExpr1 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr1&in1);
+    friend GrGLSLExpr1 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr1&in1);
+    friend GrGLSLExpr1 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr1&in1);
 
-inline GrGLSLExpr<4> operator-(const GrGLSLExpr<4>& in0, const GrGLSLExpr<1>& in1) {
-    return GrGLSLExpr<4>::Sub(in0, in1);
-}
+    friend class GrGLSLExpr<GrGLSLExpr1>;
+    friend class GrGLSLExpr<GrGLSLExpr4>;
 
-/** Casts an vec1 expression  to vec4 expresison, eg. vec1(v) -> vec4(v,v,v,v). */
-GrGLSLExpr<4> GrGLSLExprCast4(const GrGLSLExpr<1>& expr);
+    typedef GrGLSLExpr<GrGLSLExpr1> INHERITED;
+};
 
-/** Extracts alpha component from an expression of vec<4>. */
-GrGLSLExpr<1> GrGLSLExprExtractAlpha(const GrGLSLExpr<4>& expr);
+/** Class representing a float vector (vec4) GLSL expression. */
+class GrGLSLExpr4 : public GrGLSLExpr<GrGLSLExpr4> {
+public:
+    GrGLSLExpr4()
+        : INHERITED() {
+    }
+    explicit GrGLSLExpr4(int v)
+        : INHERITED(v) {
+    }
+    GrGLSLExpr4(const char* expr)
+        : INHERITED(expr) {
+    }
+    GrGLSLExpr4(const SkString& expr)
+        : INHERITED(expr) {
+    }
+
+    typedef GrGLSLExpr1 AExpr;
+    AExpr a() const;
+
+    /** GLSL vec4 cast / constructor, eg vec4(floatv) -> vec4(floatv, floatv, floatv, floatv) */
+    static GrGLSLExpr4 VectorCast(const GrGLSLExpr1& expr);
+    static GrGLSLExpr4 VectorCast(const GrGLSLExpr4& expr);
+
+private:
+    GrGLSLExpr4(const char format[], const char in0[])
+        : INHERITED(format, in0) {
+    }
+    GrGLSLExpr4(const char format[], const char in0[], const char in1[])
+        : INHERITED(format, in0, in1) {
+    }
+
+    static const char* ZerosStr();
+    static const char* OnesStr();
+    static const char* CastStr();
+    static const char* CastIntStr();
+
+    // The vector-by-scalar and scalar-by-vector binary operations.
+    friend GrGLSLExpr4 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr4&in1);
+    friend GrGLSLExpr4 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr4&in1);
+    friend GrGLSLExpr4 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr4&in1);
+    friend GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr1&in1);
+    friend GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr1&in1);
+    friend GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr1&in1);
+
+    // The vector-by-vector, i.e. component-wise, binary operations.
+    friend GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr4&in1);
+    friend GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr4&in1);
+    friend GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr4&in1);
+
+    friend class GrGLSLExpr<GrGLSLExpr4>;
+
+    typedef GrGLSLExpr<GrGLSLExpr4> INHERITED;
+};
 
 /**
  * Does an inplace mul, *=, of vec4VarName by mulFactor.
  * A semicolon and newline are added after the assignment.
  */
 void GrGLSLMulVarBy4f(SkString* outAppend, unsigned tabCnt,
-                      const char* vec4VarName, const GrGLSLExpr<4>& mulFactor);
+                      const char* vec4VarName, const GrGLSLExpr4& mulFactor);
 
 #include "GrGLSL_impl.h"
 
diff --git a/gpu/gl/GrGLSL_impl.h b/gpu/gl/GrGLSL_impl.h
index 008d512..bdd69cc 100644
--- a/gpu/gl/GrGLSL_impl.h
+++ b/gpu/gl/GrGLSL_impl.h
@@ -8,133 +8,168 @@
 #ifndef GrGLSL_impl_DEFINED
 #define GrGLSL_impl_DEFINED
 
-template<>
-inline const char* GrGLSLExpr<4>::ZerosStr() {
-    return "vec4(0)";
-}
-
-template<>
-inline const char* GrGLSLExpr<4>::OnesStr() {
-    return "vec4(1)";
-}
-
-template<>
-inline const char* GrGLSLExpr<4>::ExtractAlphaStr() {
-    return "%s.a";
-}
-
-template<>
-inline const char* GrGLSLExpr<4>::CastStr() {
-    return "vec4(%s)";
-}
-template<>
-inline const char* GrGLSLExpr<4>::CastIntStr() {
-    return "vec4(%d)";
-}
-
-template<>
-inline const char* GrGLSLExpr<1>::ZerosStr() {
-    return "0";
-}
-
-template<>
-inline const char* GrGLSLExpr<1>::OnesStr() {
-    return "1";
-}
-
-// GrGLSLExpr<1>::ExtractAlphaStr() and GrGLSLExpr<1>::CastStr() are
-// unimplemented because using them is likely an error. This is now caught
-// compile-time.
-
-template<>
-inline const char* GrGLSLExpr<1>::CastIntStr() {
-    return "%d";
-}
-
-template<>
-template<>
-inline GrGLSLExpr<4> GrGLSLExpr<4>::VectorCast(const GrGLSLExpr<4>& expr) {
-    return expr;
-}
-
-template<>
-template<>
-inline GrGLSLExpr<1>  GrGLSLExpr<1>::VectorCast(const GrGLSLExpr<1>& expr)  {
-    return expr;
-}
-
-template<int N>
-template<int M>
-inline GrGLSLExpr<N> GrGLSLExpr<N>::VectorCast(const GrGLSLExpr<M>& expr) {
+template<typename Self>
+template<typename T>
+inline Self GrGLSLExpr<Self>::VectorCastImpl(const T& expr) {
     if (expr.isZeros()) {
-        return GrGLSLExpr<N>(0);
+        return Self(0);
     }
     if (expr.isOnes()) {
-        return GrGLSLExpr<N>(1);
+        return Self(1);
     }
-    return GrGLSLExpr<N>(GrGLSLExpr<N>::CastStr(), expr.c_str());
+    return Self(Self::CastStr(), expr.c_str());
 }
 
-template<int N>
-template<int M>
-inline GrGLSLExpr<N> GrGLSLExpr<N>::Mul(const GrGLSLExpr<N>& in0, const GrGLSLExpr<M>& in1) {
-    SK_COMPILE_ASSERT(N == M || M == 1, binary_op_dimensions_incompatible);
+template<typename Self>
+template<typename T0, typename T1>
+inline Self GrGLSLExpr<Self>::Mul(T0 in0, T1 in1) {
     if (in0.isZeros() || in1.isZeros()) {
-        return GrGLSLExpr<N>(0);
+        return Self(0);
     }
     if (in0.isOnes()) {
-        return VectorCast<M>(in1);
+        return Self::VectorCast(in1);
     }
     if (in1.isOnes()) {
-        return in0;
+        return Self::VectorCast(in0);
     }
-    return GrGLSLExpr<N>("(%s * %s)", in0.c_str(), in1.c_str());
+    return Self("(%s * %s)", in0.c_str(), in1.c_str());
 }
 
-template<int N>
-template<int M>
-inline GrGLSLExpr<N> GrGLSLExpr<N>::Add(const GrGLSLExpr<N>& in0, const GrGLSLExpr<M>& in1) {
-    SK_COMPILE_ASSERT(N == M || M == 1, binary_op_dimensions_incompatible);
+template<typename Self>
+template<typename T0, typename T1>
+inline Self GrGLSLExpr<Self>::Add(T0 in0, T1 in1) {
     if (in1.isZeros()) {
-        return in0;
+        return Self::VectorCast(in0);
     }
     if (in0.isZeros()) {
-        return VectorCast<M>(in1);
+        return Self::VectorCast(in1);
     }
     if (in0.isOnes() && in1.isOnes()) {
-        return GrGLSLExpr<N>(2);
+        return Self(2);
     }
-    return GrGLSLExpr<N>("(%s + %s)", in0.c_str(), in1.c_str());
+    return Self("(%s + %s)", in0.c_str(), in1.c_str());
 }
 
-template<int N>
-template<int M>
-inline GrGLSLExpr<N> GrGLSLExpr<N>::Sub(const GrGLSLExpr<N>& in0, const GrGLSLExpr<M>& in1) {
-    SK_COMPILE_ASSERT(N == M || M == 1, binary_op_dimensions_incompatible);
+template<typename Self>
+template<typename T0, typename T1>
+inline Self GrGLSLExpr<Self>::Sub(T0 in0, T1 in1) {
     if (in1.isZeros()) {
-        return in0;
+        return Self::VectorCast(in0);
     }
     if (in1.isOnes()) {
         if (in0.isOnes()) {
-            return GrGLSLExpr<N>(0);
+            return Self(0);
         }
     }
 
-    return GrGLSLExpr<N>("(%s - %s)", in0.c_str(), in1.c_str());
+    return Self("(%s - %s)", in0.c_str(), in1.c_str());
 }
 
-inline GrGLSLExpr<4> GrGLSLExprCast4(const GrGLSLExpr<1>& expr) {
-    return GrGLSLExpr<4>::VectorCast(expr);
+template <typename Self>
+template <typename T>
+T GrGLSLExpr<Self>::extractComponents(const char format[]) const {
+    if (this->isZeros()) {
+        return T(0);
+    }
+    if (this->isOnes()) {
+        return T(1);
+    }
+    return T(format, this->c_str());
 }
 
-inline GrGLSLExpr<1> GrGLSLExprExtractAlpha(const GrGLSLExpr<4>& expr) {
-    if (expr.isZeros()) {
-        return GrGLSLExpr<1>(0);
-    }
-    if (expr.isOnes()) {
-        return GrGLSLExpr<1>(1);
-    }
-    return GrGLSLExpr<1>(GrGLSLExpr<4>::ExtractAlphaStr(), expr.c_str());
+inline GrGLSLExpr1 GrGLSLExpr1::VectorCast(const GrGLSLExpr1& expr) {
+    return expr;
+}
+
+inline const char* GrGLSLExpr1::ZerosStr() {
+    return "0";
+}
+
+inline const char* GrGLSLExpr1::OnesStr() {
+    return "1.0";
+}
+
+// GrGLSLExpr1::CastStr() is unimplemented because using them is likely an
+// error. This is now caught compile-time.
+
+inline const char* GrGLSLExpr1::CastIntStr() {
+    return "%d";
+}
+
+inline GrGLSLExpr1 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr1& in1) {
+    return GrGLSLExpr1::Mul(in0, in1);
+}
+
+inline GrGLSLExpr1 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr1& in1) {
+    return GrGLSLExpr1::Add(in0, in1);
+}
+
+inline GrGLSLExpr1 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr1& in1) {
+    return GrGLSLExpr1::Sub(in0, in1);
+}
+
+inline const char* GrGLSLExpr4::ZerosStr() {
+    return "vec4(0)";
+}
+
+inline const char* GrGLSLExpr4::OnesStr() {
+    return "vec4(1)";
+}
+
+inline const char* GrGLSLExpr4::CastStr() {
+    return "vec4(%s)";
+}
+
+inline const char* GrGLSLExpr4::CastIntStr() {
+    return "vec4(%d)";
+}
+
+inline GrGLSLExpr4 GrGLSLExpr4::VectorCast(const GrGLSLExpr1& expr) {
+    return INHERITED::VectorCastImpl(expr);
+}
+
+inline GrGLSLExpr4 GrGLSLExpr4::VectorCast(const GrGLSLExpr4& expr) {
+    return expr;
+}
+
+inline GrGLSLExpr4::AExpr GrGLSLExpr4::a() const {
+    return this->extractComponents<GrGLSLExpr4::AExpr>("%s.a");
+}
+
+inline GrGLSLExpr4 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr4& in1) {
+    return GrGLSLExpr4::Mul(in0, in1);
+}
+
+inline GrGLSLExpr4 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr4& in1) {
+    return GrGLSLExpr4::Add(in0, in1);
+}
+
+inline GrGLSLExpr4 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr4& in1) {
+    return GrGLSLExpr4::Sub(in0, in1);
+}
+
+inline GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr1& in1) {
+    return GrGLSLExpr4::Mul(in0, in1);
+}
+
+inline GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr1& in1) {
+    return GrGLSLExpr4::Add(in0, in1);
+}
+
+inline GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr1& in1) {
+    return GrGLSLExpr4::Sub(in0, in1);
+}
+
+inline GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr4& in1) {
+    return GrGLSLExpr4::Mul(in0, in1);
+}
+
+inline GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr4& in1) {
+    return GrGLSLExpr4::Add(in0, in1);
+}
+
+inline GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr4& in1) {
+    return GrGLSLExpr4::Sub(in0, in1);
 }
 
 #endif
diff --git a/gpu/gl/GrGLShaderBuilder.cpp b/gpu/gl/GrGLShaderBuilder.cpp
index 4d8df4e..004fb04 100644
--- a/gpu/gl/GrGLShaderBuilder.cpp
+++ b/gpu/gl/GrGLShaderBuilder.cpp
@@ -149,22 +149,22 @@
         const char* name;
         fColorUniform = this->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                          kVec4f_GrSLType, "Color", &name);
-        fInputColor = name;
+        fInputColor = GrGLSLExpr4(name);
     } else if (GrGLProgramDesc::kSolidWhite_ColorInput == header.fColorInput) {
-        fInputColor = GrGLSLExpr<4>(1);
+        fInputColor = GrGLSLExpr4(1);
     } else if (GrGLProgramDesc::kTransBlack_ColorInput == header.fColorInput) {
-        fInputColor = GrGLSLExpr<4>(0);
+        fInputColor = GrGLSLExpr4(0);
     }
 
     if (GrGLProgramDesc::kUniform_ColorInput == header.fCoverageInput) {
         const char* name;
         fCoverageUniform = this->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                             kVec4f_GrSLType, "Coverage", &name);
-        fInputCoverage = name;
+        fInputCoverage = GrGLSLExpr4(name);
     } else if (GrGLProgramDesc::kSolidWhite_ColorInput == header.fCoverageInput) {
-        fInputCoverage = GrGLSLExpr<4>(1);
+        fInputCoverage = GrGLSLExpr4(1);
     } else if (GrGLProgramDesc::kTransBlack_ColorInput == header.fCoverageInput) {
-        fInputCoverage = GrGLSLExpr<4>(0);
+        fInputCoverage = GrGLSLExpr4(0);
     }
 
     if (k110_GrGLSLGeneration != fGpu->glslGeneration()) {
@@ -295,7 +295,7 @@
                                             GrSLType varyingType) {
     SkString lookup;
     this->appendTextureLookup(&lookup, sampler, coordName, varyingType);
-    fFSCode.append((GrGLSLExpr<4>(modulation) * GrGLSLExpr<4>(lookup)).c_str());
+    fFSCode.append((GrGLSLExpr4(modulation) * GrGLSLExpr4(lookup)).c_str());
 }
 
 GrGLShaderBuilder::DstReadKey GrGLShaderBuilder::KeyForDstRead(const GrTexture* dstCopy,
@@ -515,11 +515,11 @@
                                              const GrEffectStage* effectStages[],
                                              const EffectKey effectKeys[],
                                              int effectCnt,
-                                             GrGLSLExpr<4>* fsInOutColor) {
+                                             GrGLSLExpr4* fsInOutColor) {
     bool effectEmitted = false;
 
-    GrGLSLExpr<4> inColor = *fsInOutColor;
-    GrGLSLExpr<4> outColor;
+    GrGLSLExpr4 inColor = *fsInOutColor;
+    GrGLSLExpr4 outColor;
 
     for (int e = 0; e < effectCnt; ++e) {
         SkASSERT(NULL != effectStages[e] && NULL != effectStages[e]->getEffect());
@@ -839,7 +839,7 @@
         const GrEffectStage* effectStages[],
         const EffectKey effectKeys[],
         int effectCnt,
-        GrGLSLExpr<4>* inOutFSColor) {
+        GrGLSLExpr4* inOutFSColor) {
 
     GrGLVertexProgramEffectsBuilder programEffectsBuilder(this, effectCnt);
     this->INHERITED::createAndEmitEffects(&programEffectsBuilder,
@@ -954,7 +954,7 @@
         const GrEffectStage* effectStages[],
         const EffectKey effectKeys[],
         int effectCnt,
-        GrGLSLExpr<4>* inOutFSColor) {
+        GrGLSLExpr4* inOutFSColor) {
 
     GrGLTexGenProgramEffectsBuilder texGenEffectsBuilder(this, effectCnt);
     this->INHERITED::createAndEmitEffects(&texGenEffectsBuilder,
diff --git a/gpu/gl/GrGLShaderBuilder.h b/gpu/gl/GrGLShaderBuilder.h
index 3eff1df..1218554 100644
--- a/gpu/gl/GrGLShaderBuilder.h
+++ b/gpu/gl/GrGLShaderBuilder.h
@@ -168,10 +168,10 @@
     /**
      * Interfaces used by GrGLProgram.
      */
-    const GrGLSLExpr<4>& getInputColor() const {
+    const GrGLSLExpr4& getInputColor() const {
         return fInputColor;
     }
-    const GrGLSLExpr<4>& getInputCoverage() const {
+    const GrGLSLExpr4& getInputCoverage() const {
         return fInputCoverage;
     }
 
@@ -186,7 +186,7 @@
     virtual GrGLProgramEffects* createAndEmitEffects(const GrEffectStage* effectStages[],
                                                      const EffectKey effectKeys[],
                                                      int effectCnt,
-                                                     GrGLSLExpr<4>* inOutFSColor) = 0;
+                                                     GrGLSLExpr4* inOutFSColor) = 0;
 
     const char* getColorOutputName() const;
     const char* enableSecondaryOutput();
@@ -211,8 +211,8 @@
 protected:
     GrGpuGL* gpu() const { return fGpu; }
 
-    void setInputColor(const GrGLSLExpr<4>& inputColor) { fInputColor = inputColor; }
-    void setInputCoverage(const GrGLSLExpr<4>& inputCoverage) { fInputCoverage = inputCoverage; }
+    void setInputColor(const GrGLSLExpr4& inputColor) { fInputColor = inputColor; }
+    void setInputCoverage(const GrGLSLExpr4& inputCoverage) { fInputCoverage = inputCoverage; }
 
     /** Add input/output variable declarations (i.e. 'varying') to the fragment shader. */
     GrGLShaderVar& fsInputAppend() { return fFSInputs.push_back(); }
@@ -227,7 +227,7 @@
                               const GrEffectStage* effectStages[],
                               const EffectKey effectKeys[],
                               int effectCnt,
-                              GrGLSLExpr<4>* inOutFSColor);
+                              GrGLSLExpr4* inOutFSColor);
 
     virtual bool compileAndAttachShaders(GrGLuint programId) const;
     virtual void bindProgramLocations(GrGLuint programId) const;
@@ -329,8 +329,8 @@
     bool                                    fSetupFragPosition;
     GrGLUniformManager::UniformHandle       fDstCopySamplerUniform;
 
-    GrGLSLExpr<4>                           fInputColor;
-    GrGLSLExpr<4>                           fInputCoverage;
+    GrGLSLExpr4                             fInputColor;
+    GrGLSLExpr4                             fInputCoverage;
 
     bool                                    fHasCustomColorOutput;
     bool                                    fHasSecondaryOutput;
@@ -396,7 +396,7 @@
                 const GrEffectStage* effectStages[],
                 const EffectKey effectKeys[],
                 int effectCnt,
-                GrGLSLExpr<4>* inOutFSColor) SK_OVERRIDE;
+                GrGLSLExpr4* inOutFSColor) SK_OVERRIDE;
 
     GrGLUniformManager::UniformHandle getViewMatrixUniform() const {
         return fViewMatrixUniform;
@@ -445,7 +445,7 @@
                 const GrEffectStage* effectStages[],
                 const EffectKey effectKeys[],
                 int effectCnt,
-                GrGLSLExpr<4>* inOutFSColor) SK_OVERRIDE;
+                GrGLSLExpr4* inOutFSColor) SK_OVERRIDE;
 
 private:
     int fNumTexCoordSets;