首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > .NET >

AlphaBlend到底能不能进行逐像素Alpha混合解决方案

2012-04-07 
AlphaBlend到底能不能进行逐像素Alpha混合各位大侠,上午好!本人最近在研究alpha混合问题时遇到了一个难题

AlphaBlend到底能不能进行逐像素Alpha混合
各位大侠,上午好!
  本人最近在研究alpha混合问题时遇到了一个难题难以解决,向各位求助。
我想要达到的效果:假设有两幅图像A和B,两幅图都有各自的图案(假设A中的图案是圆形的)以及背景色。我想将两幅图进行alpha混合,呈现半透明的效果,我只想将A图像中的圆形图案alpha混合到B图像中的一小块区域中去,而A图像的背景不混合,也就是说我只取了A图像的图案部分混合到B图像中。B 图像的其他区域仍是原来的样子不变。

本人尝试过的解决办法:
1、使用windows GDI的函数AlphaBlend
AlphaBlend函数的前几个参数不再描述,它的最后一个参数是个结构体,如下:
BLENDFUNCTION{ BYTE BlendOp;
  BYTE BlendFlags;
  BYTE SourceConstantAlpha;
  BYTE AlphaFormat;}
BlendOp必须为AC_SRC_OVER,BlendFlags必须为0;后两个参数的设置需要分两种情况。
情况一: AlphaFormat 设为 0 ,此时进行的alpha混合只需要设定一个alpha值,也就是第3个参数SourceConstantAlpha,这个参数为0时表示完全透明,255时表示完全不透明,0~255之间的值则为半透明效果。
但是这样做的一个问题就是设置的源图像和目标图像的混合区域都必须是矩形区域,也就是说A图像的背景也会和B图像进行Alpha混合,这是我要避免的,因为这样,A的背景色会影响B中图像的显示效果。并且混合之后B图像中能看到A图像的矩形轮廓,这个不能满足要求。
也就是说这种整个矩形区域共用一个alpha值混合的方法是行不通的。

情况二:AlphaFormat 设置为 AC_SRC_ALPHA, SourceConstantAlpha设置为255,msdn中说这时候,在混合时将采用per-pixel alpha(逐像素alpha)混合的方法进行混合,也就是说两幅图像的每个像素会根据源图像各个像素中alpha通道的值
取和目标进行混合(我的图像都是32bit的,具有alpha通道)。
  这样,我对A图像各个像素的alpha值进行了初始设置,对A图像中的图案区域(假设为圆形图案)的像素点,我将它的alpha值设为127(半透明),实现代码如下
  lpSurface[ x + y * lPitch ] &= 0x00FFFFFF;
  lpSurface[ x + y * lPitch ] |= 0x7F000000;
而对图案之外的背景(圆外的点)中的各个像素点的alpha值设置为0。
根据msdn中描述的在这种逐像素混合的计算公式:
  Src.Red = Src.Red * SourceConstantAlpha / 255.0;
  Src.Green = Src.Green * SourceConstantAlpha / 255.0;
  Src.Blue = Src.Blue * SourceConstantAlpha / 255.0;
  Src.Alpha = Src.Alpha * SourceConstantAlpha / 255.0;
  Dst.Red = Src.Red + (1 - Src.Alpha) * Dst.Red
  Dst.Green = Src.Green + (1 - Src.Alpha) * Dst.Green
  Dst.Blue = Src.Blue + (1 - Src.Alpha) * Dst.Blue
  Dst.Alpha = Src.Alpha + (1 - Src.Alpha) * Dst.Alpha
调用AlphaBlend将A图像混合到B上的过程,我设想是这样,A是源,B是目标,根据源的alpha值计算(已设定好)
  对于A图像中的图案区域(圆内),因为图案区域(圆内)的所有像素点的alpha值为 127,根据公式,混合到目标区域上时,颜色应该能才生半透明的效果。而对于背景色区域(圆外),因为我将背景色的所有像素点的alpha值设为0了,这样根据公式,背景色相当于是完全透明的了,不影响目标的颜色。
  最终效果按照这个步骤理论上应该是可以达到只混合A图像中的图案,而不混合A的背景色。

但是实际的效果却和我期望的不一样,根据我上面的代码,我在 ATI 显卡(型号没看出来,不会很老)上的试验,描画出来的图像很怪异,覆盖上上面的A图像中的图案区域(圆内)出现不明的条文并遮挡住了B图像,这些条文上颜色单一,并不是A的图案,但是在没有条文的地方B都显现出来了,没条文的地方又似乎A是完全透明了,很奇怪。

2、另外尝试了BitBlt 等通过 与或 或者mask遮挡等方法
  这些都不能实现真正的透明效果,这些方法只是简单的对两幅图像的颜色合并了,要么就是去掉某幅图像背景色,然后将它覆盖在另外一副图像上,所以都不会有透明效果。想要透明效果必须操作alpha。


对msdn中AlphaBlend函数及BLENDFUNCTION结构体的的描述看了很多很多遍,感觉使用方法没出错,到底问题出在哪,各位csdn的大侠们帮忙看看,出点主意,谢谢了。
函数的参数SourceConstantAlpha为0~255间的整数,Src.Alpha也应该是这么个范围,为什么公式中可以有
Dst.Blue = Src.Blue + (1 - Src.Alpha) * Dst.Blue,这其中的怎么可以有 1- Src.Alpha了,不明白了。

如果我的描述不清,你也可以参考之前别人写的一篇帖子。

他的描述如下:
楼主DelphiBoy2003(努力提升功力中)2005-09-05 15:35:21 在 Delphi / 语言基础/算法/系统设计 提问

