Remove generalized gradient code

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2223203003

Review-Url: https://codereview.chromium.org/2223203003
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 820c5b2..f189bb1 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -228,21 +228,48 @@
     desc.flatten(buffer);
 }
 
-SkGradientShaderBase::GpuColorType SkGradientShaderBase::getGpuColorType(SkColor colors[3]) const {
-    if (fColorCount <= 3) {
-        memcpy(colors, fOrigColors, fColorCount * sizeof(SkColor));
-    }
+static inline bool close_to_one_half(const SkFixed& val) {
+    return SkScalarNearlyEqual(SkFixedToScalar(val), SK_ScalarHalf);
+}
 
-    if (SkShader::kClamp_TileMode == fTileMode) {
-        if (2 == fColorCount) {
-            return kTwo_GpuColorType;
-        } else if (3 == fColorCount &&
-                   (SkScalarAbs(
-                    SkFixedToScalar(fRecs[1].fPos) - SK_ScalarHalf) < SK_Scalar1 / 1000)) {
-            return kThree_GpuColorType;
+GrGradientEffect::ColorType GrGradientEffect::determineColorTypeAndNumHardStops(
+        const SkGradientShaderBase& shader) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+    if (shader.fOrigPos) {
+        if (4 == shader.fColorCount) {
+            if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[1], 0.5f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[2], 0.5f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[3], 1.0f)) {
+
+                return kHardStopCentered_ColorType;
+            }
+        } else if (3 == shader.fColorCount) {
+            if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[1], 0.0f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
+
+                return kHardStopLeftEdged_ColorType;
+            } else if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
+                       SkScalarNearlyEqual(shader.fOrigPos[1], 1.0f) &&
+                       SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
+                
+                return kHardStopRightEdged_ColorType;
+            }
         }
     }
-    return kTexture_GpuColorType;
+#endif
+
+    if (SkShader::kClamp_TileMode == shader.getTileMode()) {
+        if (2 == shader.fColorCount) {
+            return kTwo_ColorType;
+        } else if (3 == shader.fColorCount &&
+                   close_to_one_half(shader.getRecs()[1].fPos)) {
+            return kThree_ColorType;
+        }
+    }
+
+    return kTexture_ColorType;
 }
 
 void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst,
@@ -911,114 +938,147 @@
 #include "glsl/GrGLSLUniformHandler.h"
 #include "SkGr.h"
 
-GrGradientEffect::GLSLProcessor::GLSLProcessor()
-    : fCachedYCoord(SK_ScalarMax) {
+static inline int color_type_to_color_count(GrGradientEffect::ColorType colorType) {
+    switch (colorType) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case GrGradientEffect::kHardStopCentered_ColorType:
+            return 4;
+        case GrGradientEffect::kHardStopLeftEdged_ColorType:
+        case GrGradientEffect::kHardStopRightEdged_ColorType:
+            return 3;
+#endif
+        case GrGradientEffect::kTwo_ColorType:
+            return 2;
+        case GrGradientEffect::kThree_ColorType:
+            return 3;
+        case GrGradientEffect::kTexture_ColorType:
+            return 0;
+    }
+
+    SkDEBUGFAIL("Unhandled ColorType in color_type_to_color_count()");
+    return -1;
 }
 
 void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler,
                                                    const GrGradientEffect& ge) {
-
-    if (SkGradientShaderBase::kTwo_GpuColorType == ge.getColorType()) { // 2 Color case
-        fColorStartUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                    kVec4f_GrSLType, kDefault_GrSLPrecision,
-                                                    "GradientStartColor");
-        fColorEndUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                  kVec4f_GrSLType, kDefault_GrSLPrecision,
-                                                  "GradientEndColor");
-
-    } else if (SkGradientShaderBase::kThree_GpuColorType == ge.getColorType()) { // 3 Color Case
-        fColorStartUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                    kVec4f_GrSLType,  kDefault_GrSLPrecision,
-                                                    "GradientStartColor");
-        fColorMidUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                  kVec4f_GrSLType, kDefault_GrSLPrecision,
-                                                  "GradientMidColor");
-        fColorEndUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                  kVec4f_GrSLType, kDefault_GrSLPrecision,
-                                                  "GradientEndColor");
-
-    } else { // if not a fast case
+    if (int colorCount = color_type_to_color_count(ge.getColorType())) {
+        fColorsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
+                                                     kVec4f_GrSLType,
+                                                     kDefault_GrSLPrecision,
+                                                     "Colors",
+                                                     colorCount);
+    } else {
         fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
                                              kFloat_GrSLType, kDefault_GrSLPrecision,
                                              "GradientYCoordFS");
     }
 }
 
