Add isSingleComponent bool to getConstantColorComponent

Initial step to allowing effects to use/output 1 or 4 color/coverage components. This cl doesn't change any current logic and all effects still assume they are working with 4 components.

BUG=skia:

Committed: https://skia.googlesource.com/skia/+/3b8af078281a5a20f951b9fd84f38d92b8f6217b

Review URL: https://codereview.chromium.org/608253002
diff --git a/include/gpu/GrProcessor.h b/include/gpu/GrProcessor.h
index 7053872..2dbead1 100644
--- a/include/gpu/GrProcessor.h
+++ b/include/gpu/GrProcessor.h
@@ -37,14 +37,48 @@
 
     virtual ~GrProcessor();
 
+    struct InvariantOutput{
+        GrColor fColor;
+        uint32_t fValidFlags;
+        bool fIsSingleComponent;
+
+        bool isOpaque() const {
+            return ((fValidFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(fColor));
+        }
+
+        bool isSolidWhite() const {
+            return (fValidFlags == kRGBA_GrColorComponentFlags && 0xFFFFFFFF == fColor);
+        }
+
+        /**
+         * If isSingleComponent is true, then the flag values for r, g, b, and a must all be the
+         * same. If the flags are all set then all color components must be equal.
+         */
+        SkDEBUGCODE(void validate() const;)
+
+    private:
+        SkDEBUGCODE(bool colorComponentsAllEqual() const;)
+
+        /**
+         * If alpha is valid, check that any valid R,G,B values are <= A
+         */
+        SkDEBUGCODE(bool validPreMulColor() const;)
+    };
+
     /**
-     * This function is used to perform optimizations. When called the color and validFlags params
+     * This function is used to perform optimizations. When called the invarientOuput param
      * indicate whether the input components to this effect in the FS will have known values.
-     * validFlags is a bitfield of GrColorComponentFlags. The function updates both params to
-     * indicate known values of its output. A component of the color param only has meaning if the
-     * corresponding bit in validFlags is set.
+     * In inout the validFlags member is a bitfield of GrColorComponentFlags. The isSingleComponent
+     * member indicates whether the input will be 1 or 4 bytes. The function updates the members of
+     * inout to indicate known values of its output. A component of the color member only has
+     * meaning if the corresponding bit in validFlags is set.
      */
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const = 0;
+    void computeInvariantOutput(InvariantOutput* inout) const {
+        this->onComputeInvariantOutput(inout);
+#ifdef SK_DEBUG
+        inout->validate();
+#endif
+    }
 
     /** This object, besides creating back-end-specific helper objects, is used for run-time-type-
         identification. The factory should be an instance of templated class,
@@ -158,6 +192,10 @@
         getFactory()).*/
     virtual bool onIsEqual(const GrProcessor& other) const = 0;
 
+    /** 
+     * Subclass implements this to support getConstantColorComponents(...).
+     */
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const = 0;
     friend class GrGeometryProcessor; // to set fRequiresVertexShader and build fVertexAttribTypes.
 
     SkSTArray<4, const GrCoordTransform*, true>  fCoordTransforms;
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 8a3218f..1d151e4 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -799,11 +799,6 @@
         }
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendFragmentProcessorFactory<XferEffect>::getInstance();
     }
@@ -1216,6 +1211,11 @@
         return fMode == s.fMode &&
                fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture();
     }
+    
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
 
     SkXfermode::Mode fMode;
     GrCoordTransform fBackgroundTransform;
diff --git a/src/effects/SkAlphaThresholdFilter.cpp b/src/effects/SkAlphaThresholdFilter.cpp
index 09cc618..462cfec 100644
--- a/src/effects/SkAlphaThresholdFilter.cpp
+++ b/src/effects/SkAlphaThresholdFilter.cpp
@@ -76,8 +76,6 @@
     static const char* Name() { return "Alpha Threshold"; }
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     float innerThreshold() const { return fInnerThreshold; }
     float outerThreshold() const { return fOuterThreshold; }
 
@@ -104,6 +102,8 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     float fInnerThreshold;
@@ -228,13 +228,13 @@
             this->fOuterThreshold == s.fOuterThreshold);
 }
 
-void AlphaThresholdEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
-        GrPixelConfigIsOpaque(this->texture(0)->config())) {
-        *validFlags = kA_GrColorComponentFlag;
+void AlphaThresholdEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    if (inout->isOpaque() && GrPixelConfigIsOpaque(this->texture(0)->config())) {
+        inout->fValidFlags = kA_GrColorComponentFlag;
     } else {
-        *validFlags = 0;
+        inout->fValidFlags = 0;
     }
+    inout->fIsSingleComponent = false;
 }
 
 #endif
diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp
index 85af19c..b62b33d 100644
--- a/src/effects/SkArithmeticMode.cpp
+++ b/src/effects/SkArithmeticMode.cpp
@@ -289,8 +289,6 @@
     static const char* Name() { return "Arithmetic"; }
     GrTexture* backgroundTexture() const { return fBackgroundAccess.getTexture(); }
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     float k1() const { return fK1; }
     float k2() const { return fK2; }
     float k3() const { return fK3; }
@@ -300,6 +298,8 @@
 private:
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrArithmeticEffect(float k1, float k2, float k3, float k4, bool enforcePMColor,
                        GrTexture* background);
     float                       fK1, fK2, fK3, fK4;
@@ -344,9 +344,10 @@
     return GrTBackendFragmentProcessorFactory<GrArithmeticEffect>::getInstance();
 }
 
-void GrArithmeticEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+void GrArithmeticEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
     // TODO: optimize this
-    *validFlags = 0;
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index 123b9d2..251398c 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -563,8 +563,6 @@
     typedef GrGLRectBlurEffect GLProcessor;
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     /**
      * Create a simple filter effect with custom bicubic coefficients.
      */
@@ -594,6 +592,8 @@
     GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blur_profile);
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     static bool CreateBlurProfileTexture(GrContext *context, float sigma,
                                        GrTexture **blurProfileTexture);
 
@@ -765,9 +765,9 @@
     return this->getSigma() == s.getSigma() && this->getRect() == s.getRect();
 }
 
-void GrRectBlurEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
-    return;
+void GrRectBlurEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
@@ -837,8 +837,6 @@
 
     typedef GrGLRRectBlurEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -846,6 +844,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     SkRRect             fRRect;
     float               fSigma;
     GrTextureAccess     fNinePatchAccess;
