Add GrEffect::updateKnownColorComponents(). It is used to determine whether the output of an effect has a constant output value for r,g,b, or a.
Review URL: https://codereview.appspot.com/7064057

git-svn-id: http://skia.googlecode.com/svn/trunk@7144 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/gpu/GrEffect.h b/include/gpu/GrEffect.h
index b12a346..5fca4b2 100644
--- a/include/gpu/GrEffect.h
+++ b/include/gpu/GrEffect.h
@@ -8,9 +8,10 @@
 #ifndef GrEffect_DEFINED
 #define GrEffect_DEFINED
 
-#include "GrRefCnt.h"
-#include "GrNoncopyable.h"
+#include "GrColor.h"
 #include "GrEffectUnitTest.h"
+#include "GrNoncopyable.h"
+#include "GrRefCnt.h"
 #include "GrTexture.h"
 #include "GrTextureAccess.h"
 
@@ -31,9 +32,27 @@
     GrEffect() {};
     virtual ~GrEffect();
 
-    /** If given an input texture that is/is not opaque, is this effect guaranteed to produce an
-        opaque output? */
-    virtual bool isOpaque(bool inputTextureIsOpaque) const;
+    /**
+     * Flags for getConstantColorComponents. They are defined so that the bit order reflects the
+     * GrColor shift order.
+     */
+    enum ValidComponentFlags {
+        kR_ValidComponentFlag = 1 << (GrColor_SHIFT_R / 8),
+        kG_ValidComponentFlag = 1 << (GrColor_SHIFT_G / 8),
+        kB_ValidComponentFlag = 1 << (GrColor_SHIFT_B / 8),
+        kA_ValidComponentFlag = 1 << (GrColor_SHIFT_A / 8),
+
+        kAll_ValidComponentFlags = (kR_ValidComponentFlag | kG_ValidComponentFlag |
+                                    kB_ValidComponentFlag | kA_ValidComponentFlag)
+    };
+
+    /**
+     * This function is used to perform optimizations. When called the color and validFlags params
+     * indicate whether the input components to this effect in the FS will have known values. 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.
+     */
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const = 0;
 
     /** 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,
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index 6dd5eab..3c625bb 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -153,6 +153,8 @@
     typedef GrGLBlendEffect GLEffect;
     static const char* Name() { return "Blend"; }
 
+    void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
 private:
     GrTextureAccess             fForegroundAccess;
     GrTextureAccess             fBackgroundAccess;
@@ -245,6 +247,19 @@
     return GrTBackendEffectFactory<GrBlendEffect>::getInstance();
 }
 
+void GrBlendEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // The output alpha is always 1 - (1 - FGa) * (1 - BGa). So if either FGa or BGa is known to
+    // be one then the output alpha is one. (This effect ignores its input. We should have a way to
+    // communicate this.)
+    if (GrPixelConfigIsOpaque(fForegroundAccess.getTexture()->config()) ||
+        GrPixelConfigIsOpaque(fBackgroundAccess.getTexture()->config())) {
+        *validFlags = kA_ValidComponentFlag;
+        *color = GrColorPackRGBA(0, 0, 0, 0xff);
+    } else {
+        *validFlags = 0;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GrGLBlendEffect::GrGLBlendEffect(const GrBackendEffectFactory& factory, const GrEffect& effect)
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index ed34f64..2ff4fd6 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -338,6 +338,51 @@
         return cme.fMatrix == fMatrix;
     }
 
+    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 ValidComponentFlags kRGBAFlags[] = {
+            kR_ValidComponentFlag,
+            kG_ValidComponentFlag,
+            kB_ValidComponentFlag,
+            kA_ValidComponentFlag
+        };
+        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_ValidComponentFlag;
+        // 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_EFFECT_TEST;
 
     class GLEffect : public GrGLEffect {
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 48f16b3..fc819de 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -311,6 +311,13 @@
 
     const SkLight* light() const { return fLight; }
     SkScalar surfaceScale() const { return fSurfaceScale; }
+
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // lighting shaders are complicated. We just throw up our hands.
+        *validFlags = 0;
+    }
+
 private:
     typedef GrSingleTextureEffect INHERITED;
     const SkLight* fLight;
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index eb59425..2577cb4 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -49,6 +49,13 @@
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTable_ColorFilter)
 
+    enum {
+        kA_Flag = 1 << 0,
+        kR_Flag = 1 << 1,
+        kG_Flag = 1 << 2,
+        kB_Flag = 1 << 3,
+    };
+
 protected:
     SkTable_ColorFilter(SkFlattenableReadBuffer& buffer);
     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
@@ -56,12 +63,6 @@
 private:
     mutable const SkBitmap* fBitmap; // lazily allocated
 
-    enum {
-        kA_Flag = 1 << 0,
-        kR_Flag = 1 << 1,
-        kG_Flag = 1 << 2,
-        kB_Flag = 1 << 3,
-    };
     uint8_t fStorage[256 * 4];
     unsigned fFlags;
 
@@ -226,19 +227,23 @@
 class ColorTableEffect : public GrEffect {
 public:
 
-    explicit ColorTableEffect(GrTexture* texture);
+    explicit ColorTableEffect(GrTexture* texture, unsigned flags);
     virtual ~ColorTableEffect();
 
     static const char* Name() { return "ColorTable"; }
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
     virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
 
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
     typedef GLColorTableEffect GLEffect;
 
 private:
     GR_DECLARE_EFFECT_TEST;
 
     GrTextureAccess fTextureAccess;
+    unsigned        fFlags; // currently not used in shader code, just to assist
+                            // getConstantColorComponents().
 
     typedef GrEffect INHERITED;
 };
@@ -321,8 +326,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-ColorTableEffect::ColorTableEffect(GrTexture* texture)
-    : fTextureAccess(texture, "a") {
+ColorTableEffect::ColorTableEffect(GrTexture* texture, unsigned flags)
+    : fTextureAccess(texture, "a")
+    , fFlags(flags) {
     this->addTextureAccess(&fTextureAccess);
 }
 
@@ -337,6 +343,24 @@
     return INHERITED::isEqual(sBase);
 }
 
+void ColorTableEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) 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_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kG_Flag) {
+        *validFlags &= ~kG_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kB_Flag) {
+        *validFlags &= ~kB_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kA_Flag) {
+        *validFlags &= ~kA_ValidComponentFlag;
+    }
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_EFFECT_TEST(ColorTableEffect);
@@ -344,7 +368,9 @@
 GrEffect* ColorTableEffect::TestCreate(SkRandom* random,
                                        GrContext* context,
                                        GrTexture* textures[]) {
-    return SkNEW_ARGS(ColorTableEffect, (textures[GrEffectUnitTest::kAlphaTextureIdx]));
+    static unsigned kAllFlags = SkTable_ColorFilter::kR_Flag | SkTable_ColorFilter::kG_Flag |
+                                SkTable_ColorFilter::kB_Flag | SkTable_ColorFilter::kA_Flag;
+    return SkNEW_ARGS(ColorTableEffect, (textures[GrEffectUnitTest::kAlphaTextureIdx], kAllFlags));
 }
 
 GrEffect* SkTable_ColorFilter::asNewEffect(GrContext* context) const {
@@ -352,7 +378,7 @@
     this->asComponentTable(&bitmap);
     // passing NULL because this effect does no tiling or filtering.
     GrTexture* texture = GrLockCachedBitmapTexture(context, bitmap, NULL);
-    GrEffect* effect = SkNEW_ARGS(ColorTableEffect, (texture));
+    GrEffect* effect = SkNEW_ARGS(ColorTableEffect, (texture, fFlags));
 
     // Unlock immediately, this is not great, but we don't have a way of
     // knowing when else to unlock it currently. TODO: Remove this when
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 8521bdb..7d77970 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -753,6 +753,8 @@
     SkBitmap bitmap;
     shader.getGradientTableBitmap(&bitmap);
 
+    fIsOpaque = shader.isOpaque();
+
     GrTextureStripAtlas::Desc desc;
     desc.fWidth  = bitmap.width();
     desc.fHeight = 32;
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 552013e..b2b8c47 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -243,6 +243,15 @@
                fYCoord == s.getYCoord() && fMatrix.cheapEqualTo(s.getMatrix());
     }
 
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        if (fIsOpaque && (kA_ValidComponentFlag & *validFlags) && 0xff == GrColorUnpackA(*color)) {
+            *validFlags = kA_ValidComponentFlag;
+        } else {
+            *validFlags = 0;
+        }
+    }
+
 protected:
 
     /** Populates a pair of arrays with colors and stop info to construct a random gradient.
@@ -264,6 +273,7 @@
     GrTextureStripAtlas* fAtlas;
     int fRow;
     SkMatrix fMatrix;
+    bool fIsOpaque;
 
     typedef GrEffect INHERITED;
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 3c11287..e7609d8 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -827,40 +827,52 @@
 bool GrDrawTarget::srcAlphaWillBeOne(GrVertexLayout layout) const {
     const GrDrawState& drawState = this->getDrawState();
 
+    uint32_t validComponentFlags;
+    GrColor  color;
     // Check if per-vertex or constant color may have partial alpha
-    if ((layout & kColor_VertexLayoutBit) ||
-        0xff != GrColorUnpackA(drawState.getColor())) {
-        return false;
-    }
-    // Check if color filter could introduce an alpha
-    // (TODO: Consider being more aggressive with regards to detecting 0xff
-    // final alpha from color filter).
-    if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
-        return false;
-    }
-    int stageCnt;
-    // Check whether coverage is treated as color
-    if (drawState.isCoverageDrawing()) {
-        if (0xff != GrColorUnpackA(drawState.getCoverage())) {
-            return false;
-        }
-        stageCnt = GrDrawState::kNumStages;
+    if (layout & kColor_VertexLayoutBit) {
+        validComponentFlags = 0;
     } else {
-        stageCnt = drawState.getFirstCoverageStage();
+        validComponentFlags = GrEffect::kAll_ValidComponentFlags;
+        color = drawState.getColor();
     }
-    // Check if a color stage could create a partial alpha
+
+    // Run through the color stages
+    int stageCnt = drawState.getFirstCoverageStage();
     for (int s = 0; s < stageCnt; ++s) {
         const GrEffect* effect = drawState.getStage(s).getEffect();
         if (NULL != effect) {
-            // FIXME: The param indicates whether the texture is opaque or not. However, the effect
-            // already controls its textures. It really needs to know whether the incoming color
-            // (from a uni, per-vertex colors, or previous stage) is opaque or not.
-            if (!effect->isOpaque(true)) {
-                return false;
+            effect->getConstantColorComponents(&color, &validComponentFlags);
+        }
+    }
+
+    // Check if the color filter could introduce an alpha.
+    // We could skip the above work when this is true, but it is rare and the right fix is to make
+    // the color filter a GrEffect and implement getConstantColorComponents() for it.
+    if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
+        validComponentFlags = 0;
+    }
+
+    // Check whether coverage is treated as color. If so we run through the coverage computation.
+    if (drawState.isCoverageDrawing()) {
+        GrColor coverageColor = drawState.getCoverage();
+        GrColor oldColor = color;
+        color = 0;
+        for (int c = 0; c < 4; ++c) {
+            if (validComponentFlags & (1 << c)) {
+                U8CPU a = (oldColor >> (c * 8)) & 0xff;
+                U8CPU b = (coverageColor >> (c * 8)) & 0xff;
+                color |= (SkMulDiv255Round(a, b) << (c * 8));
+            }
+        }
+        for (int s = drawState.getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
+            const GrEffect* effect = drawState.getStage(s).getEffect();
+            if (NULL != effect) {
+                effect->getConstantColorComponents(&color, &validComponentFlags);
             }
         }
     }
-    return true;
+    return (GrEffect::kA_ValidComponentFlag & validComponentFlags) && 0xff == GrColorUnpackA(color);
 }
 
 namespace {
diff --git a/src/gpu/GrEffect.cpp b/src/gpu/GrEffect.cpp
index dbfb6b0..534489f 100644
--- a/src/gpu/GrEffect.cpp
+++ b/src/gpu/GrEffect.cpp
@@ -61,10 +61,6 @@
 GrEffect::~GrEffect() {
 }
 
-bool GrEffect::isOpaque(bool inputTextureIsOpaque) const {
-    return false;
-}
-
 const char* GrEffect::name() const {
     return this->getFactory().name();
 }
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
index 14f5b64..2cf8347 100644
--- a/src/gpu/effects/GrSingleTextureEffect.cpp
+++ b/src/gpu/effects/GrSingleTextureEffect.cpp
@@ -98,6 +98,17 @@
 GrSingleTextureEffect::~GrSingleTextureEffect() {
 }
 
+void GrSingleTextureEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // If the input alpha is 0xff and the texture has no alpha channel, then the output alpha is
+    // 0xff
+    if ((*validFlags & kA_ValidComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
+        GrPixelConfigIsOpaque(fTextureAccess.getTexture()->config())) {
+        *validFlags = kA_ValidComponentFlag;
+    } else {
+        *validFlags = 0;
+    }
+}
+
 const GrBackendEffectFactory& GrSingleTextureEffect::getFactory() const {
     return GrTBackendEffectFactory<GrSingleTextureEffect>::getInstance();
 }
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index fca5e93..b732913 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -35,6 +35,10 @@
 
     static const char* Name() { return "Single Texture"; }
 
+    /** Note that if this class is sub-classed, the subclass may have to override this function.
+     */
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
     const SkMatrix& getMatrix() const { return fMatrix; }
 
     typedef GrGLSingleTextureEffect GLEffect;