-static inline void set_color_uni(const GrGLSLProgramDataManager& pdman,
-                                 const GrGLSLProgramDataManager::UniformHandle uni,
-                                 const SkColor* color) {
-       pdman.set4f(uni,
-                   SkColorGetR(*color) / 255.f,
-                   SkColorGetG(*color) / 255.f,
-                   SkColorGetB(*color) / 255.f,
-                   SkColorGetA(*color) / 255.f);
+static inline void set_after_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
+                                       const GrGLSLProgramDataManager::UniformHandle uni,
+                                       const SkTDArray<SkColor>& colors) {
+    int count = colors.count();
+    constexpr int kSmallCount = 10;
+
+    SkAutoSTArray<4*kSmallCount, float> vals(4*count);
+
+    for (int i = 0; i < colors.count(); i++) {
+        // RGBA
+        vals[4*i + 0] = SkColorGetR(colors[i]) / 255.f;
+        vals[4*i + 1] = SkColorGetG(colors[i]) / 255.f;
+        vals[4*i + 2] = SkColorGetB(colors[i]) / 255.f;
+        vals[4*i + 3] = SkColorGetA(colors[i]) / 255.f;
+    }
+
+    pdman.set4fv(uni, colors.count(), vals.get());
 }
 
-static inline void set_mul_color_uni(const GrGLSLProgramDataManager& pdman,
-                                     const GrGLSLProgramDataManager::UniformHandle uni,
-                                     const SkColor* color){
-       float a = SkColorGetA(*color) / 255.f;
-       float aDiv255 = a / 255.f;
-       pdman.set4f(uni,
-                   SkColorGetR(*color) * aDiv255,
-                   SkColorGetG(*color) * aDiv255,
-                   SkColorGetB(*color) * aDiv255,
-                   a);
+static inline void set_before_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
+                                              const GrGLSLProgramDataManager::UniformHandle uni,
+                                              const SkTDArray<SkColor>& colors) {
+    int count = colors.count();
+    constexpr int kSmallCount = 10;
+
+    SkAutoSTArray<4*kSmallCount, float> vals(4*count);
+
+    for (int i = 0; i < count; i++) {
+        float a = SkColorGetA(colors[i]) / 255.f;
+        float aDiv255 = a / 255.f;
+
+        // RGBA
+        vals[4*i + 0] = SkColorGetR(colors[i]) * aDiv255;
+        vals[4*i + 1] = SkColorGetG(colors[i]) * aDiv255;
+        vals[4*i + 2] = SkColorGetB(colors[i]) * aDiv255;
+        vals[4*i + 3] = a;
+    }
+
+    pdman.set4fv(uni, count, vals.get());
 }
 
 void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
                                                 const GrProcessor& processor) {
-
     const GrGradientEffect& e = processor.cast<GrGradientEffect>();
 
+    switch (e.getColorType()) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case GrGradientEffect::kHardStopCentered_ColorType:
+        case GrGradientEffect::kHardStopLeftEdged_ColorType:
+        case GrGradientEffect::kHardStopRightEdged_ColorType:
+#endif
+        case GrGradientEffect::kTwo_ColorType:
+        case GrGradientEffect::kThree_ColorType: {
+            if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
+                set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+            } else {
+                set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+            }
 
-    if (SkGradientShaderBase::kTwo_GpuColorType == e.getColorType()){
-
-        if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
-            set_mul_color_uni(pdman, fColorStartUni, e.getColors(0));
-            set_mul_color_uni(pdman, fColorEndUni,   e.getColors(1));
-        } else {
-            set_color_uni(pdman, fColorStartUni, e.getColors(0));
-            set_color_uni(pdman, fColorEndUni,   e.getColors(1));
+            break;
         }
 
-    } else if (SkGradientShaderBase::kThree_GpuColorType == e.getColorType()){
-
-        if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
-            set_mul_color_uni(pdman, fColorStartUni, e.getColors(0));
-            set_mul_color_uni(pdman, fColorMidUni,   e.getColors(1));
-            set_mul_color_uni(pdman, fColorEndUni,   e.getColors(2));
-        } else {
-            set_color_uni(pdman, fColorStartUni, e.getColors(0));
-            set_color_uni(pdman, fColorMidUni,   e.getColors(1));
-            set_color_uni(pdman, fColorEndUni,   e.getColors(2));
-        }
-    } else {
-
-        SkScalar yCoord = e.getYCoord();
-        if (yCoord != fCachedYCoord) {
-            pdman.set1f(fFSYUni, yCoord);
-            fCachedYCoord = yCoord;
+        case GrGradientEffect::kTexture_ColorType: {
+            SkScalar yCoord = e.getYCoord();
+            if (yCoord != fCachedYCoord) {
+                pdman.set1f(fFSYUni, yCoord);
+                fCachedYCoord = yCoord;
+            }
+            break;
         }
     }
 }
 