@@ -929,8 +929,9 @@
     return SkNEW_ARGS(GrRRectBlurEffect, (sigma, rrect, blurNinePatchTexture));
 }
 
-void GrRRectBlurEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void GrRRectBlurEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendFragmentProcessorFactory& GrRRectBlurEffect::getFactory() const {
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
index bd0d2aa..ba62817 100644
--- a/src/effects/SkColorFilters.cpp
+++ b/src/effects/SkColorFilters.cpp
@@ -195,8 +195,6 @@
         return SkNEW_ARGS(ModeColorFilterEffect, (c, mode));
     }
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     bool willUseFilterColor() const {
         SkXfermode::Coeff dstCoeff;
         SkXfermode::Coeff srcCoeff;
@@ -293,6 +291,8 @@
         return fMode == s.fMode && fColor == s.fColor;
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     SkXfermode::Mode fMode;
     GrColor fColor;
 
@@ -382,18 +382,19 @@
 
 }
 
-void ModeColorFilterEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+void ModeColorFilterEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
     float inputColor[4];
-    GrColorToRGBAFloat(*color, inputColor);
+    GrColorToRGBAFloat(inout->fColor, inputColor);
     float filterColor[4];
     GrColorToRGBAFloat(fColor, filterColor);
     MaskedColorExpr result =
         color_filter_expression(fMode,
                                 MaskedColorExpr(filterColor, kRGBA_GrColorComponentFlags),
-                                MaskedColorExpr(inputColor, *validFlags));
+                                MaskedColorExpr(inputColor, inout->fValidFlags));
 
-    *color = result.getColor();
-    *validFlags = result.getValidComponents();
+    inout->fColor = result.getColor();
+    inout->fValidFlags = result.getValidComponents();
+    inout->fIsSingleComponent = false;
 }
 
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ModeColorFilterEffect);
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index 5ac4552..8549fd7 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -349,51 +349,6 @@
         return GrTBackendFragmentProcessorFactory<ColorMatrixEffect>::getInstance();
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
-        // type flags it might be worth checking the other components.
-
-        // The matrix is defined such the 4th row determines the output alpha. The first four
-        // columns of that row multiply the input r, g, b, and a, respectively, and the last column
-        // is the "translation".
-        static const uint32_t kRGBAFlags[] = {
-            kR_GrColorComponentFlag,
-            kG_GrColorComponentFlag,
-            kB_GrColorComponentFlag,
-            kA_GrColorComponentFlag
-        };
-        static const int kShifts[] = {
-            GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
-        };
-        enum {
-            kAlphaRowStartIdx = 15,
-            kAlphaRowTranslateIdx = 19,
-        };
-
-        SkScalar outputA = 0;
-        for (int i = 0; i < 4; ++i) {
-            // If any relevant component of the color to be passed through the matrix is non-const
-            // then we can't know the final result.
-            if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
-                if (!(*validFlags & kRGBAFlags[i])) {
-                    *validFlags = 0;
-                    return;
-                } else {
-                    uint32_t component = (*color >> kShifts[i]) & 0xFF;
-                    outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
-                }
-            }
-        }
-        outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
-        *validFlags = kA_GrColorComponentFlag;
-        // We pin the color to [0,1]. This would happen to the *final* color output from the frag
-        // shader but currently the effect does not pin its own output. So in the case of over/
-        // underflow this may deviate from the actual result. Maybe the effect should pin its
-        // result if the matrix could over/underflow for any component?
-        *color = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
-    }
-
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     class GLProcessor : public GrGLFragmentProcessor {
@@ -471,6 +426,51 @@
         return cme.fMatrix == fMatrix;
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
+        // type flags it might be worth checking the other components.
+
+        // The matrix is defined such the 4th row determines the output alpha. The first four
+        // columns of that row multiply the input r, g, b, and a, respectively, and the last column
+        // is the "translation".
+        static const uint32_t kRGBAFlags[] = {
+            kR_GrColorComponentFlag,
+            kG_GrColorComponentFlag,
+            kB_GrColorComponentFlag,
+            kA_GrColorComponentFlag
+        };
+        static const int kShifts[] = {
+            GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
+        };
+        enum {
+            kAlphaRowStartIdx = 15,
+            kAlphaRowTranslateIdx = 19,
+        };
+
+        SkScalar outputA = 0;
+        for (int i = 0; i < 4; ++i) {
+            // If any relevant component of the color to be passed through the matrix is non-const
+            // then we can't know the final result.
+            if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
+                if (!(inout->fValidFlags & kRGBAFlags[i])) {
+                    inout->fValidFlags = 0;
+                    return;
+                } else {
+                    uint32_t component = (inout->fColor >> kShifts[i]) & 0xFF;
+                    outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
+                }
+            }
+        }
+        outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
+        inout->fValidFlags = kA_GrColorComponentFlag;
+        // We pin the color to [0,1]. This would happen to the *final* color output from the frag
+        // shader but currently the effect does not pin its own output. So in the case of over/
+        // underflow this may deviate from the actual result. Maybe the effect should pin its
+        // result if the matrix could over/underflow for any component?
+        inout->fColor = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
+        inout->fIsSingleComponent = false;
+    }
+
     SkColorMatrix fMatrix;
 
     typedef GrFragmentProcessor INHERITED;
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 474d9a7..ace9e02 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -351,11 +351,11 @@
     typedef GrGLDisplacementMapEffect GLProcessor;
     static const char* Name() { return "DisplacementMap"; }
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
 private:
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
                             const SkVector& scale,
@@ -491,14 +491,14 @@
     return GrTBackendFragmentProcessorFactory<GrDisplacementMapEffect>::getInstance();
 }
 
-void GrDisplacementMapEffect::getConstantColorComponents(GrColor*,
-                                                         uint32_t* validFlags) const {
+void GrDisplacementMapEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
     // Any displacement offset bringing a pixel out of bounds will output a color of (0,0,0,0),
     // so the only way we'd get a constant alpha is if the input color image has a constant alpha
     // and no displacement offset push any texture coordinates out of bounds OR if the constant
     // alpha is 0. Since this isn't trivial to compute at this point, let's assume the output is
     // not of constant color when a displacement effect is applied.
-    *validFlags = 0;
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index f766562..ce787c6 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -350,15 +350,15 @@
     SkScalar surfaceScale() const { return fSurfaceScale; }
     const SkMatrix& filterMatrix() const { return fFilterMatrix; }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        // lighting shaders are complicated. We just throw up our hands.
-        *validFlags = 0;
-    }
-
 protected:
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        // lighting shaders are complicated. We just throw up our hands.
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
 private:
     typedef GrSingleTextureEffect INHERITED;
     const SkLight* fLight;
