WIN32汇编语言教程:第07章 图形操作 · 7.4 块传送操作(1)

除了7.2小节中的绘图函数,块传送函数也是重要的图形操作函数。块传送指把源位置中的数据块按照指定的方式传送到目的位置中。把内存中的位图复制到窗口客户区以及在不同的DC中复制图形数据都要用到块传送操作,块传送完成的工作就相当于图形之间的拷贝工作。块传送函数有PatBlt,BitBlt,MaskBlt,PlgBlt,TransparentBlt和StretchBlt等。

7.4.1 块传送方式

与7.2.4小节中介绍的绘图函数的ROP操作类似,块传送函数也是可以用ROP码来定义的传送方式,但块传送函数的ROP码定义不同于7.2.4小节中的ROP码,因为这里涉及的操作对象更多。

块传送的ROP码是一个32位的整数,对应的操作涉及3种原始数据:源像素、目标像素和画刷,块传送操作的结果是目标像素的数据被3种原始数据的计算结果替换掉。

并不是每一种ROP码都要用到全部3种原始数据,有的甚至连1种也用不到,例如全黑或者全白的ROP码。块传送函数使用的ROP码总共有256种,它们是3种原始数据进行不同位操作(取反、与、或和异或)的组合,但有些ROP码对应的操作结果实在是太难想像了,比如ROP码0e20746对应的操作是((目标像素 xor 画刷) and 源像素) xor 目标像素),凭这个算式的确比较难以想像最后得到的位图是什么样子的!在实际使用中很多算法组合也并不是那么有用,所以Windows只对15种最常用的ROP码定义了预定义的助记代码,

如表7.6所示,对于这些ROP码,在程序中可以直接使用助记码,对于表中没有列出的ROP码,可以直接使用16进制数值。

表7.6 块传送函数中使用的ROP码

ROP码16进制数值新像素点算法
BLACKNESS00000042h全部为0
DSTINVERT00550009hnot 目标像素
MERGECOPY00c000cah画刷 and 源像素
MERGEPAINT00bb0226h(not 源像素)or 目标像素
NOTSRCCOPY00330008hnot 源像素
NOTSRCERASE001100a6hnot(源像素 or 目标像素)
PATCOPY00f00021h画刷
PATINVERT005a0049h画刷 xor 目标像素
PATPAINT00fb0a09h画刷 or (not 源像素)or 目标像素
SRCAND008800c6h源像素 and 目标像素
SRCCOPY00cc0020h源像素
SRCERASE00440328h源像素 and(not 目标像素)
SRCINVERT00660046h源像素 xor 目标像素
SRCPAINT00ee0086h源像素 or 目标像素
WHITENESS00ff0062h全部为1

7.4.2 块传送函数

1. PatBlt函数

PatBlt函数完成的是“图案块传送”的功能,即“PatternBlockTransfer”。使用的方法是:

invoke PatBlt,hDC,xDest,yDest,dwWidth,dwHeight,dwROP

这个函数将当前画刷的图案拷贝到hDC中以(xDest,yDest)为左上角坐标,dwWidth为宽度,dwHeigth为高度的区域中,传送的方式由dwROP中的ROP码指定。PatBlt函数的功能和矩形填充函数FillRect与InvertRect等是很像的,但它包含了它们的全部功能,如ROP码指定DSTINVERT,那么PatBlt的功能就相当于InvertRect函数;ROP码指定为PATCOPY的时候,PatBlt的功能相当于FillRect函数。

在BmpClock.asm的_CreateBackGround子程序中,当建立背景图片的时候,就是用PATCOPY方式的PatBlt函数用资源中的背景图片填充整个时钟背景的。

在所有的ROP码中,可以用在PatBlt函数中的只有一部分,所有算法中包含源像素的ROP码在PatBlt函数中都不能使用,因为PatBlt函数只涉及当前画刷和目标像素,并没有一个“源像素”,所以对于这个函数来说,表7.6中的ROP码中只有BLACKNESS,WHITENESS,DSTINVERT,PATINVERT和PATCOPY是可用的。

