Chromium Code Reviews| Index: src/effects/SkArithmeticImageFilter.cpp |
| diff --git a/src/effects/SkArithmeticImageFilter.cpp b/src/effects/SkArithmeticImageFilter.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d36f183b60914de016ee8962967f5bf97d82ecfb |
| --- /dev/null |
| +++ b/src/effects/SkArithmeticImageFilter.cpp |
| @@ -0,0 +1,312 @@ |
| +/* |
| + * Copyright 2013 The Android Open Source Project |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "SkArithmeticImageFilter.h" |
| +#include "SkArithmeticMode.h" |
| +#include "SkCanvas.h" |
| +#include "SkColorPriv.h" |
| +#include "SkFlattenableBuffers.h" |
| +#if SK_SUPPORT_GPU |
| +#include "GrContext.h" |
| +#include "gl/GrGLEffect.h" |
| +#include "gl/GrGLEffectMatrix.h" |
| +#include "GrTBackendEffectFactory.h" |
| +#include "SkImageFilterUtils.h" |
| +#endif |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +SkArithmeticImageFilter::SkArithmeticImageFilter(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, SkImageFilter* background, SkImageFilter* foreground) |
| + : INHERITED(background, foreground), fK1(k1), fK2(k2), fK3(k3), fK4(k4) |
| +{ |
| +} |
| + |
| +SkArithmeticImageFilter::~SkArithmeticImageFilter() { |
| +} |
| + |
| +SkArithmeticImageFilter::SkArithmeticImageFilter(SkFlattenableReadBuffer& buffer) |
| + : INHERITED(buffer) |
| +{ |
| + fK1 = buffer.readScalar(); |
| + fK2 = buffer.readScalar(); |
| + fK3 = buffer.readScalar(); |
| + fK4 = buffer.readScalar(); |
| +} |
| + |
| +void SkArithmeticImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { |
| + this->INHERITED::flatten(buffer); |
| + buffer.writeScalar(fK1); |
| + buffer.writeScalar(fK2); |
| + buffer.writeScalar(fK3); |
| + buffer.writeScalar(fK4); |
| +} |
| + |
| +bool SkArithmeticImageFilter::onFilterImage(Proxy* proxy, |
| + const SkBitmap& src, |
| + const SkMatrix& ctm, |
| + SkBitmap* dst, |
| + SkIPoint* offset) { |
| + SkBitmap background = src, foreground = src; |
| + SkImageFilter* foregroundInput = getInput(0); |
| + SkImageFilter* backgroundInput = getInput(1); |
| + if (backgroundInput && !backgroundInput->filterImage(proxy, src, ctm, &background, offset)) { |
| + return false; |
| + } |
| + if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) { |
| + return false; |
| + } |
| + dst->setConfig(background.config(), background.width(), background.height()); |
| + dst->allocPixels(); |
| + SkCanvas canvas(*dst); |
| + SkPaint paint; |
| + SkAutoTUnref<SkXfermode> mode(SkArithmeticMode::Create(fK1, fK2, fK3, fK4)); |
| + paint.setXfermodeMode(SkXfermode::kSrc_Mode); |
| + canvas.drawBitmap(background, 0, 0, &paint); |
| + paint.setXfermode(mode); |
| + canvas.drawBitmap(foreground, 0, 0, &paint); |
| + return true; |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +#if SK_SUPPORT_GPU |
| +class GrGLArithmeticEffect : public GrGLEffect { |
| +public: |
| + GrGLArithmeticEffect(const GrBackendEffectFactory&, const GrDrawEffect&); |
| + virtual ~GrGLArithmeticEffect(); |
| + |
| + virtual void emitCode(GrGLShaderBuilder*, |
| + const GrDrawEffect&, |
| + EffectKey, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TextureSamplerArray&) SK_OVERRIDE; |
| + |
| + static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); |
| + |
| + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; |
| + |
| +private: |
| + static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType; |
| + |
| + GrGLUniformManager::UniformHandle fK1Uni, fK2Uni, fK3Uni, fK4Uni; |
| + GrGLEffectMatrix fForegroundEffectMatrix; |
| + GrGLEffectMatrix fBackgroundEffectMatrix; |
| + |
| + typedef GrGLEffect INHERITED; |
| +}; |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +class GrArithmeticEffect : public GrEffect { |
| +public: |
| + static GrEffectRef* Create(float k1, float k2, float k3, float k4, |
| + GrTexture* foreground, |
| + GrTexture* background) { |
| + AutoEffectUnref effect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, foreground, background))); |
| + return CreateEffectRef(effect); |
| + } |
| + |
| + virtual ~GrArithmeticEffect(); |
| + |
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| + |
| + typedef GrGLArithmeticEffect GLEffect; |
| + static const char* Name() { return "Arithmetic"; } |
| + |
| + 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; } |
| + float k4() const { return fK4; } |
| + |
| +private: |
| + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; |
| + |
| + GrArithmeticEffect(float k1, float k2, float k3, float k4, GrTexture* foreground, GrTexture* background); |
| + float fK1, fK2, fK3, fK4; |
| + GrTextureAccess fForegroundAccess; |
| + GrTextureAccess fBackgroundAccess; |
| + |
| + typedef GrEffect INHERITED; |
| + |
| +}; |
| + |
| +bool SkArithmeticImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) { |
| + SkBitmap backgroundBM; |
| + if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &backgroundBM)) { |
| + return false; |
| + } |
| + GrTexture* background = (GrTexture*) backgroundBM.getTexture(); |
| + SkBitmap foregroundBM; |
| + if (!SkImageFilterUtils::GetInputResultGPU(getInput(1), proxy, src, &foregroundBM)) { |
| + return false; |
| + } |
| + GrTexture* foreground = (GrTexture*) foregroundBM.getTexture(); |
| + GrContext* context = foreground->getContext(); |
| + |
| + GrTextureDesc desc; |
| + desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; |
| + desc.fWidth = src.width(); |
| + desc.fHeight = src.height(); |
| + desc.fConfig = kSkia8888_GrPixelConfig; |
| + |
| + GrAutoScratchTexture ast(context, desc); |
| + SkAutoTUnref<GrTexture> dst(ast.detach()); |
| + |
| + GrContext::AutoRenderTarget art(context, dst->asRenderTarget()); |
| + |
| + GrPaint paint; |
| + paint.colorStage(0)->setEffect( |
| + GrArithmeticEffect::Create(SkScalarToFloat(fK1), |
| + SkScalarToFloat(fK2), |
| + SkScalarToFloat(fK3), |
| + SkScalarToFloat(fK4), |
| + foreground, |
| + background))->unref(); |
| + SkRect srcRect; |
| + src.getBounds(&srcRect); |
| + context->drawRect(paint, srcRect); |
| + return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result); |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +GrArithmeticEffect::GrArithmeticEffect(float k1, |
| + float k2, |
| + float k3, |
| + float k4, |
| + GrTexture* foreground, |
| + GrTexture* background) |
| + : fK1(k1), fK2(k2), fK3(k3), fK4(k4) |
| + , fForegroundAccess(foreground) |
| + , fBackgroundAccess(background) { |
| + this->addTextureAccess(&fForegroundAccess); |
| + this->addTextureAccess(&fBackgroundAccess); |
| +} |
| + |
| +GrArithmeticEffect::~GrArithmeticEffect() { |
| +} |
| + |
| +bool GrArithmeticEffect::onIsEqual(const GrEffect& sBase) const { |
| + const GrArithmeticEffect& s = CastEffect<GrArithmeticEffect>(sBase); |
| + return fForegroundAccess.getTexture() == s.fForegroundAccess.getTexture() && |
| + fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture() && |
| + fK1 == s.fK1 && |
| + fK2 == s.fK2 && |
| + fK3 == s.fK3 && |
| + fK4 == s.fK4; |
| +} |
| + |
| +const GrBackendEffectFactory& GrArithmeticEffect::getFactory() const { |
| + return GrTBackendEffectFactory<GrArithmeticEffect>::getInstance(); |
| +} |
| + |
| +void GrArithmeticEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { |
| + // TODO: optimize this |
| + *validFlags = 0; |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +GrGLArithmeticEffect::GrGLArithmeticEffect(const GrBackendEffectFactory& factory, |
| + const GrDrawEffect& drawEffect) |
| + : INHERITED(factory) |
| + , fForegroundEffectMatrix(kCoordsType) |
| + , fBackgroundEffectMatrix(kCoordsType) { |
| +} |
| + |
| +GrGLArithmeticEffect::~GrGLArithmeticEffect() { |
| +} |
| + |
| +void GrGLArithmeticEffect::emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect&, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TextureSamplerArray& samplers) { |
| + const char* fgCoords; |
| + const char* bgCoords; |
| + GrSLType fgCoordsType = fForegroundEffectMatrix.emitCode(builder, key, &fgCoords, NULL, "FG"); |
| + GrSLType bgCoordsType = fBackgroundEffectMatrix.emitCode(builder, key, &bgCoords, NULL, "BG"); |
| + |
| + fK1Uni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, |
|
bsalomon
2013/05/28 14:21:20
Just wondering if it'd make sense to use a vec4 fo
Stephen White
2013/05/28 15:08:34
Yeah, I thought of that. Just thought it might mak
|
| + kFloat_GrSLType, "k1"); |
| + const char* k1Uni = builder->getUniformCStr(fK1Uni); |
| + |
| + fK2Uni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, |
| + kFloat_GrSLType, "k2"); |
| + const char* k2Uni = builder->getUniformCStr(fK2Uni); |
| + |
| + fK3Uni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, |
| + kFloat_GrSLType, "k3"); |
| + const char* k3Uni = builder->getUniformCStr(fK3Uni); |
| + |
| + fK4Uni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, |
| + kFloat_GrSLType, "k4"); |
| + const char* k4Uni = builder->getUniformCStr(fK4Uni); |
| + |
| + builder->fsCodeAppend("\t\tvec4 fgColor = "); |
| + builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, |
| + samplers[0], |
| + fgCoords, |
| + fgCoordsType); |
| + builder->fsCodeAppend(";\n"); |
| + |
| + builder->fsCodeAppend("\t\tvec4 bgColor = "); |
| + builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, |
| + samplers[1], |
| + bgCoords, |
| + bgCoordsType); |
| + builder->fsCodeAppend(";\n"); |
| + builder->fsCodeAppend("\t\tfgColor.rgb = clamp(fgColor.rgb / fgColor.a, 0.0, 1.0);\n"); |
|
bsalomon
2013/05/28 14:21:20
What does clamp do if the first param is NaN?
Stephen White
2013/05/28 15:08:34
It returns zero, as far as I can tell (empirically
|
| + builder->fsCodeAppend("\t\tbgColor.rgb = clamp(bgColor.rgb / bgColor.a, 0.0, 1.0);\n"); |
| + |
| + builder->fsCodeAppendf("\t\t%s = %s * bgColor * fgColor + %s * bgColor + %s * fgColor + %s;\n", outputColor, k1Uni, k2Uni, k3Uni, k4Uni); |
| + builder->fsCodeAppendf("\t\t%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor); |
| + builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor); |
| +} |
| + |
| +void GrGLArithmeticEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) { |
| + const GrArithmeticEffect& arith = drawEffect.castEffect<GrArithmeticEffect>(); |
| + GrTexture* fgTex = arith.texture(0); |
| + GrTexture* bgTex = arith.texture(1); |
| + fForegroundEffectMatrix.setData(uman, |
| + GrEffect::MakeDivByTextureWHMatrix(fgTex), |
| + drawEffect, |
| + fgTex); |
| + fBackgroundEffectMatrix.setData(uman, |
| + GrEffect::MakeDivByTextureWHMatrix(bgTex), |
| + drawEffect, |
| + bgTex); |
| + |
| + uman.set1f(fK1Uni, arith.k1()); |
| + uman.set1f(fK2Uni, arith.k2()); |
| + uman.set1f(fK3Uni, arith.k3()); |
| + uman.set1f(fK4Uni, arith.k4()); |
| +} |
| + |
| +GrGLEffect::EffectKey GrGLArithmeticEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { |
| + const GrArithmeticEffect& arith = drawEffect.castEffect<GrArithmeticEffect>(); |
| + |
| + GrTexture* fgTex = arith.texture(0); |
| + GrTexture* bgTex = arith.texture(1); |
| + |
| + EffectKey fgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(fgTex), |
| + drawEffect, |
| + kCoordsType, |
| + fgTex); |
| + |
| + EffectKey bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex), |
| + drawEffect, |
| + kCoordsType, |
| + bgTex); |
| + bgKey <<= GrGLEffectMatrix::kKeyBits; |
| + return bgKey | fgKey; |
| +} |
| +#endif |