在這一課,我們將學習怎樣在程序中使用位圖。 更准確地說,我們要學習的是怎樣在一個窗口的客戶區中顯示位圖。
理論
位圖就是存貯於電腦中的圖片。位圖文件有相當多的格式(譯者:如.BMP.JPG.GIF.PIC 等)但Windows僅支持 Windows Bitmap Graphics 格式,即BMP文件。 本課所指的位圖也是BMP文件。 使用位圖最簡單的方法就是把它定義在資源文件(.rc)中。 定義的方法有兩種。第一種方法是把它定義為整數宏,具體如下:
#define IDB_MYBITMAP 100
IDB_MYBITMAP BITMAP "c:\project\example.bmp"
第一行我們定義了一個值為100的整數宏。 第二行我們把這個整數宏指向所要定義的位圖, 這樣,編譯器就能知道位圖所在的路徑。
另一種方法是給它起一個名字,也就是把它定義為字符串,具體如下:
MyBitMap BITMAP "c:\project\example.bmp"
兩種方法效果是一樣的。選擇哪一種方法,視乎在程序中你喜歡用整數宏還是用字符串來指向位圖。
現在我們已經把位圖定義在資源文件中,下一步就是把它顯示在窗口的客戶區上。
在程序中,我們使用API函數 LoadBitmap 取得位圖句柄。 下面是 LoadBitmap 函數的 完型:
LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR
該函數返回一個位圖句柄。函數有兩個參數,其中 hInstance 是程序句柄。 lpBitmapName 是位圖名字的指針(適用於第二種定義方法)。如果你使用了第一種 定義方法,你可以填入指向位圖的值或整數宏 (對應上例這個值就是100,整數宏是IDB_MYBITMAP)。下面是簡單的例子:
第一種方法:
.386
.model flat, stdcall
................
.const
IDB_MYBITMAP equ 100
...............
.data?
hInstance dd ?
..............
.code
.............
invoke GetModuleHandle,NULL
mov hInstance,eax
............
invoke LoadBitmap,hInstance,IDB_MYBITMAP
...........
第二種方法:
.386
.model flat, stdcall
................
.data
BitmapName db "MyBitMap",0
...............
.data?
hInstance dd ?
..............
.code
.............
invoke GetModuleHandle,NULL
mov hInstance,eax
............
invoke LoadBitmap,hInstance,addr BitmapName
...........
獲得一個設備文本(DC)句柄。你可以在響應WM_PAINT消息時通過API函數BeginPaint獲得。 如果在其它消息中則可以用API函數GetDC獲得。
創建這個DC的內存映像。這樣做的目的是建立一張“隱藏的畫紙”,把位圖 “畫”在上面,作緩沖之用。完成這項工作後,我們就通過一個函數把“畫紙”上的位圖復制 到真正的DC中。這就是在屏幕上快速顯示圖象的雙緩沖技術。(譯者:可以減少圖象抖動) 這張“畫紙”用API函數 CreateCompatibleDC 建立,下面是它的完型:
CreateCompatibleDC proto hdc:HDC
如果函數執行成功,將返回DC內存映像也即“畫紙”的句柄。
現在我們已經有了“畫紙”,可以把位圖畫在上面了。這可以通過API函數 SelectObject 完成, 其中第一個參數是“畫紙”的句柄,第二個參數則是位圖的句柄,下面是函數的完型:
SelectObject proto hdc:HDC, hGdiObject:DWORD
現在位圖已經畫在“畫紙”上了。下一步我們要把位圖復制到真正的DC中。 有很多API函數都能完成這項工作,例如 BitBlt 和 StretchBlt。 函數 BitBlt 僅僅將一個DC的內容簡單地復制到另一個DC中,而函數 StretchBlt 則能夠自動調整源DC復制內容的大小已適應目的DC的輸出區域大小,因此前者比後者速度更快。 在這裡我們只使用函數 BitBlt ,下面是它的完型:
BitBlt proto hdcDest:DWORD, nxDest:DWORD, nyDest:DWORD, nWidth:DWORD, nHeight:DWORD, hdcSrc:DWORD, nxSrc:DWORD, nySrc:DWORD, dwROP:DWORD
hdcDest 目的DC的句柄。
nxDest, nyDest 目的DC輸出區域的左上角坐標。
nWidth, nHeight 目的DC輸出區域的長和寬。
hdcSrc 源DC的句柄。
nxSrc, nySrc 源DC中所要復制區域的左上角坐標。
dwROP 屏面運算碼(ROP)。該參數用以確定復制內容的顏色與輸出區域原來的顏色按哪種運算 方式處理。通常,只需要簡單地用復制內容把輸出區域覆蓋掉。
一切辦妥後,就用API函數 DeleteObject 釋放位圖對象,也就是把位圖“抹掉”。
大功告成! 現在再來回顧一下整個過程:首先,你需要把位圖定義在資源文件中。 然後,你需要在程序中載入位圖資源,並取得位圖句柄。隨後,你需要獲得位圖輸出區域的DC,以及創建這個DC的內存映像,並把位圖放進這個DC內存映像中。最後把位圖從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
;---------------------------------------------------------------------
; 資源定義
;---------------------------------------------------------------------
#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_CREATE 消息時, 我們通過API函數 LoadBitmap 載入位圖資源,並通過函數返回值取得位圖句柄。
然後,我們就可以把位圖畫在窗口客戶區上。
.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消息時畫出位圖。首先我們通過API函數l BeginPaint 獲得窗口客戶區的DC句柄。 接著我們通過API函數 CreateCompatibleDC 創建該DC 的內存映像,並通過API函數 SelectObject 把位圖放進內存映像中。下一步,我們通過API函數 GetClientRect 取得窗口客戶區的大小。最後,我們通過API函數 BitBlt 把位圖從DC內存映像復制到真正的客戶區DC中。 完成顯示工作後,我們通過API函數 DeleteDC 釋放DC內存映像,並用API函數 EndPaint 釋放客戶區DC, 結束畫圖工作。本文來自編程入門網
.elseif uMsg==WM_DESTROY
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
當我們不再需要位圖時,通過API函數 DeleteObject 把它釋放。