第二十五课:简单的位图
位图就是存储在计算机中的图像。计算机中的图像可以使用很多的格式,但是windows仅支持windows Bitmap Graphics 文件(.bmp--位图文件)。我将在本课中涉及的就是位图文件。使用一个位图最简单的方式就是将它作为一种资源文件。有两种方式可以这样做。
1:你能用下面的定义来在资源文件中包含一个位图:
#define IDB_MYBITMAP 100
IDB_MYBITMAP BITMAP "c:\project\example.bmp"
这种方法使用一个常数来表示位图(宏定义)。第一行仅仅是创建一名为IDB_MYBITMAP,值为100的常量。我们将在程序中 用这个标号来引用这个位图。下一行声明一位图资源。它告诉资源编译器在那里能找到真实的位图文件。
2:另外一种方法是使用一名称(字符串)来表示位图,向下面这样:
MyBitMap BITMAP "c:\project\example.bmp"
这种方法要求在程序中引用位图时,是通过一字符串“MyBitMap”引用而不在是一数值。
两种方法都工作的很好,只要你知道你正在使用的是哪一种方法。现在我们在资源文件中放置一位图, 下一步就是让它显示在我们窗口的客户区上。
调用LoadBitmap来获得一位图句柄。LoadBitmap定义如下:
LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR
这个函数返回一位图句柄。 hInstance是我们程序的实例句柄。LpBitmapName是指向位图名称的指针(包括用第二种方法引用位图)。如果你想使用一个常数来引用位图,你能把它的值放在这儿。(在上面的例子中,它的值就是100)。一个简短的例子如下:
第一种方法:
#define IDB_MYBITMAP 100
IDB_MYBITMAP equ 100
.code
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke LoadBitmap,hInstance,IDB_MYBITMAP
第二种方法:
MyBitMap BITMAP "c:\project\example.bmp"
BitmapName db "MyBitMap",0
hInstance dd ?
.code
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke LoadBitmap,hInstance,addr BitmapName
获得一设备描述表的句柄 (DC)。你能在响应WM_PAINT消息时调用BeginPaint函数获得这个句柄。也可以在任何地方调用GetDC获取 。
创建一内存设备环境,作为设备描述表的它们都有相同的属性,我们仅仅是获得它。这儿的一种观念是创建一种隐藏画纸,我们能将位图画在上面。当我们完成这个操作后,我们仅仅是在一个函数调用中,将隐藏的画纸复制到实际的设备现场(设备描述表)。这就是用于在屏幕上快速显示图像的双缓冲技术的一种典型。你能通过调用CreateCompatibleDC函数来创建这张隐藏的画纸。
CreateCompatibleDC proto hdc:HDC
如果这个函数成功,它在eax中返回内存设备描述表的句柄。Hdc是设备描述表句柄,它和你想要的内存DC是一致的。
现在,你得到了那张隐藏的画纸,你能将你选择的位图画在它上面。这可以通过调用SelectObject函数来完成。其中,传入内存DC的句柄作为SelectObject 函数的第一个参数,并且用位图句柄做为函数的第二个参数。SelectObject函数定义如下:
SelectObject proto hdc:HDC, hGdiObject:DWORD
现在,位图已经画在内存设备描述表上了。在这儿我们需要做的是将位图复制到真正的显示设备中,也就是真正的设备描述表。这里有几个函数能完成这个任务,诸如,BitBlt和StretchBlt。虽然StretBlt能伸展或压缩位图以便让它适合输出区域的大小,而BitBlt仅仅是将一个DC的内容复制到另一个DC中,但是BitBlt更快速。为了简单,我们在这里使用BitBlt函数。 BitBlt函数定义如下:
BitBlt proto hdcDest:DWORD, nxDest:DWORD, nyDest:DWORD, nWidth:DWORD, nHeight:DWORD, hdcSrc:DWORD, nxSrc:DWORD, nySrc:DWORD, dwROP:DWORD
hdcDest 作为位图转移操作目的地的设备描述表的句柄。
nxDest ,nyDest 是输出区域左上角的坐标值。
nWidth ,nHeight 是输出区域的高度和宽度。
hdcSrc 作为位图转移操作源的设备描述表的句柄。
NxSrc,nySrc 源DC中需要复制的矩形区域的左上角坐标。
DwRop 是光栅操作码。它用于确定如何将位图的颜色数据和在输出区域上已经存在的颜色数据组合,
以便让输出区域达到最终结果。大多数时候,你只应该用新的数据重写已经存在的颜色数据。
当你完成了位图的操作,调用DeleteObject API函数将它删除。
现在,重述要点,你需要放置位图到资源脚本中。然后用LoadBitmap函数从资源文件中装载它。你将得到该位图的句柄。下一步,获取你想把位图画在上面的那块区域的设备描述表句柄。然后创建一内存设备描述表,这一设备描述表和你刚刚获得的设备描述表一致。选取位图到内存设备描述表中,然后将内存DC的内容复制到真正的DC中。
例子代码:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
IDB_MAIN equ 1
.data
ClassName db "SimpleWin32ASMBitmapClass",0
AppName db "Win32ASM Simple Bitmap Example",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hBitmap dd ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.while TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.break .if (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL ps:PAINTSTRUCT
LOCAL hdc:HDC
LOCAL hMemDC:HDC
LOCAL rect:RECT
.if uMsg==WM_CREATE
invoke LoadBitmap,hInstance,IDB_MAIN
mov hBitmap,eax
.elseif uMsg==WM_PAINT
invoke BeginPaint,hWnd,addr ps
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hMemDC,eax
invoke SelectObject,hMemDC,hBitmap
invoke GetClientRect,hWnd,addr rect
invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
invoke DeleteDC,hMemDC
invoke EndPaint,hWnd,addr ps
.elseif uMsg==WM_DESTROY
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
;---------------------------------------------------------------------
; The resource script
资源脚本
;---------------------------------------------------------------------
#define IDB_MAIN 1
IDB_MAIN BITMAP "tweety78.bmp"
分析:
#define IDB_MAIN 1
IDB_MAIN BITMAP "tweety78.bmp"
定义了一个名为IDB_MAIN的常量,并指派1作为它的值。然后用这个常量作为位图资源标识符。包含在资源中的位图是“tweety.bmp”,它和资源脚本文件应该保存在同一个文件夹中。
.if uMsg==WM_CREATE
invoke LoadBitmap,hInstance,IDB_MAIN
mov hBitmap,eax
在响应WM_CREAT消息时,我们调用LoadBitmap函数来从资源中装载位图,并传递资源标识符作为API函数的第二个参数。当函数返回时,我们获得位图的句柄。
现在,位图已经被装载,我们能将它画在我们主窗口的客户区上。
.elseif uMsg==WM_PAINT
invoke BeginPaint,hWnd,addr ps
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hMemDC,eax
invoke SelectObject,hMemDC,hBitmap
invoke GetClientRect,hWnd,addr rect
invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
invoke DeleteDC,hMemDC
invoke EndPaint,hWnd,addr ps
我们选择在响应WM_PAINT消息的时候绘画位图。首先,我们调用BeginPaint函数来获得设备描述表的句柄,然后,我们用CreateCompatibleDC函数来创建与之一致的内存DC。下一步,用SelectObject函数将位图选进内存DC。再用GetClientRect函数确定客户区的尺寸大小。现在,我们通过调用BitBlt函数将位图从内存DC复制到真正的DC中以便让位图能显示在客户区中。当绘画完成时,我们以后再也用不上内存DC所以我们用DeleteDc删除它。然后用EndPaint函释放客户区DC,结束绘画。
.elseif uMsg==WM_DESTROY
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
当我们在也不需要位图时,我们用DeleteObject函数将它删除。