diff --git a/src/effects/SkLumaColorFilter.cpp b/src/effects/SkLumaColorFilter.cpp
index ee2bfa6..616bcde 100644
--- a/src/effects/SkLumaColorFilter.cpp
+++ b/src/effects/SkLumaColorFilter.cpp
@@ -73,13 +73,6 @@
         return GrTBackendFragmentProcessorFactory<LumaColorFilterEffect>::getInstance();
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        // The output is always black.
-        *color = GrColorPackRGBA(0, 0, 0, GrColorUnpackA(*color));
-        *validFlags = kRGB_GrColorComponentFlags;
-    }
-
     class GLProcessor : public GrGLFragmentProcessor {
     public:
         GLProcessor(const GrBackendProcessorFactory& factory,
@@ -119,6 +112,13 @@
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE {
         return true;
     }
+
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        // The output is always black.
+        inout->fColor = GrColorPackRGBA(0, 0, 0, GrColorUnpackA(inout->fColor));
+        inout->fValidFlags = kRGB_GrColorComponentFlags;
+        inout->fIsSingleComponent = false;
+    }
 };
 
 GrFragmentProcessor* SkLumaColorFilter::asFragmentProcessor(GrContext*) const {
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index 9d7b918..d6a8e78 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -47,8 +47,6 @@
     static const char* Name() { return "Magnifier"; }
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     float x_offset() const { return fXOffset; }
     float y_offset() const { return fYOffset; }
     float x_inv_zoom() const { return fXInvZoom; }
@@ -76,6 +74,8 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     float fXOffset;
@@ -227,8 +227,9 @@
             this->fYInvInset == s.fYInvInset);
 }
 
-void GrMagnifierEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    this->updateConstantColorComponentsForModulation(color, validFlags);
+void GrMagnifierEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    this->updateInvariantOutputForModulation(inout);
+    inout->fIsSingleComponent = false;
 }
 
 #endif
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index 397e431..4c7f624 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -309,7 +309,6 @@
     typedef GrGLMorphologyEffect GLProcessor;
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 protected:
 
@@ -318,6 +317,8 @@
 private:
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
@@ -455,10 +456,11 @@
             this->type() == s.type());
 }
 
-void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+void GrMorphologyEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
     // This is valid because the color components of the result of the kernel all come
     // exactly from existing values in the source texture.
-    this->updateConstantColorComponentsForModulation(color, validFlags);
+    this->updateInvariantOutputForModulation(inout);
+    inout->fIsSingleComponent = false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp
index 88e6cad..43197de 100644
--- a/src/effects/SkPerlinNoiseShader.cpp
+++ b/src/effects/SkPerlinNoiseShader.cpp
@@ -586,6 +586,11 @@
                fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit;
     }
 
+    void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0; // This is noise. Nothing is constant.
+        inout->fIsSingleComponent = false;
+    }
+
     GrPerlinNoiseEffect(SkPerlinNoiseShader::Type type,
                         int numOctaves, bool stitchTiles,
                         SkPerlinNoiseShader::PaintingData* paintingData,
@@ -616,10 +621,6 @@
     GrTextureAccess                 fNoiseAccess;
     SkPerlinNoiseShader::PaintingData *fPaintingData;
 
-    void getConstantColorComponents(GrColor*, uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0; // This is noise. Nothing is constant.
-    }
-
 private:
     typedef GrFragmentProcessor INHERITED;
 };
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 4853f73..f6726ca 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -294,20 +294,20 @@
     static const char* Name() { return "ColorTable"; }
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     typedef GLColorTableEffect GLProcessor;
 
 private:
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     explicit ColorTableEffect(GrTexture* texture, unsigned flags);
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     GrTextureAccess fTextureAccess;
     unsigned        fFlags; // currently not used in shader code, just to assist
-                            // getConstantColorComponents().
+                            // onComputeInvariantOutput().
 
     typedef GrFragmentProcessor INHERITED;
 };
@@ -401,21 +401,22 @@
     return this->texture(0) == sBase.texture(0);
 }
 
-void ColorTableEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+void ColorTableEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
     // If we kept the table in the effect then we could actually run known inputs through the
     // table.
     if (fFlags & SkTable_ColorFilter::kR_Flag) {
-        *validFlags &= ~kR_GrColorComponentFlag;
+        inout->fValidFlags &= ~kR_GrColorComponentFlag;
     }
     if (fFlags & SkTable_ColorFilter::kG_Flag) {
-        *validFlags &= ~kG_GrColorComponentFlag;
+        inout->fValidFlags &= ~kG_GrColorComponentFlag;
     }
     if (fFlags & SkTable_ColorFilter::kB_Flag) {
-        *validFlags &= ~kB_GrColorComponentFlag;
+        inout->fValidFlags &= ~kB_GrColorComponentFlag;
     }
     if (fFlags & SkTable_ColorFilter::kA_Flag) {
-        *validFlags &= ~kA_GrColorComponentFlag;
+        inout->fValidFlags &= ~kA_GrColorComponentFlag;
     }
+    inout->fIsSingleComponent = false;
 }
 
 
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index d25873b..89b323a 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -1217,12 +1217,13 @@
     return false;
 }
 