-
 uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) {
     const GrGradientEffect& e = processor.cast<GrGradientEffect>();
 
     uint32_t key = 0;
 
-    if (SkGradientShaderBase::kTwo_GpuColorType == e.getColorType()) {
-        key |= kTwoColorKey;
-    } else if (SkGradientShaderBase::kThree_GpuColorType == e.getColorType()) {
-        key |= kThreeColorKey;
-    }
-
     if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
         key |= kPremulBeforeInterpKey;
     }
 
+    if (GrGradientEffect::kTwo_ColorType == e.getColorType()) {
+        key |= kTwoColorKey;
+    } else if (GrGradientEffect::kThree_ColorType == e.getColorType()) {
+        key |= kThreeColorKey;
+    }
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+    else if (GrGradientEffect::kHardStopCentered_ColorType == e.getColorType()) {
+        key |= kHardStopCenteredKey;
+    } else if (GrGradientEffect::kHardStopLeftEdged_ColorType == e.getColorType()) {
+        key |= kHardStopZeroZeroOneKey;
+    } else if (GrGradientEffect::kHardStopRightEdged_ColorType == e.getColorType()) {
+        key |= kHardStopZeroOneOneKey;
+    }
+   
+    if (SkShader::TileMode::kClamp_TileMode == e.fTileMode) {
+        key |= kClampTileMode;
+    } else if (SkShader::TileMode::kRepeat_TileMode == e.fTileMode) {
+        key |= kRepeatTileMode;
+    } else {
+        key |= kMirrorTileMode;
+    }
+#endif
+
     return key;
 }
 
@@ -1030,56 +1090,183 @@
                                                 const char* outputColor,
                                                 const char* inputColor,
                                                 const SamplerHandle* texSamplers) {
-    if (SkGradientShaderBase::kTwo_GpuColorType == ge.getColorType()){
-        fragBuilder->codeAppendf("\tvec4 colorTemp = mix(%s, %s, clamp(%s, 0.0, 1.0));\n",
-                                 uniformHandler->getUniformVariable(fColorStartUni).c_str(),
-                                 uniformHandler->getUniformVariable(fColorEndUni).c_str(),
-                                 gradientTValue);
-        // Note that we could skip this step if both colors are known to be opaque. Two
-        // considerations:
-        // The gradient SkShader reporting opaque is more restrictive than necessary in the two pt
-        // case. Make sure the key reflects this optimization (and note that it can use the same
-        // shader as thekBeforeIterp case). This same optimization applies to the 3 color case
-        // below.
-        if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
-            fragBuilder->codeAppend("\tcolorTemp.rgb *= colorTemp.a;\n");
+    switch (ge.getColorType()) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case kHardStopCentered_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
+
+            // Account for tile mode
+            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
+            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
+                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
+                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
+                fragBuilder->codeAppendf("    } else {");
+                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
+                fragBuilder->codeAppendf("    }");
+                fragBuilder->codeAppendf("}");
+            }
+
+            // Calculate color
+            fragBuilder->codeAppendf("float relative_t = fract(2.0 * clamp_t);");
+            if (SkShader::kClamp_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("relative_t += step(1.0, %s);", t);
+            }
+            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], relative_t);", colors,
+                                     colors);
+            fragBuilder->codeAppendf("if (clamp_t >= 0.5) {");
+            fragBuilder->codeAppendf("    colorTemp = mix(%s[2], %s[3], relative_t);", colors,
+                                     colors);
+            fragBuilder->codeAppendf("}");
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            fragBuilder->codeAppendf("%s = %s;", outputColor,
+                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
+
+            break;
         }
 
