WIN32汇编语言教程:第07章 图形操作 · 7.4 块传送操作(2)
程序用SRCPAINT操作码将图片C和已经镂空的背景进行or操作,得到的结果就是将由遮掩图片B指定的图片C中的不规则区域画到了背景上面。
为了简化起见,BmpClock程序中的遮掩图片是预先设计好的,实际使用中也可以通过扫描源位图中的像素位,找出背景颜色并动态生成一个遮掩位图,虽然这样可能对速度有一些影响,但灵活性要高得多。
在游戏程序中,将一个不规则的图形如人或物体等图形叠加到背景上面使用的就是这样的技术。
3. MaskBlt函数
MaskBlt函数允许在一个图片中对不同的部分以不同的ROP码进行操作,它的语法是:
invoke MaskBlt,hDcDest,xDest,yDest,dwWidth,dwHeigt,hDcSrc,xSrc,ySrc,hMaskBmp,xMask,yMask,dwROP
它和BitBlt的不同之处是多了一个遮掩图片句柄hMaskBmp(注意:是位图句柄而不是DC句柄)以及hMaskBmp的开始坐标。
MaskBlt函数同样是将hDcSrc以(xSrc,ySrc)为左上角的矩形区域以指定ROP码操作方式传送到hDcDest中以(xDest,yDest)为左上角的矩形区域中,矩形的宽度和高度由dwWidth和dwHeight指定,函数的特殊之处是可以指定两个ROP码,传送时使用哪个ROP码要参考遮掩图片,参考的位置从遮掩图片的(xMask,yMask)坐标开始。
hMaskBmp指定了一幅黑白位图,如果位图中对应位置的像素为黑(即为0),那么使用背景ROP码,如果对应位置的像素为白(即为1),那么使用前景ROP码。注意:遮掩位图一定要是黑白两色的,如果使用其他颜色深度的位图,那么函数调用将会失败。
读者一定有个问题:参数中只有一个dwROP参数,如何指定两个ROP码?这正是这个函数比较费解的地方,实际上dwROP参数是两个ROP码的组合,组合的算法是:
((背景ROP码 shl 8)& 0ff000000h)or 前景ROP码
灵活使用这个函数可以带来很多的方便,比如在_CreateBackGround子程序中,以“;$$$$$”为注释之间的8句代码完全可以改成下面的4句:
invoke CreatePatternBrush,@hBmpBack invoke SelectObject,hDcBack,eax invoke DeleteObject,eax invoke MaskBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,\ @hDcCircle,0,0,@hBmpMask,0,0,\ ((SRCCOPY shl 8) and 0ff000000h) or PATCOPY
相比之下少了一个PatBlt和一个BitBlt调用。因为程序的要求是遮掩图片中黑色的部分使用边框图片,而白色部分使用背景,所以将背景ROP设置为SRCCOPY就可以将需要的边框部分复制过去,而前景ROP使用PATCOPY就免除了使用PatBlt填充背景的步骤。当然,使用前需要把Mask1.bmp和Mask2.bmp重新保存成黑白色,因为现在这两个位图文件是256色格式的。
如果hMaskBmp不指定(参数写为NULL),那么MasmBlt函数就相当于BitBlt函数,只不过ROP是使用dwROP中指定的前景ROP码。
4. PlgBlt函数
PlgBlt实现平行四边形旋转传送操作(Parallelogram Block Transfer),它复制一幅位图,同时将其转换成一个平行四边形,所以利用它可对位图进行旋转处理。PlgBlt函数的使用语法是:
invoke PlgBlt,hdcDest,lpPoint,\ hdcSrc,xSrc,ySrc,dwWidth,dwHeight,\ hBmpMask,xMask,yMask
这个函数和ROP码无关,但它同样指定了一个单色的遮掩位图,遮掩位图中为1的像素对应的位置会被传送,为0的像素不被传送。(xSrc,ySrc)指定了源DC中需要传送的矩形的左上角,dwWidth和dwHeight指定宽度和高度,这个矩形将被旋转后传送到目的DC中去,旋转后的平行四边形位置由lpPoint指定的POINT结构阵列指出。
lpPoint是一个指针,指向含有3个POINT结构的内存中(这种使用POINT结构数组的方法在PolyLine中已经使用过),其中第一个点对应于一个平行四边形的左上角位置,第二个点代表右上角位置,第三个点代表右下角的位置,不需要第四个点是因为它可以从上面3个点的坐标推导出来。
5. StretchBlt函数
StretchBlt函数实现像素的缩放功能,它的语法是:
invoke StretchBlt,hDcDest,xDest,yDest,dwWidthDest,dwHeightDest,\ hDcSrc,xSrc,ySrc,dwWidthSrc,dwHeightSrc,dwROP
这个函数将源hDcSrc中以(xSrc,ySrc)为左上角,dwWidthSrc和dwHeightSrc为宽度和高度的矩形以dwROP指定的方式传送到目标hDcDest中去,目标位置是(xDest,yDest),新的矩形区域大小为dwWidthDest和dwHeight,如果源DC中的矩形大小和目标DC中的不一样,函数会将像素数据自动缩放。
但是StretchBlt对像素的缩放办法仅仅是删除多余的像素(从大到小)或者重复像素(从小到大),并不像一些图形处理软件一样进行插值计算,所以缩放的效果并不好,笔者建议如果能够不用这个函数就不要去用它,除非对图形的质量并没有要求。
6. TransparentBlt函数
还有一个函数可以方便地实现不规则区域的像素拷贝操作,那就是TransparentBlt函数,它的用法如下:
invoke TransparentBlt,hDcDest,xDest,yDest,dwWidthDest,dwHeightDest,\ hDcSrc,xSrc,ySrc,dwWidthSrc,dwHeightSrc,crTransparent
可以看出,它和StretchBlt函数的参数很像,也有dwWidthSrc和dwHeightSrc参数,难道这个函数也可以缩放吗?答案是肯定的,它也可以进行像素缩放。TransparentBlt函数的最后一个参数指定了一个透明色,源DC指定的矩形区域中和这个颜色相同的像素不会被拷贝,所以,BmpClock程序中的下列两个语句:
invoke BitBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,@hDcMask,0,0,SRCAND invoke BitBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,\ @hDcCircle,0,0,SRCPAINT
完全可以用下面这一句来代替:
invoke TransparentBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,\ @hDcCircle,0,0,CLOCK_SIZE,CLOCK_SIZE,0
这样甚至连遮掩图片Back1.bmp和Back2.bmp都可以省掉了。
当然,这个函数的传送方式使用拷贝方式,如果需要用到ROP码,那么只有使用其他函数了。由此可见,各种块传送函数都有它们的优缺点,在实际应用中,最好的办法就是根据实际情况灵活使用。
TransparentBlt函数并不包含在Gdi32.dll中,而是在Msimg32.dll中,所以使用时注意在源程序头部加上下面的包含语句:
include Msimg32.inc includelib Msimg32.lib