-void GrGradientEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    if (fIsOpaque && (kA_GrColorComponentFlag & *validFlags) && 0xff == GrColorUnpackA(*color)) {
-        *validFlags = kA_GrColorComponentFlag;
+void GrGradientEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    if (fIsOpaque && inout->isOpaque()) {
+        inout->fValidFlags = kA_GrColorComponentFlag;
     } else {
-        *validFlags = 0;
+        inout->fValidFlags = 0;
     }
+    inout->fIsSingleComponent = false;
 }
 
 int GrGradientEffect::RandomGradientParams(SkRandom* random,
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index b81b562..33d8add 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -342,8 +342,6 @@
     bool useAtlas() const { return SkToBool(-1 != fRow); }
     SkScalar getYCoord() const { return fYCoord; };
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     SkGradientShaderBase::GpuColorType getColorType() const { return fColorType; }
 
     enum PremulType {
@@ -376,6 +374,8 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
 
 private:
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index dc74459..1aa90c3 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -517,11 +517,6 @@
 
     static const char* Name() { return "QuadEdge"; }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     const GrShaderVar& inQuadEdge() const { return fInQuadEdge; }
 
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
@@ -593,6 +588,11 @@
         return true;
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     const GrShaderVar& fInQuadEdge;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index 5720982..0b895d1 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -30,11 +30,6 @@
 
     static const char* Name() { return "AlignedRectEdge"; }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     const GrShaderVar& inRect() const { return fInRect; }
 
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
@@ -112,6 +107,11 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE { return true; }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
 
     typedef GrGeometryProcessor INHERITED;
@@ -155,11 +155,6 @@
 
     static const char* Name() { return "RectEdge"; }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     const GrShaderVar& inRectEdge() const { return fInRectEdge; }
     const GrShaderVar& inWidthHeight() const { return fInWidthHeight; }
 
@@ -257,6 +252,11 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE { return true; }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     const GrShaderVar& fInRectEdge;
     const GrShaderVar& fInWidthHeight;
 
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index 872d20a..b4cf9c8 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -421,26 +421,26 @@
         return true;
     }
 
-    GrColor coverage;
-    uint32_t validComponentFlags;
+    GrProcessor::InvariantOutput inout;
+    inout.fIsSingleComponent = false;
     // Initialize to an unknown starting coverage if per-vertex coverage is specified.
     if (this->hasCoverageVertexAttribute()) {
-        validComponentFlags = 0;
+        inout.fValidFlags = 0;
     } else {
-        coverage = fCoverage;
-        validComponentFlags = kRGBA_GrColorComponentFlags;
+        inout.fColor = fCoverage;
+        inout.fValidFlags = kRGBA_GrColorComponentFlags;
     }
 
     // Run through the coverage stages and see if the coverage will be all ones at the end.
     if (this->hasGeometryProcessor()) {
         const GrGeometryProcessor* gp = fGeometryProcessor->getGeometryProcessor();
-        gp->getConstantColorComponents(&coverage, &validComponentFlags);
+        gp->computeInvariantOutput(&inout);
     }
     for (int s = 0; s < this->numCoverageStages(); ++s) {
         const GrProcessor* processor = this->getCoverageStage(s).getProcessor();
-        processor->getConstantColorComponents(&coverage, &validComponentFlags);
+        processor->computeInvariantOutput(&inout);
     }
-    return (kRGBA_GrColorComponentFlags == validComponentFlags) && (0xffffffff == coverage);
+    return inout.isSolidWhite();
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -755,55 +755,54 @@
 
 
 bool GrDrawState::srcAlphaWillBeOne() const {
-    uint32_t validComponentFlags;
-    GrColor color;
+    GrProcessor::InvariantOutput inoutColor;
+    inoutColor.fIsSingleComponent = false;
     // Check if per-vertex or constant color may have partial alpha
     if (this->hasColorVertexAttribute()) {
         if (fHints & kVertexColorsAreOpaque_Hint) {
-            validComponentFlags = kA_GrColorComponentFlag;
-            color = 0xFF << GrColor_SHIFT_A;
+            inoutColor.fValidFlags = kA_GrColorComponentFlag;
+            inoutColor.fColor = 0xFF << GrColor_SHIFT_A;
         } else {
-            validComponentFlags = 0;
-            color = 0; // not strictly necessary but we get false alarms from tools about uninit.
+            inoutColor.fValidFlags = 0;
+            // not strictly necessary but we get false alarms from tools about uninit.
+            inoutColor.fColor = 0;
         }
     } else {
-        validComponentFlags = kRGBA_GrColorComponentFlags;
-        color = this->getColor();
+        inoutColor.fValidFlags = kRGBA_GrColorComponentFlags;
+        inoutColor.fColor = this->getColor();
     }
 
     // Run through the color stages
     for (int s = 0; s < this->numColorStages(); ++s) {
         const GrProcessor* processor = this->getColorStage(s).getProcessor();
-        processor->getConstantColorComponents(&color, &validComponentFlags);
+        processor->computeInvariantOutput(&inoutColor);
     }
 
     // Check whether coverage is treated as color. If so we run through the coverage computation.
     if (this->isCoverageDrawing()) {
         // The shader generated for coverage drawing runs the full coverage computation and then
         // makes the shader output be the multiplication of color and coverage. We mirror that here.
-        GrColor coverage;
-        uint32_t coverageComponentFlags;
+        GrProcessor::InvariantOutput inoutCoverage;
+        inoutCoverage.fIsSingleComponent = false;
         if (this->hasCoverageVertexAttribute()) {
-            coverageComponentFlags = 0;
-            coverage = 0; // suppresses any warnings.
+            inoutCoverage.fValidFlags = 0;
+            inoutCoverage.fColor = 0; // suppresses any warnings.
         } else {
-            coverageComponentFlags = kRGBA_GrColorComponentFlags;
-            coverage = this->getCoverageColor();
+            inoutCoverage.fValidFlags = kRGBA_GrColorComponentFlags;
+            inoutCoverage.fColor = this->getCoverageColor();
         }
 
         // Run through the coverage stages
         for (int s = 0; s < this->numCoverageStages(); ++s) {
             const GrProcessor* processor = this->getCoverageStage(s).getProcessor();
-            processor->getConstantColorComponents(&coverage, &coverageComponentFlags);
+            processor->computeInvariantOutput(&inoutCoverage);
         }
 
         // Since the shader will multiply coverage and color, the only way the final A==1 is if
         // coverage and color both have A==1.
-        return (kA_GrColorComponentFlag & validComponentFlags & coverageComponentFlags) &&
-                0xFF == GrColorUnpackA(color) && 0xFF == GrColorUnpackA(coverage);
-
+        return (inoutColor.isOpaque() && inoutCoverage.isOpaque());
     }
 
-    return (kA_GrColorComponentFlag & validComponentFlags) && 0xFF == GrColorUnpackA(color);
+    return inoutColor.isOpaque();
 }
 
diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp
index 98a5689..6a79203 100644
--- a/src/gpu/GrOptDrawState.cpp
+++ b/src/gpu/GrOptDrawState.cpp
@@ -170,18 +170,19 @@
     int firstColorStage = 0;
 
     // Set up color and flags for ConstantColorComponent checks
-    GrColor color;
-    uint32_t validComponentFlags;
+    GrProcessor::InvariantOutput inout;
+    inout.fIsSingleComponent = false;
     if (!this->hasColorVertexAttribute()) {
-        color = ds.getColor();
-        validComponentFlags = kRGBA_GrColorComponentFlags;
+        inout.fColor = ds.getColor();
+        inout.fValidFlags = kRGBA_GrColorComponentFlags;
     } else {
         if (ds.vertexColorsAreOpaque()) {
-            color = 0xFF << GrColor_SHIFT_A;
-            validComponentFlags = kA_GrColorComponentFlag;
+            inout.fColor = 0xFF << GrColor_SHIFT_A;
+            inout.fValidFlags = kA_GrColorComponentFlag;
         } else {
-            validComponentFlags = 0;
-            color = 0; // not strictly necessary but we get false alarms from tools about uninit.
+            inout.fValidFlags = 0;
+            // not strictly necessary but we get false alarms from tools about uninit.
+            inout.fColor = 0;
         }
     }
 
@@ -191,10 +192,10 @@
             firstColorStage = i;
             fInputColorIsUsed = false;
         }
