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