转载:https://www.write-bug.com/article/1953.html
StretchBlt函数
从源矩形中复制一个位图到目标矩形,必要时按目标设备设置的模式进行图像的拉伸或压缩。
函数声明
BOOL StretchBlt( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop );
参数
-
hdcDest:指向目标设备环境的句柄。
-
nXOriginDest:指定目标矩形左上角的X轴坐标,按逻辑单位表示坐标。
-
nYOriginDest:指定目标矩形左上角的Y轴坐标,按逻辑单位表示坐标。
-
nWidthDest:指定目标矩形的宽度,按逻辑单位表示宽度。
-
nHeightDest:指定目标矩形的高度,按逻辑单位表示高度。
-
hdcSrc:指向源设备环境的句柄。
-
nXOriginSrc:指向源矩形区域左上角的X轴坐标,按逻辑单位表示坐标。
-
nYOriginSrc:指向源矩形区域左上角的Y轴坐标,按逻辑单位表示坐标。
-
nWidthSrc:指定源矩形的宽度,按逻辑单位表示宽度。
-
nHeightSrc:指定源矩形的高度,按逻辑单位表示高度。
-
dwRop:指定要进行的光栅操作。光栅操作码定义了系统如何在输出操作中组合颜色,这些操作包括刷子、源位图和目标位图等对象。
下面列出了一些常见的光栅操作代码:
VALUE | MEANING |
---|---|
BLACKNESS | 表示使用与物理调色板的索引0相关的色彩来填充目标矩形区域,对缺省的物理调色板而言,该颜色为黑色 |
DSTINVERT | 表示使目标矩形区域颜色取反 |
MERGECOPY | 表示使用布尔型的AND(与)操作符将源矩形区域的颜色与特定模式组合一起 |
MERGEPAINT | 通过使用布尔型的OR(或)操作符将反向的源矩形区域的颜色与目标矩形区域的颜色合并 |
NOTSRCCOPY | 将源矩形区域颜色取反,于拷贝到目标矩形区域 |
NOTSRCERASE | 使用布尔类型的OR(或)操作符组合源和目标矩形区域的颜色值,然后将合成的颜色取反 |
PATCOPY | 将特定的模式拷贝到目标位图上 |
PATPAINT | 通过使用布尔OR(或)操作符将源矩形区域取反后的颜色值与特定模式的颜色合并。然后使用OR(或)操作符将该操作的结果与目标矩形区域内的颜色合并 |
PATINVERT | 通过使用XOR(异或)操作符将源和目标矩形区域内的颜色合并 |
SRCAND | 通过使用AND(与)操作符来将源和目标矩形区域内的颜色合并 |
SRCCOPY | 将源矩形区域直接拷贝到目标矩形区域 |
SRCERASE | 通过使用AND(与)操作符将目标矩形区域颜色取反后与源矩形区域的颜色值合并 |
SRCINVERT | 通过使用布尔型的XOR(异或)操作符将源和目标矩形区域的颜色合并 |
SRCPAINT | 通过使用布尔型的OR(或)操作符将源和目标矩形区域的颜色合并 |
WHITENESS | 使用与物理调色板中索引1有关的颜色填充目标矩形区域。对于缺省物理调色板来说,这个颜色就是白色 |
返回值
- 如果函数成功,那么返回值非零;如果函数失败,则返回值为零。
什么是位图透明处理?
至于如何使用 BitBlt 函数绘制位图,我在《BMP位图的绘制》这篇有讲过,当然这只是其中一种绘图方法而已,并不是唯一的。
那我就先来介绍下,什么是透明位图的绘制。用下面的图片直观表示吧:
如果我们有两张图片,一张是背景图:
一张是人物图:
现在,我想把人物绘制到背景图上,那么按照通常的方法就是,把人物的图片直接在背景图上显示就好,效果如下所示:
大家可能会看到有些问题了吧,我们其实想要的是下面这张图片的效果:
没错,所谓的透明位图的绘制就像上面图片表示出来的那样。就是我们只绘制图片特定的地方,而且形状都是不规则的,其他的不进行绘制。就对上上面的例子来说,就是把人物抠出来绘制,背景的黑色不绘制,即背景像似透明一样,所以这便是位图的透明绘制。
透明原理
我们知道,黑色的RGB值为(0, 0, 0),二进制为:
00000000 00000000 00000000
白色的RGB值为(255, 255, 255),二进制为:
11111111 11111111 11111111
所以,请注意理解下面两个结论:
-
任何RGB值的颜色,只要和黑色RGB(0, 0, 0)做“与”操作,都会变为黑色;任何RGB值的颜色,只要和白色RGB(255, 255, 255)做“与”操作,还是原来的颜色
-
任何RGB值的颜色,只要和黑色RGB(0, 0, 0)做“或”操作,还是原来的颜色;任何RGB值的颜色,只要和白色RGB(255, 255, 255)做“或”操作,都会变为白色
现在,我们先来看下下面这张图片的分析:
上图左边为“前景图”,右边黑白图称为“屏蔽图”。
如果,我们要将人物从图片中抠出来,那么就需要准备类似上面所示的两张图片,一张是前景图,一张是屏蔽图。其中,前景图背景为黑色;屏蔽图背景为白色,人物部分为黑色。
根据上面我们总结的两个结论,透明原理解析如下:
首先,我们把人物图片中的“屏蔽图”里的每一个RGB值和背景图里的绘制部分的每个RGB值进行“与”操作,这时图片效果为:“屏蔽图”的黑色部分在背景图上还是保持黑色,白色部分保持原来的背景图的颜色不变。
然后,我们继续在上述的基础上,把人物图片中的“前景图”里的每一个RGB值和上述结果的背景图里的绘制部分的每个RGB值进行“或”操作,那么就会得到下面图片的效果:“前景图”的黑色部分还是保持背景图原来的颜色,人物部分在背景图上,就成功绘制上去了。
这样,便实现了人物的抠图了,即背景透明处理。
编程实现原理
对于,StretchBlt 函数来说,它包含了 BitBlt 函数的所有功能,同时它还能对绘制的位图进行伸缩绘制。同样,它的最后一个参数指定了源矩形区域的颜色数据,如何与目标矩形区域的颜色数据组合以完成最后的颜色。我们只要将“屏蔽图”先做“与”操作,然后再使用“前景图”做“或”操作就好。
主要实现步骤如下所示:
-
首先将整个背景图贴到目的地DC中
-
屏蔽图与背景图做“AND”(Raster值为SRCAND)运算,贴到目的地DC中
-
前景图与背景图做“OR”(Raster值SRCPAINT)运算,贴到目的地DC中
编码实现
绘制背景位图
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; }
绘制透明位图
BOOL PaintTransparentBmp(HWND hWnd) { // 获取窗口的客户区域的显示设备上下文环境的句柄 HDC hDC = ::GetDC(hWnd); // 创建一个与hDC兼容的内存设备上下文环境 HDC hBuf = ::CreateCompatibleDC(hDC); // 加载位图, 获取位图句柄 HBITMAP hBmp1 = (HBITMAP)::LoadImage(NULL, "image\1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); HBITMAP hBmp2 = (HBITMAP)::LoadImage(NULL, "image\2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄 HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp1); // "与"操作绘制 ::StretchBlt(hDC, 260, 170, 200, 200, hBuf, 0, 0, 101, 121, SRCAND); // 选择位图句柄到hBuf中 ::SelectObject(hBuf, hBmp2); // "或"操作绘制 ::StretchBlt(hDC, 260, 170, 200, 200, hBuf, 0, 0, 101, 121, SRCPAINT); // 还原位图对象 ::SelectObject(hBuf, hOldBmp); // 释放位图 ::DeleteObject(hBmp1); ::DeleteObject(hBmp2); // 释放兼容的内存设备上下文环境 ::DeleteDC(hBuf); // 释放设备上下文环境 ::ReleaseDC(hWnd, hDC); return TRUE; }
程序测试
调用上述封装好的函数,直接测试。人物位图成功背景透明绘制到背景图上,而且人物图片成功放大绘制了。
总结
要注意理解上面的位图透明实现的原理,这算是透明处理最本质的原理了,其他函数可能也会实现同样的效果,但是原理还是本文讲的这个。如果仍然不了解绘图过程,那么建议多动手练几遍,加深印象。
其中,StretchBlt 函数包含了 BitBlt 的所有功能,它自己还额外具备图片的伸缩功能,使用起来比 BitBlt 更灵活。
参考
参考自《Windows黑客编程技术详解》一书