# 使用AlphaBlend函数实现位图半透明绘制
# 背景
自己使用VC和VS写过很多小游戏,而且不是用现成的游戏引擎,纯粹是使用GDI函数来进行绘图。所以,积累了一些绘图的经验。
那么,对于位图半透明的绘制,在小游戏中使用也比较多。例如烟雾、光等之类的绘制。在没有了解 AlphaBlend 函数之前,绘制半透明位图都是获取两张图片的RGB数据,然后按指定透明度计算出混合后的RGB的值,再显示出来。这样,需要自己计算的过程,算是麻烦。而现在,AlphaBlend 函数直接封装了这步操作,提供了方便使用的接口。
现在,我们就来介绍使用 AlphaBlend 函数绘制半透明位图,写成文档,分享给大家。
# 函数介绍
## AlphaBlend 函数
> 该函数用来显示具有指定透明度的图像。
>
> 函数声明
>
> ```c++
> AlphaBlend(
> HDC hdcDest,
> int nXOriginDest,
> int nYOriginDest,
> int nWidthDest,
> int hHeightDest,
> HDC hdcSrc,
> int nXOriginSrc,
> int nYOriginSrc,
> int nWidthSrc,
> int nHeightSrc,
> BLENDFUNCTION blendFunction
> );
> ```
>
> 参数
>
> - hdcDest:指向目标设备环境的句柄。
>
> - nXOriginDest:指定目标矩形区域左上角的X轴坐标,按逻辑单位。
>
> - nYOriginDest:指定目标矩形区域左上角的Y轴坐标,按逻辑单位。
>
> - nWidthDest:指定目标矩形区域的宽度,按逻辑单位。
>
> - hHeightdest:指向目标矩形区域的高度,按逻辑单位。
>
> - hdcSrc:指向源设备环境的句柄。
>
> - nXOriginSrc:指定源矩形区域左上角的X轴坐标,按逻辑单位。
>
> - nYOriginSrc:指定源矩形区域左上角的Y轴坐标,按逻辑单位。
>
> - nWidthSrc:指定源矩形区域的宽度,按逻辑单位。
>
> - nHeightSrc:指定源矩形区域的高度,按逻辑单位。
>
> - blendFunction:指定用于源位图和目标位图使用的alpha混合功能,用于整个源位图的全局alpha值和格式信息。最后一个参数blendFunction是一个BLENDFUNCTION结构。
>
> BLENDFUNCTION结构介绍如下:
>
> typedef struct _BLENDFUNCTION {
> BYTE BlendOp;
> BYTE BlendFlags;
> BYTE SourceConstantAlpha;
> BYTE AlphaFormat;
> }BLENDFUNCTION, *PBLENDFUNCTION, *LPBLENDFUNCTION;
>
> BlendOp: 这个参数必须也只能为AC_SRC_OVER(0x00),意思就是把源图片覆盖到目标之上。
>
> BlendFlags: 必须为0 。
>
> SourceConstantAlpha: 简写为SCA,指定源图片的透明度,这个值是会和源图片的Alpha通道值合并计算的;即设置透明度,0为完全透明,255为完全不透明 。
>
> AlphaFormat: 可以填两种,一种是0x00,一种是AC_SRC_ALPHA (0x01);0表示常量alpha值,AC_SRC_ALPHA表示每个像素有各自的alpha通道。
>
> 返回值
>
> - 如果函数执行成功,那么返回值为TRUE;如果函数执行失败,那么返回值为FALSE。
# 透明度绘制介绍
下面,我们举例说明,什么是透明度绘制。现在,有两张位图,一张是背景图:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/3279974564dd1307b4e02e6a4527311b.writebug)
另一张是其它图片:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/f9940a02ea20d2613f26af9565a55088.writebug)
那么,半透明绘制,就是要将两张图片绘制在一起,实现下面的效果:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/48ab570f9150bd1a0bdfa944cfe03b88.writebug)
# 实现原理
半透明位图绘制的功能实现,主要是靠 AlphaBlend 函数完成的。AlphaBlend 函数的最后一个参数 BLENDFUNCTION 结构指定用于源位图和目标位图使用的alpha混合功能。
其中,BLENDFUNCTION 结构的 SourceConstantAlpha 变量,指定了源图片的透明度,这个值是会和源图片的 Alpha 通道值合并计算的;即设置透明度,0为完全透明,255为完全不透明 。也就是说,只要我们控制这个变量的值,就控制了位图绘制的透明度。
# 编码实现
## 导入库文件
```c++
# include <WinGDI.h>
# pragma comment(lib, "Msimg32.lib")
```
## 绘制背景位图
```c++
BOOL PaintBmp(HWND hWnd)
{
// 获取窗口的客户区域的显示设备上下文环境的句柄
HDC hDC = ::GetDC(hWnd);
// 创建一个与hDC兼容的内存设备上下文环境
HDC hBuf = ::CreateCompatibleDC(hDC);
// 加载位图, 获取位图句柄
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\\bg.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
// 选择位图句柄到hBuf中, 并获取返回的原来位图句柄
HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp);
// 绘图
::BitBlt(hDC, 0, 0, 764, 397, hBuf, 0, 0, SRCCOPY);
// 还原位图对象
::SelectObject(hBuf, hOldBmp);
// 释放位图
::DeleteObject(hBmp);
// 释放兼容的内存设备上下文环境
::DeleteDC(hBuf);
// 释放设备上下文环境
::ReleaseDC(hWnd, hDC);
return TRUE;
}
```
## 指定透明度绘制
```c++
BOOL PaintAlphaBlendBmp(HWND hWnd)
{
// 获取窗口的客户区域的显示设备上下文环境的句柄
HDC hDC = ::GetDC(hWnd);
// 创建一个与hDC兼容的内存设备上下文环境
HDC hBuf = ::CreateCompatibleDC(hDC);
// 加载位图, 获取位图句柄
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\\1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
// 选择位图句柄到hBuf中, 并获取返回的原来位图句柄
HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp);
// 设置透明度参数
BLENDFUNCTION ftn = { 0 };
ftn.BlendOp = 0;
ftn.BlendFlags = 0;
ftn.SourceConstantAlpha = 200; // 透明度指定
ftn.AlphaFormat = 0;
// 指定透明度绘制
::AlphaBlend(hDC, 0, 0, 650, 350, hBuf, 0, 0, 650, 350, ftn);
// 还原位图对象
::SelectObject(hBuf, hOldBmp);
// 释放位图
::DeleteObject(hBmp);
// 释放兼容的内存设备上下文环境
::DeleteDC(hBuf);
// 释放设备上下文环境
::ReleaseDC(hWnd, hDC);
return TRUE;
}
```
# 程序测试
调用上述封装好的函数进行测试,两幅位图成功按照指定的透明度混合显示:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/ae7af380809986f44317e3ebade693ec.writebug)
# 总结
这个小程序重点是要理解 AlphaBlend 函数的使用方式,可以自己更改透明度的值尝试几次,加深理解。这个函数同样支持对位图伸缩绘制,使用起来比较灵活。