Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2012 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkBlendImageFilter.h"
      9 #include "SkCanvas.h"
     10 #include "SkColorPriv.h"
     11 #include "SkFlattenableBuffers.h"
     12 #if SK_SUPPORT_GPU
     13 #include "GrContext.h"
     14 #include "gl/GrGLEffect.h"
     15 #include "gl/GrGLEffectMatrix.h"
     16 #include "GrTBackendEffectFactory.h"
     17 #include "SkImageFilterUtils.h"
     18 #endif
     19 
     20 namespace {
     21 
     22 SkXfermode::Mode modeToXfermode(SkBlendImageFilter::Mode mode)
     23 {
     24     switch (mode) {
     25       case SkBlendImageFilter::kNormal_Mode:
     26         return SkXfermode::kSrcOver_Mode;
     27       case SkBlendImageFilter::kMultiply_Mode:
     28         return SkXfermode::kModulate_Mode;
     29       case SkBlendImageFilter::kScreen_Mode:
     30         return SkXfermode::kScreen_Mode;
     31       case SkBlendImageFilter::kDarken_Mode:
     32         return SkXfermode::kDarken_Mode;
     33       case SkBlendImageFilter::kLighten_Mode:
     34         return SkXfermode::kLighten_Mode;
     35     }
     36     SkASSERT(0);
     37     return SkXfermode::kSrcOver_Mode;
     38 }
     39 
     40 SkPMColor multiply_proc(SkPMColor src, SkPMColor dst) {
     41     int omsa = 255 - SkGetPackedA32(src);
     42     int sr = SkGetPackedR32(src), sg = SkGetPackedG32(src), sb = SkGetPackedB32(src);
     43     int omda = 255 - SkGetPackedA32(dst);
     44     int dr = SkGetPackedR32(dst), dg = SkGetPackedG32(dst), db = SkGetPackedB32(dst);
     45     int a = 255 - SkMulDiv255Round(omsa, omda);
     46     int r = SkMulDiv255Round(omsa, dr) + SkMulDiv255Round(omda, sr) + SkMulDiv255Round(sr, dr);
     47     int g = SkMulDiv255Round(omsa, dg) + SkMulDiv255Round(omda, sg) + SkMulDiv255Round(sg, dg);
     48     int b = SkMulDiv255Round(omsa, db) + SkMulDiv255Round(omda, sb) + SkMulDiv255Round(sb, db);
     49     return SkPackARGB32(a, r, g, b);
     50 }
     51 
     52 };
     53 
     54 ///////////////////////////////////////////////////////////////////////////////
     55 
     56 SkBlendImageFilter::SkBlendImageFilter(SkBlendImageFilter::Mode mode, SkImageFilter* background, SkImageFilter* foreground)
     57   : INHERITED(background, foreground), fMode(mode)
     58 {
     59 }
     60 
     61 SkBlendImageFilter::~SkBlendImageFilter() {
     62 }
     63 
     64 SkBlendImageFilter::SkBlendImageFilter(SkFlattenableReadBuffer& buffer)
     65   : INHERITED(buffer)
     66 {
     67     fMode = (SkBlendImageFilter::Mode) buffer.readInt();
     68 }
     69 
     70 void SkBlendImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     71     this->INHERITED::flatten(buffer);
     72     buffer.writeInt((int) fMode);
     73 }
     74 
     75 bool SkBlendImageFilter::onFilterImage(Proxy* proxy,
     76                                        const SkBitmap& src,
     77                                        const SkMatrix& ctm,
     78                                        SkBitmap* dst,
     79                                        SkIPoint* offset) {
     80     SkBitmap background, foreground = src;
     81     SkImageFilter* backgroundInput = getBackgroundInput();
     82     SkImageFilter* foregroundInput = getForegroundInput();
     83     SkASSERT(NULL != backgroundInput);
     84     if (!backgroundInput->filterImage(proxy, src, ctm, &background, offset)) {
     85         return false;
     86     }
     87     if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) {
     88         return false;
     89     }
     90     SkAutoLockPixels alp_foreground(foreground), alp_background(background);
     91     if (!foreground.getPixels() || !background.getPixels()) {
     92         return false;
     93     }
     94     dst->setConfig(background.config(), background.width(), background.height());
     95     dst->allocPixels();
     96     SkCanvas canvas(*dst);
     97     SkPaint paint;
     98     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     99     canvas.drawBitmap(background, 0, 0, &paint);
    100     // FEBlend's multiply mode is (1 - Sa) * Da + (1 - Da) * Sc + Sc * Dc
    101     // Skia's is just Sc * Dc.  So we use a custom proc to implement FEBlend's
    102     // version.
    103     if (fMode == SkBlendImageFilter::kMultiply_Mode) {
    104         paint.setXfermode(new SkProcXfermode(multiply_proc))->unref();
    105     } else {
    106         paint.setXfermodeMode(modeToXfermode(fMode));
    107     }
    108     canvas.drawBitmap(foreground, 0, 0, &paint);
    109     return true;
    110 }
    111 
    112 ///////////////////////////////////////////////////////////////////////////////
    113 
    114 #if SK_SUPPORT_GPU
    115 class GrGLBlendEffect : public GrGLEffect {
    116 public:
    117     GrGLBlendEffect(const GrBackendEffectFactory& factory,
    118                     const GrEffectRef& effect);
    119     virtual ~GrGLBlendEffect();
    120 
    121     virtual void emitCode(GrGLShaderBuilder*,
    122                           const GrEffectStage&,
    123                           EffectKey,
    124                           const char* vertexCoords,
    125                           const char* outputColor,
    126                           const char* inputColor,
    127                           const TextureSamplerArray&) SK_OVERRIDE;
    128 
    129     static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
    130 
    131     virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
    132 
    133 private:
    134     SkBlendImageFilter::Mode    fMode;
    135     GrGLEffectMatrix            fForegroundEffectMatrix;
    136     GrGLEffectMatrix            fBackgroundEffectMatrix;
    137 
    138     typedef GrGLEffect INHERITED;
    139 };
    140 
    141 ///////////////////////////////////////////////////////////////////////////////
    142 
    143 class GrBlendEffect : public GrEffect {
    144 public:
    145     static GrEffectRef* Create(SkBlendImageFilter::Mode mode,
    146                                GrTexture* foreground,
    147                                GrTexture* background) {
    148         AutoEffectUnref effect(SkNEW_ARGS(GrBlendEffect, (mode, foreground, background)));
    149         return CreateEffectRef(effect);
    150     }
    151 
    152     virtual ~GrBlendEffect();
    153 
    154     const GrBackendEffectFactory& getFactory() const;
    155     SkBlendImageFilter::Mode mode() const { return fMode; }
    156 
    157     typedef GrGLBlendEffect GLEffect;
    158     static const char* Name() { return "Blend"; }
    159 
    160     void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
    161 
    162 private:
    163     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
    164 
    165     GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, GrTexture* background);
    166     GrTextureAccess             fForegroundAccess;
    167     GrTextureAccess             fBackgroundAccess;
    168     SkBlendImageFilter::Mode    fMode;
    169 
    170     typedef GrEffect INHERITED;
    171 };
    172 
    173 bool SkBlendImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
    174     SkBitmap backgroundBM;
    175     if (!SkImageFilterUtils::GetInputResultGPU(getBackgroundInput(), proxy, src, &backgroundBM)) {
    176         return false;
    177     }
    178     GrTexture* background = (GrTexture*) backgroundBM.getTexture();
    179     SkBitmap foregroundBM;
    180     if (!SkImageFilterUtils::GetInputResultGPU(getForegroundInput(), proxy, src, &foregroundBM)) {
    181         return false;
    182     }
    183     GrTexture* foreground = (GrTexture*) foregroundBM.getTexture();
    184     GrContext* context = foreground->getContext();
    185 
    186     GrTextureDesc desc;
    187     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    188     desc.fWidth = src.width();
    189     desc.fHeight = src.height();
    190     desc.fConfig = kSkia8888_GrPixelConfig;
    191 
    192     GrAutoScratchTexture ast(context, desc);
    193     SkAutoTUnref<GrTexture> dst(ast.detach());
    194 
    195     GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
    196 
    197     GrPaint paint;
    198     paint.colorStage(0)->setEffect(
    199         GrBlendEffect::Create(fMode, foreground, background))->unref();
    200     SkRect srcRect;
    201     src.getBounds(&srcRect);
    202     context->drawRect(paint, srcRect);
    203     return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
    204 }
    205 
    206 ///////////////////////////////////////////////////////////////////////////////
    207 
    208 GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode,
    209                              GrTexture* foreground,
    210                              GrTexture* background)
    211     : fForegroundAccess(foreground)
    212     , fBackgroundAccess(background)
    213     , fMode(mode) {
    214     this->addTextureAccess(&fForegroundAccess);
    215     this->addTextureAccess(&fBackgroundAccess);
    216 }
    217 
    218 GrBlendEffect::~GrBlendEffect() {
    219 }
    220 
    221 bool GrBlendEffect::onIsEqual(const GrEffect& sBase) const {
    222     const GrBlendEffect& s = CastEffect<GrBlendEffect>(sBase);
    223     return fForegroundAccess.getTexture() == s.fForegroundAccess.getTexture() &&
    224            fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture() &&
    225            fMode == s.fMode;
    226 }
    227 
    228 const GrBackendEffectFactory& GrBlendEffect::getFactory() const {
    229     return GrTBackendEffectFactory<GrBlendEffect>::getInstance();
    230 }
    231 
    232 void GrBlendEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
    233     // The output alpha is always 1 - (1 - FGa) * (1 - BGa). So if either FGa or BGa is known to
    234     // be one then the output alpha is one. (This effect ignores its input. We should have a way to
    235     // communicate this.)
    236     if (GrPixelConfigIsOpaque(fForegroundAccess.getTexture()->config()) ||
    237         GrPixelConfigIsOpaque(fBackgroundAccess.getTexture()->config())) {
    238         *validFlags = kA_ValidComponentFlag;
    239         *color = GrColorPackRGBA(0, 0, 0, 0xff);
    240     } else {
    241         *validFlags = 0;
    242     }
    243 }
    244 
    245 ///////////////////////////////////////////////////////////////////////////////
    246 
    247 GrGLBlendEffect::GrGLBlendEffect(const GrBackendEffectFactory& factory, const GrEffectRef& effect)
    248     : INHERITED(factory),
    249       fMode(CastEffect<GrBlendEffect>(effect).mode()) {
    250 }
    251 
    252 GrGLBlendEffect::~GrGLBlendEffect() {
    253 }
    254 
    255 void GrGLBlendEffect::emitCode(GrGLShaderBuilder* builder,
    256                                const GrEffectStage&,
    257                                EffectKey key,
    258                                const char* vertexCoords,
    259                                const char* outputColor,
    260                                const char* inputColor,
    261                                const TextureSamplerArray& samplers) {
    262     const char* fgCoords;
    263     const char* bgCoords;
    264     GrSLType fgCoordsType =  fForegroundEffectMatrix.emitCode(
    265         builder, key, vertexCoords, &fgCoords, NULL, "FG");
    266     GrSLType bgCoordsType =  fBackgroundEffectMatrix.emitCode(
    267         builder, key, vertexCoords, &bgCoords, NULL, "BG");
    268 
    269     SkString* code = &builder->fFSCode;
    270     const char* bgColor = "bgColor";
    271     const char* fgColor = "fgColor";
    272 
    273     code->appendf("\t\tvec4 %s = ", fgColor);
    274     builder->appendTextureLookup(code, samplers[0], fgCoords, fgCoordsType);
    275     code->append(";\n");
    276 
    277     code->appendf("\t\tvec4 %s = ", bgColor);
    278     builder->appendTextureLookup(code, samplers[1], bgCoords, bgCoordsType);
    279     code->append(";\n");
    280 
    281     code->appendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.b);\n", outputColor, bgColor, fgColor);
    282     switch (fMode) {
    283       case SkBlendImageFilter::kNormal_Mode:
    284         code->appendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + %s.rgb;\n", outputColor, fgColor, bgColor, fgColor);
    285         break;
    286       case SkBlendImageFilter::kMultiply_Mode:
    287         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);
    288         break;
    289       case SkBlendImageFilter::kScreen_Mode:
    290         code->appendf("\t\t%s.rgb = %s.rgb + %s.rgb - %s.rgb * %s.rgb;\n", outputColor, bgColor, fgColor, fgColor, bgColor);
    291         break;
    292       case SkBlendImageFilter::kDarken_Mode:
    293         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);
    294         break;
    295       case SkBlendImageFilter::kLighten_Mode:
    296         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);
    297         break;
    298     }
    299 }
    300 
    301 void GrGLBlendEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
    302     const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage);
    303     GrTexture* fgTex = blend.texture(0);
    304     GrTexture* bgTex = blend.texture(1);
    305     fForegroundEffectMatrix.setData(uman,
    306                                     GrEffect::MakeDivByTextureWHMatrix(fgTex),
    307                                     stage.getCoordChangeMatrix(),
    308                                     fgTex);
    309     fBackgroundEffectMatrix.setData(uman,
    310                                     GrEffect::MakeDivByTextureWHMatrix(bgTex),
    311                                     stage.getCoordChangeMatrix(),
    312                                     bgTex);
    313 
    314 }
    315 
    316 GrGLEffect::EffectKey GrGLBlendEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
    317     const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage);
    318 
    319     GrTexture* fgTex = blend.texture(0);
    320     GrTexture* bgTex = blend.texture(1);
    321 
    322     EffectKey fgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(fgTex),
    323                                                stage.getCoordChangeMatrix(),
    324                                                fgTex);
    325 
    326     EffectKey bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
    327                                                stage.getCoordChangeMatrix(),
    328                                                bgTex);
    329     bgKey <<= GrGLEffectMatrix::kKeyBits;
    330     EffectKey modeKey = blend.mode() << (2 * GrGLEffectMatrix::kKeyBits);
    331 
    332     return  modeKey | bgKey | fgKey;
    333 }
    334 #endif
    335