-        fragBuilder->codeAppendf("\t%s = %s;\n", outputColor,
-                                 (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
-    } else if (SkGradientShaderBase::kThree_GpuColorType == ge.getColorType()) {
-        fragBuilder->codeAppendf("\tfloat oneMinus2t = 1.0 - (2.0 * (%s));\n",
-                                 gradientTValue);
-        fragBuilder->codeAppendf("\tvec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s;\n",
-                                 uniformHandler->getUniformVariable(fColorStartUni).c_str());
-        if (!glslCaps->canUseMinAndAbsTogether()) {
-            // The Tegra3 compiler will sometimes never return if we have
-            // min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression.
-            fragBuilder->codeAppend("\tfloat minAbs = abs(oneMinus2t);\n");
-            fragBuilder->codeAppend("\tminAbs = minAbs > 1.0 ? 1.0 : minAbs;\n");
-            fragBuilder->codeAppendf("\tcolorTemp += (1.0 - minAbs) * %s;\n",
-                                     uniformHandler->getUniformVariable(fColorMidUni).c_str());
-        } else {
-            fragBuilder->codeAppendf("\tcolorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s;\n",
-                                     uniformHandler->getUniformVariable(fColorMidUni).c_str());
-        }
-        fragBuilder->codeAppendf("\tcolorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s;\n",
-                                 uniformHandler->getUniformVariable(fColorEndUni).c_str());
-        if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
-            fragBuilder->codeAppend("\tcolorTemp.rgb *= colorTemp.a;\n");
+        case kHardStopLeftEdged_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
+
+            // Account for tile mode
+            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
+            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
+                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
+                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
+                fragBuilder->codeAppendf("    } else {");
+                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
+                fragBuilder->codeAppendf("    }");
+                fragBuilder->codeAppendf("}");
+            }
+
+            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[1], %s[2], clamp_t);", colors,
+                                     colors);
+            if (SkShader::kClamp_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0) {", t);
+                fragBuilder->codeAppendf("    colorTemp = %s[0];", colors);
+                fragBuilder->codeAppendf("}");
+            }
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            fragBuilder->codeAppendf("%s = %s;", outputColor,
+                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
+
+            break;
         }
 
-        fragBuilder->codeAppendf("\t%s = %s;\n", outputColor,
-                                 (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
-    } else {
-        fragBuilder->codeAppendf("\tvec2 coord = vec2(%s, %s);\n",
-                                 gradientTValue,
-                                 uniformHandler->getUniformVariable(fFSYUni).c_str());
-        fragBuilder->codeAppendf("\t%s = ", outputColor);
-        fragBuilder->appendTextureLookupAndModulate(inputColor,
-                                                    texSamplers[0],
-                                                    "coord");
-        fragBuilder->codeAppend(";\n");
+        case kHardStopRightEdged_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
+
+            // Account for tile mode
+            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
+            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
+                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
+                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
+                fragBuilder->codeAppendf("    } else {");
+                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
+                fragBuilder->codeAppendf("    }");
+                fragBuilder->codeAppendf("}");
+            }
+
+            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp_t);", colors,
+                                     colors);
+            if (SkShader::kClamp_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s > 1.0) {", t);
+                fragBuilder->codeAppendf("    colorTemp = %s[2];", colors);
+                fragBuilder->codeAppendf("}");
+            }
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            fragBuilder->codeAppendf("%s = %s;", outputColor,
+                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
+
+            break;
+        }
+#endif
+
+        case kTwo_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp(%s, 0.0, 1.0));",
+                                     colors, colors, t);
+
+            // We could skip this step if both colors are known to be opaque. Two
+            // considerations:
+            // The gradient SkShader reporting opaque is more restrictive than necessary in the two
+            // pt case. Make sure the key reflects this optimization (and note that it can use the
+            // same shader as thekBeforeIterp case). This same optimization applies to the 3 color
+            // case below.
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+
+            fragBuilder->codeAppendf("%s = %s;", outputColor,
+                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
+
+            break;
+        }
+
+        case kThree_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("float oneMinus2t = 1.0 - (2.0 * %s);", t);
+            fragBuilder->codeAppendf("vec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s[0];",
+                                     colors);
+            if (!glslCaps->canUseMinAndAbsTogether()) {
+                // The Tegra3 compiler will sometimes never return if we have
+                // min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression.
+                fragBuilder->codeAppendf("float minAbs = abs(oneMinus2t);");
+                fragBuilder->codeAppendf("minAbs = minAbs > 1.0 ? 1.0 : minAbs;");
+                fragBuilder->codeAppendf("colorTemp += (1.0 - minAbs) * %s[1];", colors);
+            } else {
+                fragBuilder->codeAppendf("colorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s[1];",
+                                         colors);
+            }
+            fragBuilder->codeAppendf("colorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s[2];", colors);
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+
+            fragBuilder->codeAppendf("%s = %s;", outputColor,
+                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
+
+            break;
+        }
+
+        case kTexture_ColorType: {
+            const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni);
+
+            fragBuilder->codeAppendf("vec2 coord = vec2(%s, %s);", gradientTValue, fsyuni);
+            fragBuilder->codeAppendf("%s = ", outputColor);
+            fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord");
+            fragBuilder->codeAppend(";");
+
+            break;
+        }
     }
 }
 
