使用StretchBlt函数实现绘制透明位图

Badgirl

发布日期: 2018-12-29 15:13:23 浏览量: 1696
评分:
star star star star star star star star star_border star_border
*转载请注明来自write-bug.com

背景

在自己本科的时候,喜欢用 VC6.0 写一些小游戏来锻炼自己的编程水平。那些小游戏的游戏画面绘制都是使用Windows提供的GDI(图形设备接口)函数实现的,并没有调用现成的游戏引擎。所以,使得自己对GDI函数比较熟悉,能用它开发各式各样的小游戏。

其中,那些小游戏都会用到的一个关键技术,便是透明位图的绘制。现在,我把之前自己开发小游戏时,绘制透明位图的方法总结一下,分别为大家介绍一下三种实现方法:一是使用 BitBlt 函数实现;二是使用 StretchBlt 函数实现;三是使用 TransparentBlt 函数实现。

这三种方法,分别为其写一遍文档进行介绍,本文介绍的是 StretchBlt 函数实现透明位图的绘制。

函数介绍

StretchBlt函数

从源矩形中复制一个位图到目标矩形,必要时按目标设备设置的模式进行图像的拉伸或压缩。

函数声明

  1. BOOL StretchBlt(
  2. HDC hdcDest,
  3. int nXOriginDest,
  4. int nYOriginDest,
  5. int nWidthDest,
  6. int nHeightDest,
  7. HDC hdcSrc,
  8. int nXOriginSrc,
  9. int nYOriginSrc,
  10. int nWidthSrc,
  11. int nHeightSrc,
  12. DWORD dwRop
  13. );

参数

  • 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),二进制为:

  1. 00000000 00000000 00000000

白色的RGB值为(255, 255, 255),二进制为:

  1. 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中

编码实现

绘制背景位图

  1. BOOL PaintBmp(HWND hWnd)
  2. {
  3. // 获取窗口的客户区域的显示设备上下文环境的句柄
  4. HDC hDC = ::GetDC(hWnd);
  5. // 创建一个与hDC兼容的内存设备上下文环境
  6. HDC hBuf = ::CreateCompatibleDC(hDC);
  7. // 加载位图, 获取位图句柄
  8. HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\\bg.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  9. // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄
  10. HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp);
  11. // 绘图
  12. ::BitBlt(hDC, 0, 0, 764, 397, hBuf, 0, 0, SRCCOPY);
  13. // 还原位图对象
  14. ::SelectObject(hBuf, hOldBmp);
  15. // 释放位图
  16. ::DeleteObject(hBmp);
  17. // 释放兼容的内存设备上下文环境
  18. ::DeleteDC(hBuf);
  19. // 释放设备上下文环境
  20. ::ReleaseDC(hWnd, hDC);
  21. return TRUE;
  22. }

绘制透明位图

  1. BOOL PaintTransparentBmp(HWND hWnd)
  2. {
  3. // 获取窗口的客户区域的显示设备上下文环境的句柄
  4. HDC hDC = ::GetDC(hWnd);
  5. // 创建一个与hDC兼容的内存设备上下文环境
  6. HDC hBuf = ::CreateCompatibleDC(hDC);
  7. // 加载位图, 获取位图句柄
  8. HBITMAP hBmp1 = (HBITMAP)::LoadImage(NULL, "image\\1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  9. HBITMAP hBmp2 = (HBITMAP)::LoadImage(NULL, "image\\2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  10. // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄
  11. HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp1);
  12. // "与"操作绘制
  13. ::StretchBlt(hDC, 260, 170, 200, 200, hBuf, 0, 0, 101, 121, SRCAND);
  14. // 选择位图句柄到hBuf中
  15. ::SelectObject(hBuf, hBmp2);
  16. // "或"操作绘制
  17. ::StretchBlt(hDC, 260, 170, 200, 200, hBuf, 0, 0, 101, 121, SRCPAINT);
  18. // 还原位图对象
  19. ::SelectObject(hBuf, hOldBmp);
  20. // 释放位图
  21. ::DeleteObject(hBmp1);
  22. ::DeleteObject(hBmp2);
  23. // 释放兼容的内存设备上下文环境
  24. ::DeleteDC(hBuf);
  25. // 释放设备上下文环境
  26. ::ReleaseDC(hWnd, hDC);
  27. return TRUE;
  28. }

程序测试

调用上述封装好的函数,直接测试。人物位图成功背景透明绘制到背景图上,而且人物图片成功放大绘制了。

总结

要注意理解上面的位图透明实现的原理,这算是透明处理最本质的原理了,其他函数可能也会实现同样的效果,但是原理还是本文讲的这个。如果仍然不了解绘图过程,那么建议多动手练几遍,加深印象。

其中,StretchBlt 函数包含了 BitBlt 的所有功能,它自己还额外具备图片的伸缩功能,使用起来比 BitBlt 更灵活。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download TransparentPaint_StretchBlt_Test.7z ( 429.28kb, 10次下载 )

发送私信

大概没去过的地方都叫远方

37
文章数
15
评论数
eject