解决方案
不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:
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
从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。
在VC中,系统提供了AlphaBlend函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:
BOOL AlphaBlend(
HDC hdcDest,
// 目标设备环境句柄
int nXOriginDest,
// 目标坐标x
int nYOriginDest,
// 目标坐标y
int nWidthDest,
// 目标宽度
int nHeightDest,
//目标高度
HDC hdcSrc,
//源设备环境句柄
int nXOriginSrc,
// 源坐标x
int nYOriginSrc,
// 源坐标y
int nWidthSrc,
//源宽度
int nHeightSrc,
//源高度
BLENDFUNCTION blendFunction
// 合成方式具体数据结构
);
BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:
typedef struct _BLENDFUNCTION {
BYTE BlendOp;
BYTE BlendFlags;//必须为零
BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示
BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
}BLENDFUNCTION, PBLENDFUNCTION, LPBLENDFUNCTION;
由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。
编程实现
了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。
首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:
class CWipeImageDlg : public CDialog
{
// Construction
public:
BLENDFUNCTION m_bf;
CBitmap cross,lantern;
BITMAP bmp;
int bmpWidth,bmpHeight;
CDC dcForCross,dcForLantern;
CDC dc;
BOOL bShowLantern;
………(系统自动生成部分)
};
接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
BOOL CWipeImageDlg::OnInitDialog()
{
………(系统自动生成部分)
// TODO: Add extra initialization here
//初始化全局成员变量
this->bShowLantern=TRUE;
m_bf.BlendOp = AC_SRC_OVER;
m_bf.BlendFlags = 0;
m_bf.SourceConstantAlpha =10;
m_bf.AlphaFormat = 0;
//为节约篇幅,以下代码中略去对操作不成功的处理代码
if(!cross.LoadBitmap(IDB_CROSS))
{
AfxMessageBox("装载位图出错!");
return FALSE;
}
cross.GetBitmap(&&bmp);
lantern.LoadBitmap(IDB_LANTERN);
cross.GetBitmap(&&bmp);
//获得位图的大小信息
bmpWidth=bmp.bmWidth;
bmpHeight=bmp.bmHeight;
dc=this->GetDC();
dcForCross.CreateCompatibleDC(dc);
dcForLantern.CreateCompatibleDC(dc);
//将位图装入设备环境句柄
dcForCross.SelectObject(&&cross);
dcForLantern.SelectObject(&&lantern);
//打开计时器
SetTimer(1000,50,NULL);
return TRUE;
}
void CWipeImageDlg::OnTimer(UINT nIDEvent)
{
//图片透明度每次递增5点
m_bf.SourceConstantAlpha+=5;
//当第一幅图片完全可见之后,显示另一张图片
if(m_bf.SourceConstantAlpha>=200)
{
m_bf.SourceConstantAlpha=10;
//将bShowLantern做为显示标志,确认应该显示哪一张图片
bShowLantern=!bShowLantern;
}
if(bShowLantern)
{
//按透明度递增的方式显示“吊灯”图片 AlphaBlend(dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);
}
else
{
//按透明度递增的方式显示“背景”图片
AlphaBlend(dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);
}
CDialog::OnTimer(nIDEvent);
}
编译说明
由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
延伸阅读
文章来源于领测软件测试网 https://www.ltesting.net/