-        fp->getConstantColorComponents(&color, &validComponentFlags);
-        if (kRGBA_GrColorComponentFlags == validComponentFlags) {
+        fp->computeInvariantOutput(&inout);
+        if (kRGBA_GrColorComponentFlags == inout.fValidFlags) {
             firstColorStage = i + 1;
-            fColor = color;
+            fColor = inout.fColor;
             fInputColorIsUsed = true;
             this->removeFixedFunctionVertexAttribs(0x1 << kColor_GrVertexAttribBinding);
         }
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 4ae0ebc..7a84f80 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -75,11 +75,6 @@
         }
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     const GrShaderVar& inCircleEdge() const { return fInCircleEdge; }
 
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
@@ -150,6 +145,11 @@
         return cee.fStroke == fStroke;
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     const GrShaderVar& fInCircleEdge;
     bool fStroke;
 
@@ -192,11 +192,6 @@
         }
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendGeometryProcessorFactory<EllipseEdgeEffect>::getInstance();
     }
@@ -291,6 +286,11 @@
         return eee.fStroke == fStroke;
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     const GrShaderVar& fInEllipseOffset;
     const GrShaderVar& fInEllipseRadii;
     bool fStroke;
@@ -341,11 +341,6 @@
         }
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendGeometryProcessorFactory<DIEllipseEdgeEffect>::getInstance();
     }
@@ -460,6 +455,11 @@
         return eee.fMode == fMode;
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     const GrShaderVar& fInEllipseOffsets0;
     const GrShaderVar& fInEllipseOffsets1;
     Mode fMode;
diff --git a/src/gpu/GrPaint.cpp b/src/gpu/GrPaint.cpp
index 84fdf42..ffde5f9 100644
--- a/src/gpu/GrPaint.cpp
+++ b/src/gpu/GrPaint.cpp
@@ -52,28 +52,32 @@
 
     // TODO: Share this implementation with GrDrawState
 
-    GrColor coverage = GrColorPackRGBA(fCoverage, fCoverage, fCoverage, fCoverage);
-    uint32_t coverageComps = kRGBA_GrColorComponentFlags;
+    GrProcessor::InvariantOutput inout;
+    inout.fColor = GrColorPackRGBA(fCoverage, fCoverage, fCoverage, fCoverage);
+    inout.fValidFlags = kRGBA_GrColorComponentFlags;
+    inout.fIsSingleComponent = false;
     int count = fCoverageStages.count();
     for (int i = 0; i < count; ++i) {
-        fCoverageStages[i].getProcessor()->getConstantColorComponents(&coverage, &coverageComps);
+        fCoverageStages[i].getProcessor()->computeInvariantOutput(&inout);
     }
-    if (kRGBA_GrColorComponentFlags != coverageComps || 0xffffffff != coverage) {
+    if (!inout.isSolidWhite()) {
         return false;
     }
 
-    GrColor color = fColor;
-    uint32_t colorComps = kRGBA_GrColorComponentFlags;
+    inout.fColor = fColor;
+    inout.fValidFlags = kRGBA_GrColorComponentFlags;
+    inout.fIsSingleComponent = false;
     count = fColorStages.count();
     for (int i = 0; i < count; ++i) {
-        fColorStages[i].getProcessor()->getConstantColorComponents(&color, &colorComps);
+        fColorStages[i].getProcessor()->computeInvariantOutput(&inout);
     }
 
     SkASSERT((NULL == solidColor) == (NULL == solidColorKnownComponents));
 
     GrBlendCoeff srcCoeff = fSrcBlendCoeff;
     GrBlendCoeff dstCoeff = fDstBlendCoeff;
-    GrSimplifyBlend(&srcCoeff, &dstCoeff, color, colorComps, 0, 0, 0);
+    GrSimplifyBlend(&srcCoeff, &dstCoeff, inout.fColor, inout.fValidFlags,
+                    0, 0, 0);
 
     bool opaque = kZero_GrBlendCoeff == dstCoeff && !GrBlendCoeffRefsDst(srcCoeff);
     if (solidColor) {
@@ -85,8 +89,8 @@
                     break;
 
                 case kOne_GrBlendCoeff:
-                    *solidColor = color;
-                    *solidColorKnownComponents = colorComps;
+                    *solidColor = inout.fColor;
+                    *solidColorKnownComponents = inout.fValidFlags;
                     break;
 
                 // The src coeff should never refer to the src and if it refers to dst then opaque
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 45298b5..26e0053 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -88,4 +88,46 @@
         SkASSERT(this->textureAccess(i) == other.textureAccess(i));
     }
 }