2. BitBlt函数

PatBlt函数能完成的工作BitBlt函数都能完成,BitBlt是“数据块传送”的意思,即“Bit BlockTransfer”。BitBlt函数的用法是:

invoke   BitBlt,hDcDest,xDest,yDest,dwWidth,dwHeigt,hDcSrc,xSrc,ySrc,dwROP

这个函数将源hDcSrc中以(xSrc,ySrc)为左上角的一个矩形区域传送到目标hDcDest中以(xDest,yDest)为左上角的地方去,矩形的宽度为dwWidth,高度为dwHeight,当然目标DC中的最后结果是由dwROP中的ROP码定义的源、目标和画刷三者数据的组合。

灵活使用ROP码可以实现很多的功能,比如在一个背景图片上叠加一个非矩形的位图,游戏程序中人物在背景上面的移动就是这样的一个例子。BmpClock程序中也实现了类似的功能——读者可以注意到,程序可以自由更换背景和边框,但是边框是中空的,它相当于以一个不规则的图形叠加在背景上面,图7.10示范了实现的方法。

分析一下BmpClock.asm中的_CreateBackGround子程序就可以发现,程序用到了资源中的Back1.bmp,Mask1.bmp和Circle1.bmp这3个图片(在图7.10中以A,B,C来表示),子程序将3个图片装入内存后,创建了3个DC来存取它们,对应的DC句柄分别放在@hBmpBack,@hBmpMask和@hBmpCircle中。

好了!现在的任务是将图片C中需要的部分(非黑色部分)透明叠放在以图片A形成的背景上,得到时钟背景图片D。继续做准备工作:为图片D建立一个未初始化位图和内存DC,DC句柄存放在hDcBack中。


图7.10 在背景上叠加不规则图形的方法

如图7.10中的步骤1所示,首先,程序用CreatePatternBrush建立以图片A为图案的画刷,用PatBlt函数以这个画刷填充整个图片:

   invoke CreatePatternBrush,@hBmpBack
   push      eax
   invoke SelectObject,hDcBack,eax
   invoke PatBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,PATCOPY
   pop    eax
   invoke DeleteObject,eax

现在如果直接将图片C拷贝上去,虽然需要的部分是拷贝上去了,但是图片C中的黑色部分也会覆盖全部的背景像素,为了让黑色部分的背景像素保持不变,应该使用or操作,因为黑色的颜色值为0,任何数据和0进行or操作将保持不变,查看ROP码可以发现,SRCPAINT使用的是or操作,所以可以使用SRCPAINT操作码进行BitBlt操作。

但还有个问题:图片C中的非黑色部分如果也用or操作绘画到背景上,那么经过和背景像素的or操作后就不是原来的颜色值了,为了让这部分不变,解决的办法是预先将背景中对应的部分先绘制成黑色,这样对应图片C中的黑色部分将保持背景颜色,而非黑色部分将使用图片C中的像素。遮掩图片B就是这样用的,它是一个黑白两色的图片,黑色部分是图片C中要在背景上“镂空”的部分,在步骤2中,程序使用下列语句将图片B用SRCAND操作码绘制到背景上:

invoke BitBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,@hDcMask,0,0,SRCAND

查表7.6可以发现,SRCAND进行源像素和目标像素的and操作,任何数和1进行and将保持不变,和0进行and将变成0,这样背景中对应图片B中的白色部分将保持不变,对应图片B中的黑色部分将被“镂空”。

接下来就是最后的步骤3了:

invoke BitBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,@hDcCircle,0,0,SRCPAINT

上页:第07章 图形操作 · 7.3 创建和使用位图(5) 下页:第07章 图形操作 · 7.4 块传送操作(2)

第07章 图形操作

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号