位圖中的畫(huà)刷
BRICKS系列的最后一個(gè)項(xiàng)目是BRICKS3,如程序14-5所示。乍看此程序,您可能會(huì)有這種感覺(jué):程序代碼哪里去了呢?
BRICKS3.ASM ;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.DATA
szAppName TCHAR "BRICKS3",0
.DATA?
hInstance HINSTANCE ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
LOCAL hBitmap:HBITMAP
LOCAL hBrush:HBRUSH
invoke LoadBitmap,hInst, CTEXT ("Bricks")
mov hBitmap,eax
invoke CreatePatternBrush,hBitmap
mov hBrush,eax
invoke DeleteObject,hBitmap
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL, IDI_INFORMATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
mov eax,hBrush
mov wndclass.hbrBackground,eax
lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("CreatePatternBrush Demo"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
.if uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START
此程序與BRICKS1使用同一個(gè)BRICKS.BMP文件,而且窗口看上去也相同。
正如您看到的一樣,窗口消息處理程序沒(méi)有更多的內(nèi)容。BRICKS3實(shí)際上使用磚塊圖案作為窗口類(lèi)別背景畫(huà)刷,它在WNDCLASS結(jié)構(gòu)的hbrBackground字段中定義。
您現(xiàn)在可能猜想GDI畫(huà)刷是很小的位圖,通常是8個(gè)像素見(jiàn)方。如果將LOGBRUSH結(jié)構(gòu)的lbStyle字段設(shè)定為BS_PATTERN,然后呼叫CreatePatternBrush或CreateBrushIndirect,您就可以在位圖外面 來(lái)建立畫(huà)刷了。此位圖至少是寬高各8個(gè)像素。如果再大,Windows 98將只使用位圖的左上角作為畫(huà)刷。而Windows NT不受此限制,它會(huì)使用整個(gè)位圖。
請(qǐng)記住,畫(huà)刷和位圖都是GDI對(duì)象,而且您應(yīng)該在程序終止前刪除您在程序中建立畫(huà)刷和位圖。如果您依據(jù)位圖建立畫(huà)刷,那么在用畫(huà)刷畫(huà)圖時(shí),Windows將復(fù)制位圖位到畫(huà)刷所繪制的區(qū)域內(nèi)。呼 叫CreatePatternBrush(或者CreateBrushIndirect)之后,您可以立即刪除位圖而不會(huì)影響到畫(huà)筆。類(lèi)似地,您也可以刪除畫(huà)刷而不會(huì)影響到您選進(jìn)的原始位圖。注意,BRICKS3在建立畫(huà)刷后刪除了位 圖,并在程序終止前刪除了畫(huà)刷。
繪制位圖
在窗口中繪圖時(shí),我們已經(jīng)將位圖當(dāng)成繪圖來(lái)源使用過(guò)了。這要求先將位圖選進(jìn)內(nèi)存設(shè)備內(nèi)容,并呼叫BitBlt或者StretchBlt。您也可以用內(nèi)存設(shè)備內(nèi)容句柄作為所有實(shí)際呼叫的GDI函數(shù)中的第一 參數(shù)。內(nèi)存設(shè)備內(nèi)容的動(dòng)作與實(shí)際的設(shè)備內(nèi)容相同,除非顯示平面是位圖。
程序14-6所示的HELLOBIT程序展示了此項(xiàng)技術(shù)。程序在一個(gè)小位圖上顯示了字符串「Hello, world!」,然后從位圖到程序顯示區(qū)域執(zhí)行BitBlt或StretchBlt(依照選擇的菜單選項(xiàng)而定)。
HELLOBIT.C ;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_BIG equ 40001
IDM_SMALL equ 40002
.DATA
szAppName TCHAR "HelloBit",0
iSize DWORD IDM_BIG
szTxt TCHAR " Hello, world! ",0
.DATA?
hInstance HINSTANCE ?
cxBitmap DWORD ?
cyBitmap DWORD ?
cxClient DWORD ?
cyClient DWORD ?
hBitmap HBITMAP ?
hdcMem HDC ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL, IDI_INFORMATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX
lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("LoadBitmap Demo"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL hMenu:HMENU
LOCAL x,y :DWORD
LOCAL ps :PAINTSTRUCT
LOCAL sizel :SIZEL
.if uMsg == WM_CREATE
invoke GetDC,hwnd
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
invoke lstrlen,addr szTxt
mov ebx,eax
invoke GetTextExtentPoint32,hdc,addr szTxt, ebx ,addr sizel
lea esi,sizel
mov eax,[esi] ;sizel.cx
mov cxBitmap,eax
mov eax,[esi+4] ;sizel.cy
mov cyBitmap,eax
invoke CreateCompatibleBitmap,hdc, cxBitmap, cyBitmap
mov hBitmap,eax
invoke ReleaseDC,hwnd, hdc
invoke SelectObject,hdcMem, hBitmap
invoke lstrlen,addr szTxt
mov ebx,eax
invoke TextOut,hdcMem, 0, 0, addr szTxt, ebx
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax
mov eax,lParam
shr eax,16
mov cyClient,eax
xor eax,eax
ret
.elseif uMsg == WM_COMMAND
invoke GetMenu,hwnd
mov hMenu,eax
mov eax,wParam
and eax,0FFFFh
.if (eax==IDM_BIG)||(eax==IDM_SMALL)
invoke CheckMenuItem,hMenu, iSize, MF_UNCHECKED
mov eax,wParam
and eax,0FFFFh
mov iSize,eax
invoke CheckMenuItem,hMenu, iSize, MF_CHECKED
invoke InvalidateRect,hwnd, NULL, TRUE
.endif
xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
mov eax,iSize
.if eax==IDM_BIG
invoke StretchBlt,hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY
.elseif eax==IDM_SMALL
xor eax,eax
mov y,eax
loopy:
xor eax,eax
mov x,eax
loopx:
invoke BitBlt,hdc, x, y, cxBitmap, cyBitmap,hdcMem, 0, 0, SRCCOPY
mov eax,cxBitmap
add x,eax
mov eax,x
.if eax<cxClient
jmp loopx
.endif
mov eax,cyBitmap
add y,eax
mov eax,y
.if eax<cyClient
jmp loopy
.endif
.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
invoke DeleteDC,hdcMem
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START
程序從呼叫GetTextExtentPoint32確定字符串的像素尺寸開(kāi)始。這些尺寸將成為與視頻顯示兼容的位圖尺寸。當(dāng)此位圖被選進(jìn)內(nèi)存設(shè)備內(nèi)容(也與視頻顯示兼容)后,再呼叫TextOut將文字顯示在 位圖上。內(nèi)存設(shè)備內(nèi)容在程序執(zhí)行期間保留。在處理WM_DESTROY信息期間,HELLOBIT刪除了位圖和內(nèi)存設(shè)備內(nèi)容。
HELLOBIT中的一條菜單選項(xiàng)允許您顯示位圖尺寸,此尺寸或者是顯示區(qū)域中水平和垂直方向平鋪的實(shí)際尺寸,或者是縮放成顯示區(qū)域大小的尺寸,如圖14-4所示。正與您所見(jiàn)到的一樣,這不是顯 示大尺寸字符的好方法!它只是小字體的放大版,并帶有放大時(shí)產(chǎn)生的鋸齒線。
圖14-4 HELLOBIT的屏幕顯示 |
您可能想知道一個(gè)程序,例如HELLOBIT,是否需要處理WM_DISPLAYCHANGE消息。只要使用者(或者其它應(yīng)用程序)修改了視頻顯示大小或者顏色深度,應(yīng)用程序就接收到此訊息。其中顏色深度的 改變會(huì)導(dǎo)致內(nèi)存設(shè)備內(nèi)容和視頻設(shè)備內(nèi)容不兼容。但這并不會(huì)發(fā)生,因?yàn)楫?dāng)顯示模式修改后,Windows自動(dòng)修改了內(nèi)存設(shè)備內(nèi)容的顏色分辨率。選進(jìn)內(nèi)存設(shè)備內(nèi)容的位圖仍然保持原樣,但不會(huì)造成任何問(wèn) 題。
陰影位圖
在內(nèi)存設(shè)備內(nèi)容繪圖(也就是位圖)的技術(shù)是執(zhí)行「陰影位圖(shadow bitmap)」的關(guān)鍵。此位圖包含窗口顯示區(qū)域中顯示的所有內(nèi)容。這樣,對(duì)WM_PAINT消息的處理就簡(jiǎn)化到簡(jiǎn)單的BitBlt。
陰影位圖在繪畫(huà)程序中最有用。程序14-7所示的SKETCH程序并不是一個(gè)最完美的繪畫(huà)程序,但它是一個(gè)開(kāi)始。
程序14-7 SKETCH SKETCH.ASM;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.DATA
szAppName TCHAR "Sketch",0
.DATA?
fLeftButtonDown BOOL ?
fRightButtonDown BOOL ?
hBitmap HBITMAP ?
hdcMem HDC ?
cxBitmap DWORD ?
cyBitmap DWORD ?
cxClient DWORD ?
cyClient DWORD ?
xMouse DWORD ?
yMouse DWORD ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX
mov wndclass.lpszMenuName,NULL
lea eax,szAppName
mov wndclass.lpszClassName,eax
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Sketch"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
.if (hWnd == NULL)
invoke MessageBox,NULL, CTEXT ("Not enough memory to create bitmap!"),addr szAppName, MB_ICONERROR
xor eax,eax
ret
.endif
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
GetLargestDisplayMode proc pcxBitmap:DWORD,pcyBitmap:DWORD
LOCAL devmode:DEVMODE
LOCAL iModeNum:DWORD
xor eax,eax
mov iModeNum,eax
mov esi,pcxBitmap
mov [esi],eax
mov esi,pcyBitmap
mov [esi],eax
invoke RtlZeroMemory,addr devmode,sizeof (DEVMODE)
mov ax,sizeof DEVMODE
mov devmode.dmSize,ax
@@:
invoke EnumDisplaySettings,NULL, iModeNum,addr devmode
.if eax==0
jmp @f
.endif
inc iModeNum
mov esi,pcxBitmap
mov eax,[esi]
.if eax<devmode.dmPelsWidth
mov eax,devmode.dmPelsWidth
.endif
mov esi,pcxBitmap
mov [esi],eax
mov esi,pcyBitmap
mov eax,[esi]
.if eax<devmode.dmPelsHeight
mov eax,devmode.dmPelsHeight
.endif
mov esi,pcyBitmap
mov [esi],eax
jmp @b
@@:
ret
GetLargestDisplayMode endp
WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT
LOCAL szBuffer[64 + MAX_PATH]:TCHAR
.if uMsg == WM_CREATE
invoke GetLargestDisplayMode,addr cxBitmap,addr cyBitmap
invoke GetDC,hwnd
mov hdc,eax
invoke wsprintf,addr szBuffer, CTXT ("%d??%d"),cxBitmap,cyBitmap ;這個(gè)地方是我設(shè)計(jì)用來(lái)debug的
invoke SetWindowText,hwnd,addr szBuffer
invoke CreateCompatibleBitmap,hdc, cxBitmap, cyBitmap
mov hBitmap,eax
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
invoke ReleaseDC,hwnd, hdc
.if (hBitmap==0);no memory for bitmap
invoke DeleteDC,hdcMem
mov eax,-1
ret
.endif
invoke SelectObject,hdcMem, hBitmap
invoke PatBlt,hdcMem, 0, 0, cxBitmap, cyBitmap, WHITENESS
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax
mov eax,lParam
shr eax,16
mov cyClient,eax
xor eax,eax
ret
.elseif uMsg == WM_LBUTTONDOWN
.if fRightButtonDown==0
invoke SetCapture,hwnd
.endif
invoke SetWindowText,hwnd,CTXT("DOWN")
mov eax,lParam
and eax,0FFFFh
mov xMouse,eax
mov eax,lParam
shr eax,16
mov yMouse,eax
mov fLeftButtonDown,TRUE
xor eax,eax
ret
.elseif uMsg ==WM_LBUTTONUP
.if fLeftButtonDown!=0
invoke SetCapture,NULL
.endif
invoke SetWindowText,hwnd,CTXT("UPUP")
mov fLeftButtonDown,FALSE
xor eax,eax
ret
.elseif uMsg == WM_RBUTTONDOWN
.if fLeftButtonDown==0
invoke SetCapture,hwnd
.endif
mov eax,lParam
and eax,0FFFFh
mov xMouse,eax
mov eax,lParam
shr eax,16
mov yMouse,eax
mov fRightButtonDown,TRUE
xor eax,eax
ret
.elseif uMsg == WM_RBUTTONUP
.if fRightButtonDown!=0
invoke SetCapture,NULL
.endif
mov fRightButtonDown,FALSE
xor eax,eax
ret
.elseif uMsg == WM_MOUSEMOVE
.if (fLeftButtonDown==0) && (fRightButtonDown==0)
xor eax,eax
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
.if fLeftButtonDown==0
mov eax,WHITE_PEN
.else
mov eax,BLACK_PEN
.endif
invoke GetStockObject,eax
invoke SelectObject,hdc,eax
.if fLeftButtonDown==0
mov eax,WHITE_PEN
.else
mov eax,BLACK_PEN
.endif
invoke GetStockObject,eax
invoke SelectObject,hdcMem,eax
invoke MoveToEx,hdc, xMouse, yMouse, NULL
invoke MoveToEx,hdcMem, xMouse, yMouse, NULL
mov eax,lParam
and eax,0FFFFh
movsx ecx,ax
mov xMouse,ecx
mov eax,lParam
shr eax,16
movsx ecx,ax
mov yMouse,ecx
invoke LineTo,hdc, xMouse, yMouse
invoke LineTo,hdcMem, xMouse, yMouse
invoke ReleaseDC,hwnd, hdc
xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke BitBlt,hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
invoke DeleteDC,hdcMem
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START
要想在SKETCH中畫(huà)線,請(qǐng)按下鼠標(biāo)左鍵并拖動(dòng)鼠標(biāo)。要擦掉畫(huà)過(guò)的東西(更確切地說(shuō),是畫(huà)白線),請(qǐng)按下鼠標(biāo)右鍵并拖動(dòng)鼠標(biāo)。要清空整個(gè)窗口,請(qǐng)…結(jié)束程序,然后重新加載,一切從頭再來(lái) 。圖14-5中顯示的SKETCH程序圖樣表達(dá)了對(duì)頻果公司的麥金塔計(jì)算機(jī)早期廣告的敬意。
圖14-5 SKETCH的屏幕顯示 |
此陰影位圖應(yīng)多大?在本程序中,它應(yīng)該大到能包含最大化窗口的整個(gè)顯示區(qū)域。這一問(wèn)題很容易根據(jù)GetSystemMetrics信息計(jì)算得出,但如果使用者修改了顯示設(shè)定后再顯示,進(jìn)而擴(kuò)大了最大 化時(shí)窗口的尺寸,這時(shí)將發(fā)生什么呢?SKETCH程序在EnumDisplaySettings函數(shù)的幫助下解決了此問(wèn)題。此函數(shù)使用DEVMODE結(jié)構(gòu)來(lái)傳回全部有效視頻顯示模式的信息。第一次呼叫此函數(shù)時(shí),應(yīng)將 EnumDisplaySettings的第二參數(shù)設(shè)為0,以后每次呼叫此值都增加。EnumDisplaySettings傳回FALSE時(shí)完成。
與此同時(shí),SKETCH將建立一個(gè)陰影位圖,它比目前視頻顯示模式的表面還多四倍,而且需要幾兆字節(jié)的內(nèi)存。由于如此,SKETCH將檢查位圖是否建立成功了,如果沒(méi)有建立,就從WM_CREATE傳回-1 ,以表示錯(cuò)誤。
在WM_MOUSEMOVE消息處理期間,按下鼠標(biāo)左鍵或者右鍵,并在內(nèi)存設(shè)備內(nèi)容和顯示區(qū)域設(shè)備內(nèi)容中畫(huà)線時(shí),SKETCH攔截鼠標(biāo)。如果畫(huà)線方式更復(fù)雜的話,您可能想在一個(gè)函數(shù)中實(shí)作,程序?qū)⒑艚?此函數(shù)兩次-一次畫(huà)在視頻設(shè)備內(nèi)容上,一次畫(huà)在內(nèi)存設(shè)備內(nèi)容上。
下面是一個(gè)有趣的實(shí)驗(yàn):使SKETCH窗口小于全畫(huà)面尺寸。隨著鼠標(biāo)左鍵的按下,將鼠標(biāo)拖出窗口的右下角。因?yàn)镾KETCH攔截鼠標(biāo),所以它繼續(xù)接收并處理WM_MOUSEMOVE消息?,F(xiàn)在擴(kuò)大窗口,您將 看到陰影位圖包含您在SKETCH窗口外所畫(huà)的內(nèi)容。
在菜單中使用位圖
您也可以用位圖在菜單上顯示選項(xiàng)。如果您聯(lián)想起菜單中文件夾、剪貼簿和資源回收筒的圖片,那么不要再想那些圖片了。您應(yīng)該考慮一下,菜單上顯示位圖對(duì)畫(huà)圖程序用途有多大,想象一下在 菜單中使用不同字體和字體大小、線寬、陰影圖案以及顏色。
GRAFMENU是展示圖形菜單選項(xiàng)的范例程序。此程序頂層菜單如圖14-6所示。放大的字母來(lái)自于40×16像素的單色位圖文件,該文件在Visual C++ Developer Studio建立。從菜單上選擇「FONT」將彈出三個(gè)選擇項(xiàng)-「Courier New」、「 Arial」和「Times New Roman」。它們是標(biāo)準(zhǔn)的Windows TrueType字體,并且每一個(gè)都按其相關(guān)的字體顯示,如圖14-7所示。這些位圖在程序中用內(nèi)存設(shè)備內(nèi)容建立。
圖14-6 GRAFMENU程序的頂層菜單 |
圖14-7 GRAFMENU程序彈出的「FONT」菜單 |
最后,在拉下系統(tǒng)菜單時(shí),您將獲得一些「輔助」信息,用「HELP」表示了新使用者的在線求助項(xiàng)目(參見(jiàn)圖14-8)。此64×64像素的單色位圖是在Developer Studio中建立的。
圖14-8 GRAFMENU程序系統(tǒng)菜單 |
GRAFMENU程序,包括四個(gè)Developer Studio中建立的位圖,如程序14-8所示。
GRAFMENU.ASM;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FONT_COUR equ 101
IDM_FONT_ARIAL equ 102
IDM_FONT_TIMES equ 103
IDM_HELP equ 104
IDM_EDIT_UNDO equ 40005
IDM_EDIT_CUT equ 40006
IDM_EDIT_COPY equ 40007
IDM_EDIT_PASTE equ 40008
IDM_EDIT_CLEAR equ 40009
IDM_FILE_NEW equ 40010
IDM_FILE_OPEN equ 40011
IDM_FILE_SAVE equ 40012
IDM_FILE_SAVE_AS equ 40013
.DATA
szAppName TCHAR "GrafMenu",0
Font1 db "Courier New",0
Font2 db "Arial",0
Font3 db "Times New Roman",0
szFaceName DWORD offset Font1
DWORD offset Font2
DWORD offset Font3
iCurrentFont DWORD IDM_FONT_COUR ;
.DATA?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL, IDI_INFORMATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX
lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Bitmap Menu Demonstration"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
CopyMemory proc Dest:DWORD, Source:DWORD, mlength:DWORD
cld ;Work upwards
mov esi, Source ;Source address
mov edi, Dest ;Destination address
mov ecx, mlength ;Get size in bytes
rep movsb ; repeat copy util all done
ret
CopyMemory endp
;StretchBitmap: Scales bitmap to display resolution
StretchBitmap proc hBitmap1:HBITMAP
LOCAL bm1:BITMAP
LOCAL bm2:BITMAP
LOCAL hBitmap2:HBITMAP
LOCAL hdc, hdcMem1, hdcMem2:HDC
LOCAL cxChar, cyChar:DWORD
;Get the width and height of a system font character
invoke GetDialogBaseUnits
mov cxChar,eax
and cxChar,0FFFFh
shr eax,16
mov cyChar,eax
;Create 2 memory DCs compatible with the display
invoke CreateIC,CTEXT ("DISPLAY"), NULL, NULL, NULL
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hdcMem1,eax
invoke CreateCompatibleDC,hdc
mov hdcMem2,eax
invoke DeleteDC,hdc
;Get the dimensions of the bitmap to be stretched
invoke GetObject,hBitmap1, sizeof (BITMAP), addr bm1
;Scale these dimensions based on the system font size
invoke CopyMemory,addr bm2,addr bm1,sizeof(BITMAP)
mov eax,cxChar
mov ecx,bm2.bmWidth
mul ecx
shr eax,2
mov bm2.bmWidth,eax
mov eax,cyChar
mov ecx,bm2.bmHeight
mul ecx
shr eax,3
mov bm2.bmHeight,eax
mov eax,bm2.bmWidth
add eax,15
shr eax,4
shl eax,2
mov bm2.bmWidthBytes,eax
;Create a new bitmap of larger size
invoke CreateBitmapIndirect,addr bm2
mov hBitmap2,eax
;Select the bitmaps in the memory DCs and do a StretchBlt
invoke SelectObject,hdcMem1, hBitmap1
invoke SelectObject,hdcMem2, hBitmap2
invoke StretchBlt,hdcMem2, 0, 0, bm2.bmWidth, \
bm2.bmHeight,hdcMem1, 0, 0, \
bm1.bmWidth, bm1.bmHeight, SRCCOPY
;Clean up
invoke DeleteDC,hdcMem1
invoke DeleteDC,hdcMem2
invoke DeleteObject,hBitmap1
mov eax,hBitmap2
ret
StretchBitmap endp
; AddHelpToSys: Adds bitmap Help item to system menu
AddHelpToSys proc hInstance:HINSTANCE, hwnd:HWND
LOCAL hBitmap:HBITMAP
LOCAL hMenu:HMENU
invoke GetSystemMenu,hwnd, FALSE
mov hMenu,eax
invoke LoadBitmap,hInstance, CTEXT ("BitmapHelp")
invoke StretchBitmap,eax
mov hBitmap,eax
invoke AppendMenu,hMenu, MF_SEPARATOR, 0, NULL
invoke AppendMenu,hMenu, MF_BITMAP, IDM_HELP,hBitmap
mov eax,eax
ret
AddHelpToSys endp
;GetBitmapFont: Creates bitmaps with font names
GetBitmapFont proc i:DWORD
LOCAL hBitmap:HBITMAP
LOCAL hdc, hdcMem:HDC
LOCAL hFont:HFONT
LOCAL sizeGB:SIZEL
LOCAL tm:TEXTMETRIC
invoke CreateIC,CTEXT ("DISPLAY"), NULL, NULL, NULL
mov hdc,eax
invoke GetTextMetrics,hdc,addr tm
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
mov ebx,tm.tmHeight
shl ebx,1
mov eax,i
shl eax,2
mov eax,[szFaceName+eax]
invoke CreateFont,ebx, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,eax
mov hFont,eax
invoke SelectObject,hdcMem, hFont
mov hFont,eax
mov eax,i
shl eax,2
mov eax,[szFaceName+eax]
push eax
invoke lstrlen,eax
mov ecx,eax
pop ebx
invoke GetTextExtentPoint32,hdcMem, ebx,ecx, addr sizeGB
invoke CreateBitmap,sizeGB.x, sizeGB.y, 1, 1, NULL
mov hBitmap,eax
invoke SelectObject,hdcMem, hBitmap
mov eax,i
shl eax,2
mov eax,[szFaceName+eax]
push eax
invoke lstrlen,eax
pop ebx
invoke TextOut,hdcMem, 0, 0, ebx,eax
invoke SelectObject,hdcMem, hFont
invoke DeleteObject,eax
invoke DeleteDC,hdcMem
invoke DeleteDC,hdc
mov eax,hBitmap
ret
GetBitmapFont endp
;CreateMyMenu: Assembles menu from components
CreateMyMenu proc hInstance:HINSTANCE
LOCAL hBitmap:HBITMAP
LOCAL hMenu, hMenuPopup:HMENU
LOCAL i:DWORD
invoke CreateMenu
mov hMenu,eax
invoke LoadMenu,hInstance, CTEXT ("MenuFile")
mov hMenuPopup,eax
invoke LoadBitmap,hInstance, CTEXT ("BitmapFile")
invoke StretchBitmap,eax
mov hBitmap,eax
invoke AppendMenu,hMenu, MF_BITMAP or MF_POPUP, hMenuPopup,hBitmap
invoke LoadMenu,hInstance, CTEXT ("MenuEdit")
mov hMenuPopup,eax
invoke LoadBitmap,hInstance, CTEXT ("BitmapEdit")
invoke StretchBitmap,eax
mov hBitmap,eax
invoke AppendMenu,hMenu, MF_BITMAP or MF_POPUP,hMenuPopup,hBitmap
invoke CreateMenu
mov hMenuPopup,eax
mov i,0
@@:
invoke GetBitmapFont,i
mov hBitmap,eax
invoke AppendMenu,hMenuPopup, MF_BITMAP, IDM_FONT_COUR + i,hBitmap
inc i
cmp i,3
jNz @b
invoke LoadBitmap,hInstance, CTEXT ("BitmapFont")
invoke StretchBitmap,eax
mov hBitmap,eax
invoke AppendMenu,hMenu, MF_BITMAP or MF_POPUP, hMenuPopup,hBitmap
mov eax,hMenu
ret
CreateMyMenu endp
;DeleteAllBitmaps: Deletes all the bitmaps in the menu
DeleteAllBitmaps proc hwnd:HWND
LOCAL hMenu:HMENU
LOCAL i:DWORD
LOCAL mii:MENUITEMINFO
;MENUITEMINFOA STRUCT
; cbSize DWORD ?
; fMask DWORD ?
; fType DWORD ?
; fState DWORD ?
; wID DWORD ?
; hSubMenu DWORD ?
; hbmpChecked DWORD ?
; hbmpUnchecked DWORD ?
; dwItemData DWORD ?
; dwTypeData DWORD ?
; cch DWORD ?
;MENUITEMINFOA ENDS
mov eax,sizeof (MENUITEMINFO)
mov mii.cbSize,eax
mov mii.fMask,MIIM_SUBMENU or MIIM_TYPE
;Delete Help bitmap on system menu
invoke GetSystemMenu,hwnd, FALSE
mov hMenu,eax
invoke GetMenuItemInfo,hMenu, IDM_HELP, FALSE,addr mii
invoke DeleteObject,mii.dwTypeData
;Delete top-level menu bitmaps
invoke GetMenu,hwnd
mov hMenu,eax
mov i,0
@@:
invoke GetMenuItemInfo,hMenu, i, TRUE,addr mii
invoke DeleteObject,mii.dwTypeData
inc i
cmp i,3
jNz @b
;Delete bitmap items on Font menu
mov eax,mii.hSubMenu
mov hMenu,eax
mov i,0
@@:
invoke GetMenuItemInfo,hMenu, i, TRUE,addr mii
invoke DeleteObject,mii.dwTypeData
inc i
cmp i,3
jNz @b
ret
DeleteAllBitmaps endp
WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hMenu:HMENU
.if uMsg == WM_CREATE
mov esi,lParam
mov eax,[esi+4]
invoke AddHelpToSys,eax,hwnd
mov esi,lParam
mov eax,[esi+4]
invoke CreateMyMenu,eax
mov hMenu,eax
invoke SetMenu,hwnd, hMenu
invoke CheckMenuItem,hMenu, iCurrentFont, MF_CHECKED
xor eax,eax
ret
.elseif uMsg == WM_SYSCOMMAND
mov eax,wParam
and eax,0FFFFh
.if eax==IDM_HELP
invoke MessageBox,hwnd,CTEXT ("Help not yet implemented!"),\
addr szAppName, MB_OK or MB_ICONEXCLAMATION
xor eax,eax
ret
.endif
.elseif uMsg == WM_COMMAND
mov eax,wParam
and eax,0FFFFh
.if (eax==IDM_FILE_NEW)||\
(eax==IDM_FILE_OPEN)||\
(eax==IDM_FILE_SAVE)||\
(eax==IDM_FILE_SAVE_AS)||\
(eax==IDM_EDIT_UNDO)||\
(eax==IDM_EDIT_CUT)||\
(eax==IDM_EDIT_COPY)||\
(eax==IDM_EDIT_PASTE)||\
(eax==IDM_EDIT_CLEAR)
invoke MessageBeep,0
xor eax,eax
ret
.elseif (eax==IDM_FONT_COUR)||\
(eax==IDM_FONT_ARIAL)||\
(eax==IDM_FONT_TIMES)
invoke GetMenu,hwnd
mov hMenu,eax
invoke CheckMenuItem,hMenu, iCurrentFont, MF_UNCHECKED
mov eax,wParam
and eax,0FFFFh
mov iCurrentFont,eax
invoke CheckMenuItem,hMenu, iCurrentFont, MF_CHECKED
xor eax,eax
ret
.endif
.elseif uMsg == WM_DESTROY
invoke DeleteAllBitmaps,hwnd
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START
EDITLABL.BMP |
FILELABL.BMP |
FONTLABL.BMP
|
BIGHELP.BMP |
要將位圖插入菜單,可以利用AppendMenu或InsertMenu。位圖有兩個(gè)來(lái)源:可以在Visual C++ Developer Studio建立位圖,包括資源腳本中的位圖文件,并在程序使用LoadBitmap時(shí)將位圖資源加載到內(nèi)存,然后呼叫AppendMenu或InsertMenu將位圖附加到菜單上。但是用這種方法會(huì)有一些問(wèn)題:位圖 不適于所有顯示模式的分辨率和縱橫比;有時(shí)您需要縮放加載的位圖以解決此問(wèn)題。另一種方法是:在程序內(nèi)部建立位圖,并將它選進(jìn)內(nèi)存設(shè)備內(nèi)容,畫(huà)出來(lái),然后再附加到菜單中。
GRAFMENU中的GetBitmapFont函數(shù)的參數(shù)為0、1或2,傳回一個(gè)位圖句柄。此位圖包含字符串「Courier New」、「Arial」或「Times New Roman」,而且字體是各自對(duì)應(yīng)的字體,大小是正常系統(tǒng)字體的兩倍。讓我們看看GetBitmapFont是怎么做的。(下面的程序代碼與GRAFMENU.C文件中的有些不同。為了清楚起見(jiàn),我用「Arial」字 體相應(yīng)的值代替了引用szFaceName數(shù)組。)
第一步是用TEXTMETRIC結(jié)構(gòu)來(lái)確定目前系統(tǒng)字體的大小,并建立一個(gè)與目前屏幕兼容的內(nèi)存設(shè)備內(nèi)容:
hdc = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ; GetTextMetrics (hdc, &tm) ; hdcMem = CreateCompatibleDC (hdc) ;
CreateFont函數(shù)建立了一種邏輯字體,該字體高是系統(tǒng)字體的兩倍,而且邏輯名稱為「Arial」:
hFont = CreateFont (2 * tm.tmHeight, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TEXT ("Arial")) ;
從內(nèi)存設(shè)備內(nèi)容中選擇該字體,然后儲(chǔ)存內(nèi)定字體句柄:
hFont = (HFONT) SelectObject (hdcMem, hFont) ;
現(xiàn)在,當(dāng)我們向內(nèi)存設(shè)備內(nèi)容寫(xiě)一些文字時(shí),Windows就會(huì)使用選進(jìn)設(shè)備內(nèi)容的TrueType Arial字體了。
但這個(gè)內(nèi)存設(shè)備內(nèi)容最初只有一個(gè)單像素單色設(shè)備平面。我們必須建立一個(gè)足夠大的位圖以容納我們所要顯示的文字。通過(guò)GetTextExtentPoint32函數(shù),可以取得文字的大小,而用CreateBitmap 可以根據(jù)這些尺寸來(lái)建立位圖:
GetTextExtentPoint32 (hdcMem, TEXT ("Arial"), 5, &size) ; hBitmap = CreateBitmap (size.cx, size.cy, 1, 1, NULL) ; SelectObject (hdcMem, hBitmap) ;
現(xiàn)在這個(gè)設(shè)備內(nèi)容是一個(gè)單色的顯示平面,大小也是嚴(yán)格的文字尺寸。我們現(xiàn)在要做的就是書(shū)寫(xiě)文字:
TextOut (hdcMem, 0, 0, TEXT ("Arial"), 5) ;
除了清除,所有的工作都完成了。要清除,我們可以用SelectObject將系統(tǒng)字體(帶有句柄hFont)重新選進(jìn)設(shè)備內(nèi)容,然后刪除SelectObject傳回的前一個(gè)字體句柄,也就是Arial字體句柄:
DeleteObject (SelectObject (hdcMem, hFont)) ;
現(xiàn)在可以刪除兩個(gè)設(shè)備內(nèi)容:
DeleteDC (hdcMem) ; DeleteDC (hdc) ;
這樣,我們就獲得了一個(gè)位圖,該位圖上有Arial字體的字符串「Arial」。
當(dāng)我們需要縮放字體以適應(yīng)不同顯示分辨率或縱橫比時(shí),內(nèi)存設(shè)備內(nèi)容也能解決問(wèn)題。在GRAFMENU程序中,我建立了四個(gè)位圖,這些位圖只適用于系統(tǒng)字體高8像素、寬4像素的顯示。對(duì)于其它尺 寸的系統(tǒng)字體,只能縮放位圖。GRAFMENU中的StretchBitmap函數(shù)完成此功能。
第一步是獲得顯示的設(shè)備內(nèi)容,然后取得系統(tǒng)字體的文字規(guī)格,接下來(lái)建立兩個(gè)內(nèi)存設(shè)備內(nèi)容:
hdc = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ; GetTextMetrics (hdc, &tm) ; hdcMem1 = CreateCompatibleDC (hdc) ; hdcMem2 = CreateCompatibleDC (hdc) ; DeleteDC (hdc) ;
傳遞給函數(shù)的位圖句柄是hBitmap1。程序能用GetObject獲得位圖的大小:
GetObject (hBitmap1, sizeof (BITMAP), (PSTR) &bm1) ;
此操作將尺寸復(fù)制到BITMAP型態(tài)的結(jié)構(gòu)bm1中。結(jié)構(gòu)bm2等于結(jié)構(gòu)bm1,然后根據(jù)系統(tǒng)字體大小來(lái)修改某些字段:
bm2 = bm1 ; bm2.bmWidth = (tm.tmAveCharWidth * bm2.bmWidth) / 4 ; bm2.bmHeight = (tm.tmHeight * bm2.bmHeight) / 8 ; bm2.bmWidthBytes = ((bm2.bmWidth + 15) / 16) * 2 ;
下一個(gè)位圖帶有句柄hBitmap2,可以根據(jù)動(dòng)態(tài)的尺寸建立:
hBitmap2 = CreateBitmapIndirect (&bm2) ;
然后將這兩個(gè)位圖選進(jìn)兩個(gè)內(nèi)存設(shè)備內(nèi)容中:
SelectObject (hdcMem1, hBitmap1) ; SelectObject (hdcMem2, hBitmap2) ;
我們想把第一個(gè)位圖復(fù)制給第二個(gè)位圖,并在此程序中進(jìn)行拉伸。這包括StretchBlt呼叫:
StretchBlt (hdcMem2, 0, 0, bm2.bmWidth, bm2.bmHeight, hdcMem1, 0, 0, bm1.bmWidth, bm1.bmHeight, SRCCOPY) ;
現(xiàn)在第二幅圖適當(dāng)?shù)乜s放了,我們可將其用到菜單中。剩下的清除工作很簡(jiǎn)單:
DeleteDC (hdcMem1) ; DeleteDC (hdcMem2) ; DeleteObject (hBitmap1) ;
在建造菜單時(shí),GRAFMENU中的CreateMyMenu函數(shù)呼叫了StretchBitmap和GetBitmapFont函數(shù)。GRAFMENU在資源文件中定義了兩個(gè)菜單,在選擇「File」和「Edit」選項(xiàng)時(shí)會(huì)彈出這兩個(gè)菜單。函數(shù) 開(kāi)始先取得一個(gè)空菜單的句柄:
hMenu = CreateMenu () ;
從資源文件加載「File」的彈出式菜單(包括四個(gè)選項(xiàng):「New」、「Open」、「Save」和「Save as」):
hMenuPopup = LoadMenu (hInstance, TEXT ("MenuFile")) ;
從資源文件還加載了包含「FILE」的位圖,并用StretchBitmap進(jìn)行了拉伸:
hBitmapFile = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapFile"))) ;
位圖句柄和彈出式菜單句柄都是AppendMenu呼叫的參數(shù):
AppendMenu (hMenu, MF_BITMAP | MF_POPUP, hMenuPopup, (PTSTR) (LONG) hBitmapFile) ;
「Edit」菜單類(lèi)似程序如下:
hMenuPopup = LoadMenu (hInstance, TEXT ("MenuEdit")) ; hBitmapEdit = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapEdit"))) ; AppendMenu (hMenu, MF_BITMAP | MF_POPUP, hMenuPopup, (PTSTR)(LONG) hBitmapEdit) ;
呼叫GetBitmapFont函數(shù)可以構(gòu)造這三種不同字體的彈出式菜單:
hMenuPopup = CreateMenu () ; for (i = 0 ; i < 3 ; i++) { hBitmapPopFont [i] = GetBitmapFont (i) ; AppendMenu (hMenuPopup, MF_BITMAP, IDM_FONT_COUR + i, (PTSTR) (LONG) hMenuPopupFont [i]) ; }
然后將彈出式菜單添加到菜單中:
hBitmapFont = StretchBitmap (LoadBitmap (hInstance, "BitmapFont")) ; AppendMenu (hMenu, MF_BITMAP | MF_POPUP, hMenuPopup, (PTSTR) (LONG) hBitmapFont) ;
WndProc通過(guò)呼叫SetMenu,完成了窗口菜單的建立工作。
GRAFMENU還改變了AddHelpToSys函數(shù)中的系統(tǒng)菜單。此函數(shù)首先獲得一個(gè)系統(tǒng)菜單句柄:
hMenu = GetSystemMenu (hwnd, FALSE) ;
這將載入「HELP」位圖,并將其拉伸到適當(dāng)尺寸:
hBitmapHelp = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapHelp"))) ;
這將給系統(tǒng)菜單添加一條分隔線和拉伸的位圖:
AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ; AppendMenu (hMenu, MF_BITMAP, IDM_HELP, (PTSTR)(LONG) hBitmapHelp) ;
GRAFMENU在退出之前呼叫一個(gè)函數(shù)來(lái)清除并刪除所有位圖。
下面是在菜單中使用位圖的一些注意事項(xiàng)。
在頂層菜單中,Windows調(diào)整菜單列的高度以適應(yīng)最高的位圖。其它位圖(或字符串)是根據(jù)菜單列的頂端對(duì)齊的。如果在頂層菜單中使用了位圖,那么從使用常數(shù)SM_CYMENU的GetSystemMetrics得 到的菜單列大小將不再有效。
執(zhí)行GRAFMENU期間可以看到:在彈出式菜單中,您可使用帶有位圖菜單項(xiàng)的勾選標(biāo)記,但勾選標(biāo)記是正常尺寸。如果不滿意,您可以建立一個(gè)自訂的勾選標(biāo)記,并使用SetMenuItemBitmaps。
在菜單中使用非文字(或者使用非系統(tǒng)字體的文字)的另一種方法是「擁有者繪制」菜單。
菜單的鍵盤(pán)接口是另一個(gè)問(wèn)題。當(dāng)菜單含有文字時(shí),Windows會(huì)自動(dòng)添加鍵盤(pán)接口。要選擇一個(gè)菜單項(xiàng),可以使用Alt與字符串中的一個(gè)字母的組合鍵。而一旦在菜單中放置了位圖,就刪除了鍵盤(pán) 接口。即使位圖表達(dá)了一定的含義,但Windows并不知道。
目前我們可以使用WM_MENUCHAR消息。當(dāng)您按下Alt和與菜單項(xiàng)不相符的一個(gè)字符鍵的組合鍵時(shí),Windows將向您的窗口消息處理程序發(fā)送一個(gè)WM_MENUCHAR消息。GRAFMENU需要截取WM_MENUCHAR消息 并檢查wParam的值(即按鍵的ASCII碼)。如果這個(gè)值對(duì)應(yīng)一個(gè)菜單項(xiàng),那么向Windows傳回雙字組:其中高字組為2,低字組是與該鍵相關(guān)的菜單項(xiàng)索引值。然后由Windows處理余下的事。
非矩形位圖圖像
位圖都是矩形,但不需要都顯示成矩形。例如,假定您有一個(gè)矩形位圖圖像,但您卻想將它顯示成橢圓形。
首先,這聽(tīng)起來(lái)很簡(jiǎn)單。您只需將圖像加載Visual C++ Developer Studio或者Windows的「畫(huà)圖」程序,然后用白色的畫(huà)筆將圖像四周畫(huà)上白色。這時(shí)將獲得一幅橢圓形的圖像,而橢圓的外面就 成了白色。只有當(dāng)背景色為白色時(shí)此位圖才能正確顯示,如果在其它背景色上顯示,您就會(huì)發(fā)現(xiàn)橢圓形的圖像和背景之間有一個(gè)白色的矩形。這種效果不好。
有一種非常通用的技術(shù)可解決此類(lèi)問(wèn)題。這種技術(shù)包括「屏蔽(mask)」位圖和一些位映像操作。屏蔽是一種單色位圖,它與您要顯示的矩形位圖圖像尺寸相同。每個(gè)屏蔽的像素都對(duì)應(yīng)位圖圖像 的一個(gè)像素。屏蔽像素是1(白色),對(duì)應(yīng)著位圖像素顯示;是0(黑色),則顯示背景色。(或者屏蔽位圖與此相反,這根據(jù)您使用的位映像操作而有一些相對(duì)應(yīng)的變化。)
讓我們看看BITMASK程序是如何實(shí)作這一技術(shù)的。如程序14-9所示。
BITMASK.ASM
;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.DATA
szAppName TCHAR "BitMask",0
.DATA?
hInstance HINSTANCE ?
hBitmapImag HBITMAP ?
hBitmapMask HBITMAP ?
cxClient DWORD ?
cyClient DWORD ?
cxBitmap DWORD ?
cyBitmap DWORD ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL, IDI_INFORMATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
invoke GetStockObject,LTGRAY_BRUSH
mov wndclass.hbrBackground,eax
lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Bitmap Masking Demo"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bitmap:BITMAP
LOCAL hdc, hdcMemImag, hdcMemMask:HDC
LOCAL x, y:DWORD
LOCAL ps:PAINTSTRUCT
.if uMsg == WM_CREATE
mov esi,lParam
mov eax,[esi+4]
mov hInstance,eax
; Load the original image and get its size
invoke LoadBitmap,hInstance, CTEXT ("Matthew")
mov hBitmapImag,eax
invoke GetObject,hBitmapImag, sizeof (BITMAP),addr bitmap
mov eax,bitmap.bmWidth
mov cxBitmap,eax
mov eax,bitmap.bmHeight
mov cyBitmap,eax
;Select the original image into a memory DC
invoke CreateCompatibleDC,NULL
mov hdcMemImag,eax
invoke SelectObject,hdcMemImag, hBitmapImag
;Create the monochrome mask bitmap and memory DC
invoke CreateBitmap,cxBitmap, cyBitmap, 1, 1, NULL
mov hBitmapMask,eax
invoke CreateCompatibleDC,NULL
mov hdcMemMask,eax
invoke SelectObject,hdcMemMask, hBitmapMask
;Color the mask bitmap black with a white ellipse
invoke GetStockObject,BLACK_BRUSH
invoke SelectObject,hdcMemMask, eax
invoke Rectangle,hdcMemMask, 0, 0, cxBitmap, cyBitmap
invoke GetStockObject,WHITE_BRUSH
invoke SelectObject,hdcMemMask,eax
invoke Ellipse,hdcMemMask, 0, 0, cxBitmap, cyBitmap
;Mask the original image
invoke BitBlt,hdcMemImag, 0, 0, cxBitmap, cyBitmap, hdcMemMask, 0, 0, SRCAND
invoke DeleteDC,hdcMemImag
invoke DeleteDC,hdcMemMask
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax
mov eax,lParam
shr eax,16
mov cyClient,eax
xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
;Select bitmaps into memory DCs
invoke CreateCompatibleDC,hdc
mov hdcMemImag,eax
invoke SelectObject,hdcMemImag, hBitmapImag
invoke CreateCompatibleDC,hdc
mov hdcMemMask,eax
invoke SelectObject,hdcMemMask, hBitmapMask
;Center image
mov eax,cxClient
sub eax,cxBitmap
shr eax,1
mov x,eax
mov eax,cyClient
sub eax,cyBitmap
shr eax,1
mov y,eax
;Do the bitblts
invoke BitBlt,hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 220326h
invoke BitBlt,hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT
invoke DeleteDC,hdcMemImag
invoke DeleteDC,hdcMemMask
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
invoke DeleteObject,hBitmapImag
invoke DeleteObject,hBitmapMask
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START
資源文件中的MATTHEW.BMP文件是原作者侄子的一幅黑白數(shù)字照片,寬200像素,高320像素,每像素8位。不過(guò),另外制作個(gè)BITMASK只是因?yàn)榇宋募膬?nèi)容是任何東西都可以。
注意,BITMASK將窗口背景設(shè)為亮灰色。這樣就確保我們能正確地屏蔽位圖,而不只是將其涂成白色。
下面讓我們看一下WM_CREATE的處理程序:BITMASK用LoadBitmap函數(shù)獲得hBitmapImag變量中原始圖像的句柄。用GetObject函數(shù)可取得位圖的寬度高度。然后將位圖句柄選進(jìn)句柄為hdcMemImag的 內(nèi)存設(shè)備內(nèi)容中。
程序建立的下一個(gè)單色位圖與原來(lái)的圖大小相同,其句柄儲(chǔ)存在hBitmapMask,并選進(jìn)句柄為hdcMemMask的內(nèi)存設(shè)備內(nèi)容中。在內(nèi)存設(shè)備內(nèi)容中,使用GDI函數(shù),屏蔽位圖就涂成了黑色背景和一個(gè) 白色的橢圓:
SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ; Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ; SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ; Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;
因?yàn)檫@是一個(gè)單色的位圖,所以黑色區(qū)域的位是0,而白色區(qū)域的位是1。
然后BitBlt呼叫就按此屏蔽修改了原圖像:
BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap, hdcMemMask, 0, 0, SRCAND) ;
SRCAND位映像操作在來(lái)源位(屏蔽位圖)和目的位(原圖像)之間執(zhí)行了位AND操作。只要屏蔽位圖是白色,就顯示目的;只要屏蔽是黑色,則目的就也是黑色?,F(xiàn)在原圖像中就形成了一個(gè)黑色包 圍的橢圓區(qū)域。
現(xiàn)在讓我們看一下WM_PAINT處理程序。此程序同時(shí)改變了選進(jìn)內(nèi)存設(shè)備內(nèi)容中的圖像位圖和屏蔽位圖。兩次BitBlt呼叫完成了這個(gè)魔術(shù),第一次在窗口上執(zhí)行屏蔽位圖的BitBlt:
BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326) ;
這里使用了一個(gè)沒(méi)有名稱的位映像操作。邏輯運(yùn)算子是D & ~S。回憶來(lái)源-即屏蔽位圖-是黑色(位值0)包圍的一個(gè)白色(位值1)橢圓。位映像操作首先將來(lái)源反色,也就是改成白色包圍的黑色橢圓。然后位操作在這個(gè)已轉(zhuǎn)換的來(lái)源和目的(即窗口上 )之間執(zhí)行位AND操作。當(dāng)目的和位值1「AND」時(shí)保持不變;與位值0「AND」時(shí),目的將變黑。因此,BitBlt操作將在窗口上畫(huà)一個(gè)黑色的橢圓。
第二次的BitBlt呼叫則在窗口中繪制圖像位圖:
BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT) ;
位映像操作在來(lái)源和目的之間執(zhí)行位「OR」操作。由于來(lái)源位圖的外面是黑色,因此保持目的不變;而在橢圓區(qū)域內(nèi),目的是黑色,因此圖像就原封不動(dòng)地復(fù)制了過(guò)來(lái)。執(zhí)行結(jié)果如圖14-9所示。
注意事項(xiàng):
有時(shí)您需要一個(gè)很復(fù)雜的屏蔽-例如,抹去原始圖像的整個(gè)背景。您將需要在畫(huà)圖程序中手工建立然后將其儲(chǔ)存到成文件。
圖14-9 BITMASK的屏幕顯示 |
如果正在為Windows NT/XP 編寫(xiě)類(lèi)似的應(yīng)用程序,那么您可以使用與MASKBIT程序類(lèi)似的MaskBlt函數(shù),而只需要更少的函數(shù)呼叫。Windows NT還包括另一個(gè)類(lèi)似BitBlt的函數(shù),Windows 98不支持該函數(shù)。此函數(shù)是PlgBlt(「平行四邊形位塊移動(dòng):parallelogram blt」)。這個(gè)函數(shù)可以對(duì)圖像進(jìn)行旋轉(zhuǎn)或者傾斜位圖圖像。
最后,如果在您的機(jī)器上執(zhí)行BITMASK程序,您就只會(huì)看見(jiàn)黑色、白色和兩個(gè)灰色的陰影,這是因?yàn)槟鷪?zhí)行的顯示模式是16色或256色。對(duì)于16色模式,顯示效果無(wú)法改進(jìn),但在256色模式下可以 改變調(diào)色盤(pán)以顯示灰階。
簡(jiǎn)單的動(dòng)畫(huà)
小張的位圖顯示起來(lái)非???,因此可以將位圖和Windows定時(shí)器聯(lián)合使用,來(lái)完成一些基本的動(dòng)畫(huà)。
現(xiàn)在開(kāi)始這個(gè)彈球程序。
BOUNCE程序,如程序14-10所示,產(chǎn)生了一個(gè)在窗口顯示區(qū)域彈來(lái)彈去的小球。該程序利用定時(shí)器來(lái)控制小球的行進(jìn)速度。小球本身是一幅位圖,程序首先通過(guò)建立位圖來(lái)建立小球,將其選進(jìn)內(nèi)存 設(shè)備內(nèi)容,然后呼叫一些簡(jiǎn)單的GDI函數(shù)。程序用BitBlt從一個(gè)內(nèi)存設(shè)備內(nèi)容將這個(gè)位圖小球畫(huà)到顯示器上。
BOUNCE.ASM;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
ID_TIMER equ 1
.DATA
szAppName TCHAR "Bounce",0
dbg DWORD 0
.DATA?
hInstance HINSTANCE ?
hBitmap HBITMAP ?
cxClient DWORD ?
cyClient DWORD ?
xCenter DWORD ?
yCenter DWORD ?
cxTotal DWORD ?
cyTotal DWORD ?
cxRadius DWORD ?
cyRadius DWORD ?
cxMove DWORD ?
cyMove DWORD ?
xPixel DWORD ?
yPixel DWORD ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL, IDI_INFORMATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX
lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Bouncing Ball"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hBrush :HBRUSH
LOCAL hdc, hdcMem :HDC
LOCAL iScale :DWORD
.if uMsg == WM_CREATE
invoke GetDC,hwnd
mov hdc,eax
invoke GetDeviceCaps,hdc, ASPECTX
mov xPixel,eax
invoke GetDeviceCaps,hdc, ASPECTY
mov yPixel,eax
invoke ReleaseDC,hwnd, hdc
invoke SetTimer,hwnd, ID_TIMER, 30, NULL
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax
shr eax,1
mov xCenter,eax
mov eax,lParam
shr eax,16
mov cyClient,eax
shr eax,1
mov yCenter,eax
mov eax,cxClient
mov ecx,xPixel
mul ecx
mov ebx,eax
mov eax,cyClient
mov ecx,yPixel
mul ecx
.if eax>ebx
mov eax,ebx
.endif
shr eax,4
mov iScale,eax
xor edx,edx
mov eax,iScale
mov ecx,xPixel
div ecx
mov cxRadius,eax
xor edx,edx
mov eax,iScale
mov ecx,yPixel
div ecx
mov cyRadius,eax
mov eax,cxRadius
shr eax,1
.if eax<1
mov eax,1
.endif
mov cxMove,eax
mov eax,cyRadius
shr eax,1
.if eax<1
mov eax,1
.endif
mov cyMove,eax
mov eax,cxRadius
add eax,cxMove
shl eax,1
mov cxTotal,eax
mov eax,cyRadius
add eax,cyMove
shl eax,1
mov cyTotal,eax
.if hBitmap!=0
invoke DeleteObject,hBitmap
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
invoke CreateCompatibleBitmap,hdc, cxTotal, cyTotal
mov hBitmap,eax
invoke ReleaseDC,hwnd, hdc
invoke SelectObject,hdcMem, hBitmap
mov eax,cxTotal
inc eax
mov ebx,cyTotal
inc ebx
invoke Rectangle,hdcMem, -1, -1, eax,ebx
invoke CreateHatchBrush,HS_DIAGCROSS, 0
mov hBrush,eax
invoke SelectObject,hdcMem, hBrush
mov eax,0FF0FFh
invoke SetBkColor,hdcMem, eax
mov eax,cxTotal
sub eax,cxMove
mov ebx,cyTotal
sub ebx,cyMove
invoke Ellipse,hdcMem, cxMove, cyMove,eax,ebx
invoke DeleteDC,hdcMem
invoke DeleteObject,hBrush
xor eax,eax
ret
.elseif uMsg == WM_TIMER
.if (hBitmap==0)
jmp Go_Ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
invoke SelectObject,hdcMem, hBitmap
mov eax,xCenter
mov ebx,cxTotal
shr ebx,1
sub eax,ebx
mov ecx,yCenter
mov edx,cyTotal
shr edx,1
sub ecx,edx
invoke BitBlt,hdc, eax,ecx, cxTotal, cyTotal,hdcMem, 0, 0, SRCCOPY
invoke ReleaseDC,hwnd, hdc
invoke DeleteDC,hdcMem
mov eax,cxMove
add xCenter,eax
mov eax,cyMove
add yCenter,eax
mov eax,xCenter
sub eax,cxRadius
cmp eax,0
jl @f
mov eax,xCenter
add eax,cxRadius
.if (eax >= cxClient)
@@:
mov eax,cxMove
neg eax
mov cxMove,eax
.endif
mov eax,yCenter
sub eax,cyRadius
cmp eax,0
jl @f
mov eax,yCenter
add eax,cyRadius
.if (eax >= cyClient)
@@:
mov eax,cyMove
neg eax
mov cyMove,eax
.endif
Go_Ret:
;invoke KillTimer,hwnd, ID_TIMER
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
.if hBitmap!=0
invoke DeleteObject,hBitmap
.endif
invoke KillTimer,hwnd, ID_TIMER
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START
BOUNCE每次收到一個(gè)WM_SIZE消息時(shí)都重畫(huà)小球。這就需要與視頻顯示器兼容的內(nèi)存設(shè)備內(nèi)容:
hdcMem = CreateCompatibleDC (hdc) ;
小球的直徑設(shè)為窗口顯示區(qū)域高度或?qū)挾戎休^短者的十六分之一。不過(guò),程序構(gòu)造的位圖卻比小球大:從位圖中心到位圖四個(gè)邊的距離是小球半徑的1.5倍:
hBitmap = CreateCompatibleBitmap (hdc, cxTotal, cyTotal) ;
將位圖選進(jìn)內(nèi)存設(shè)備內(nèi)容后,整個(gè)位圖背景設(shè)成白色:
Rectangle (hdcMem, -1, -1, xTotal + 1, yTotal + 1) ;
那些不固定的坐標(biāo)使矩形邊框在位圖之外著色。一個(gè)對(duì)角線開(kāi)口的畫(huà)刷選進(jìn)內(nèi)存設(shè)備內(nèi)容,并將小球畫(huà)在位圖的中央:
Ellipse (hdcMem, xMove, yMove, xTotal - xMove, yTotal - yMove) ;
當(dāng)小球移動(dòng)時(shí),小球邊界的空白會(huì)有效地刪除前一時(shí)刻的小球圖像。在另一個(gè)位置重畫(huà)小球只需在BitBlt呼叫中使用SRCCOPY的ROP代碼:
BitBlt (hdc, xCenter - cxTotal / 2, yCenter - cyTotal / 2, cxTotal, cyTotal, hdcMem, 0, 0, SRCCOPY) ;
BOUNCE程序只是展示了在顯示器上移動(dòng)圖像的最簡(jiǎn)單的方法。在一般情況下,這種方法并不能令人滿意。如果您對(duì)動(dòng)畫(huà)感興趣,那么除了在來(lái)源和目的之間執(zhí)行或操作以外,您還應(yīng)該研究其它的 ROP代碼(例如SRCINVERT)。其它動(dòng)畫(huà)技術(shù)包括Windows調(diào)色盤(pán)(以及AnimatePalette函數(shù))和CreateDIBSection函數(shù)。對(duì)于更高級(jí)的動(dòng)畫(huà)您只好放棄GDI而使用DirectX接口了。
窗口外的位圖
SCRAMBLE程序,如程序14-11所示,編寫(xiě)非常粗糙,我本來(lái)不應(yīng)該展示這個(gè)程序,但它示范了一些有趣的技術(shù),而且在交換兩個(gè)顯示矩形內(nèi)容的BitBlt操作的程序中,用內(nèi)存設(shè)備內(nèi)容作為臨時(shí)儲(chǔ)存 空間。
程序14-11 SCRAMBLE SCRAMBLE.ASM ;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include libc.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib Libc.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
NUM equ 300
.DATA
szAppName TCHAR "SCRAMBLE",0
.DATA?
hInstance HINSTANCE ?
iKeep DWORD NUM*4 dup(?)
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL hdcScr, hdcMem:HDC
LOCAL cXX, cYY:DWORD
LOCAL hBitmap:HBITMAP
LOCAL hwnd :HWND
LOCAL i, j, x1, y1, x2, y2 :DWORD
LOCAL tmp:SYSTEMTIME
invoke GetDesktopWindow
mov hwnd,eax
invoke LockWindowUpdate,eax
.if eax!=0
invoke GetDCEx,hwnd, NULL, DCX_CACHE or DCX_LOCKWINDOWUPDATE
mov hdcScr,eax
invoke CreateCompatibleDC,hdcScr
mov hdcMem,eax
invoke GetSystemMetrics,SM_CXSCREEN
xor edx,edx
mov ecx,10
div ecx
mov cXX,eax
invoke GetSystemMetrics,SM_CYSCREEN
xor edx,edx
mov ecx,10
div ecx
mov cYY,eax
invoke CreateCompatibleBitmap,hdcScr, cXX, cYY
mov hBitmap,eax
invoke SelectObject,hdcMem, hBitmap
invoke GetSystemTime,addr tmp
movsx eax,tmp.wMilliseconds ;只取后面的DWORD
invoke srand,eax
mov i,0
Loopi:
mov j,0
Loopj:
.if i==0
mov eax,j
shl eax,4 ;數(shù)組和DWORD
lea esi,iKeep
add esi,eax
push esi
invoke rand
pop esi
xor edx,edx
mov ecx,10
div ecx
mov eax,edx
mov ecx,cXX
mul ecx
mov x1,eax
mov [esi],eax
push esi
invoke rand
pop esi
xor edx,edx
mov ecx,10
div ecx
mov eax,edx
mov ecx,cYY
mul ecx
mov y1,eax
mov [esi+4],eax
push esi
invoke rand
pop esi
xor edx,edx
mov ecx,10
div ecx
mov eax,edx
mov ecx,cXX
mul ecx
mov x2,eax
mov [esi+8],eax
push esi
invoke rand
pop esi
xor edx,edx
mov ecx,10
div ecx
mov eax,edx
mov ecx,cYY
mul ecx
mov y2,eax
mov [esi+12],eax
.else
mov eax,j
shl eax,4 ;數(shù)組和DWORD
lea esi,iKeep
add esi,(NUM-1)*16
sub esi,eax
mov eax,[esi]
mov x1,eax
mov eax,[esi+4]
mov y1,eax
mov eax,[esi+8]
mov x2,eax
mov eax,[esi+12]
mov y2,eax
.endif
invoke BitBlt,hdcMem, 0, 0, cXX, cYY, hdcScr, x1, y1, SRCCOPY
invoke BitBlt,hdcScr, x1, y1, cXX, cYY, hdcScr, x2, y2, SRCCOPY
invoke BitBlt,hdcScr, x2, y2, cXX, cYY, hdcMem, 0, 0, SRCCOPY
invoke Sleep,1
invoke MessageBeep,0
inc j
.if j!=NUM
jmp Loopj
.endif
inc i
.if i!=2
jmp Loopi
.endif
invoke DeleteDC,hdcMem
invoke ReleaseDC,hwnd, hdcScr
invoke DeleteObject,hBitmap
invoke LockWindowUpdate,NULL
.endif
ret
WinMain endp
END START
SCRAMBLE沒(méi)有窗口消息處理程序。在WinMain中,它首先呼叫帶有桌面窗口句柄的LockWindowUpdate。此函數(shù)暫時(shí)防止其它程序更新屏幕。然后SCRAMBLE通過(guò)呼叫帶有參數(shù)DCX_LOCKWINDOWUPDATE的 GetDCEx來(lái)獲得整個(gè)屏幕的設(shè)備內(nèi)容。這樣就只有SCRAMBLE可以更新屏幕了。
然后SCRAMBLE確定全屏幕的尺寸,并將長(zhǎng)寬分別除以10。程序用這個(gè)尺寸(名稱是cx和cy)來(lái)建立一個(gè)位圖,并將該位圖選進(jìn)內(nèi)存設(shè)備內(nèi)容。
使用C語(yǔ)言的rand函數(shù),SCRAMBLE計(jì)算出四個(gè)隨機(jī)值(兩個(gè)坐標(biāo)點(diǎn))作為cx和cy的倍數(shù)。程序透過(guò)三次呼叫BitBlt函數(shù)來(lái)交換兩個(gè)矩形塊中顯示的內(nèi)容。第一次將從第一個(gè)坐標(biāo)點(diǎn)開(kāi)始的矩形復(fù)制到 內(nèi)存設(shè)備內(nèi)容。第二次BitBlt將從第二坐標(biāo)點(diǎn)開(kāi)始的矩形復(fù)制到第一點(diǎn)開(kāi)始的位置。第三次將內(nèi)存設(shè)備內(nèi)容中的矩形復(fù)制到第二個(gè)坐標(biāo)點(diǎn)開(kāi)始的區(qū)域。
此程序?qū)⒂行У亟粨Q顯示器上兩個(gè)矩形中的內(nèi)容。SCRAMBLE執(zhí)行300次交換,這時(shí)的屏幕顯示肯定是一團(tuán)糟。但不用擔(dān)心,因?yàn)镾CRAMBLE記得是怎么把顯示弄得這樣一團(tuán)糟的,接著在退出前它會(huì)按 相反的次序恢復(fù)原來(lái)的桌面顯示(鎖定屏幕前的畫(huà)面)!
您也可以用內(nèi)存設(shè)備內(nèi)容將一個(gè)位圖復(fù)制給另一個(gè)位圖。例如,假定您要建立一個(gè)位圖,該位圖只包含另一個(gè)位圖左上角的圖形。如果原來(lái)的圖像句柄為hBitmap,那么您可以將其尺寸復(fù)制到一個(gè) BITMAP型態(tài)的結(jié)構(gòu)中:
GetObject (hBitmap, sizeof (BITMAP), &bm) ;
然后建立一個(gè)未初始化的新位圖,該位圖的尺寸是原來(lái)圖的1/4:
hBitmap2 = CreateBitmap ( bm.bmWidth / 2, bm.bmHeight / 2, bm.bmPlanes, bm.bmBitsPixel, NULL) ;
現(xiàn)在建立兩個(gè)內(nèi)存設(shè)備內(nèi)容,并將原來(lái)位圖和新位圖選分別進(jìn)這兩個(gè)內(nèi)存設(shè)備內(nèi)容:
hdcMem1 = CreateCompatibleDC (hdc) ; hdcMem2 = CreateCompatibleDC (hdc) ; SelectObject (hdcMem1, hBitmap) ; SelectObject (hdcMem2, hBitmap2) ;
最后,將第一個(gè)位圖的左上角復(fù)制給第二個(gè):
BitBlt ( hdcMem2, 0, 0, bm.bmWidth / 2, bm.bmHeight / 2, hdcMem1, 0, 0, SRCCOPY) ;
剩下的只是清除工作:
DeleteDC (hdcMem1) ; DeleteDC (hdcMem2) ; DeleteObject (hBitmap) ;
BLOWUP.C程序,如圖14-21所示,也用窗口更新鎖定來(lái)在程序窗口之外顯示一個(gè)捕捉的矩形。此程序允許使用者用鼠標(biāo)圈選屏幕上的矩形區(qū)域,然后BLOWUP將該區(qū)域的內(nèi)容復(fù)制到位圖。在WM_PAINT 消息處理期間,位圖復(fù)制到程序的顯示區(qū)域,必要時(shí)將拉伸或壓縮。(參見(jiàn)程序14-12。)
程序14-12 BLOWUP BLOWUP.ASM;MASMPlus 代碼模板 - 普通的 Windows 程序代碼
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_EDIT_CUT equ 40001
IDM_EDIT_COPY equ 40002
IDM_EDIT_PASTE equ 40003
IDM_EDIT_DELETE equ 40004
.DATA
szAppName TCHAR "BLOWUP",0,0
ptBeg POINT <0,0>
ptEnd POINT <0,0>
bCapturing BOOL 0
bBlocking BOOL 0
.DATA?
hBitmap HBITMAP ?
hwndScr HWND ?
szBuffer TCHAR 100 dup(?)
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
LOCAL hAccel:HACCEL
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX
lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Blow-Up Mouse Demo"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
.if (hWnd == NULL)
invoke MessageBox,NULL, CTEXT ("Not enough memory to create bitmap!"),addr szAppName, MB_ICONERROR
xor eax,eax
ret
.endif
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
lea eax,szAppName
invoke LoadAccelerators,hInst,eax
mov hAccel,eax
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateAccelerator,hInst, hAccel,addr msg
.if eax==0
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endif
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
InvertBlock proc hwndScrIB:HWND,hwnd:HWND,ptBegAddr:DWORD,ptEndAddr:DWORD
LOCAL hdc:HDC
LOCAL aa,bb,cc,ff:DWORD
invoke GetDCEx,hwndScrIB, NULL, DCX_CACHE or DCX_LOCKWINDOWUPDATE
mov hdc,eax
invoke ClientToScreen,hwnd,ptBegAddr ;將用戶坐標(biāo)轉(zhuǎn)換成屏幕坐標(biāo)
invoke ClientToScreen,hwnd,ptEndAddr
mov esi,ptBegAddr
mov edi,ptEndAddr
push DSTINVERT
mov eax,[edi+4]
sub eax,[esi+4]
push eax
mov aa,eax
mov eax,[edi]
sub eax,[esi]
push eax
mov bb,eax
mov eax,[esi+4]
push eax
mov cc,eax
mov eax,[esi]
push eax
mov ff,eax
push hdc
call PatBlt
invoke wsprintf,addr szBuffer, CTXT ("[%d??%d] [%d??%d]"),aa,bb,cc,ff
invoke SetWindowText,hwnd,addr szBuffer
invoke ScreenToClient,hwnd,ptBegAddr
invoke ScreenToClient,hwnd,ptEndAddr
invoke ReleaseDC,hwndScrIB, hdc
ret
InvertBlock endp
CopyBitmap proc hBitmapSrc:HBITMAP
LOCAL bitmap:BITMAP
LOCAL hBitmapDst:HBITMAP
LOCAL hdcSrc, hdcDst:HDC
invoke GetObject,hBitmapSrc, sizeof (BITMAP),addr bitmap
invoke CreateBitmapIndirect,addr bitmap
mov hBitmapDst,eax
invoke CreateCompatibleDC,NULL
mov hdcSrc,eax
invoke CreateCompatibleDC,NULL
mov hdcDst,eax
invoke SelectObject,hdcSrc, hBitmapSrc
invoke SelectObject,hdcDst, hBitmapDst
invoke BitBlt,hdcDst, 0, 0, bitmap.bmWidth, bitmap.bmHeight,hdcSrc, 0, 0, SRCCOPY
invoke DeleteDC,hdcSrc
invoke DeleteDC,hdcDst
mov eax,hBitmapDst
ret
CopyBitmap endp
GetABS proc value:DWORD
cmp value,0
jl @f
mov eax,value
jmp GoEnd
@@:
mov eax,value
neg eax
GoEnd:
ret
GetABS endp
WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bm:BITMAP
LOCAL hBitmapClip:HBITMAP
LOCAL hdc, hdcMem:HDC
LOCAL iEnable:DWORD
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
.if uMsg == WM_LBUTTONDOWN ;按下左鍵
.if bCapturing==0 ;如果此時(shí)還沒(méi)有開(kāi)始抓圖
invoke GetDesktopWindow ;則取得整個(gè)窗口的handle
mov hwndScr,eax
;invoke LockWindowUpdate,eax ;鎖定指定窗口,禁止它更新
.if eax!=0
mov bCapturing,TRUE ;標(biāo)記開(kāi)始抓圖
invoke SetCapture,hwnd ;在指定窗口里設(shè)置鼠標(biāo)捕獲
invoke LoadCursor,NULL, IDC_CROSS
invoke SetCursor,eax
.else
invoke MessageBeep,0
.endif
.endif
xor eax,eax
ret
.elseif uMsg == WM_RBUTTONDOWN ;按下右鍵
.if bCapturing!=0 ;如果已經(jīng)開(kāi)始抓圖
mov bBlocking,TRUE ;設(shè)置抓圖區(qū)域標(biāo)志
mov eax,lParam
and eax,0FFFFh
mov ptBeg.x,eax
mov eax,lParam
shr eax,16
mov ptBeg.y,eax ;鼠標(biāo)的坐標(biāo)
mov esi,offset ptBeg
mov edi,offset ptEnd
mov eax,[esi]
mov [edi],eax
mov eax,[esi+4]
mov [edi+4],eax
invoke InvertBlock,hwndScr, hwnd,addr ptBeg,addr ptEnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_MOUSEMOVE
.if bBlocking!=0
invoke InvertBlock,hwndScr, hwnd,addr ptBeg,addr ptEnd
mov eax,lParam
and eax,0FFFFh
mov ptEnd.x,eax
mov eax,lParam
shr eax,16
mov ptEnd.y,eax
invoke InvertBlock,hwndScr, hwnd,addr ptBeg,addr ptEnd
.endif
xor eax,eax
ret
.elseif (uMsg == WM_LBUTTONUP)||(uMsg == WM_RBUTTONUP)
.if bBlocking!=0
invoke InvertBlock,hwndScr, hwnd,addr ptBeg,addr ptEnd
mov eax,lParam
and eax,0FFFFh
mov ptEnd.x,eax
mov eax,lParam
shr eax,16
mov ptEnd.y,eax
.if hBitmap!=0
invoke DeleteObject,hBitmap
mov hBitmap,NULL
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
mov esi,offset ptBeg
mov edi,offset ptEnd
mov eax,[edi+4]
sub eax,[esi+4]
invoke GetABS,eax
push eax
mov eax,[edi]
sub eax,[esi]
invoke GetABS,eax
push eax
push hdc
call CreateCompatibleBitmap
;invoke CreateCompatibleBitmap (hdc,abs (ptEnd.x - ptBeg.x),abs (ptEnd.y - ptBeg.y)) ;
mov hBitmap,eax
invoke SelectObject,hdcMem, hBitmap
mov esi,offset ptBeg
mov edi,offset ptEnd
push SRCCOPY
mov eax,[edi+4]
sub eax,[esi+4]
push eax
mov eax,[edi]
sub eax,[esi]
push eax
push [esi+4]
push [esi]
push hdc
mov eax,[edi+4]
sub eax,[esi+4]
invoke GetABS,eax
push eax
mov eax,[edi]
sub eax,[esi]
invoke GetABS,eax
push eax
push 0
push 0
push hdcMem
call StretchBlt
;invoke StretchBlt (hdcMem, 0, 0,abs (ptEnd.x - ptBeg.x),abs (ptEnd.y - ptBeg.y),
; hdc, ptBeg.x, ptBeg.y, ptEnd.x - ptBeg.x,ptEnd.y - ptBeg.y, SRCCOPY) ;
invoke DeleteDC,hdcMem
invoke ReleaseDC,hwnd, hdc
invoke InvalidateRect,hwnd, NULL, TRUE
.endif
.if (bBlocking!=0) || (bCapturing!=0)
mov eax,FALSE
mov bBlocking,eax
mov bCapturing,eax
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax
invoke ReleaseCapture
invoke LockWindowUpdate,NULL
.endif
xor eax,eax
ret
.elseif (uMsg == WM_INITMENUPOPUP)
invoke IsClipboardFormatAvailable,CF_BITMAP
.if eax==0
mov eax,MF_GRAYED
.else
mov eax,MF_ENABLED
.endif
invoke EnableMenuItem,wParam, IDM_EDIT_PASTE, iEnable
.if hBitmap==0
mov eax,MF_GRAYED
.else
mov eax,MF_ENABLED
.endif
mov iEnable,eax
invoke EnableMenuItem,wParam, IDM_EDIT_CUT, iEnable
invoke EnableMenuItem,wParam, IDM_EDIT_COPY, iEnable
invoke EnableMenuItem,wParam, IDM_EDIT_DELETE, iEnable
xor eax,eax
ret
.elseif (uMsg == WM_COMMAND)
mov eax,lParam
and eax,0FFFFh
.if (eax==IDM_EDIT_CUT)||(eax==IDM_EDIT_COPY)
.if hBitmap!=0
invoke CopyBitmap,hBitmap
mov hBitmapClip,eax
invoke OpenClipboard,hwnd
invoke EmptyClipboard
invoke SetClipboardData,CF_BITMAP, hBitmapClip
.endif
mov eax,lParam
and eax,0FFFFh
.if eax==IDM_EDIT_COPY
xor eax,eax
ret
.endif
jmp @f ;fall through for IDM_EDIT_CUT
.elseif eax==IDM_EDIT_DELETE
@@:
.if hBitmap!=0
invoke DeleteObject,hBitmap
mov hBitmap,NULL
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.elseif eax== IDM_EDIT_PASTE
.if (hBitmap!=0)
invoke DeleteObject,hBitmap
mov hBitmap,NULL
.endif
invoke OpenClipboard,hwnd
invoke GetClipboardData,CF_BITMAP
mov hBitmapClip,eax
.if (hBitmapClip!=0)
invoke CopyBitmap,hBitmapClip
mov hBitmap,eax
.endif
invoke CloseClipboard
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.endif
.elseif (uMsg == WM_PAINT)
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if (hBitmap!=0)
invoke GetClientRect,hwnd,addr rect
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
invoke SelectObject,hdcMem, hBitmap
invoke GetObject,hBitmap, sizeof (BITMAP), addr bm
invoke SetStretchBltMode,hdc, COLORONCOLOR
invoke StretchBlt,hdc, 0, 0, rect.right, rect.bottom,hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY
invoke DeleteDC,hdcMem
.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif (uMsg == WM_DESTROY)
.if (hBitmap!=0)
invoke DeleteObject,hBitmap
.endif
invoke PostQuitMessage,0
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START
圖14-10 BLOWUP顯示的一個(gè)范例 |
由于鼠標(biāo)攔截的限制,所以開(kāi)始使用BLOWUP時(shí)會(huì)有些困難,需要逐漸適應(yīng)。下面是使用本程序的方法:
鼠標(biāo)光標(biāo)恢復(fù)成箭頭狀,這時(shí)您圈選的矩形區(qū)域已復(fù)制到了BLOWUP的顯示區(qū)域,并作了適當(dāng)?shù)膲嚎s或拉伸變化。
如果您從右上角到左下角選取的話,BLOWUP將顯示矩形區(qū)域的鏡像。如果從左下到右上角選取,BLOWUP將顯示顛倒的圖像。如果從右上角至左上角選取,程序?qū)⒕C合兩種效果。
BLOWUP還 包含將位圖復(fù)制到剪貼簿,以及將剪貼簿中的位圖復(fù)制到程序的處理功能。BLOWUP處理WM_INITMENUPOPUP消息來(lái)啟用或禁用「Edit」菜單中的不同選項(xiàng),并通過(guò)WM_COMMAND消息來(lái)處理這些菜單項(xiàng)。您應(yīng) 該對(duì)這些程序代碼的結(jié)構(gòu)比較熟悉,因?yàn)樗鼈兣c第十二章中的復(fù)制和粘貼文字項(xiàng)目的處理方式在本質(zhì)上是一樣的。
不過(guò),對(duì)于位圖,剪貼簿對(duì)象不是整體句柄而是位圖句柄。當(dāng)您使用CF_BITMAP時(shí), GetClipboardData函數(shù)傳回一個(gè)HBITMAP對(duì)象,而且SetClipboardData函數(shù)接收一個(gè)HBITMAP對(duì)象。如果您想將位圖傳送給剪貼簿又想保留副本以供程序本身使用,那么您必須復(fù)制位圖。同樣,如 果您從剪貼簿上粘貼了一幅位圖,也應(yīng)該做一個(gè)副本。BLOWUP中的CopyBitmap函數(shù)是通過(guò)取得現(xiàn)存位圖的BITMAP結(jié)構(gòu),并在CreateBitmapIndirect函數(shù)中用這個(gè)結(jié)構(gòu)建立一個(gè)新位圖來(lái)完成此項(xiàng)操作的。( 變量名的后綴Src和Dst分別代表「來(lái)源」和「目的」。) 兩個(gè)位圖都被選進(jìn)內(nèi)存設(shè)備內(nèi)容,而且通過(guò)呼叫BitBlt來(lái)復(fù)制位圖內(nèi)容。(另一種復(fù)制位的方法,可以先按位圖大小配置一塊內(nèi)存,然后為來(lái)源位圖呼叫GetBitmapBits,為目的位圖呼叫 SetBitmapBits。)
我發(fā)現(xiàn)BLOWUP對(duì)于檢查Windows及其應(yīng)用程序中大量分散的小位圖和圖片非常有用
聯(lián)系客服