@@ -1092,56 +1279,87 @@
 
     fIsOpaque = shader.isOpaque();
 
-    fColorType = shader.getGpuColorType(&fColors[0]);
+    fColorType = this->determineColorTypeAndNumHardStops(shader);
 
-    // The two and three color specializations do not currently support tiling.
-    if (SkGradientShaderBase::kTwo_GpuColorType == fColorType ||
-        SkGradientShaderBase::kThree_GpuColorType == fColorType) {
-        fRow = -1;
-
-        if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) {
-            fPremulType = kBeforeInterp_PremulType;
-        } else {
-            fPremulType = kAfterInterp_PremulType;
+    if (kTexture_ColorType != fColorType) {
+        if (shader.fOrigColors) {
+            fColors = SkTDArray<SkColor>(shader.fOrigColors, shader.fColorCount);
         }
-        fCoordTransform.reset(kCoordSet, matrix);
-    } else {
-        // doesn't matter how this is set, just be consistent because it is part of the effect key.
-        fPremulType = kBeforeInterp_PremulType;
-        SkBitmap bitmap;
-        shader.getGradientTableBitmap(&bitmap);
 
-        GrTextureStripAtlas::Desc desc;
-        desc.fWidth  = bitmap.width();
-        desc.fHeight = 32;
-        desc.fRowHeight = bitmap.height();
-        desc.fContext = ctx;
-        desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *ctx->caps());
-        fAtlas = GrTextureStripAtlas::GetAtlas(desc);
-        SkASSERT(fAtlas);
-
-        // We always filter the gradient table. Each table is one row of a texture, always y-clamp.
-        GrTextureParams params;
-        params.setFilterMode(GrTextureParams::kBilerp_FilterMode);
-        params.setTileModeX(tileMode);
-
-        fRow = fAtlas->lockRow(bitmap);
-        if (-1 != fRow) {
-            fYCoord = fAtlas->getYOffset(fRow) + SK_ScalarHalf * fAtlas->getNormalizedTexelHeight();
-            fCoordTransform.reset(kCoordSet, matrix, fAtlas->getTexture(), params.filterMode());
-            fTextureAccess.reset(fAtlas->getTexture(), params);
-        } else {
-            SkAutoTUnref<GrTexture> texture(
-                GrRefCachedBitmapTexture(ctx, bitmap, params, SkSourceGammaTreatment::kRespect));
-            if (!texture) {
-                return;
-            }
-            fCoordTransform.reset(kCoordSet, matrix, texture, params.filterMode());
-            fTextureAccess.reset(texture, params);
-            fYCoord = SK_ScalarHalf;
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        if (shader.fOrigPos) {
+            fPositions = SkTDArray<SkScalar>(shader.fOrigPos, shader.fColorCount);
         }
-        this->addTextureAccess(&fTextureAccess);
+
+        fTileMode = tileMode;
+#endif
     }
+
+    switch (fColorType) {
+        // The two and three color specializations do not currently support tiling.
+        case kTwo_ColorType:
+        case kThree_ColorType:
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case kHardStopLeftEdged_ColorType:
+        case kHardStopRightEdged_ColorType:
+        case kHardStopCentered_ColorType:
+#endif
+            fRow = -1;
+
+            if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) {
+                fPremulType = kBeforeInterp_PremulType;
+            } else {
+                fPremulType = kAfterInterp_PremulType;
+            }
+
+            fCoordTransform.reset(kCoordSet, matrix);
+
+            break;
+        case kTexture_ColorType:
+            // doesn't matter how this is set, just be consistent because it is part of the
+            // effect key.
+            fPremulType = kBeforeInterp_PremulType;
+
+            SkBitmap bitmap;
+            shader.getGradientTableBitmap(&bitmap);
+
+            GrTextureStripAtlas::Desc desc;
+            desc.fWidth  = bitmap.width();
+            desc.fHeight = 32;
+            desc.fRowHeight = bitmap.height();
+            desc.fContext = ctx;
+            desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *ctx->caps());
+            fAtlas = GrTextureStripAtlas::GetAtlas(desc);
+            SkASSERT(fAtlas);
+
+            // We always filter the gradient table. Each table is one row of a texture, always
+            // y-clamp.
+            GrTextureParams params;
+            params.setFilterMode(GrTextureParams::kBilerp_FilterMode);
+            params.setTileModeX(tileMode);
+
+            fRow = fAtlas->lockRow(bitmap);
+            if (-1 != fRow) {
+                fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
+                fCoordTransform.reset(kCoordSet, matrix, fAtlas->getTexture(), params.filterMode());
+                fTextureAccess.reset(fAtlas->getTexture(), params);
+            } else {
+                SkAutoTUnref<GrTexture> texture(
+                    GrRefCachedBitmapTexture(ctx, bitmap, params,
+                                             SkSourceGammaTreatment::kRespect));
+                if (!texture) {
+                    return;
+                }
+                fCoordTransform.reset(kCoordSet, matrix, texture, params.filterMode());
+                fTextureAccess.reset(texture, params);
+                fYCoord = SK_ScalarHalf;
+            }
+
+            this->addTextureAccess(&fTextureAccess);
+
+            break;
+    }
+
     this->addCoordTransform(&fCoordTransform);
 }
 
