Expand modulate, add, subtract, extract component glsl helpers.
Review URL: https://codereview.chromium.org/13895006

git-svn-id: http://skia.googlecode.com/svn/trunk@8755 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index d011db2..22964e5 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -508,7 +508,7 @@
                                    "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n\t\t}\n");
 
             SkString modulate;
-            GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
+            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
 
             builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 8280d94..3e9bc1b 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -560,7 +560,7 @@
             builder->fsCodeAppend("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
 
             SkString modulate;
-            GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
+            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
 
             builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
@@ -653,7 +653,7 @@
             builder->fsCodeAppendf("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
 
             SkString modulate;
-            GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
+            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
 
             builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index e1abc31..ea15ce3 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -100,7 +100,7 @@
                     fsWidthHeightName);
 
             SkString modulate;
-            GrGLSLModulate4f(&modulate, inputColor, "coverage");
+            GrGLSLModulatef<4>(&modulate, inputColor, "coverage");
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
         }
 
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index ff468fd..42fb2b7 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -108,7 +108,7 @@
             }
 
             SkString modulate;
-            GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
+            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
         }
 
@@ -234,7 +234,7 @@
             }
 
             SkString modulate;
-            GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
+            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
         }
 
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
index 172558d..c2dc360 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -41,7 +41,7 @@
             builder->addVarying(kVec2f_GrSLType, "textureCoords", &vsVaryingName, &fsCoordName);
             const char* attrName =
                 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str();
-            builder->vsCodeAppendf("\t%s = %s;", vsVaryingName, attrName);
+            builder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, attrName);
         } else {
             fsCoordSLType = fEffectMatrix.get()->emitCode(builder, key, &fsCoordName);
         }
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 51cae5c..f1280ea 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -13,6 +13,7 @@
 #include "GrGLEffect.h"
 #include "GrGpuGL.h"
 #include "GrGLShaderVar.h"
+#include "GrGLSL.h"
 #include "SkTrace.h"
 #include "SkXfermode.h"
 
@@ -112,7 +113,6 @@
 }
 
 namespace {
-
 // given two blend coeffecients determine whether the src
 // and/or dst computation can be omitted.
 inline void need_blend_inputs(SkXfermode::Coeff srcCoeff,
@@ -211,12 +211,12 @@
     blend_term_string(&constStr, uniformCoeff, filterColor, inColor, filterColor);
 
     SkString sum;
-    GrGLSLAdd4f(&sum, colorStr.c_str(), constStr.c_str());
+    GrGLSLAddf<4>(&sum, colorStr.c_str(), constStr.c_str());
     builder->fsCodeAppendf("\t%s = %s;\n", outputVar, sum.c_str());
 }
 }
 
-void GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
+GrSLConstantVec GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
     switch (fDesc.fColorInput) {
         case GrGLProgramDesc::kAttribute_ColorInput: {
             builder->addAttribute(kVec4f_GrSLType, COL_ATTR_NAME);
@@ -224,54 +224,57 @@
             builder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
             builder->vsCodeAppendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
             *inColor = fsName;
-            } break;
+            return kNone_GrSLConstantVec;
+        }
         case GrGLProgramDesc::kUniform_ColorInput: {
             const char* name;
             fUniformHandles.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                                             kVec4f_GrSLType, "Color", &name);
             *inColor = name;
-            break;
+            return kNone_GrSLConstantVec;
         }
         case GrGLProgramDesc::kTransBlack_ColorInput:
-            GrAssert(!"needComputedColor should be false.");
-            break;
+            inColor->reset();
+            return kZeros_GrSLConstantVec;
         case GrGLProgramDesc::kSolidWhite_ColorInput:
-            break;
+            inColor->reset();
+            return kOnes_GrSLConstantVec;
         default:
             GrCrash("Unknown color type.");
-            break;
+            return kNone_GrSLConstantVec;
     }
 }
 
-void GrGLProgram::genUniformCoverage(GrGLShaderBuilder* builder, SkString* inOutCoverage) {
-    const char* covUniName;
-    fUniformHandles.fCoverageUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                                                       kVec4f_GrSLType, "Coverage", &covUniName);
-    if (inOutCoverage->size()) {
-        builder->fsCodeAppendf("\tvec4 uniCoverage = %s * %s;\n",
-                               covUniName, inOutCoverage->c_str());
-        *inOutCoverage = "uniCoverage";
-    } else {
-        *inOutCoverage = covUniName;
+GrSLConstantVec GrGLProgram::genInputCoverage(GrGLShaderBuilder* builder, SkString* inCoverage) {
+    switch (fDesc.fCoverageInput) {
+        case GrGLProgramDesc::kAttribute_ColorInput: {
+            builder->addAttribute(kVec4f_GrSLType, COV_ATTR_NAME);
+            const char *vsName, *fsName;
+            builder->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName);
+            builder->vsCodeAppendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
+            *inCoverage = fsName;
+            return kNone_GrSLConstantVec;
+        }
+        case GrGLProgramDesc::kUniform_ColorInput: {
+            const char* name;
+            fUniformHandles.fCoverageUni =
+                builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                    kVec4f_GrSLType, "Coverage", &name);
+            *inCoverage = name;
+            return kNone_GrSLConstantVec;
+        }
+        case GrGLProgramDesc::kTransBlack_ColorInput:
+            inCoverage->reset();
+            return kZeros_GrSLConstantVec;
+        case GrGLProgramDesc::kSolidWhite_ColorInput:
+            inCoverage->reset();
+            return kOnes_GrSLConstantVec;
+        default:
+            GrCrash("Unknown color type.");
+            return kNone_GrSLConstantVec;
     }
 }
 