+
+void GrProcessor::InvariantOutput::validate() const {
+    if (fIsSingleComponent) {
+        SkASSERT(0 == fValidFlags || kRGBA_GrColorComponentFlags == fValidFlags);
+        if (kRGBA_GrColorComponentFlags == fValidFlags) {
+            SkASSERT(this->colorComponentsAllEqual());
+        }
+    }
+
+    SkASSERT(this->validPreMulColor());
+}
+
+bool GrProcessor::InvariantOutput::colorComponentsAllEqual() const {
+    unsigned colorA = GrColorUnpackA(fColor);
+    return(GrColorUnpackR(fColor) == colorA &&
+           GrColorUnpackG(fColor) == colorA &&
+           GrColorUnpackB(fColor) == colorA);
+}
+
+bool GrProcessor::InvariantOutput::validPreMulColor() const {
+    if (kA_GrColorComponentFlag & fValidFlags) {
+        float c[4];
+        GrColorToRGBAFloat(fColor, c);
+        if (kR_GrColorComponentFlag & fValidFlags) {
+            if (c[0] > c[3]) {
+                return false;
+            }
+        }
+        if (kG_GrColorComponentFlag & fValidFlags) {
+            if (c[1] > c[3]) {
+                return false;
+            }
+        }
+        if (kB_GrColorComponentFlag & fValidFlags) {
+            if (c[2] > c[3]) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
 #endif
+
diff --git a/src/gpu/effects/GrBezierEffect.h b/src/gpu/effects/GrBezierEffect.h
index cb79ac3..4631a2c 100644
--- a/src/gpu/effects/GrBezierEffect.h
+++ b/src/gpu/effects/GrBezierEffect.h
@@ -97,11 +97,6 @@
 
     typedef GrGLConicEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -109,6 +104,11 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     GrPrimitiveEdgeType   fEdgeType;
     const GrShaderVar& fInConicCoeffs;
 
@@ -170,11 +170,6 @@
 
     typedef GrGLQuadEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -182,6 +177,11 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     GrPrimitiveEdgeType   fEdgeType;
     const GrShaderVar& fInHairQuadEdge;
 
@@ -245,11 +245,6 @@
 
     typedef GrGLCubicEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        *validFlags = 0;
-    }
-
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -257,6 +252,11 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     GrPrimitiveEdgeType   fEdgeType;
     const GrShaderVar& fInCubicCoeffs;
 
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index d73e604..f5cc9d1 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -169,9 +169,10 @@
            fDomain == s.fDomain;
 }
 
-void GrBicubicEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+void GrBicubicEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
     // FIXME: Perhaps we can do better.
-    *validFlags = 0;
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
     return;
 }
 
diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h
index 03476da..e36aa8d 100644
--- a/src/gpu/effects/GrBicubicEffect.h
+++ b/src/gpu/effects/GrBicubicEffect.h
@@ -29,7 +29,6 @@
     typedef GrGLBicubicEffect GLProcessor;
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     const GrTextureDomain& domain() const { return fDomain; }
 
@@ -93,6 +92,8 @@
                     const SkMatrix &matrix, const SkRect& domain);
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     float           fCoefficients[16];
     GrTextureDomain fDomain;
 
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 3042d86..f60be2a 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -126,9 +126,9 @@
            other.fPMConversion == fPMConversion;
 }
 
-void GrConfigConversionEffect::getConstantColorComponents(GrColor* color,
-                                                          uint32_t* validFlags) const {
-    this->updateConstantColorComponentsForModulation(color, validFlags);
+void GrConfigConversionEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    this->updateInvariantOutputForModulation(inout);
+    inout->fIsSingleComponent = false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index 765e49b..aa2c874 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -43,8 +43,6 @@
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     bool swapsRedAndBlue() const { return fSwapRedAndBlue; }
     PMConversion  pmConversion() const { return fPMConversion; }
 
@@ -65,6 +63,8 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     bool            fSwapRedAndBlue;
     PMConversion    fPMConversion;
 
diff --git a/src/gpu/effects/GrConvexPolyEffect.cpp b/src/gpu/effects/GrConvexPolyEffect.cpp
index 4857a9e..836596a 100644
--- a/src/gpu/effects/GrConvexPolyEffect.cpp
+++ b/src/gpu/effects/GrConvexPolyEffect.cpp
@@ -29,17 +29,6 @@
         return SkNEW_ARGS(AARectEffect, (edgeType, rect));
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        if (fRect.isEmpty()) {
-            // An empty rect will have no coverage anywhere.
-            *color = 0x00000000;
-            *validFlags = kRGBA_GrColorComponentFlags;
-        } else {
-            *validFlags = 0;
-        }
-    }
-
     GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
@@ -54,6 +43,17 @@
         return fRect == aare.fRect;
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        if (fRect.isEmpty()) {
+            // An empty rect will have no coverage anywhere.
+            inout->fColor = 0x00000000;
+            inout->fValidFlags = kRGBA_GrColorComponentFlags;
+        } else {
+            inout->fValidFlags = 0;
+        }
+        inout->fIsSingleComponent = false;
+    }
+
     SkRect              fRect;
     GrPrimitiveEdgeType fEdgeType;
 
@@ -328,8 +328,9 @@
 
 GrConvexPolyEffect::~GrConvexPolyEffect() {}
 
