| /* |
| * Copyright 2012 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 "SkBlendImageFilter.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 |
| |
| namespace { |
| |
| SkXfermode::Mode modeToXfermode(SkBlendImageFilter::Mode mode) |
| { |
| switch (mode) { |
| case SkBlendImageFilter::kNormal_Mode: |
| return SkXfermode::kSrcOver_Mode; |
| case SkBlendImageFilter::kMultiply_Mode: |
| return SkXfermode::kModulate_Mode; |
| case SkBlendImageFilter::kScreen_Mode: |
| return SkXfermode::kScreen_Mode; |
| case SkBlendImageFilter::kDarken_Mode: |
| return SkXfermode::kDarken_Mode; |
| case SkBlendImageFilter::kLighten_Mode: |
| return SkXfermode::kLighten_Mode; |
| } |
| SkASSERT(0); |
| return SkXfermode::kSrcOver_Mode; |
| } |
| |
| SkPMColor multiply_proc(SkPMColor src, SkPMColor dst) { |
| int omsa = 255 - SkGetPackedA32(src); |
| int sr = SkGetPackedR32(src), sg = SkGetPackedG32(src), sb = SkGetPackedB32(src); |
| int omda = 255 - SkGetPackedA32(dst); |
| int dr = SkGetPackedR32(dst), dg = SkGetPackedG32(dst), db = SkGetPackedB32(dst); |
| int a = 255 - SkMulDiv255Round(omsa, omda); |
| int r = SkMulDiv255Round(omsa, dr) + SkMulDiv255Round(omda, sr) + SkMulDiv255Round(sr, dr); |
| int g = SkMulDiv255Round(omsa, dg) + SkMulDiv255Round(omda, sg) + SkMulDiv255Round(sg, dg); |
| int b = SkMulDiv255Round(omsa, db) + SkMulDiv255Round(omda, sb) + SkMulDiv255Round(sb, db); |
| return SkPackARGB32(a, r, g, b); |
| } |
| |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| SkBlendImageFilter::SkBlendImageFilter(SkBlendImageFilter::Mode mode, SkImageFilter* background, SkImageFilter* foreground) |
| : INHERITED(background, foreground), fMode(mode) |
| { |
| } |
| |
| SkBlendImageFilter::~SkBlendImageFilter() { |
| } |
| |
| SkBlendImageFilter::SkBlendImageFilter(SkFlattenableReadBuffer& buffer) |
| : INHERITED(buffer) |
| { |
| fMode = (SkBlendImageFilter::Mode) buffer.readInt(); |
| } |
| |
| void SkBlendImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { |
| this->INHERITED::flatten(buffer); |
| buffer.writeInt((int) fMode); |
| } |
| |
| bool SkBlendImageFilter::onFilterImage(Proxy* proxy, |
| const SkBitmap& src, |
| const SkMatrix& ctm, |
| SkBitmap* dst, |
| SkIPoint* offset) { |
| SkBitmap background, foreground = src; |
| SkImageFilter* backgroundInput = getBackgroundInput(); |
| SkImageFilter* foregroundInput = getForegroundInput(); |
| SkASSERT(NULL != backgroundInput); |
| if (!backgroundInput->filterImage(proxy, src, ctm, &background, offset)) { |
| return false; |
| } |
| if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) { |
| return false; |
| } |
| SkAutoLockPixels alp_foreground(foreground), alp_background(background); |
| if (!foreground.getPixels() || !background.getPixels()) { |
| return false; |
| } |
| dst->setConfig(background.config(), background.width(), background.height()); |
| dst->allocPixels(); |
| SkCanvas canvas(*dst); |
| SkPaint paint; |
| paint.setXfermodeMode(SkXfermode::kSrc_Mode); |
| canvas.drawBitmap(background, 0, 0, &paint); |
| // FEBlend's multiply mode is (1 - Sa) * Da + (1 - Da) * Sc + Sc * Dc |
| // Skia's is just Sc * Dc. So we use a custom proc to implement FEBlend's |
| // version. |
| if (fMode == SkBlendImageFilter::kMultiply_Mode) { |
| paint.setXfermode(new SkProcXfermode(multiply_proc))->unref(); |
| } else { |
| paint.setXfermodeMode(modeToXfermode(fMode)); |
| } |
| canvas.drawBitmap(foreground, 0, 0, &paint); |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #if SK_SUPPORT_GPU |
| class GrGLBlendEffect : public GrGLEffect { |
| public: |
| GrGLBlendEffect(const GrBackendEffectFactory& factory, |
| const GrEffectRef& effect); |
| virtual ~GrGLBlendEffect(); |
| |
| virtual void emitCode(GrGLShaderBuilder*, |
| const GrEffectStage&, |
| EffectKey, |
| const char* vertexCoords, |
| const char* outputColor, |
| const char* inputColor, |
| const TextureSamplerArray&) SK_OVERRIDE; |
| |
| static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&); |
| |
| virtual void setData(const GrGLUniformManager&, const GrEffectStage&); |
| |
| private: |
| SkBlendImageFilter::Mode fMode; |
| GrGLEffectMatrix fForegroundEffectMatrix; |
| GrGLEffectMatrix fBackgroundEffectMatrix; |
| |
| typedef GrGLEffect INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| class GrBlendEffect : public GrEffect { |
| public: |
| static GrEffectRef* Create(SkBlendImageFilter::Mode mode, |
| GrTexture* foreground, |
| GrTexture* background) { |
| AutoEffectUnref effect(SkNEW_ARGS(GrBlendEffect, (mode, foreground, background))); |
| return CreateEffectRef(effect); |
| } |
| |
| virtual ~GrBlendEffect(); |
| |
| const GrBackendEffectFactory& getFactory() const; |
| SkBlendImageFilter::Mode mode() const { return fMode; } |
| |
| typedef GrGLBlendEffect GLEffect; |
| static const char* Name() { return "Blend"; } |
| |
| void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; |
| |
| private: |
| virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; |
| |
| GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, GrTexture* background); |
| GrTextureAccess fForegroundAccess; |
| GrTextureAccess fBackgroundAccess; |
| SkBlendImageFilter::Mode fMode; |
| |
| typedef GrEffect INHERITED; |
| }; |
| |
| bool SkBlendImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) { |
| SkBitmap backgroundBM; |
| if (!SkImageFilterUtils::GetInputResultGPU(getBackgroundInput(), proxy, src, &backgroundBM)) { |
| return false; |
| } |
| GrTexture* background = (GrTexture*) backgroundBM.getTexture(); |
| SkBitmap foregroundBM; |
| if (!SkImageFilterUtils::GetInputResultGPU(getForegroundInput(), 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( |
| GrBlendEffect::Create(fMode, foreground, background))->unref(); |
| SkRect srcRect; |
| src.getBounds(&srcRect); |
| context->drawRect(paint, srcRect); |
| return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode, |
| GrTexture* foreground, |
| GrTexture* background) |
| : fForegroundAccess(foreground) |
| , fBackgroundAccess(background) |
| , fMode(mode) { |
| this->addTextureAccess(&fForegroundAccess); |
| this->addTextureAccess(&fBackgroundAccess); |
| } |
| |
| GrBlendEffect::~GrBlendEffect() { |
| } |
| |
| bool GrBlendEffect::onIsEqual(const GrEffect& sBase) const { |
| const GrBlendEffect& s = CastEffect<GrBlendEffect>(sBase); |
| return fForegroundAccess.getTexture() == s.fForegroundAccess.getTexture() && |
| fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture() && |
| fMode == s.fMode; |
| } |
| |
| const GrBackendEffectFactory& GrBlendEffect::getFactory() const { |
| 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 GrEffectRef& effect) |
| : INHERITED(factory), |
| fMode(CastEffect<GrBlendEffect>(effect).mode()) { |
| } |
| |
| GrGLBlendEffect::~GrGLBlendEffect() { |
| } |
| |
| void GrGLBlendEffect::emitCode(GrGLShaderBuilder* builder, |
| const GrEffectStage&, |
| EffectKey key, |
| const char* vertexCoords, |
| const char* outputColor, |
| const char* inputColor, |
| const TextureSamplerArray& samplers) { |
| const char* fgCoords; |
| const char* bgCoords; |
| GrSLType fgCoordsType = fForegroundEffectMatrix.emitCode( |
| builder, key, vertexCoords, &fgCoords, NULL, "FG"); |
| GrSLType bgCoordsType = fBackgroundEffectMatrix.emitCode( |
| builder, key, vertexCoords, &bgCoords, NULL, "BG"); |
| |
| SkString* code = &builder->fFSCode; |
| const char* bgColor = "bgColor"; |
| const char* fgColor = "fgColor"; |
| |
| code->appendf("\t\tvec4 %s = ", fgColor); |
| builder->appendTextureLookup(code, samplers[0], fgCoords, fgCoordsType); |
| code->append(";\n"); |
| |
| code->appendf("\t\tvec4 %s = ", bgColor); |
| builder->appendTextureLookup(code, samplers[1], bgCoords, bgCoordsType); |
| code->append(";\n"); |
| |
| code->appendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.b);\n", outputColor, bgColor, fgColor); |
| switch (fMode) { |
| case SkBlendImageFilter::kNormal_Mode: |
| code->appendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + %s.rgb;\n", outputColor, fgColor, bgColor, fgColor); |
| break; |
| case SkBlendImageFilter::kMultiply_Mode: |
| code->appendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb + %s.rgb * %s.rgb;\n", outputColor, fgColor, bgColor, bgColor, fgColor, fgColor, bgColor); |
| break; |
| case SkBlendImageFilter::kScreen_Mode: |
| code->appendf("\t\t%s.rgb = %s.rgb + %s.rgb - %s.rgb * %s.rgb;\n", outputColor, bgColor, fgColor, fgColor, bgColor); |
| break; |
| case SkBlendImageFilter::kDarken_Mode: |
| code->appendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, (1.0 - %s.a) * %s.rgb + %s.rgb);\n", outputColor, fgColor, bgColor, fgColor, bgColor, fgColor, bgColor); |
| break; |
| case SkBlendImageFilter::kLighten_Mode: |
| code->appendf("\t\t%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, (1.0 - %s.a) * %s.rgb + %s.rgb);\n", outputColor, fgColor, bgColor, fgColor, bgColor, fgColor, bgColor); |
| break; |
| } |
| } |
| |
| void GrGLBlendEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) { |
| const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage); |
| GrTexture* fgTex = blend.texture(0); |
| GrTexture* bgTex = blend.texture(1); |
| fForegroundEffectMatrix.setData(uman, |
| GrEffect::MakeDivByTextureWHMatrix(fgTex), |
| stage.getCoordChangeMatrix(), |
| fgTex); |
| fBackgroundEffectMatrix.setData(uman, |
| GrEffect::MakeDivByTextureWHMatrix(bgTex), |
| stage.getCoordChangeMatrix(), |
| bgTex); |
| |
| } |
| |
| GrGLEffect::EffectKey GrGLBlendEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) { |
| const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage); |
| |
| GrTexture* fgTex = blend.texture(0); |
| GrTexture* bgTex = blend.texture(1); |
| |
| EffectKey fgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(fgTex), |
| stage.getCoordChangeMatrix(), |
| fgTex); |
| |
| EffectKey bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex), |
| stage.getCoordChangeMatrix(), |
| bgTex); |
| bgKey <<= GrGLEffectMatrix::kKeyBits; |
| EffectKey modeKey = blend.mode() << (2 * GrGLEffectMatrix::kKeyBits); |
| |
| return modeKey | bgKey | fgKey; |
| } |
| #endif |