Add constant color GrFP.

Review URL: https://codereview.chromium.org/978713002
diff --git a/gm/constcolorprocessor.cpp b/gm/constcolorprocessor.cpp
new file mode 100644
index 0000000..672e07e
--- /dev/null
+++ b/gm/constcolorprocessor.cpp
@@ -0,0 +1,197 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This test only works with the GPU backend.
+
+#include "gm.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrTest.h"
+#include "effects/GrConstColorProcessor.h"
+#include "SkGr.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+/**
+ * This GM directly exercises GrConstColorProcessor.
+ */
+class ConstColorProcessor : public GM {
+public:
+    ConstColorProcessor() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    SkString onShortName() override {
+        return SkString("const_color_processor");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onOnceBeforeDraw() override {
+        SkColor colors[] = { 0xFFFF0000, 0x2000FF00, 0xFF0000FF};
+        SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(kRectSize, kRectSize) };
+        fShader.reset(SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
+                       SkShader::kClamp_TileMode));
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
+        if (NULL == rt) {
+            return;
+        }
+        GrContext* context = rt->getContext();
+        if (NULL == context) {
+            this->drawGpuOnlyMessage(canvas);
+            return;
+        }
+
+        static const GrColor kColors[] = {
+            0xFFFFFFFF,
+            0xFFFF00FF,
+            0x80000000,
+            0x00000000,
+        };
+
+        static const SkColor kPaintColors[] = {
+            0xFFFFFFFF,
+            0xFFFF0000,
+            0x80FF0000,
+            0x00000000,
+        };
+
+        static const char* kModeStrs[] {
+            "kIgnore",
+            "kModulateRGBA",
+            "kModulateA",
+        };
+        GR_STATIC_ASSERT(SK_ARRAY_COUNT(kModeStrs) == GrConstColorProcessor::kInputModeCnt);
+
+        SkScalar y = kPad;
+        SkScalar x = kPad;
+        SkScalar maxW = 0;
+        for (size_t paintType = 0; paintType < SK_ARRAY_COUNT(kPaintColors) + 1; ++paintType) {
+            for (size_t procColor = 0; procColor < SK_ARRAY_COUNT(kColors); ++procColor) {
+                for (int m = 0; m < GrConstColorProcessor::kInputModeCnt; ++m) {
+                    // translate by x,y for the canvas draws and the test target draws.
+                    canvas->save();
+                    canvas->translate(x, y);
+                    const SkMatrix viewMatrix = SkMatrix::MakeTrans(x, y);
+
+                    // rect to draw
+                    SkRect renderRect = SkRect::MakeXYWH(0, 0, kRectSize, kRectSize);
+
+                    GrTestTarget tt;
+                    context->getTestTarget(&tt);
+                    if (NULL == tt.target()) {
+                        SkDEBUGFAIL("Couldn't get Gr test target.");
+                        return;
+                    }
+
+                    GrPaint grPaint;
+                    SkPaint skPaint;
+                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
+                        skPaint.setShader(fShader);
+                    } else {
+                        skPaint.setColor(kPaintColors[paintType]);
+                    }
+                    SkPaint2GrPaintShader(context, rt, skPaint, viewMatrix, false, &grPaint);
+
+                    GrConstColorProcessor::InputMode mode = (GrConstColorProcessor::InputMode) m;
+                    GrColor color = kColors[procColor];
+                    SkAutoTUnref<GrFragmentProcessor> fp(GrConstColorProcessor::Create(color, mode));
+
+                    GrPipelineBuilder pipelineBuilder;
+                    GrClip clip;
+                    pipelineBuilder.setFromPaint(grPaint, rt, clip);
+                    pipelineBuilder.addColorProcessor(fp);
+
+                    tt.target()->drawSimpleRect(&pipelineBuilder,
+                                                grPaint.getColor(),
+                                                viewMatrix,
+                                                renderRect);
+
+                    // Draw labels for the input to the processor and the processor to the right of
+                    // the test rect. The input label appears above the processor label.
+                    SkPaint labelPaint;
+                    labelPaint.setAntiAlias(true);
+                    labelPaint.setTextSize(10.f);
+                    SkString inputLabel;
+                    inputLabel.set("Input: ");
+                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
+                        inputLabel.append("gradient");
+                    } else {
+                        inputLabel.appendf("0x%08x", kPaintColors[paintType]);
+                    }
+                    SkString procLabel;
+                    procLabel.printf("Proc: [0x%08x, %s]", kColors[procColor], kModeStrs[m]);
+
+                    SkRect inputLabelBounds;
+                    // get the bounds of the text in order to position it
+                    labelPaint.measureText(inputLabel.c_str(), inputLabel.size(),
+                                           &inputLabelBounds);
+                    canvas->drawText(inputLabel.c_str(), inputLabel.size(),
+                                     renderRect.fRight + kPad,
+                                     -inputLabelBounds.fTop, labelPaint);
+                    // update the bounds to reflect the offset we used to draw it.
+                    inputLabelBounds.offset(renderRect.fRight + kPad, -inputLabelBounds.fTop);
+
+                    SkRect procLabelBounds;
+                    labelPaint.measureText(procLabel.c_str(), procLabel.size(),
+                                           &procLabelBounds);
+                    canvas->drawText(procLabel.c_str(), procLabel.size(),
+                                     renderRect.fRight + kPad,
+                                     inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop,
+                                     labelPaint);
+                    procLabelBounds.offset(renderRect.fRight + kPad,
+                                           inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop);
+
+                    labelPaint.setStrokeWidth(0);
+                    labelPaint.setStyle(SkPaint::kStroke_Style);
+                    canvas->drawRect(renderRect, labelPaint);
+
+                    canvas->restore();
+
+                    // update x and y for the next test case.
+                    SkScalar height = renderRect.height();
+                    SkScalar width = SkTMax(inputLabelBounds.fRight, procLabelBounds.fRight);
+                    maxW = SkTMax(maxW, width);
+                    y += height + kPad;
+                    if (y + height > kHeight) {
+                        y = kPad;
+                        x += maxW + kPad;
+                        maxW = 0;
+                    }
+                }
+            }
+        }
+    }
+
+private:
+    // Use this as a way of generating and input FP
+    SkAutoTUnref<SkShader>      fShader;
+
+    static const SkScalar       kPad;
+    static const SkScalar       kRectSize;
+    static const int            kWidth  = 820;
+    static const int            kHeight = 500;
+
+    typedef GM INHERITED;
+};
+
+const SkScalar ConstColorProcessor::kPad = 10.f;
+const SkScalar ConstColorProcessor::kRectSize = 20.f;
+
+DEF_GM( return SkNEW(ConstColorProcessor); )
+}
+
+#endif
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 8effe7f..c575194 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -65,6 +65,7 @@
         '../gm/complexclip3.cpp',
         '../gm/composeshader.cpp',
         '../gm/conicpaths.cpp',