-void GrConvexPolyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void GrConvexPolyEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendFragmentProcessorFactory& GrConvexPolyEffect::getFactory() const {
diff --git a/src/gpu/effects/GrConvexPolyEffect.h b/src/gpu/effects/GrConvexPolyEffect.h
index e474939..7f76f79 100644
--- a/src/gpu/effects/GrConvexPolyEffect.h
+++ b/src/gpu/effects/GrConvexPolyEffect.h
@@ -70,8 +70,6 @@
 
     typedef GrGLConvexPolyEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -79,6 +77,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrPrimitiveEdgeType    fEdgeType;
     int                    fEdgeCount;
     SkScalar               fEdges[3 * kMaxEdges];
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
index f2a2d5a..f61f378 100644
--- a/src/gpu/effects/GrConvolutionEffect.h
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -64,12 +64,6 @@
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
-    virtual void getConstantColorComponents(GrColor*, uint32_t* validFlags) const {
-        // If the texture was opaque we could know that the output color if we knew the sum of the
-        // kernel values.
-        *validFlags = 0;
-    }
-
     enum {
         // This was decided based on the min allowed value for the max texture
         // samples per fragment program run in DX9SM2 (32). A sigma param of 4.0
@@ -103,6 +97,13 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const {
+        // If the texture was opaque we could know that the output color if we knew the sum of the
+        // kernel values.
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     typedef Gr1DKernelEffect INHERITED;
diff --git a/src/gpu/effects/GrCustomCoordsTextureEffect.cpp b/src/gpu/effects/GrCustomCoordsTextureEffect.cpp
index b9794c1..7362ba9 100644
--- a/src/gpu/effects/GrCustomCoordsTextureEffect.cpp
+++ b/src/gpu/effects/GrCustomCoordsTextureEffect.cpp
@@ -72,14 +72,13 @@
     return fTextureAccess == cte.fTextureAccess;
 }
 
-void GrCustomCoordsTextureEffect::getConstantColorComponents(GrColor* color,
-                                                             uint32_t* validFlags) const {
-    if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
-        GrPixelConfigIsOpaque(this->texture(0)->config())) {
-        *validFlags = kA_GrColorComponentFlag;
+void GrCustomCoordsTextureEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    if (inout->isOpaque() && GrPixelConfigIsOpaque(this->texture(0)->config())) {
+        inout->fValidFlags = kA_GrColorComponentFlag;
     } else {
-        *validFlags = 0;
+        inout->fValidFlags = 0;
     }
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendGeometryProcessorFactory& GrCustomCoordsTextureEffect::getFactory() const {
diff --git a/src/gpu/effects/GrCustomCoordsTextureEffect.h b/src/gpu/effects/GrCustomCoordsTextureEffect.h
index f48a144..0a6af7d 100644
--- a/src/gpu/effects/GrCustomCoordsTextureEffect.h
+++ b/src/gpu/effects/GrCustomCoordsTextureEffect.h
@@ -28,8 +28,6 @@
 
     static const char* Name() { return "Texture"; }
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     const GrShaderVar& inTextureCoords() const { return fInTextureCoords; }
 
     typedef GrGLCustomCoordsTextureEffect GLProcessor;
@@ -41,6 +39,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrTextureAccess    fTextureAccess;
     const GrShaderVar& fInTextureCoords;
 
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index 704a6ac..4df1f02 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -456,8 +456,6 @@
 
     typedef GLDashingCircleEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -465,6 +463,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrPrimitiveEdgeType    fEdgeType;
     const GrShaderVar&  fInCoord;
     SkScalar            fIntervalLength;
@@ -584,8 +584,9 @@
 
 DashingCircleEffect::~DashingCircleEffect() {}
 
-void DashingCircleEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void DashingCircleEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendGeometryProcessorFactory& DashingCircleEffect::getFactory() const {
@@ -668,8 +669,6 @@
 
     typedef GLDashingLineEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -677,6 +676,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrPrimitiveEdgeType    fEdgeType;
     const GrShaderVar&  fInCoord;
     SkRect              fRect;
@@ -807,8 +808,9 @@
 
 DashingLineEffect::~DashingLineEffect() {}
 
-void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void DashingLineEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendGeometryProcessorFactory& DashingLineEffect::getFactory() const {
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
index d5d3348..245a035 100755
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
@@ -206,14 +206,14 @@
            fFlags == cte.fFlags;
 }
 
-void GrDistanceFieldTextureEffect::getConstantColorComponents(GrColor* color,
-                                                             uint32_t* validFlags) const {
-    if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
-        GrPixelConfigIsOpaque(this->texture(0)->config())) {
-        *validFlags = kA_GrColorComponentFlag;
+void GrDistanceFieldTextureEffect::onComputeInvariantOutput(
+    InvariantOutput* inout) const {
+    if (inout->isOpaque() && GrPixelConfigIsOpaque(this->texture(0)->config())) {
+        inout->fValidFlags = kA_GrColorComponentFlag;
     } else {
-        *validFlags = 0;
+        inout->fValidFlags = 0;
     }
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendGeometryProcessorFactory& GrDistanceFieldTextureEffect::getFactory() const {
@@ -476,14 +476,14 @@
             fFlags == cte.fFlags);
 }
 
-void GrDistanceFieldLCDTextureEffect::getConstantColorComponents(GrColor* color,
-                                                                 uint32_t* validFlags) const {
-    if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
-        GrPixelConfigIsOpaque(this->texture(0)->config())) {
-        *validFlags = kA_GrColorComponentFlag;
+void GrDistanceFieldLCDTextureEffect::onComputeInvariantOutput(
+    InvariantOutput* inout) const {
+    if (inout->isOpaque() && GrPixelConfigIsOpaque(this->texture(0)->config())) {
+        inout->fValidFlags = kA_GrColorComponentFlag;
     } else {
-        *validFlags = 0;
+        inout->fValidFlags = 0;
     }
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendGeometryProcessorFactory& GrDistanceFieldLCDTextureEffect::getFactory() const {
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h
index b8d774d..efd622e 100644
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.h
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.h
@@ -58,8 +58,6 @@
 
     static const char* Name() { return "DistanceFieldTexture"; }
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     const GrShaderVar& inTextureCoords() const { return fInTextureCoords; }
 #ifdef SK_GAMMA_APPLY_TO_A8
     float getLuminance() const { return fLuminance; }
@@ -79,6 +77,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrTextureAccess    fTextureAccess;
 #ifdef SK_GAMMA_APPLY_TO_A8
     GrTextureAccess    fGammaTextureAccess;
@@ -112,7 +112,6 @@
     static const char* Name() { return "DistanceFieldLCDTexture"; }
 
     const GrShaderVar& inTextureCoords() const { return fInTextureCoords; }
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
     GrColor getTextColor() const { return fTextColor; }
     uint32_t getFlags() const { return fFlags; }
 
@@ -128,6 +127,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GrTextureAccess    fTextureAccess;
     GrTextureAccess    fGammaTextureAccess;
     GrColor            fTextColor;
diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp
index fe7447d..e7f90ac 100644
--- a/src/gpu/effects/GrDitherEffect.cpp
+++ b/src/gpu/effects/GrDitherEffect.cpp
@@ -30,8 +30,6 @@
 
     typedef GLDitherEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendFragmentProcessorFactory<DitherEffect>::getInstance();
     }
@@ -44,13 +42,16 @@
     // All dither effects are equal
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE { return true; }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     typedef GrFragmentProcessor INHERITED;
 };
 
-void DitherEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void DitherEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.h b/src/gpu/effects/GrMatrixConvolutionEffect.h
index 4cc4296..4d76f4c 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.h
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.h
@@ -52,12 +52,6 @@
 
     virtual ~GrMatrixConvolutionEffect();
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        // TODO: Try to do better?
-        *validFlags = 0;
-    }
-
     static const char* Name() { return "MatrixConvolution"; }
     const SkIRect& bounds() const { return fBounds; }
     const SkISize& kernelSize() const { return fKernelSize; }
@@ -85,6 +79,12 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        // TODO: Try to do better?
+        inout->fValidFlags = 0;
+        inout->fIsSingleComponent = false;
+    }
+
     SkIRect         fBounds;
     SkISize         fKernelSize;
     float           fKernel[MAX_KERNEL_SIZE];
diff --git a/src/gpu/effects/GrOvalEffect.cpp b/src/gpu/effects/GrOvalEffect.cpp
index 10fd8a6..2a04e16 100644
--- a/src/gpu/effects/GrOvalEffect.cpp
+++ b/src/gpu/effects/GrOvalEffect.cpp
@@ -32,8 +32,6 @@
 
     typedef GLCircleEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -41,6 +39,8 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     SkPoint             fCenter;
     SkScalar            fRadius;
     GrPrimitiveEdgeType    fEdgeType;
@@ -56,8 +56,9 @@
     return SkNEW_ARGS(CircleEffect, (edgeType, center, radius));
 }
 
-void CircleEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void CircleEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendFragmentProcessorFactory& CircleEffect::getFactory() const {
@@ -204,8 +205,6 @@
 
     typedef GLEllipseEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -213,6 +212,8 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     SkPoint             fCenter;
     SkVector            fRadii;
     GrPrimitiveEdgeType    fEdgeType;
@@ -230,8 +231,9 @@
     return SkNEW_ARGS(EllipseEffect, (edgeType, center, rx, ry));
 }
 
-void EllipseEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void EllipseEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendFragmentProcessorFactory& EllipseEffect::getFactory() const {
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp
index f5131fe..0af564c 100644
--- a/src/gpu/effects/GrRRectEffect.cpp
+++ b/src/gpu/effects/GrRRectEffect.cpp
@@ -59,8 +59,6 @@
 
     typedef GLCircularRRectEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -68,6 +66,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     SkRRect                fRRect;
     GrPrimitiveEdgeType    fEdgeType;
     uint32_t               fCircularCornerFlags;
@@ -86,8 +86,9 @@
     return SkNEW_ARGS(CircularRRectEffect, (edgeType, circularCornerFlags, rrect));
 }
 
-void CircularRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void CircularRRectEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendFragmentProcessorFactory& CircularRRectEffect::getFactory() const {
@@ -399,8 +400,6 @@
 
     typedef GLEllipticalRRectEffect GLProcessor;
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
 
 private:
@@ -408,6 +407,8 @@
 
     virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     SkRRect             fRRect;
     GrPrimitiveEdgeType    fEdgeType;
 
@@ -424,8 +425,9 @@
     return SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrect));
 }
 
-void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    *validFlags = 0;
+void EllipticalRRectEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendFragmentProcessorFactory& EllipticalRRectEffect::getFactory() const {
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
index 6743ddb..a7707da 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -41,8 +41,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void GrSimpleTextureEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    this->updateConstantColorComponentsForModulation(color, validFlags);
+void GrSimpleTextureEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    this->updateInvariantOutputForModulation(inout);
+    inout->fIsSingleComponent = false;
 }
 
 const GrBackendFragmentProcessorFactory& GrSimpleTextureEffect::getFactory() const {
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
index dc9cf85..88c5ca2 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.h
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -49,8 +49,6 @@
 
     static const char* Name() { return "Texture"; }
 
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     typedef GrGLSimpleTextureEffect GLProcessor;
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
@@ -75,6 +73,8 @@
         return this->hasSameTextureParamsMatrixAndSourceCoords(ste);
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     typedef GrSingleTextureEffect INHERITED;
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index 6349ee7..cba322e 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -44,16 +44,15 @@
     }
 
     /**
-     * Can be used as a helper to implement subclass getConstantColorComponents(). It assumes that
+     * Can be used as a helper to implement subclass onComputeInvariantOutput(). It assumes that
      * the subclass output color will be a modulation of the input color with a value read from the
      * texture.
      */
-    void updateConstantColorComponentsForModulation(GrColor* color, uint32_t* validFlags) const {
-        if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
-            GrPixelConfigIsOpaque(this->texture(0)->config())) {
-            *validFlags = kA_GrColorComponentFlag;
+    void updateInvariantOutputForModulation(InvariantOutput* inout) const {
+        if (inout->isOpaque() && GrPixelConfigIsOpaque(this->texture(0)->config())) {
+            inout->fValidFlags = kA_GrColorComponentFlag;
         } else {
-            *validFlags = 0;
+            inout->fValidFlags = 0;
         }
     }
 
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index 1d3b37d..90e3b41 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -269,12 +269,13 @@
            this->fTextureDomain == s.fTextureDomain;
 }
 
-void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+void GrTextureDomainEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
     if (GrTextureDomain::kDecal_Mode == fTextureDomain.mode()) { // TODO: helper
-        *validFlags = 0;
+        inout->fValidFlags = 0;
     } else {
-        this->updateConstantColorComponentsForModulation(color, validFlags);
+        this->updateInvariantOutputForModulation(inout);
     }
+    inout->fIsSingleComponent = false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index 5751bad..ec86847 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -159,7 +159,6 @@
     typedef GrGLTextureDomainEffect GLProcessor;
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     const GrTextureDomain& textureDomain() const { return fTextureDomain; }
 
@@ -176,6 +175,8 @@
 
     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     typedef GrSingleTextureEffect INHERITED;
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index 436106c..cdbe654 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -28,13 +28,6 @@
         return GrTBackendFragmentProcessorFactory<YUVtoRGBEffect>::getInstance();
     }
 
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        // YUV is opaque
-        *color = 0xFF;
-        *validFlags = kA_GrColorComponentFlag;
-    }
-
     SkYUVColorSpace getColorSpace() const {
         return fColorSpace;
     }