@@ -1152,30 +1370,27 @@
 }
 
 bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
-    const GrGradientEffect& s = processor.cast<GrGradientEffect>();
+    const GrGradientEffect& ge = processor.cast<GrGradientEffect>();
 
-    if (this->fColorType == s.getColorType()){
-
-        if (SkGradientShaderBase::kTwo_GpuColorType == fColorType) {
-            if (this->getPremulType() != s.getPremulType() ||
-                *this->getColors(0) != *s.getColors(0) ||
-                *this->getColors(1) != *s.getColors(1)) {
-                return false;
-            }
-        } else if (SkGradientShaderBase::kThree_GpuColorType == fColorType) {
-            if (this->getPremulType() != s.getPremulType() ||
-                *this->getColors(0) != *s.getColors(0) ||
-                *this->getColors(1) != *s.getColors(1) ||
-                *this->getColors(2) != *s.getColors(2)) {
+    if (this->fColorType == ge.getColorType()) {
+        if (kTexture_ColorType == fColorType) {
+            if (fYCoord != ge.getYCoord()) {
                 return false;
             }
         } else {
-            if (fYCoord != s.getYCoord()) {
+            if (this->getPremulType() != ge.getPremulType() ||
+                this->fColors.count() != ge.fColors.count()) {
                 return false;
             }
+
+            for (int i = 0; i < this->fColors.count(); i++) {
+                if (*this->getColors(i) != *ge.getColors(i)) {
+                    return false;
+                }
+            }
         }
 
-        SkASSERT(this->useAtlas() == s.useAtlas());
+        SkASSERT(this->useAtlas() == ge.useAtlas());
         return true;
     }