From 371e105da5d9fdfff3b4242b37ff6fc09214c8c8 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Fri, 11 Jan 2013 21:08:55 +0000 Subject: 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 --- src/effects/SkBlendImageFilter.cpp | 15 +++++++ src/effects/SkColorMatrixFilter.cpp | 45 ++++++++++++++++++++ src/effects/SkLightingImageFilter.cpp | 7 ++++ src/effects/SkTableColorFilter.cpp | 48 ++++++++++++++++----- src/effects/gradients/SkGradientShader.cpp | 2 + src/effects/gradients/SkGradientShaderPriv.h | 10 +++++ src/gpu/GrDrawTarget.cpp | 62 +++++++++++++++++----------- src/gpu/GrEffect.cpp | 4 -- src/gpu/effects/GrSingleTextureEffect.cpp | 11 +++++ src/gpu/effects/GrSingleTextureEffect.h | 4 ++ 10 files changed, 168 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp index 6dd5eab4c5..3c625bb0b4 100644 --- a/src/effects/SkBlendImageFilter.cpp +++ b/src/effects/SkBlendImageFilter.cpp @@ -153,6 +153,8 @@ public: 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 @@ const GrBackendEffectFactory& GrBlendEffect::getFactory() const { return GrTBackendEffectFactory::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 ed34f64ea3..2ff4fd6f48 100644 --- a/src/effects/SkColorMatrixFilter.cpp +++ b/src/effects/SkColorMatrixFilter.cpp @@ -338,6 +338,51 @@ public: 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(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 48f16b3ff8..fc819deb17 100644 --- a/src/effects/SkLightingImageFilter.cpp +++ b/src/effects/SkLightingImageFilter.cpp @@ -311,6 +311,13 @@ public: 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 eb59425aa7..2577cb4b76 100644 --- a/src/effects/SkTableColorFilter.cpp +++ b/src/effects/SkTableColorFilter.cpp @@ -49,6 +49,13 @@ public: 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 @@ protected: 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 GLColorTableEffect; 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 @@ GrGLEffect::EffectKey GLColorTableEffect::GenKey(const GrEffectStage&, const GrG /////////////////////////////////////////////////////////////////////////////// -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 @@ bool ColorTableEffect::isEqual(const GrEffect& sBase) const { 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 @@ GR_DEFINE_EFFECT_TEST(ColorTableEffect); 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 @@ GrEffect* SkTable_ColorFilter::asNewEffect(GrContext* context) const { 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 8521bdbf61..7d779708f6 100644 --- a/src/effects/gradients/SkGradientShader.cpp +++ b/src/effects/gradients/SkGradientShader.cpp @@ -753,6 +753,8 @@ GrGradientEffect::GrGradientEffect(GrContext* ctx, 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 552013efa5..b2b8c47a1c 100644 --- a/src/effects/gradients/SkGradientShaderPriv.h +++ b/src/effects/gradients/SkGradientShaderPriv.h @@ -243,6 +243,15 @@ public: 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 @@ private: GrTextureStripAtlas* fAtlas; int fRow; SkMatrix fMatrix; + bool fIsOpaque; typedef GrEffect INHERITED; diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 3c112871e5..e7609d88fc 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -827,40 +827,52 @@ bool GrDrawTarget::canTweakAlphaForCoverage() const { 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 dbfb6b014a..534489fd4e 100644 --- a/src/gpu/GrEffect.cpp +++ b/src/gpu/GrEffect.cpp @@ -61,10 +61,6 @@ int32_t GrBackendEffectFactory::fCurrEffectClassID = GrBackendEffectFactory::kIl 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 14f5b64472..2cf83472d7 100644 --- a/src/gpu/effects/GrSingleTextureEffect.cpp +++ b/src/gpu/effects/GrSingleTextureEffect.cpp @@ -98,6 +98,17 @@ GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, 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::getInstance(); } diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h index fca5e93858..b732913c38 100644 --- a/src/gpu/effects/GrSingleTextureEffect.h +++ b/src/gpu/effects/GrSingleTextureEffect.h @@ -35,6 +35,10 @@ public: 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; -- cgit v1.2.3