我现在有三副图片,其中一副为背景图,另外两幅图是一个需要透明的图片和一个掩码图片,这两幅图片进行透明贴图操作,并且需要把得到的透明贴图使用 AlphaBlend函数贴到背景图片上,我举例吧,我的一副图片是一个圆形,并且另外一副图片是这个圆形的掩码图片,背景图是一个风景图片,我现在想的是把这个圆形的贴图使用Alpha运算贴到背景图上,我写的代码无论如何只能实现没有透明前的圆形(也就是包含了边界的矩形区域的图片)Alpha的贴到背景图片上,并不能只贴一个圆形到背景图片上!

3 楼Idle_(阿呆)回复于 2005-09-06 12:43:16 得分 5

去看一下 Graphics.TransparentStretchBlt的实现你就知道怎么做了Top
4 楼Linux2001(闭关开发中)回复于 2005-09-09 16:02:41 得分 40

我发一个Demo给你,把邮箱告诉我

参考连接
http://topic.csdn.net/t/20050905/15/4250543.html

这是之前有人发过的一个类似的帖子,可是这里面的4楼我找不到联系方式了,有人能通过昵称之类的找到他的空间吗/

[解决办法]
AlphaFormat 设置为 AC_SRC_ALPHA时,AlphaBlend函数中的图像1必须是32位的,而且必须是PARGB格式,因为32位有ARGB和PARGB之分,PARGB就是pre-multiply ARGB的意思,比如某个像素对应的ARGB颜色分量分别为:100(A),45,56,120,则对应的PARGB数据为 100(A),45*100/255,56*100/255,120*100/125,请确认你的原始数据是否是这种格式。
[解决办法]

Delphi(Pascal) code
// 简化版。要求32位图像,且源和目标尺寸一致。// 参数:目标扫描线首址,源扫描线首址,图像宽度,图像高度procedure AlphaBlendSysthesis(dstScan0, srcScan0: Pointer; Width, Height: Integer);asm    imul      ecx, Height    pxor      mm7, mm7  @PixelLoop:    movd      mm0, [edx]    movd      mm1, [eax]    punpcklbw mm0, mm7    punpcklbw mm1, mm7    movq      mm2, mm0    punpckhwd mm2, mm2    punpckhdq mm2, mm2    psubw     mm0, mm1    pmullw    mm0, mm2    psllw     mm1, 8    paddw     mm0, mm1    psrlw     mm0, 8    packuswb  mm0, mm0    movd      [eax], mm0    add       eax, 4    add       edx, 4    loop      @PixelLoop    emmsend; 


[解决办法]

Delphi(Pascal) code
function IntToByte(i: Integer): Byte;begin  if i > 255 then    Result := 255  else if i < 0 then    Result := 0  else    Result := i;end;procedure BmpAlphaBlend(var dBmp: TBitMap; sBmp: TBitmap; Pos: TPoint; Alpha: integer; TranColor: TColor = -1);type  tagRGBTRIPLE = packed record    rgbtBlue: Byte;    rgbtGreen: Byte;    rgbtRed: Byte;  end;  TRGBTriple = tagRGBTRIPLE;  PRGBTripleArray = ^TRGBTripleArray;  TRGBTripleArray = array[0..32767] of TRGBTriple;  function GetSLColor(pRGB: TRGBTriple): TColor;  begin    Result := RGB(pRGB.rgbtRed, pRGB.rgbtGreen, pRGB.rgbtBlue);  end;var  p0, p1: PRGBTripleArray;  r, g, b, p, x, y: Integer;begin  sBmp.PixelFormat := pf24bit;  dBmp.PixelFormat := pf24bit;  if TranColor = -1 then    TranColor := sBmp.Canvas.Pixels[0, 0];  for y := 0 to sBmp.Height - 1 do    if (y + Pos.y >= 0) and (y + Pos.Y < dBmp.Height) then    begin      p0 := dBmp.ScanLine[y + Pos.y];      p1 := sBmp.ScanLine[y];      for x := 0 to sBmp.Width - 1 do        if (x + pos.X >= 0) and (x + Pos.X < dBmp.Width) then          if GetSLCOlor(p1[x]) <> TranColor then          begin            p0[x + pos.X].rgbtRed := IntToByte((p0[x + pos.X].rgbtRed * (100 - Alpha) +              p1[x].rgbtRed * Alpha) div 100);            p0[x + pos.X].rgbtGreen := IntToByte((p0[x + pos.X].rgbtGreen * (100 - Alpha) +              p1[x].rgbtGreen * Alpha) div 100);            p0[x + pos.X].rgbtBlue := IntToByte((p0[x + pos.X].rgbtBlue * (100 - Alpha) +              p1[x].rgbtBlue * Alpha) div 100);          end;    end;end;procedure TForm1.Button1Click(Sender: TObject);var  brBmp, srcBmp: TBitmap;begin//  brBmp := TBitmap.Create;  brBmp.LoadFromFile('c:\a.bmp');  brBmp.PixelFormat := pf24bit;  srcBmp := TBitmap.Create;  srcBmp.LoadFromFile('c:\b.bmp');  srcBmp.PixelFormat := pf24bit;  BmpAlphaBlend(    brBmp, //背景图(大图,也是结果图)    srcBmp, //需要合成的小图    Point(60, 60), //画小图的坐标    100, //半透明度(0--100,100表示不透明)    -1//需要透明的颜色(用于不规则图形,-1就表示以0,0位置的颜色为透明色)    );  canvas.Draw(0, 0, brBmp);  brBmp.Free;  srcBmp.Free;end; 

热点排行