+        '../gm/constcolorprocessor.cpp',
         '../gm/convexpaths.cpp',
         '../gm/convexpolyclip.cpp',
         '../gm/convexpolyeffect.cpp',
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 8d2dcbd..a2c227a 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -38,6 +38,7 @@
       '<(skia_include_path)/gpu/GrUserConfig.h',
       '<(skia_include_path)/gpu/GrXferProcessor.h',
 
+      '<(skia_include_path)/gpu/effects/GrConstColorProcessor.h',
       '<(skia_include_path)/gpu/effects/GrCoverageSetOpXP.h',
       '<(skia_include_path)/gpu/effects/GrCustomXfermode.h',
       '<(skia_include_path)/gpu/effects/GrPorterDuffXferProcessor.h',
@@ -204,6 +205,7 @@
       '<(skia_src_path)/gpu/effects/Gr1DKernelEffect.h',
       '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrConstColorProcessor.cpp',
       '<(skia_src_path)/gpu/effects/GrCoverageSetOpXP.cpp',
       '<(skia_src_path)/gpu/effects/GrCustomXfermode.cpp',
       '<(skia_src_path)/gpu/effects/GrCustomXfermodePriv.h',
diff --git a/include/gpu/GrInvariantOutput.h b/include/gpu/GrInvariantOutput.h
index 4977333..fd0a7f8 100644
--- a/include/gpu/GrInvariantOutput.h
+++ b/include/gpu/GrInvariantOutput.h
@@ -79,6 +79,7 @@
     };
 
     void mulByUnknownOpaqueFourComponents() {
+        SkDEBUGCODE(this->validate());
         if (this->isOpaque()) {
             fValidFlags = kA_GrColorComponentFlag;
             fIsSingleComponent = false;
@@ -87,26 +88,32 @@
             // multiplied is opaque.
             this->mulByUnknownFourComponents();
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void mulByUnknownFourComponents() {
+        SkDEBUGCODE(this->validate());
         if (this->hasZeroAlpha()) {
             this->internalSetToTransparentBlack();
         } else {
             this->internalSetToUnknown();
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void mulByUnknownSingleComponent() {
+        SkDEBUGCODE(this->validate());
         if (this->hasZeroAlpha()) {
             this->internalSetToTransparentBlack();
         } else {
             // We don't need to change fIsSingleComponent in this case
             fValidFlags = 0;
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void mulByKnownSingleComponent(uint8_t alpha) {
+        SkDEBUGCODE(this->validate());
         if (this->hasZeroAlpha() || 0 == alpha) {
             this->internalSetToTransparentBlack();
         } else {
@@ -116,20 +123,89 @@
                                          SkMulDiv255Round(GrColorUnpackG(fColor), alpha),
                                          SkMulDiv255Round(GrColorUnpackB(fColor), alpha),
                                          SkMulDiv255Round(GrColorUnpackA(fColor), alpha));
+                // We don't need to change fIsSingleComponent in this case
             }
         }
+        SkDEBUGCODE(this->validate());
+    }
+
+    void mulByKnownFourComponents(GrColor color) {
+        SkDEBUGCODE(this->validate());
+        uint32_t a;
+        if (GetAlphaAndCheckSingleChannel(color, &a)) {
+            this->mulByKnownSingleComponent(a);
+        } else {
+            if (color != 0xffffffff) {
+                fColor = GrColorPackRGBA(
+                    SkMulDiv255Round(GrColorUnpackR(fColor), GrColorUnpackR(color)),
+                    SkMulDiv255Round(GrColorUnpackG(fColor), GrColorUnpackG(color)),
+                    SkMulDiv255Round(GrColorUnpackB(fColor), GrColorUnpackB(color)),
+                    SkMulDiv255Round(GrColorUnpackA(fColor), a));
+                if (kRGBA_GrColorComponentFlags == fValidFlags) {
+                    fIsSingleComponent = GetAlphaAndCheckSingleChannel(fColor, &a);
+                }
+            }
+        }
+        SkDEBUGCODE(this->validate());
+    }
+
+    // Ignores the incoming color's RGB and muls its alpha by color.
+    void mulAlphaByKnownFourComponents(GrColor color) {
+        SkDEBUGCODE(this->validate());
+        uint32_t a;
+        if (GetAlphaAndCheckSingleChannel(color, &a)) {
+            this->mulAlphaByKnownSingleComponent(a);
+        } else if (fValidFlags & kA_GrColorComponentFlag) {
+            GrColor preAlpha = GrColorUnpackA(fColor);
+            if (0 == preAlpha) {
+                this->internalSetToTransparentBlack();
+            } else {
+                fColor = GrColorPackRGBA(
+                    SkMulDiv255Round(preAlpha, GrColorUnpackR(color)),
+                    SkMulDiv255Round(preAlpha, GrColorUnpackG(color)),
+                    SkMulDiv255Round(preAlpha, GrColorUnpackB(color)),
+                    SkMulDiv255Round(preAlpha, a));
+                fValidFlags = kRGBA_GrColorComponentFlags;
+            }
+        } else {
+            fValidFlags = 0;
+        }
+        SkDEBUGCODE(this->validate());
+    }
+
+    // Ignores the incoming color's RGB and muls its alpha by the alpha param and sets all channels
+    // equal to that value.
+    void mulAlphaByKnownSingleComponent(uint8_t alpha) {
+        SkDEBUGCODE(this->validate());
+        if (0 == alpha || this->hasZeroAlpha()) {
+            this->internalSetToTransparentBlack();
+        } else {
+            if (fValidFlags & kA_GrColorComponentFlag) {
+                GrColor a = GrColorUnpackA(fColor);
+                a = SkMulDiv255Round(alpha, a);
+                fColor = GrColorPackRGBA(a, a, a, a);
+                fValidFlags = kRGBA_GrColorComponentFlags;
+            } else {
+                fValidFlags = 0;
+            }
+            fIsSingleComponent = true;
+        }
+        SkDEBUGCODE(this->validate());
     }
 
     void invalidateComponents(uint8_t invalidateFlags, ReadInput readsInput) {
+        SkDEBUGCODE(this->validate());
         fValidFlags &= ~invalidateFlags;
         fIsSingleComponent = false;
         fNonMulStageFound = true;
         if (kWillNot_ReadInput == readsInput) {
             fWillUseInputColor = false;
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void setToOther(uint8_t validFlags, GrColor color, ReadInput readsInput) {
+        SkDEBUGCODE(this->validate());
         fValidFlags = validFlags;
         fColor = color;
         fIsSingleComponent = false;
@@ -137,14 +213,28 @@
         if (kWillNot_ReadInput == readsInput) {
             fWillUseInputColor = false;
         }
+        if (kRGBA_GrColorComponentFlags == fValidFlags) {
+            uint32_t a;
+            if (GetAlphaAndCheckSingleChannel(color, &a)) {
+                fIsSingleComponent = true;
+            }
+        } else if (kA_GrColorComponentFlag & fValidFlags) {
+            // Assuming fColor is premul means if a is 0 the color must be all 0s.
+            if (!GrColorUnpackA(fColor)) {
+                this->internalSetToTransparentBlack();
+            }
+        }
+        SkDEBUGCODE(this->validate());
     }
 
     void setToUnknown(ReadInput readsInput) {
+        SkDEBUGCODE(this->validate());
         this->internalSetToUnknown();
         fNonMulStageFound= true;
         if (kWillNot_ReadInput == readsInput) {
             fWillUseInputColor = false;
         }
+        SkDEBUGCODE(this->validate());
     }
 
     // Temporary setter to handle LCD text correctly until we improve texture pixel config queries
@@ -165,6 +255,13 @@
 private:
     friend class GrProcOptInfo;
 
+    /** Extracts the alpha channel and returns true if r,g,b == a. */
+    static bool GetAlphaAndCheckSingleChannel(GrColor color, uint32_t* alpha) {
+        *alpha = GrColorUnpackA(color);
+        return *alpha == GrColorUnpackR(color) && *alpha == GrColorUnpackG(color) &&
+               *alpha == GrColorUnpackB(color);
+    }
+
     void reset(GrColor color, GrColorComponentFlags flags, bool isSingleComponent) {
         fColor = color;
         fValidFlags = flags;
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 7cb614d..026b256 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -78,8 +78,9 @@
 // Sets the color of GrPaint to the value of the parameter paintColor
 // Callers may subsequently modify the GrPaint. Setting constantColor indicates
 // that the final paint will draw the same color at every pixel. This allows
-// an optimization where the the color filter can be applied to the SkPaint's
-// color once while converting to GrPaint and then ignored.
+// an optimization where the color filter can be applied to the SkPaint's
+// color once while converting to GrPaint and then ignored. TODO: Remove this
+// bool and use the invariant info to automatically apply the color filter.
 void SkPaint2GrPaintNoShader(GrContext* context, GrRenderTarget*, const SkPaint& skPaint,
                              GrColor paintColor, bool constantColor, GrPaint* grPaint);
 
diff --git a/include/gpu/effects/GrConstColorProcessor.h b/include/gpu/effects/GrConstColorProcessor.h
new file mode 100644
index 0000000..27ee0df
--- /dev/null
+++ b/include/gpu/effects/GrConstColorProcessor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrColorProcessor_DEFINED
+#define GrColorProcessor_DEFINED
+
+#include "GrFragmentProcessor.h"
+
+class GrInvariantOutput;
+
+/**
+ * This is a simple GrFragmentProcessor that outputs a constant color. It may do one of the
+ * following with its input color: ignore it, or multiply it by the constant color, multiply its
+ * alpha by the constant color and ignore the input color's r, g, and b.
+ */
+class GrConstColorProcessor : public GrFragmentProcessor {
+public:
+    enum InputMode {
+        kIgnore_InputMode,
+        kModulateRGBA_InputMode,
+        kModulateA_InputMode,
+
+        kLastInputMode = kModulateA_InputMode
+    };
+    static const int kInputModeCnt = kLastInputMode + 1;
+
+    static GrFragmentProcessor* Create(GrColor color, InputMode mode) {
+        return SkNEW_ARGS(GrConstColorProcessor, (color, mode));
+    }
+
+    ~GrConstColorProcessor() override {}
+
+    const char* name() const override { return "Color"; }
+
+    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const override;
+
+    GrGLFragmentProcessor* createGLInstance() const override;
+
+    GrColor color() const { return fColor; }
+
+    InputMode inputMode() const { return fMode; }
+
+private:
+    GrConstColorProcessor(GrColor color, InputMode mode) : fColor(color), fMode(mode) {
+        this->initClassID<GrConstColorProcessor>();
+    }
+
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    GrColor     fColor;
+    InputMode   fMode;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 4bda2e1..1457bed 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -49,7 +49,7 @@
  * we verify the count is as expected.  If a new factory is added, then these numbers must be
  * manually adjusted.
  */
-static const int kFPFactoryCount = 37;
+static const int kFPFactoryCount = 38;
 static const int kGPFactoryCount = 14;
 static const int kXPFactoryCount = 5;
 
diff --git a/src/gpu/effects/GrConstColorProcessor.cpp b/src/gpu/effects/GrConstColorProcessor.cpp
new file mode 100644
index 0000000..5f28694
--- /dev/null
+++ b/src/gpu/effects/GrConstColorProcessor.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "effects/GrConstColorProcessor.h"
+#include "gl/GrGLProcessor.h"
+#include "gl/GrGLSL.h"
+#include "gl/builders/GrGLProgramBuilder.h"
+
+class GLConstColorProcessor : public GrGLFragmentProcessor {
+public:
+    GLConstColorProcessor() : fPrevColor(GrColor_ILLEGAL) {}
+
+    void emitCode(GrGLFPBuilder* builder,
+                  const GrFragmentProcessor& fp,
+                  const char* outputColor,
+                  const char* inputColor,
+                  const TransformedCoordsArray& coords,
+                  const TextureSamplerArray& samplers) override {
+        GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+        const char* colorUni;
+        fColorUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                            kVec4f_GrSLType, kMedium_GrSLPrecision, "constantColor",
+                                            &colorUni);
+        switch (fp.cast<GrConstColorProcessor>().inputMode()) {
+            case GrConstColorProcessor::kIgnore_InputMode:
+                fsBuilder->codeAppendf("%s = %s;", outputColor, colorUni);
+                break;
+            case GrConstColorProcessor::kModulateRGBA_InputMode:
+                fsBuilder->codeAppendf("%s = %s * %s;", outputColor, inputColor, colorUni);
+                break;
+            case GrConstColorProcessor::kModulateA_InputMode:
+                fsBuilder->codeAppendf("%s = %s.a * %s;", outputColor, inputColor, colorUni);
+                break;
+        }
+    }
+
+    void setData(const GrGLProgramDataManager& pdm, const GrProcessor& processor) override {
+        GrColor color = processor.cast<GrConstColorProcessor>().color();
+        // We use the "illegal" color value as an uninit sentinel. However, ut isn't inherently
+        // illegal to use this processor with unpremul colors. So we correctly handle the case
+        // when the "illegal" color is used but we will always upload it.
+        if (GrColor_ILLEGAL == color || fPrevColor != color) {
+            static const GrGLfloat scale = 1.f / 255.f;
+            GrGLfloat floatColor[4] = {
+                GrColorUnpackR(color) * scale,
+                GrColorUnpackG(color) * scale,
+                GrColorUnpackB(color) * scale,
+                GrColorUnpackA(color) * scale,
+            };
+            pdm.set4fv(fColorUniform, 1, floatColor);
+            fPrevColor = color;
+        }
+    }
+
+private:
+    GrGLProgramDataManager::UniformHandle fColorUniform;
+    GrColor                               fPrevColor;
+
+    typedef GrGLFragmentProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrConstColorProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
+    if (kIgnore_InputMode == fMode) {
+        inout->setToOther(kRGBA_GrColorComponentFlags, fColor, GrInvariantOutput::kWill_ReadInput);
+    } else {
+        GrColor r = GrColorUnpackR(fColor);
+        bool colorIsSingleChannel = r == GrColorUnpackG(fColor) && r == GrColorUnpackB(fColor) &&
+                                    r == GrColorUnpackA(fColor);
+        if (kModulateRGBA_InputMode == fMode) {
+            if (colorIsSingleChannel) {
+                inout->mulByKnownSingleComponent(r);
+            } else {
+                inout->mulByKnownFourComponents(fColor);
+            }
+        } else {
+            if (colorIsSingleChannel) {
+                inout->mulAlphaByKnownSingleComponent(r);
+            } else {
+                inout->mulAlphaByKnownFourComponents(fColor);
+            }
+        }
+    }
+}
+
+void GrConstColorProcessor::getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder* b) const {
+    b->add32(fMode);
+}
+
+GrGLFragmentProcessor* GrConstColorProcessor::createGLInstance() const  {
+    return SkNEW(GLConstColorProcessor);
+}
+
+bool GrConstColorProcessor::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrConstColorProcessor& that = other.cast<GrConstColorProcessor>();
+    return fMode == that.fMode && fColor == that.fColor;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConstColorProcessor);
+
+GrFragmentProcessor* GrConstColorProcessor::TestCreate(SkRandom* random,
+                                                       GrContext*,
+                                                       const GrDrawTargetCaps&,
+                                                       GrTexture*[]) {
+    GrColor color;
+    int colorPicker = random->nextULessThan(3);
+    switch (colorPicker) {
+        case 0: {
+            uint32_t a = random->nextULessThan(0x100);
+            uint32_t r = random->nextULessThan(a+1);
+            uint32_t g = random->nextULessThan(a+1);
+            uint32_t b = random->nextULessThan(a+1);
+            color = GrColorPackRGBA(r, g, b, a);
+            break;
+        }
+        case 1:
+            color = 0;
+            break;
+        case 2:
+            color = random->nextULessThan(0x100);
+            color = color | (color << 8) | (color << 16) | (color << 24);
+            break;
+    }
+    InputMode mode = static_cast<InputMode>(random->nextULessThan(kInputModeCnt));
+    return GrConstColorProcessor::Create(color, mode);
+}