# 使用StretchBlt函数实现绘制透明位图
# 背景
在自己本科的时候,喜欢用 VC6.0 写一些小游戏来锻炼自己的编程水平。那些小游戏的游戏画面绘制都是使用Windows提供的GDI(图形设备接口)函数实现的,并没有调用现成的游戏引擎。所以,使得自己对GDI函数比较熟悉,能用它开发各式各样的小游戏。
其中,那些小游戏都会用到的一个关键技术,便是透明位图的绘制。现在,我把之前自己开发小游戏时,绘制透明位图的方法总结一下,分别为大家介绍一下三种实现方法:一是使用 BitBlt 函数实现;二是使用 StretchBlt 函数实现;三是使用 TransparentBlt 函数实现。
这三种方法,分别为其写一遍文档进行介绍,本文介绍的是 StretchBlt 函数实现透明位图的绘制。
# 函数介绍
## StretchBlt函数
> 从源矩形中复制一个位图到目标矩形,必要时按目标设备设置的模式进行图像的拉伸或压缩。
>
> 函数声明
>
> ```c++
> 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位图的绘制》这篇有讲过,当然这只是其中一种绘图方法而已,并不是唯一的。
那我就先来介绍下,什么是透明位图的绘制。用下面的图片直观表示吧:
如果我们有两张图片,一张是背景图:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/575219d585bd7b2cbfeb235bf42a9064.writebug)
一张是人物图:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/9c727af850f3571ec39837ea12809780.writebug)
现在,我想把人物绘制到背景图上,那么按照通常的方法就是,把人物的图片直接在背景图上显示就好,效果如下所示:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/5fd248a53f88220202c71d0aa2bd1f34.writebug)
大家可能会看到有些问题了吧,我们其实想要的是下面这张图片的效果:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/be36b4d7c11521abbf2adeba68b5a135.writebug)
没错,所谓的透明位图的绘制就像上面图片表示出来的那样。就是我们只绘制图片特定的地方,而且形状都是不规则的,其他的不进行绘制。就对上上面的例子来说,就是把人物抠出来绘制,背景的黑色不绘制,即背景像似透明一样,所以这便是位图的透明绘制。
# 透明原理
我们知道,黑色的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)做“或”操作,都会变为白色
现在,我们先来看下下面这张图片的分析:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/295e8f2722bb8a420eb10b390a3620fa.writebug)
上图左边为“前景图”,右边黑白图称为“屏蔽图”。
如果,我们要将人物从图片中抠出来,那么就需要准备类似上面所示的两张图片,一张是前景图,一张是屏蔽图。其中,前景图背景为黑色;屏蔽图背景为白色,人物部分为黑色。
根据上面我们总结的两个结论,透明原理解析如下:
首先,我们把人物图片中的“屏蔽图”里的每一个RGB值和背景图里的绘制部分的每个RGB值进行“与”操作,这时图片效果为:“屏蔽图”的黑色部分在背景图上还是保持黑色,白色部分保持原来的背景图的颜色不变。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/421492746163f516d722d46f261373c8.writebug)
然后,我们继续在上述的基础上,把人物图片中的“前景图”里的每一个RGB值和上述结果的背景图里的绘制部分的每个RGB值进行“或”操作,那么就会得到下面图片的效果:“前景图”的黑色部分还是保持背景图原来的颜色,人物部分在背景图上,就成功绘制上去了。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/b57d4c9e165a6d82553a8b841f