-namespace {
-void gen_attribute_coverage(GrGLShaderBuilder* builder,
-                            SkString* inOutCoverage) {
-    builder->addAttribute(kVec4f_GrSLType, COV_ATTR_NAME);
-    const char *vsName, *fsName;
-    builder->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName);
-    builder->vsCodeAppendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
-    if (inOutCoverage->size()) {
-        builder->fsCodeAppendf("\tvec4 attrCoverage = %s * %s;\n", fsName, inOutCoverage->c_str());
-        *inOutCoverage = "attrCoverage";
-    } else {
-        *inOutCoverage = fsName;
-    }
-}
-}
-
 void GrGLProgram::genGeometryShader(GrGLShaderBuilder* builder) const {
 #if GR_GL_EXPERIMENTAL_GS
     // TODO: The builder should add all this glue code.
@@ -420,45 +423,6 @@
 
     GrGLShaderBuilder builder(fContext.info(), fUniformManager, fDesc);
 
-    SkXfermode::Coeff colorCoeff, uniformCoeff;
-    // The rest of transfer mode color filters have not been implemented
-    if (fDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
-        GR_DEBUGCODE(bool success =)
-            SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
-                                    (fDesc.fColorFilterXfermode),
-                                    &uniformCoeff, &colorCoeff);
-        GR_DEBUGASSERT(success);
-    } else {
-        colorCoeff = SkXfermode::kOne_Coeff;
-        uniformCoeff = SkXfermode::kZero_Coeff;
-    }
-
-    // no need to do the color filter if coverage is 0. The output color is scaled by the coverage.
-    // All the dual source outputs are scaled by the coverage as well.
-    if (GrGLProgramDesc::kTransBlack_ColorInput == fDesc.fCoverageInput) {
-        colorCoeff = SkXfermode::kZero_Coeff;
-        uniformCoeff = SkXfermode::kZero_Coeff;
-    }
-
-    // If we know the final color is going to be all zeros then we can
-    // simplify the color filter coefficients. needComputedColor will then
-    // come out false below.
-    if (GrGLProgramDesc::kTransBlack_ColorInput == fDesc.fColorInput) {
-        colorCoeff = SkXfermode::kZero_Coeff;
-        if (SkXfermode::kDC_Coeff == uniformCoeff ||
-            SkXfermode::kDA_Coeff == uniformCoeff) {
-            uniformCoeff = SkXfermode::kZero_Coeff;
-        } else if (SkXfermode::kIDC_Coeff == uniformCoeff ||
-                   SkXfermode::kIDA_Coeff == uniformCoeff) {
-            uniformCoeff = SkXfermode::kOne_Coeff;
-        }
-    }
-
-    bool needColorFilterUniform;
-    bool needComputedColor;
-    need_blend_inputs(uniformCoeff, colorCoeff,
-                      &needColorFilterUniform, &needComputedColor);
-
     // the dual source output has no canonical var name, have to
     // declare an output, which is incompatible with gl_FragColor/gl_FragData.
     bool dualSourceOutputWritten = false;
@@ -482,10 +446,7 @@
 
     // incoming color to current stage being processed.
     SkString inColor;
-
-    if (needComputedColor) {
-        this->genInputColor(&builder, &inColor);
-    }
+    GrSLConstantVec knownColorValue = this->genInputColor(&builder, &inColor);
 
     // we output point size in the GS if present
     if (fDesc.fEmitsPointSize
@@ -496,15 +457,29 @@
         builder.vsCodeAppend("\tgl_PointSize = 1.0;\n");
     }
 
-    ///////////////////////////////////////////////////////////////////////////
-    // compute the final color
+    // Get the coeffs for the Mode-based color filter, determine if color is needed.
+    SkXfermode::Coeff colorCoeff;
+    SkXfermode::Coeff filterColorCoeff;
+    SkAssertResult(
+        SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>(fDesc.fColorFilterXfermode),
+                                &filterColorCoeff,
+                                &colorCoeff));
+    bool needColor, needFilterColor;
+    need_blend_inputs(filterColorCoeff, colorCoeff, &needFilterColor, &needColor);
 
-    // if we have color stages string them together, feeding the output color
-    // of each to the next and generating code for each stage.
-    if (needComputedColor) {
+    if (needColor) {
+        ///////////////////////////////////////////////////////////////////////////
+        // compute the color
+        // if we have color stages string them together, feeding the output color
+        // of each to the next and generating code for each stage.
         SkString outColor;
         for (int s = 0; s < fDesc.fFirstCoverageStage; ++s) {
             if (GrGLEffect::kNoEffectKey != fDesc.fEffectKeys[s]) {
+                if (kZeros_GrSLConstantVec == knownColorValue) {
+                    // Effects have no way to communicate zeros, they treat an empty string as ones.
+                    inColor = "initialColor";
+                    builder.fsCodeAppendf("\tvec4 %s = %s;\n", inColor.c_str(), GrGLSLZerosVecf(4));
+                }
                 // create var to hold stage result
                 outColor = "color";
                 outColor.appendS32(s);
@@ -518,137 +493,131 @@
                                                             &fUniformHandles.fEffectSamplerUnis[s]);
                 builder.setNonStage();
                 inColor = outColor;
+                knownColorValue = kNone_GrSLConstantVec;
             }
         }
     }
 
-    // if have all ones or zeros for the "dst" input to the color filter then we
-    // may be able to make additional optimizations.
-    if (needColorFilterUniform && needComputedColor && !inColor.size()) {
-        GrAssert(GrGLProgramDesc::kSolidWhite_ColorInput == fDesc.fColorInput);
-        bool uniformCoeffIsZero = SkXfermode::kIDC_Coeff == uniformCoeff ||
-                                  SkXfermode::kIDA_Coeff == uniformCoeff;
-        if (uniformCoeffIsZero) {
-            uniformCoeff = SkXfermode::kZero_Coeff;
-            bool bogus;
-            need_blend_inputs(SkXfermode::kZero_Coeff, colorCoeff,
-                              &needColorFilterUniform, &bogus);
-        }
-    }
-    const char* colorFilterColorUniName = NULL;
-    if (needColorFilterUniform) {
-        fUniformHandles.fColorFilterUni = builder.addUniform(
-                                                        GrGLShaderBuilder::kFragment_ShaderType,
-                                                        kVec4f_GrSLType, "FilterColor",
-                                                        &colorFilterColorUniName);
-    }
-    bool wroteFragColorZero = false;
-    if (SkXfermode::kZero_Coeff == uniformCoeff &&
-        SkXfermode::kZero_Coeff == colorCoeff) {
-        builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), GrGLSLZerosVecf(4));
-        wroteFragColorZero = true;
-    } else if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) {
+    // Insert the color filter. This will soon be replaced by a color effect.
+    if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) {
+        const char* colorFilterColorUniName = NULL;
+        fUniformHandles.fColorFilterUni = builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                             kVec4f_GrSLType, "FilterColor",
+                                                             &colorFilterColorUniName);
+
         builder.fsCodeAppend("\tvec4 filteredColor;\n");
