windows环境下32位汇编语言程序设计-第36章
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
BITMAPCOREHEADER ENDS
BITMAPINFOHEADER STRUCT
bcSize DWORD ? ;本结构长度
bcWidth WORD ? ;位图宽度
bcHeight WORD ? ;位图高度
bcPlanes WORD ? ;位图的色平面数
bcBitCount WORD ? ;位图的颜色深度
bipression DWORD ? ;位图的压缩方式
biSizeImage DWORD ? ;图形尺寸
biXPelsPerMeter DWORD ? ;图形x方向分辨率,单位是像素/米
biYPelsPerMeter DWORD ? ;图形y方向分辨率,单位是像素/米
biClrUsed DWORD ?
biClrImportant DWORD ?
BITMAPINFOHEADER ENDS
这两个数据结构主要包含了位图的一些参数,在这些数据结构的后面,就是位图的像素数据了,整个bmp文件就由这3部分组成。
要使用DIB,可以首先将整个文件读到内存中,然后从这些数据结构中得知位图的各种参数,最后使用SetDIBitsToDevice函数将位图数据复制到一个hDC中,如果这个hDC对应一个未初始化的位图,那么就相当于得到了包含磁盘bmp位图数据的位图句柄,并且可以在任何地方使用它。当然,在这以后可以将读入文件数据的内存释放掉。
SetDIBitsToDevice函数的用法是:
invoke SetDIBitsToDevice;hDC;xDest;yDest;
dwWidth;dwHeight;xSrc;ySrc;uStartScan;cScanLines;
lpvBits;lpbmi;fuColorUse
hDC是目的DC的句柄,xDest和yDest指定了位图复制到hDC的左上角位置,dwWidth和dwHeight指定了要复制的宽度和高度,xSrc和ySrc指定DIB中要复制的左上角位置,uStartScan和cScanLines指定开始复制的扫描线和要复制的扫描线数,最后,lpvBits指向DIB中的像素数据部分,lpbmi指向DIB中的BITMAPINFO或BITMAPCOREINFO结构,fuColorUse指定了DIB中数据的类型,用DIB_RGB_COLORS表示数据是RGB类型的。
子程序_CreateDIBitmap分析一个DIB文件的参数并返回包含整个DIB位图数据的位图句柄,读者可以在任何地方使用这个位图句柄。子程序的输入参数_hWnd用来获取参考hDC的窗口句柄,_lpFileData是将DIB文件整个读入内存后的内存指针。代码如下:
_CreateDIBitmap proc _hWnd;_lpFileData
local @lpBitmapInfo;@lpBitmapBits
local @dwWidth;@dwHeight
local @hDc;@hBitmap
pushad
mov @hBitmap;0
mov esi;_lpFileData
mov eax;BITMAPFILEHEADER。bfOffBits 'esi'
add eax;esi
mov @lpBitmapBits;eax
add esi;sizeof BITMAPFILEHEADER
mov @lpBitmapInfo;esi
。if BITMAPINFO。bmiHeader。biSize 'esi' sizeof BITMAPCOREHEADER
movzx eax;BITMAPCOREHEADER。bcWidth 'esi'
movzx ebx;BITMAPCOREHEADER。bcHeight 'esi'
。else
mov eax;BITMAPINFOHEADER。biWidth 'esi'
mov ebx;BITMAPINFOHEADER。biHeight 'esi'
。endif
mov @dwWidth;eax
mov @dwHeight;ebx
;********************************************************************
; 建立空的 Bitmap Object
;********************************************************************
invoke GetDC;_hWnd
push eax
invoke CreatepatibleDC;eax
mov @hDc;eax
pop eax
push eax
invoke CreatepatibleBitmap;eax;@dwWidth;@dwHeight
mov @hBitmap;eax
invoke SelectObject;@hDc;@hBitmap
pop eax
invoke ReleaseDC;hWinMain;eax
;********************************************************************
; 将文件内容设置到建立的 Bitmap 中
;********************************************************************
invoke SetDIBitsToDevice;@hDc;0;0;@dwWidth;@dwHeight;
0;0;0;@dwHeight;
@lpBitmapBits;@lpBitmapInfo;DIB_RGB_COLORS
。if eax 0
invoke DeleteObject;@hBitmap
mov @hBitmap;0
。endif
invoke DeleteDC;@hDc
popad
mov eax;@hBitmap
ret
_CreateDIBitmap endp
_CreateDIBitmap子程序首先分析DIB文件的数据,确定BITMAPFILEHEADER后面的数据结构是BITMAPINFO还是BITMAPCOREINFO,并从结构中获取位图的高度和宽度,然后建立一个未初始化的位图,并用SetDIBitsToDevice函数将位图数据拷贝到这个位图中,最后将位图句柄返回以供后面使用。
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第7章 图形操作
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进制数值
新像素点算法
BLACKNESS
00000042h
全部为0
DSTINVERT
00550009h
not 目标像素
MERGECOPY
00c000cah
画刷 and 源像素
MERGEPAINT
00bb0226h
(not 源像素)or 目标像素
NOTSRCCOPY
00330008h
not 源像素
NOTSRCERASE
001100a6h
not(源像素 or 目标像素)
PATCOPY
00f00021h
画刷
PATINVERT
005a0049h
画刷 xor 目标像素
PATPAINT
00fb0a09h
画刷 or (not 源像素)or 目标像素
SRCAND
008800c6h
源像素 and 目标像素
SRCCOPY
00cc0020h
源像素
SRCERASE
00440328h
源像素 and(not 目标像素)
SRCINVERT
00660046h
源像素 xor 目标像素
SRCPAINT
00ee0086h
源像素 or 目标像素
WHITENESS
00ff0062h
全部为1
7。4。2 块传送函数
1。 PatBlt函数
PatBlt函数完成的是“图案块传送”的功能,即“Pattern Block Transfer”。使用的方法是:
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 Block Transfer”。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
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第7章 图形操作
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指定了一幅黑白位图,如果位图