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 |