-        const char* color = adjustInColor(inColor);
-        add_color_filter(&builder, "filteredColor", uniformCoeff,
+        const char* color;
+        // add_color_filter requires a real input string.
+        if (knownColorValue == kOnes_GrSLConstantVec) {
+            color = GrGLSLOnesVecf(4);
+        } else if (knownColorValue == kZeros_GrSLConstantVec) {
+            color = GrGLSLZerosVecf(4);
+        } else {
+            color = inColor.c_str();
+        }
+        add_color_filter(&builder, "filteredColor", filterColorCoeff,
                          colorCoeff, colorFilterColorUniName, color);
         inColor = "filteredColor";
     }
 
     ///////////////////////////////////////////////////////////////////////////
-    // compute the partial coverage (coverage stages and edge aa)
+    // compute the partial coverage
 
+    // incoming coverage to current stage being processed.
     SkString inCoverage;
-    bool coverageIsZero = GrGLProgramDesc::kTransBlack_ColorInput == fDesc.fCoverageInput;
-    // we don't need to compute coverage at all if we know the final shader
-    // output will be zero and we don't have a dual src blend output.
-    if (!wroteFragColorZero || GrGLProgramDesc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
-
-        if (!coverageIsZero) {
-            switch (fDesc.fCoverageInput) {
-                case GrGLProgramDesc::kSolidWhite_ColorInput:
-                    // empty string implies solid white
-                    break;
-                case GrGLProgramDesc::kAttribute_ColorInput:
-                    gen_attribute_coverage(&builder, &inCoverage);
-                    break;
-                case GrGLProgramDesc::kUniform_ColorInput:
-                    this->genUniformCoverage(&builder, &inCoverage);
-                    break;
-                default:
-                    GrCrash("Unexpected input coverage.");
+    GrSLConstantVec knownCoverageValue = this->genInputCoverage(&builder, &inCoverage);
+    
+    SkString outCoverage;
+    for (int s = fDesc.fFirstCoverageStage; s < GrDrawState::kNumStages; ++s) {
+        if (fDesc.fEffectKeys[s]) {
+            if (kZeros_GrSLConstantVec == knownCoverageValue) {
+                // Effects have no way to communicate zeros, they treat an empty string as ones.
+                inCoverage = "initialCoverage";
+                builder.fsCodeAppendf("\tvec4 %s = %s;\n", inCoverage.c_str(), GrGLSLZerosVecf(4));
             }
+            // create var to hold stage output
+            outCoverage = "coverage";
+            outCoverage.appendS32(s);
+            builder.fsCodeAppendf("\tvec4 %s;\n", outCoverage.c_str());
 
-            SkString outCoverage;
-            const int& startStage = fDesc.fFirstCoverageStage;
-            for (int s = startStage; s < GrDrawState::kNumStages; ++s) {
-                if (fDesc.fEffectKeys[s]) {
-                    // create var to hold stage output
-                    outCoverage = "coverage";
-                    outCoverage.appendS32(s);
-                    builder.fsCodeAppendf("\tvec4 %s;\n", outCoverage.c_str());
-
-                    builder.setCurrentStage(s);
-                    fEffects[s] = builder.createAndEmitGLEffect(
-                                                    *stages[s],
-                                                    fDesc.fEffectKeys[s],
-                                                    inCoverage.size() ? inCoverage.c_str() : NULL,
-                                                    outCoverage.c_str(),
-                                                    &fUniformHandles.fEffectSamplerUnis[s]);
-                    builder.setNonStage();
-                    inCoverage = outCoverage;
-                }
-            }
-
-            // discard if coverage is zero
-            if (fDesc.fDiscardIfZeroCoverage && !outCoverage.isEmpty()) {
-                builder.fsCodeAppendf(
-                    "\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n",
-                    outCoverage.c_str());
-            }
+            builder.setCurrentStage(s);
+            fEffects[s] = builder.createAndEmitGLEffect(
+                                            *stages[s],
+                                            fDesc.fEffectKeys[s],
+                                            inCoverage.size() ? inCoverage.c_str() : NULL,
+                                            outCoverage.c_str(),
+                                            &fUniformHandles.fEffectSamplerUnis[s]);
+            builder.setNonStage();
+            inCoverage = outCoverage;
+            knownCoverageValue = kNone_GrSLConstantVec;
         }
+    }
 
-        if (GrGLProgramDesc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
-            builder.fFSOutputs.push_back().set(kVec4f_GrSLType,
-                                               GrGLShaderVar::kOut_TypeModifier,
-                                               dual_source_output_name());
-            bool outputIsZero = coverageIsZero;
-            SkString coeff;
-            if (!outputIsZero &&
-                GrGLProgramDesc::kCoverage_DualSrcOutput != fDesc.fDualSrcOutput && !wroteFragColorZero) {
-                if (!inColor.size()) {
-                    outputIsZero = true;
-                } else {
-                    if (GrGLProgramDesc::kCoverageISA_DualSrcOutput == fDesc.fDualSrcOutput) {
-                        coeff.printf("(1 - %s.a)", inColor.c_str());
-                    } else {
-                        coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
-                    }
-                }
-            }
-            if (outputIsZero) {
-                builder.fsCodeAppendf("\t%s = %s;\n", dual_source_output_name(), GrGLSLZerosVecf(4));
-            } else {
-                SkString modulate;
-                GrGLSLModulate4f(&modulate, coeff.c_str(), inCoverage.c_str());
-                builder.fsCodeAppendf("\t%s = %s;\n", dual_source_output_name(), modulate.c_str());
-            }
-            dualSourceOutputWritten = true;
+    // discard if coverage is zero
+    if (fDesc.fDiscardIfZeroCoverage && kOnes_GrSLConstantVec != knownCoverageValue) {
+        if (kZeros_GrSLConstantVec == knownCoverageValue) {
+            // This is unfortunate.
+            builder.fsCodeAppend("\tdiscard;\n");
+        } else {
+            builder.fsCodeAppendf("\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n",
+                                  inCoverage.c_str());
         }
     }
 
+    if (GrGLProgramDesc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
+        builder.fFSOutputs.push_back().set(kVec4f_GrSLType,
+                                           GrGLShaderVar::kOut_TypeModifier,
+                                           dual_source_output_name());
+        // default coeff to ones for kCoverage_DualSrcOutput
+        SkString coeff;
+        GrSLConstantVec knownCoeffValue = kOnes_GrSLConstantVec;
+        if (GrGLProgramDesc::kCoverageISA_DualSrcOutput == fDesc.fDualSrcOutput) {
+            // Get (1-A) into coeff
+            SkString inColorAlpha;
+            GrGLSLGetComponent4f(&inColorAlpha,
+                                    inColor.c_str(),
+                                    kA_GrColorComponentFlag,
+                                    knownColorValue,
+                                    true);
+            knownCoeffValue = GrGLSLSubtractf<1>(&coeff,
+                                                 NULL,
+                                                 inColorAlpha.c_str(),
+                                                 kOnes_GrSLConstantVec,
+                                                 knownColorValue,
+                                                 true);
+        } else if (GrGLProgramDesc::kCoverageISC_DualSrcOutput == fDesc.fDualSrcOutput) {
+            // Get (1-RGBA) into coeff
+            knownCoeffValue = GrGLSLSubtractf<4>(&coeff,
+                                                 NULL,
+                                                 inColor.c_str(),
+                                                 kOnes_GrSLConstantVec,
+                                                 knownColorValue,
+                                                 true);
+        }
+        // Get coeff * coverage into modulate and then write that to the dual source output.
+        SkString modulate;
+        GrGLSLModulatef<4>(&modulate,
+                           coeff.c_str(),
+                           inCoverage.c_str(),
+                           knownCoeffValue,
+                           knownCoverageValue,
+                           false);
+        builder.fsCodeAppendf("\t%s = %s;\n", dual_source_output_name(), modulate.c_str());
+        dualSourceOutputWritten = true;
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // combine color and coverage as frag color
 
-    if (!wroteFragColorZero) {
-        if (coverageIsZero) {
-            builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), GrGLSLZerosVecf(4));
-        } else {
-            SkString modulate;
-            GrGLSLModulate4f(&modulate, inColor.c_str(), inCoverage.c_str());
-            builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), modulate.c_str());
-        }
-    }
+    // Get color * coverage into modulate and write that to frag shader's output.
+    SkString modulate;
+    GrGLSLModulatef<4>(&modulate,
+                       inColor.c_str(),
+                       inCoverage.c_str(),
+                       knownColorValue,
+                       knownCoverageValue,
+                       false);
+    builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), modulate.c_str());
 
     ///////////////////////////////////////////////////////////////////////////
     // insert GS
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 4221d73..578b4a9 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -125,14 +125,14 @@
      */
     bool genProgram(const GrEffectStage* stages[]);
 