@@ -117,6 +110,13 @@
                fColorSpace == s.getColorSpace();
     }
 
+    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
+        // YUV is opaque
+        inout->fColor = 0xFF;
+        inout->fValidFlags = kA_GrColorComponentFlag;
+        inout->fIsSingleComponent = false;
+    }
+
     GrCoordTransform fCoordTransform;
     GrTextureAccess fYAccess;
     GrTextureAccess fUAccess;
diff --git a/tests/GpuColorFilterTest.cpp b/tests/GpuColorFilterTest.cpp
index 202756b..a61fe0b 100644
--- a/tests/GpuColorFilterTest.cpp
+++ b/tests/GpuColorFilterTest.cpp
@@ -99,12 +99,14 @@
         const GetConstantComponentTestCase& test = filterTests[i];
         SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(test.filterColor, test.filterMode));
         SkAutoTUnref<GrFragmentProcessor> effect(cf->asFragmentProcessor(grContext));
-        GrColor color = test.inputColor;
-        uint32_t components = test.inputComponents;
-        effect->getConstantColorComponents(&color, &components);
+        GrProcessor::InvariantOutput inout;
+        inout.fColor = test.inputColor;
+        inout.fValidFlags = test.inputComponents;
+        inout.fIsSingleComponent = false;
+        effect->computeInvariantOutput(&inout);
 
-        REPORTER_ASSERT(reporter, filterColor(color, components) == test.outputColor);
-        REPORTER_ASSERT(reporter, test.outputComponents == components);
+        REPORTER_ASSERT(reporter, filterColor(inout.fColor, inout.fValidFlags) == test.outputColor);
+        REPORTER_ASSERT(reporter, test.outputComponents == inout.fValidFlags);
     }
 }