透明贴图
绘制透明位图是指在绘制位图时只绘制除了指定颜色外的其余部分,而指定的颜色就被称为“透明色”。通过将位图的背景色指定成“透明色”,在绘制时不绘制带“透明色”的这部分背景,仅绘制图像,这样就可以将图像透明地绘制到窗口上。
绘制包含透明色的位图的方法有许多种,比较常用的是屏蔽绘制和非屏蔽绘制,屏蔽绘制指的是事先做一张掩码位图,非屏蔽绘制是动态生成掩码位图。下面将分别详细介绍这两种绘制方法。
在写透明贴图的方法前先来说说透明贴图,所谓的透明贴图并不是真正意义上的透明,而是图像不需要显示的部分显示出的颜色是当前对话框的背景色,也就是说把图像不需要显示的颜色当成背景色,显示的部分作为前景色,透明就是把背景换成对话框背景。
要实现这种透明有两种方法,一种是让背景色成为白色,然后与对话框背景色进行与运算;还有一种是让背景色成为黑色,然后与对话框背景色进行或运算。这样位图的背景色就消失了。
了解了透明的概念,就可以避免透明贴图的一些缺点,如果位图的前景色也存在许多白色,则采用将背景色变成黑色,与对话框背景色进行或运算的方法,这样前景色的白色部分才不会被误当成背景色透明显示;反过来,如果位图前景色存在大量黑色部分是,则采用将背景色变成白色,与对话框背景色进行与运算的方法。当然,如果前景色同时存在大量白色和黑色时,则透明贴图会遇到大麻烦,需要想办法将位图的白色或黑色部分做一些修改后再进行透明贴图。
屏蔽绘制
绘制透明位图的关键就是创建一个“掩码”位图(mask bitmap),这个“掩码”位图是一个单色位图,它是位图中图像的一个单色剪影。
在MFC中,绘图需要使用设备描述表,透明贴图时需要创建两个内存设备描述表,一个是用于存放位图的设备描述表(imgDC),一个是用于存放“掩码”位图的设备描述表(maskDC)。在“掩码”位图设备描述表中制作“掩码”位图的方法是先创建一个单色的bitmap,放入掩码设备描述表(maskDC)中,然后使用拷贝粘贴的方式将存放有位图的设备描述表(imgDC)绘制到掩码设备描述表上,这样,掩码设备描述表显示的位图即是“掩码”位图。
在详细介绍实现过程之前先介绍下所使用的画图函数以及函数参数所代表的功能;
整个绘制过程需要使用到BitBlt()函数,关于这个函数,MSDN上的说明是这样的:
这个函数的功能是把源设备上下文中的一个矩形区域中所有像素的颜色比特信息传输给目标设备上下文。
BOOL BitBlt(int x, int y, int nWidth,int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop);
函数参数如下:
Int x 表示绘制位图目标左上角x坐标;
Int y 表示绘制位图目标左上角y坐标;
Int nWidth 表示绘制位图目标的区域宽度;
Int nHeight 表示绘制位图目标的区域高度;
CDC* pSrcDC 表示存储源位图的设备描述表;
Int xSrc 表示源位图的左上角x坐标;
Int ySrc 表示源位图的左上角y坐标;
DWORD dwRop 表示栅格运算标志;
dwRop的取值与值的描述如下表所示:
值 | 描述 |
BLACKNESS | 用调色板中索引为0的颜色(默认是黑色)填充目标矩形。 |
CAPTUREBLT | 用调色板中索引为0的颜色(默认是黑色)填充目标矩形。 |
DSTINVERT | 将目标矩形反色。 |
MERGECOPY | 将源矩形中的颜色与当前目标设备环境选中的画刷通过逻辑与操作进行混合。 |
MERGEPAINT | 将源矩形的颜色反色后与目标矩形的颜色通过逻辑或操作进行混合。 |
NOMIRRORBITMAP | 防止位图镜像翻转。 |
NOTSRCCOPY | 源矩形反色复制到目标矩形。 |
NOTSRCERASE | 将源矩形与目标矩形的颜色通过逻辑或操作混合后,再反色。 |
PATCOPY | 将目标设备环境选中的画刷复制到目标位图。 |
PATINVERT | 将目标设备环境选中的画刷与目标矩形中的颜色进行逻辑异或操作,复制到目标位图。 |
PATPAINT | 将目标设备环境选中的画刷与源矩形中的颜色的反色进行逻辑或操作,其结果再与目标矩形的颜色进行逻辑或操作。 |
SRCAND | 将源矩形与目标矩形的颜色进行逻辑与操作。 |
SRCCOPY | 直接将源矩形拷贝到目标矩形,最常用的绘制位图属性。 |
SRCERASE | 将目标矩形的反色与源矩形的颜色进行逻辑与操作。 |
SRCINVERT | 将源矩形的颜色与目标矩形进行逻辑异或操作。 |
SRCPAINT | 将源矩形的颜色与目标矩形进行逻辑或操作。 |
WHITENESS | 用调色板中索引为1的颜色(默认是白色)填充目标矩形。 |
原图如下所示,
下面是整个实现过程:
1) 创建一张大小与需要绘制图像相同的位图作为“掩码”位图;
2) 将新创建的“掩码”位图存储至掩码位图的设备描述表中;
3) 把位图设备描述表的背景设置成“透明色”,即不需要显示的颜色;
4) 复制粘贴位图到“掩码”位图的设备描述表中,这个时候“掩码”位图设备描述表中存放的位图与位图设备描述表中的位图一样;
5) 把需要透明绘制的位图与对话框绘图相应区域的背景进行逻辑异或操作绘制到对话框上;
结果如下图所示,
6) 把“掩码”位图与这个时候对话框相应区域的背景进行逻辑与的操作;
结果如下图所示,
这个时候显示出来的对话框背景就是需要绘制透明位图的剪影了;
7) 最后一步重复步骤5的操作,把需要透明绘制的位图与对话框绘图相应区域的背景进行逻辑异或操作绘制到对话框上;
结果如下图所示,
这个时候大功告成,位图已经透明地显示在了对话框中;
8) 当然最后不要忘记了把系统的画笔还给系统,删除使用过的GDIObject,释放非空的指针,最后把新建的设备描述表也删除;
具体代码如下显示,这段代码只是截取了在WM_PAINT消息中对话框客户区画图的部分,位图的加载和删除分别写在OnInitDialog()函数和析构函数中;
CDC imgDC,maskDC; //定义存储位图的位图设备描述表imgDC和存储掩码位图的掩码位图
//设备描述表maskDC
CRect rcWnd, rcPic;
imgDC.CreateCompatibleDC(NULL); //初始化设备描述表
maskDC.CreateCompatibleDC(NULL); //初始化设备描述表
CBitmap bmpMask;
BITMAP bm;
m_bmpSample.GetBitmap(&bm);
GetClientRect(rcWnd);
rcPic.left = (rcWnd.Width() -bm.bmWidth)/2;
rcPic.right = rcPic.left + bm.bmWidth;
rcPic.top = (rcWnd.Height()- bm.bmHeight)/2;
rcPic.bottom = rcPic.top + bm.bmHeight;
bmpMask.CreateBitmap(bm.bmWidth, bm.bmHeight, 1,1, NULL); //初始化掩码位图
CBitmap * pOldBmp = imgDC.SelectObject(&m_bmpSample); //存储位图至位图设备描述表
CBitmap * pOldMaskBmp = maskDC.SelectObject(&bmpMask); //存储掩码位图至掩码位图
//设备描述表
imgDC.SetBkColor(RGB(255, 255, 0)); //设置位图设备描述表的背景色为透明色
maskDC.BitBlt(0, 0, bm.bmWidth, bm.bmHeight,&imgDC, 0, 0, SRCCOPY);
dc.BitBlt(rcPic.left, rcPic.top, bm.bmWidth, bm.bmHeight,&imgDC, 0, 0, SRCINVERT);
dc.BitBlt(rcPic.left, rcPic.top, bm.bmWidth, bm.bmHeight,&maskDC, 0, 0, SRCAND);
dc.BitBlt(rcPic.left, rcPic.top, bm.bmWidth, bm.bmHeight,&imgDC, 0, 0, SRCINVERT);
imgDC.SelectObject(pOldBmp);
maskDC.SelectObject(pOldMaskBmp);
bmpMask.DeleteObject();
pOldBmp = NULL;
pOldMaskBmp = NULL;
DeleteDC(imgDC);
DeleteDC(maskDC);
非屏蔽绘制
说到非屏蔽绘制透明贴图就是动态生成掩码位图,最常用的方法就是TransparentBlt()函数,关于这个函数MSDN上是这样描述的:TransparentBlt()函数从指定源设备上下文中的矩形区域中像素的色彩信息转化为目标设备上下文中。
TransparentBlt()函数在Windows98/Windows2000以上版本运行,系统中需要包含 Msimg32.dll,使用时可以链接 Msimg32.lib。
Windows98下的TransparentBlt()会产生资源泄漏,所以不建议在WIN98下使用该函数。
TransparentBlt()函数原型如下:
BOOL TransparentBlt(
HDC hdcDest, //目标DC
int nXOriginDest, //目标X偏移
int nYOriginDest, //目标Y偏移
int nWidthDest, //目标宽度
int hHeightDest, //目标高度
HDC hdcSrc, //源DC
int nXOriginSrc, //源X起点
int nYOriginSrc, //源Y起点
int nWidthSrc, //源宽度
int nHeightSrc, //源高度
UINT crTransparent //透明色,COLORREF类型
);