-    void genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
+    GrSLConstantVec genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
+
+    GrSLConstantVec genInputCoverage(GrGLShaderBuilder* builder, SkString* inCoverage);
 
     void genGeometryShader(GrGLShaderBuilder* segments) const;
 
     typedef GrGLUniformManager::UniformHandle UniformHandle;
 
-    void genUniformCoverage(GrGLShaderBuilder* segments, SkString* inOutCoverage);
-
     // Creates a GL program ID, binds shader attributes to GL vertex attrs, and links the program
     bool bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& builder,
                                           bool bindColorOut,
diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp
index 3ec7fc4..ff6b463 100644
--- a/src/gpu/gl/GrGLSL.cpp
+++ b/src/gpu/gl/GrGLSL.cpp
@@ -87,64 +87,16 @@
     return GrGLSLVectorNonhomogCoords(GrSLTypeToVecLength(type));
 }
 
-GrSLConstantVec GrGLSLModulate4f(SkString* outAppend,
-                                 const char* in0,
-                                 const char* in1,
-                                 GrSLConstantVec default0,
-                                 GrSLConstantVec default1) {
-    GrAssert(NULL != outAppend);
-
-    bool has0 = NULL != in0 && '\0' != *in0;
-    bool has1 = NULL != in1 && '\0' != *in1;
-
-    GrAssert(has0 || kNone_GrSLConstantVec != default0);
-    GrAssert(has1 || kNone_GrSLConstantVec != default1);
-
-    if (!has0 && !has1) {
-        GrAssert(kZeros_GrSLConstantVec == default0 || kOnes_GrSLConstantVec == default0);
-        GrAssert(kZeros_GrSLConstantVec == default1 || kOnes_GrSLConstantVec == default1);
-        if (kZeros_GrSLConstantVec == default0 || kZeros_GrSLConstantVec == default1) {
-            outAppend->append(GrGLSLZerosVecf(4));
-            return kZeros_GrSLConstantVec;
-        } else {
-            // both inputs are ones vectors
-            outAppend->append(GrGLSLOnesVecf(4));
-            return kOnes_GrSLConstantVec;
-        }
-    } else if (!has0) {
-        GrAssert(kZeros_GrSLConstantVec == default0 || kOnes_GrSLConstantVec == default0);
-        if (kZeros_GrSLConstantVec == default0) {
-            outAppend->append(GrGLSLZerosVecf(4));
-            return kZeros_GrSLConstantVec;
-        } else {
-            outAppend->appendf("vec4(%s)", in1);
-            return kNone_GrSLConstantVec;
-        }
-    } else if (!has1) {
-        GrAssert(kZeros_GrSLConstantVec == default1 || kOnes_GrSLConstantVec == default1);
-        if (kZeros_GrSLConstantVec == default1) {
-            outAppend->append(GrGLSLZerosVecf(4));
-            return kZeros_GrSLConstantVec;
-        } else {
-            outAppend->appendf("vec4(%s)", in0);
-            return kNone_GrSLConstantVec;
-        }
-    } else {
-        outAppend->appendf("vec4(%s * %s)", in0, in1);
-        return kNone_GrSLConstantVec;
-    }
-}
-
 namespace {
-void append_tabs(SkString* outAppend, int tabCnt) {
-    static const char kTabs[] = "\t\t\t\t\t\t\t\t";
-    while (tabCnt) {
-        int cnt = GrMin((int)GR_ARRAY_COUNT(kTabs), tabCnt);
-        outAppend->append(kTabs, cnt);
-        tabCnt -= cnt;
+    void append_tabs(SkString* outAppend, int tabCnt) {
+        static const char kTabs[] = "\t\t\t\t\t\t\t\t";
+        while (tabCnt) {
+            int cnt = GrMin((int)GR_ARRAY_COUNT(kTabs), tabCnt);
+            outAppend->append(kTabs, cnt);
+            tabCnt -= cnt;
+        }
     }
 }
-}
 
 GrSLConstantVec GrGLSLMulVarBy4f(SkString* outAppend,
                                  int tabCnt,
@@ -152,11 +104,11 @@
                                  const char* mulFactor,
                                  GrSLConstantVec mulFactorDefault) {
     bool haveFactor = NULL != mulFactor && '\0' != *mulFactor;
-
+    
     GrAssert(NULL != outAppend);
     GrAssert(NULL != vec4VarName);
     GrAssert(kNone_GrSLConstantVec != mulFactorDefault || haveFactor);
-
+    
     if (!haveFactor) {
         if (kOnes_GrSLConstantVec == mulFactorDefault) {
             return kNone_GrSLConstantVec;
@@ -172,31 +124,24 @@
     return kNone_GrSLConstantVec;
 }
 
-GrSLConstantVec GrGLSLAdd4f(SkString* outAppend,
-                            const char* in0,
-                            const char* in1,
-                            GrSLConstantVec default0,
-                            GrSLConstantVec default1) {
-    GrAssert(NULL != outAppend);
-
-    bool has0 = NULL != in0 && '\0' != *in0;
-    bool has1 = NULL != in1 && '\0' != *in1;
-
-    if (!has0 && !has1) {
-        GrAssert(kZeros_GrSLConstantVec == default0);
-        GrAssert(kZeros_GrSLConstantVec == default1);
-        outAppend->append(GrGLSLZerosVecf(4));
-        return kZeros_GrSLConstantVec;
-    } else if (!has0) {
-        GrAssert(kZeros_GrSLConstantVec == default0);
-        outAppend->appendf("vec4(%s)", in1);
-        return kNone_GrSLConstantVec;
-    } else if (!has1) {
-        GrAssert(kZeros_GrSLConstantVec == default1);
-        outAppend->appendf("vec4(%s)", in0);
-        return kNone_GrSLConstantVec;
+GrSLConstantVec GrGLSLGetComponent4f(SkString* outAppend,
+                                     const char* expr,
+                                     GrColorComponentFlags component,
+                                     GrSLConstantVec defaultExpr,
+                                     bool omitIfConst) {
+    if (NULL == expr || '\0' == *expr) {
+        GrAssert(defaultExpr != kNone_GrSLConstantVec);
+        if (!omitIfConst) {
+            if (kOnes_GrSLConstantVec == defaultExpr) {
+                outAppend->append("1.0");
+            } else {
+                GrAssert(kZeros_GrSLConstantVec == defaultExpr);
+                outAppend->append("0.0");
+            }
+        }
+        return defaultExpr;
     } else {
-        outAppend->appendf("(vec4(%s) + vec4(%s))", in0, in1);
+        outAppend->appendf("(%s).%c", expr, GrColorComponentFlagToChar(component));
         return kNone_GrSLConstantVec;
     }
 }
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
index 940501c..869b0e1 100644
--- a/src/gpu/gl/GrGLSL.h
+++ b/src/gpu/gl/GrGLSL.h
@@ -9,6 +9,7 @@
 #define GrGLSL_DEFINED
 
 #include "gl/GrGLInterface.h"
+#include "GrColor.h"
 #include "GrTypesPriv.h"
 
 class GrGLShaderVar;
@@ -103,10 +104,38 @@
 bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
                              const char* nameIfDeclared,
                              GrGLShaderVar* var);
+/**
+ * Converts a GrSLType to a string containing the name of the equivalent GLSL type.
+ */
+static const char* GrGLSLTypeString(GrSLType t) {
+    switch (t) {
+        case kVoid_GrSLType:
+            return "void";
+        case kFloat_GrSLType:
+            return "float";
+        case kVec2f_GrSLType:
+            return "vec2";
+        case kVec3f_GrSLType:
+            return "vec3";
+        case kVec4f_GrSLType:
+            return "vec4";
+        case kMat33f_GrSLType:
+            return "mat3";
+        case kMat44f_GrSLType:
+            return "mat4";
+        case kSampler2D_GrSLType:
+            return "sampler2D";
+        default:
+            GrCrash("Unknown shader var type.");
+            return ""; // suppress warning
+    }
+}
 
-/** Convert a count of 1..n floats into the corresponding type enum,
-    e.g. 1 -> kFloat_GrSLType, 2 -> kVec2_GrSLType, ... */
-GrSLType GrSLFloatVectorType(int count);
+/** Return the type enum for a vector of floats of length n (1..4),
+    e.g. 1 -> "float", 2 -> "vec2", ... */
+static inline const char* GrGLSLFloatVectorTypeString(int n) {
+    return GrGLSLTypeString(GrSLFloatVectorType(n));
+}
 
 /** Return the GLSL swizzle operator for a homogenous component of a vector
     with the given number of coordinates, e.g. 2 -> ".y", 3 -> ".z" */
@@ -119,19 +148,58 @@
 const char* GrGLSLVectorNonhomogCoords(GrSLType type);
 
 /**
-  * Produces a string that is the result of modulating two inputs. The inputs must be vec4 or
-  * float. The result is always a vec4. The inputs may be expressions, not just identifier names.
-  * Either can be NULL or "" in which case the default params control whether vec4(1,1,1,1) or
-  * vec4(0,0,0,0) is assumed. It is an error to pass kNone for default<i> if in<i> is NULL or "".
-  * Note that when if function determines that the result is a zeros or ones vec then any expression
-  * represented by in0 or in1 will not be emitted. The return value indicates whether a zeros, ones
-  * or neither was appended.
+  * Produces a string that is the result of modulating two inputs. The inputs must be vecN or
+  * float. The result is always a vecN. The inputs may be expressions, not just identifier names.
+  * Either can be NULL or "" in which case the default params control whether a vector of ones or
+  * zeros. It is an error to pass kNone for default<i> if in<i> is NULL or "". Note that when the
+  * function determines that the result is a zeros or ones vec then any expression represented by
+  * or in1 will not be emitted (side effects won't occur). The return value indicates whether a
+  * known zeros or ones vector resulted. The output can be suppressed when known vector is produced
+  * by passing true for omitIfConstVec.
   */
-GrSLConstantVec GrGLSLModulate4f(SkString* outAppend,
-                                 const char* in0,
-                                 const char* in1,
-                                 GrSLConstantVec default0 = kOnes_GrSLConstantVec,
-                                 GrSLConstantVec default1 = kOnes_GrSLConstantVec);
+template <int N>
+GrSLConstantVec GrGLSLModulatef(SkString* outAppend,
+                                const char* in0,
+                                const char* in1,
+                                GrSLConstantVec default0 = kOnes_GrSLConstantVec,
+                                GrSLConstantVec default1 = kOnes_GrSLConstantVec,
+                                bool omitIfConstVec = false);
+
+/**
+ * Produces a string that is the result of adding two inputs. The inputs must be vecN or
+ * float. The result is always a vecN. The inputs may be expressions, not just identifier names.
+ * Either can be NULL or "" in which case the default params control whether a vector of ones or
+ * zeros. It is an error to pass kNone for default<i> if in<i> is NULL or "". Note that when the
+ * function determines that the result is a zeros or ones vec then any expression represented by
+ * or in1 will not be emitted (side effects won't occur). The return value indicates whether a
+ * known zeros or ones vector resulted. The output can be suppressed when known vector is produced
+ * by passing true for omitIfConstVec.
+ */
+template <int N>
+GrSLConstantVec GrGLSLAddf(SkString* outAppend,
+                           const char* in0,
+                           const char* in1,
+                           GrSLConstantVec default0 = kZeros_GrSLConstantVec,
+                           GrSLConstantVec default1 = kZeros_GrSLConstantVec,
+                           bool omitIfConstVec = false);
+
+/**
+ * Produces a string that is the result of subtracting two inputs. The inputs must be vecN or
+ * float. The result is always a vecN. The inputs may be expressions, not just identifier names.
+ * Either can be NULL or "" in which case the default params control whether a vector of ones or
+ * zeros. It is an error to pass kNone for default<i> if in<i> is NULL or "". Note that when the
+ * function determines that the result is a zeros or ones vec then any expression represented by
+ * or in1 will not be emitted (side effects won't occur). The return value indicates whether a
+ * known zeros or ones vector resulted. The output can be suppressed when known vector is produced
+ * by passing true for omitIfConstVec.
+ */
+template <int N>
+GrSLConstantVec GrGLSLSubtractf(SkString* outAppend,
+                                const char* in0,
+                                const char* in1,
+                                GrSLConstantVec default0 = kZeros_GrSLConstantVec,
+                                GrSLConstantVec default1 = kZeros_GrSLConstantVec,
+                                bool omitIfConstVec = false);
 
 /**
  * Does an inplace mul, *=, of vec4VarName by mulFactor. If mulFactorDefault is not kNone then
@@ -148,18 +216,17 @@
                                  GrSLConstantVec mulFactorDefault = kOnes_GrSLConstantVec);
 
 /**
-  * Produces a string that is the result of adding two inputs. The inputs must be vec4 or float.
-  * The result is always a vec4. The inputs may be expressions, not just identifier names. Either
-  * can be NULL or "" in which case if the default is kZeros then vec4(0,0,0,0) is assumed. It is an
-  * error to pass kOnes for either default or to pass kNone for default<i> if in<i> is NULL or "".
-  * Note that if the function determines that the result is a zeros vec any expression represented
-  * by in0 or in1 will not be emitted. The return value indicates whether a zeros vec was appended
-  * or not.
-  */
-GrSLConstantVec GrGLSLAdd4f(SkString* outAppend,
-                            const char* in0,
-                            const char* in1,
-                            GrSLConstantVec default0 = kZeros_GrSLConstantVec,
-                            GrSLConstantVec default1 = kZeros_GrSLConstantVec);
+ * Given an expression that evaluates to a GLSL vec4, extract a component. If expr is NULL or ""
+ * the value of defaultExpr is used. It is an error to pass an empty expr and have set defaultExpr
+ * to kNone. The return value indicates whether the value is known to be 0 or 1. If omitIfConst is
+ * set then nothing is appended when the return is not kNone.
+ */
+GrSLConstantVec GrGLSLGetComponent4f(SkString* outAppend,
+                                     const char* expr,
+                                     GrColorComponentFlags component,
+                                     GrSLConstantVec defaultExpr = kNone_GrSLConstantVec,
+                                     bool omitIfConst = false);
+
+#include "GrGLSL_impl.h"
 
 #endif
diff --git a/src/gpu/gl/GrGLSL_impl.h b/src/gpu/gl/GrGLSL_impl.h
new file mode 100644
index 0000000..a60057b
--- /dev/null
+++ b/src/gpu/gl/GrGLSL_impl.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLSL_impl_DEFINED
+#define GrGLSL_impl_DEFINED
+
+#include "SkString.h"
+
+namespace {
+template<int N>
+GrSLConstantVec return_const_vecf(GrSLConstantVec constVec, SkString* outAppend, bool omitAppend) {
+    GrAssert(kNone_GrSLConstantVec != constVec);
+    if (!omitAppend) {
+        if (kZeros_GrSLConstantVec == constVec) {
+            outAppend->append(GrGLSLZerosVecf(N));
+        } else {
+            outAppend->append(GrGLSLOnesVecf(N));
+        }
+    }
+    return constVec;
+}
+}
+
+template <int N>
+GrSLConstantVec GrGLSLModulatef(SkString* outAppend,
+                                const char* in0,
+                                const char* in1,
+                                GrSLConstantVec default0,
+                                GrSLConstantVec default1,
+                                bool omitIfConstVec) {
+    GrAssert(N > 0 && N <= 4);
+    GrAssert(NULL != outAppend);
+    
+    bool has0 = NULL != in0 && '\0' != *in0;
+    bool has1 = NULL != in1 && '\0' != *in1;
+    
+    GrAssert(has0 || kNone_GrSLConstantVec != default0);
+    GrAssert(has1 || kNone_GrSLConstantVec != default1);
+    
+    if (!has0 && !has1) {
+        GrAssert(kZeros_GrSLConstantVec == default0 || kOnes_GrSLConstantVec == default0);
+        GrAssert(kZeros_GrSLConstantVec == default1 || kOnes_GrSLConstantVec == default1);
+        if (kZeros_GrSLConstantVec == default0 || kZeros_GrSLConstantVec == default1) {
+            return return_const_vecf<N>(kZeros_GrSLConstantVec, outAppend, omitIfConstVec);
+        } else {
+            // both inputs are ones vectors
+            return return_const_vecf<N>(kOnes_GrSLConstantVec, outAppend, omitIfConstVec);
+        }
+    } else if (!has0) {
+        GrAssert(kZeros_GrSLConstantVec == default0 || kOnes_GrSLConstantVec == default0);
+        if (kZeros_GrSLConstantVec == default0) {
+            return return_const_vecf<N>(kZeros_GrSLConstantVec, outAppend, omitIfConstVec);
+        } else {
+            outAppend->appendf("%s(%s)", GrGLSLFloatVectorTypeString(N), in1);
+            return kNone_GrSLConstantVec;
+        }
+    } else if (!has1) {
+        GrAssert(kZeros_GrSLConstantVec == default1 || kOnes_GrSLConstantVec == default1);
+        if (kZeros_GrSLConstantVec == default1) {
+            return return_const_vecf<N>(kZeros_GrSLConstantVec, outAppend, omitIfConstVec);
+        } else {
+            outAppend->appendf("%s(%s)", GrGLSLFloatVectorTypeString(N), in0);
+            return kNone_GrSLConstantVec;
+        }
+    } else {
+        outAppend->appendf("%s((%s) * (%s))", GrGLSLFloatVectorTypeString(N), in0, in1);
+        return kNone_GrSLConstantVec;
+    }
+}
+
+template <int N>
+GrSLConstantVec GrGLSLAddf(SkString* outAppend,
+                           const char* in0,
+                           const char* in1,
+                           GrSLConstantVec default0,
+                           GrSLConstantVec default1,
+                           bool omitIfConstVec) {
+    GrAssert(N > 0 && N <= 4);
+    GrAssert(NULL != outAppend);
+    
+    bool has0 = NULL != in0 && '\0' != *in0;
+    bool has1 = NULL != in1 && '\0' != *in1;
+    
+    if (!has0 && !has1) {
+        GrAssert(kNone_GrSLConstantVec != default0);
+        GrAssert(kNone_GrSLConstantVec != default1);
+        int sum = (kOnes_GrSLConstantVec == default0) + (kOnes_GrSLConstantVec == default1);
+        if (0 == sum) {
+            return return_const_vecf<N>(kZeros_GrSLConstantVec, outAppend, omitIfConstVec);
+        } else if (1 == sum) {
+            outAppend->append(GrGLSLOnesVecf(N));
+            return return_const_vecf<N>(kOnes_GrSLConstantVec, outAppend, omitIfConstVec);
+        } else {
+            GrAssert(2 == sum);
+            outAppend->appendf("%s(2)", GrGLSLFloatVectorTypeString(N));
+            return kNone_GrSLConstantVec;
+        }
+    } else if (!has0) {
+        GrAssert(kNone_GrSLConstantVec != default0);
+        if (kZeros_GrSLConstantVec == default0) {
+            outAppend->appendf("%s(%s)", GrGLSLFloatVectorTypeString(N), in1);
+        } else {
+            outAppend->appendf("%s(%s) + %s",
+                               GrGLSLFloatVectorTypeString(N),
+                               in1,
+                               GrGLSLOnesVecf(N));
+        }
+        return kNone_GrSLConstantVec;
+    } else if (!has1) {
+        GrAssert(kNone_GrSLConstantVec != default1);
+        if (kZeros_GrSLConstantVec == default1) {
+            outAppend->appendf("%s(%s)", GrGLSLFloatVectorTypeString(N), in0);
+        } else {
+            outAppend->appendf("%s(%s) + %s",
+                               GrGLSLFloatVectorTypeString(N),
+                               in0,
+                               GrGLSLOnesVecf(N));
+        }
+        return kNone_GrSLConstantVec;
+    } else {
+        outAppend->appendf("(%s(%s) + %s(%s))",
+                           GrGLSLFloatVectorTypeString(N),
+                           in0,
+                           GrGLSLFloatVectorTypeString(N),
+                           in1);
+        return kNone_GrSLConstantVec;
+    }
+}
+
+template <int N>
+GrSLConstantVec GrGLSLSubtractf(SkString* outAppend,
+                                 const char* in0,
+                                 const char* in1,
+                                 GrSLConstantVec default0,
+                                 GrSLConstantVec default1,
+                                 bool omitIfConstVec) {
+    GrAssert(N > 0 && N <= 4);
+    GrAssert(NULL != outAppend);
+    
+    bool has0 = NULL != in0 && '\0' != *in0;
+    bool has1 = NULL != in1 && '\0' != *in1;
+    
+    if (!has0 && !has1) {
+        GrAssert(kNone_GrSLConstantVec != default0);
+        GrAssert(kNone_GrSLConstantVec != default1);
+        int diff = (kOnes_GrSLConstantVec == default0) - (kOnes_GrSLConstantVec == default1);
+        if (-1 == diff) {
+            outAppend->appendf("%s(-1)", GrGLSLFloatVectorTypeString(N));
+            return kNone_GrSLConstantVec;
+        } else if (0 == diff) {
+            return return_const_vecf<N>(kZeros_GrSLConstantVec, outAppend, omitIfConstVec);
+        } else {
+            GrAssert(1 == diff);
+            return return_const_vecf<N>(kOnes_GrSLConstantVec, outAppend, omitIfConstVec);
+        }
+    } else if (!has0) {
+        GrAssert(kNone_GrSLConstantVec != default0);
+        if (kZeros_GrSLConstantVec == default0) {
+            outAppend->appendf("-%s(%s)", GrGLSLFloatVectorTypeString(N), in1);
+        } else {
+            outAppend->appendf("%s - %s(%s)",
+                               GrGLSLOnesVecf(N),
+                               GrGLSLFloatVectorTypeString(N),
+                               in1);
+        }
+        return kNone_GrSLConstantVec;
+    } else if (!has1) {
+        GrAssert(kNone_GrSLConstantVec != default1);
+        if (kZeros_GrSLConstantVec == default1) {
+            outAppend->appendf("%s(%s)", GrGLSLFloatVectorTypeString(N), in0);
+        } else {
+            outAppend->appendf("%s(%s) - %s",
+                               GrGLSLFloatVectorTypeString(N),
+                               in0,
+                               GrGLSLOnesVecf(N));
+        }
+        return kNone_GrSLConstantVec;
+    } else {
+        outAppend->appendf("(%s(%s) - %s(%s))",
+                           GrGLSLFloatVectorTypeString(N),
+                           in0,
+                           GrGLSLFloatVectorTypeString(N),
+                           in1);
+        return kNone_GrSLConstantVec;
+    }
+}
+
+
+#endif
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index f7ecf36..60e9188 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -273,7 +273,7 @@
     GrAssert(kFragment_ShaderType == type);
     SkString lookup;
     this->appendTextureLookup(&lookup, sampler, coordName, varyingType);
-    GrGLSLModulate4f(&fFSCode, modulation, lookup.c_str());
+    GrGLSLModulatef<4>(&fFSCode, modulation, lookup.c_str());
 }
 
 GrBackendEffectFactory::EffectKey GrGLShaderBuilder::KeyForTextureAccess(
@@ -485,7 +485,7 @@
                                      const char* body,
                                      SkString* outName) {
     GrAssert(kFragment_ShaderType == shader);
-    fFSFunctions.append(GrGLShaderVar::TypeString(returnType));
+    fFSFunctions.append(GrGLSLTypeString(returnType));
     if (kNonStageIdx != fCurrentStageIdx) {
         outName->printf(" %s_%d", name, fCurrentStageIdx);
     } else {
diff --git a/src/gpu/gl/GrGLShaderVar.h b/src/gpu/gl/GrGLShaderVar.h
index ecf0b47..7f2bf45 100644
--- a/src/gpu/gl/GrGLShaderVar.h
+++ b/src/gpu/gl/GrGLShaderVar.h
@@ -268,46 +268,22 @@
         if (this->isArray()) {
             if (this->isUnsizedArray()) {
                 out->appendf("%s %s[]",
-                             TypeString(effectiveType),
+                             GrGLSLTypeString(effectiveType),
                              this->getName().c_str());
             } else {
                 GrAssert(this->getArrayCount() > 0);
                 out->appendf("%s %s[%d]",
-                             TypeString(effectiveType),
+                             GrGLSLTypeString(effectiveType),
                              this->getName().c_str(),
                              this->getArrayCount());
             }
         } else {
             out->appendf("%s %s",
-                         TypeString(effectiveType),
+                         GrGLSLTypeString(effectiveType),
                          this->getName().c_str());
         }
     }
 
-    static const char* TypeString(GrSLType t) {
-        switch (t) {
-            case kVoid_GrSLType:
-                return "void";
-            case kFloat_GrSLType:
-                return "float";
-            case kVec2f_GrSLType:
-                return "vec2";
-            case kVec3f_GrSLType:
-                return "vec3";
-            case kVec4f_GrSLType:
-                return "vec4";
-            case kMat33f_GrSLType:
-                return "mat3";
-            case kMat44f_GrSLType:
-                return "mat4";
-            case kSampler2D_GrSLType:
-                return "sampler2D";
-            default:
-                GrCrash("Unknown shader var type.");
-                return ""; // suppress warning
-        }
-    }
-
     void appendArrayAccess(int index, SkString* out) const {
         out->appendf("%s[%d]%s",
                      this->getName().c_str(),