WIN32汇编语言教程:第07章 图形操作 · 7.3 创建和使用位图(5)
创建一个位图需要的参数是高度、宽度以及颜色深度,要创建位图必须得知这些参数。使用CreateCompatibleBitmap创建位图的时候,参数中有一个hDC,这是个参考hDC,也就是说,新位图的颜色深度和hDC对应的“设备环境”的颜色深度相同(注意:有个hDC参数的意思并不是将创建的位图选入这个hDC中)。CreateBitmap函数则直接在参数dwPlanes和dwBitsPerPel中指定了颜色深度。两个函数的dwWidth和dwHeight参数指定创建的位图的宽度和高度。
在例子程序的_CreateBackGround子程序中,为了建立背景图片和时钟图片,需要建立两个未初始化的位图和操作它们的DC,所以程序一开始用GetDC函数获取主窗口的hDC来当做参考DC,然后用CreateCompatibleDC函数建立了两个DC(句柄放在全局变量hDcBack和hDcClock中),并用CreateCompatibleBitmap建立了两个位图(句柄放入hBmpBack和hBmpClock中),接下来用SelectObject将这两个位图选入新建的hDC中。
创建背景图片的过程中还要用到资源中的背景图片、边框图片和边框的遮掩图片,对于这些图片,程序用LoadBitmap函数装入,并使用CreateCompatibleDC为每个图片建立一个DC。
对于不再使用的位图,要用DeleteObject函数将它们删除。所以在子程序的最后,使用DeleteObject函数将临时使用的位图句柄删除,并使用DeleteDC将操作这些位图的hDC删除。
操作未初始化位图需要用到CreateCompatibleDC和CreateCompatibleBitmap函数,初学者常犯的错误是用CreateCompatibleDC返回的hDC当做CreateCompatibleBitmap函数的参考hDC,这样的结果是建立的位图是单色的,正确的做法是两个函数的参考hDC都使用窗口客户区的hDC。
7.3.3 使用设备无关位图
设备无关位图简称为DIB,这在5.3.1小节中已经有所介绍。DIB一般是存放在磁盘上的以bmp为扩展名的位图文件,使用DIB的关键是如何将DIB中的数据转化为一个内存中的位图并返回一个位图句柄。
bmp文件的文件结构是这样定义的:文件的开始是一个BITMAPFILEHEADER结构,这个结构定义如下:
BITMAPFILEHEADER STRUCT bfType WORD ? ;文件标识,必须是“BM” bfSize DWORD ? ;文件长度 bfReserved1 WORD ? ;0 bfReserved2 WORD ? ;0 bfOffBits DWORD ? ;位图像素数据在文件中的起始位置 BITMAPFILEHEADER ENDS
BITMAPFILEHEADER结构的后面要么是BITMAPCOREHEADER结构,要么是BITMAPINFOHEADER结构和索引色表,这两种结构的定义如下:
BITMAPCOREHEADER STRUCT bcSize DWORD ? ;本结构长度 bcWidth WORD ? ;位图宽度 bcHeight WORD ? ;位图高度 bcPlanes WORD ? ;位图的色平面数 bcBitCount WORD ? ;位图的颜色深度 BITMAPCOREHEADER ENDS BITMAPINFOHEADER STRUCT bcSize DWORD ? ;本结构长度 bcWidth WORD ? ;位图宽度 bcHeight WORD ? ;位图高度 bcPlanes WORD ? ;位图的色平面数 bcBitCount WORD ? ;位图的颜色深度 biCompression 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 CreateCompatibleDC,eax mov @hDc,eax pop eax push eax invoke CreateCompatibleBitmap,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函数将位图数据拷贝到这个位图中,最后将位图句柄返回以供后面使用。