国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
見招拆招《Windows程序設(shè)計》(十二) 第二部分

位圖中的畫刷

BRICKS系列的最后一個項目是BRICKS3,如程序14-5所示。乍看此程序,您可能會有這種感覺:程序代碼哪里去了呢?

程序14-5 BRICKS3
        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使用同一個BRICKS.BMP文件,而且窗口看上去也相同。

正如您看到的一樣,窗口消息處理程序沒有更多的內(nèi)容。BRICKS3實際上使用磚塊圖案作為窗口類別背景畫刷,它在WNDCLASS結(jié)構(gòu)的hbrBackground字段中定義。

您現(xiàn)在可能猜想GDI畫刷是很小的位圖,通常是8個像素見方。如果將LOGBRUSH結(jié)構(gòu)的lbStyle字段設(shè)定為BS_PATTERN,然后呼叫CreatePatternBrush或CreateBrushIndirect,您就可以在位圖外面 來建立畫刷了。此位圖至少是寬高各8個像素。如果再大,Windows 98將只使用位圖的左上角作為畫刷。而Windows NT不受此限制,它會使用整個位圖。

請記住,畫刷和位圖都是GDI對象,而且您應(yīng)該在程序終止前刪除您在程序中建立畫刷和位圖。如果您依據(jù)位圖建立畫刷,那么在用畫刷畫圖時,Windows將復(fù)制位圖位到畫刷所繪制的區(qū)域內(nèi)。呼 叫CreatePatternBrush(或者CreateBrushIndirect)之后,您可以立即刪除位圖而不會影響到畫筆。類似地,您也可以刪除畫刷而不會影響到您選進的原始位圖。注意,BRICKS3在建立畫刷后刪除了位 圖,并在程序終止前刪除了畫刷。

繪制位圖

在窗口中繪圖時,我們已經(jīng)將位圖當成繪圖來源使用過了。這要求先將位圖選進內(nèi)存設(shè)備內(nèi)容,并呼叫BitBlt或者StretchBlt。您也可以用內(nèi)存設(shè)備內(nèi)容句柄作為所有實際呼叫的GDI函數(shù)中的第一 參數(shù)。內(nèi)存設(shè)備內(nèi)容的動作與實際的設(shè)備內(nèi)容相同,除非顯示平面是位圖。

程序14-6所示的HELLOBIT程序展示了此項技術(shù)。程序在一個小位圖上顯示了字符串「Hello, world!」,然后從位圖到程序顯示區(qū)域執(zhí)行BitBlt或StretchBlt(依照選擇的菜單選項而定)。

程序14-6 HELLOBIT
        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確定字符串的像素尺寸開始。這些尺寸將成為與視頻顯示兼容的位圖尺寸。當此位圖被選進內(nèi)存設(shè)備內(nèi)容(也與視頻顯示兼容)后,再呼叫TextOut將文字顯示在 位圖上。內(nèi)存設(shè)備內(nèi)容在程序執(zhí)行期間保留。在處理WM_DESTROY信息期間,HELLOBIT刪除了位圖和內(nèi)存設(shè)備內(nèi)容。

HELLOBIT中的一條菜單選項允許您顯示位圖尺寸,此尺寸或者是顯示區(qū)域中水平和垂直方向平鋪的實際尺寸,或者是縮放成顯示區(qū)域大小的尺寸,如圖14-4所示。正與您所見到的一樣,這不是顯 示大尺寸字符的好方法!它只是小字體的放大版,并帶有放大時產(chǎn)生的鋸齒線。


 

圖14-4 HELLOBIT的屏幕顯示

您可能想知道一個程序,例如HELLOBIT,是否需要處理WM_DISPLAYCHANGE消息。只要使用者(或者其它應(yīng)用程序)修改了視頻顯示大小或者顏色深度,應(yīng)用程序就接收到此訊息。其中顏色深度的 改變會導(dǎo)致內(nèi)存設(shè)備內(nèi)容和視頻設(shè)備內(nèi)容不兼容。但這并不會發(fā)生,因為當顯示模式修改后,Windows自動修改了內(nèi)存設(shè)備內(nèi)容的顏色分辨率。選進內(nèi)存設(shè)備內(nèi)容的位圖仍然保持原樣,但不會造成任何問 題。

陰影位圖

在內(nèi)存設(shè)備內(nèi)容繪圖(也就是位圖)的技術(shù)是執(zhí)行「陰影位圖(shadow bitmap)」的關(guān)鍵。此位圖包含窗口顯示區(qū)域中顯示的所有內(nèi)容。這樣,對WM_PAINT消息的處理就簡化到簡單的BitBlt。

陰影位圖在繪畫程序中最有用。程序14-7所示的SKETCH程序并不是一個最完美的繪畫程序,但它是一個開始。

程序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 ;這個地方是我設(shè)計用來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中畫線,請按下鼠標左鍵并拖動鼠標。要擦掉畫過的東西(更確切地說,是畫白線),請按下鼠標右鍵并拖動鼠標。要清空整個窗口,請…結(jié)束程序,然后重新加載,一切從頭再來 。圖14-5中顯示的SKETCH程序圖樣表達了對頻果公司的麥金塔計算機早期廣告的敬意。


 

圖14-5 SKETCH的屏幕顯示

此陰影位圖應(yīng)多大?在本程序中,它應(yīng)該大到能包含最大化窗口的整個顯示區(qū)域。這一問題很容易根據(jù)GetSystemMetrics信息計算得出,但如果使用者修改了顯示設(shè)定后再顯示,進而擴大了最大 化時窗口的尺寸,這時將發(fā)生什么呢?SKETCH程序在EnumDisplaySettings函數(shù)的幫助下解決了此問題。此函數(shù)使用DEVMODE結(jié)構(gòu)來傳回全部有效視頻顯示模式的信息。第一次呼叫此函數(shù)時,應(yīng)將 EnumDisplaySettings的第二參數(shù)設(shè)為0,以后每次呼叫此值都增加。EnumDisplaySettings傳回FALSE時完成。

與此同時,SKETCH將建立一個陰影位圖,它比目前視頻顯示模式的表面還多四倍,而且需要幾兆字節(jié)的內(nèi)存。由于如此,SKETCH將檢查位圖是否建立成功了,如果沒有建立,就從WM_CREATE傳回-1 ,以表示錯誤。

在WM_MOUSEMOVE消息處理期間,按下鼠標左鍵或者右鍵,并在內(nèi)存設(shè)備內(nèi)容和顯示區(qū)域設(shè)備內(nèi)容中畫線時,SKETCH攔截鼠標。如果畫線方式更復(fù)雜的話,您可能想在一個函數(shù)中實作,程序?qū)⒑艚?此函數(shù)兩次-一次畫在視頻設(shè)備內(nèi)容上,一次畫在內(nèi)存設(shè)備內(nèi)容上。

下面是一個有趣的實驗:使SKETCH窗口小于全畫面尺寸。隨著鼠標左鍵的按下,將鼠標拖出窗口的右下角。因為SKETCH攔截鼠標,所以它繼續(xù)接收并處理WM_MOUSEMOVE消息。現(xiàn)在擴大窗口,您將 看到陰影位圖包含您在SKETCH窗口外所畫的內(nèi)容。

在菜單中使用位圖

您也可以用位圖在菜單上顯示選項。如果您聯(lián)想起菜單中文件夾、剪貼簿和資源回收筒的圖片,那么不要再想那些圖片了。您應(yīng)該考慮一下,菜單上顯示位圖對畫圖程序用途有多大,想象一下在 菜單中使用不同字體和字體大小、線寬、陰影圖案以及顏色。

GRAFMENU是展示圖形菜單選項的范例程序。此程序頂層菜單如圖14-6所示。放大的字母來自于40×16像素的單色位圖文件,該文件在Visual C++ Developer Studio建立。從菜單上選擇「FONT」將彈出三個選擇項-「Courier New」、「 Arial」和「Times New Roman」。它們是標準的Windows TrueType字體,并且每一個都按其相關(guān)的字體顯示,如圖14-7所示。這些位圖在程序中用內(nèi)存設(shè)備內(nèi)容建立。


 

圖14-6 GRAFMENU程序的頂層菜單


 

圖14-7 GRAFMENU程序彈出的「FONT」菜單

最后,在拉下系統(tǒng)菜單時,您將獲得一些「輔助」信息,用「HELP」表示了新使用者的在線求助項目(參見圖14-8)。此64×64像素的單色位圖是在Developer Studio中建立的。


 

圖14-8 GRAFMENU程序系統(tǒng)菜單

GRAFMENU程序,包括四個Developer Studio中建立的位圖,如程序14-8所示。

程序14-8 GRAFMENU
        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。位圖有兩個來源:可以在Visual C++ Developer Studio建立位圖,包括資源腳本中的位圖文件,并在程序使用LoadBitmap時將位圖資源加載到內(nèi)存,然后呼叫AppendMenu或InsertMenu將位圖附加到菜單上。但是用這種方法會有一些問題:位圖 不適于所有顯示模式的分辨率和縱橫比;有時您需要縮放加載的位圖以解決此問題。另一種方法是:在程序內(nèi)部建立位圖,并將它選進內(nèi)存設(shè)備內(nèi)容,畫出來,然后再附加到菜單中。

GRAFMENU中的GetBitmapFont函數(shù)的參數(shù)為0、1或2,傳回一個位圖句柄。此位圖包含字符串「Courier New」、「Arial」或「Times New Roman」,而且字體是各自對應(yīng)的字體,大小是正常系統(tǒng)字體的兩倍。讓我們看看GetBitmapFont是怎么做的。(下面的程序代碼與GRAFMENU.C文件中的有些不同。為了清楚起見,我用「Arial」字 體相應(yīng)的值代替了引用szFaceName數(shù)組。)

第一步是用TEXTMETRIC結(jié)構(gòu)來確定目前系統(tǒng)字體的大小,并建立一個與目前屏幕兼容的內(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)容中選擇該字體,然后儲存內(nèi)定字體句柄:

hFont = (HFONT) SelectObject (hdcMem, hFont) ;        

現(xiàn)在,當我們向內(nèi)存設(shè)備內(nèi)容寫一些文字時,Windows就會使用選進設(shè)備內(nèi)容的TrueType Arial字體了。

但這個內(nèi)存設(shè)備內(nèi)容最初只有一個單像素單色設(shè)備平面。我們必須建立一個足夠大的位圖以容納我們所要顯示的文字。通過GetTextExtentPoint32函數(shù),可以取得文字的大小,而用CreateBitmap 可以根據(jù)這些尺寸來建立位圖:

GetTextExtentPoint32 (hdcMem, TEXT ("Arial"), 5, &size) ;        hBitmap = CreateBitmap (size.cx, size.cy, 1, 1, NULL) ;        SelectObject (hdcMem, hBitmap) ;        

現(xiàn)在這個設(shè)備內(nèi)容是一個單色的顯示平面,大小也是嚴格的文字尺寸。我們現(xiàn)在要做的就是書寫文字:

TextOut (hdcMem, 0, 0, TEXT ("Arial"), 5) ;        

除了清除,所有的工作都完成了。要清除,我們可以用SelectObject將系統(tǒng)字體(帶有句柄hFont)重新選進設(shè)備內(nèi)容,然后刪除SelectObject傳回的前一個字體句柄,也就是Arial字體句柄:

DeleteObject (SelectObject (hdcMem, hFont)) ;        

現(xiàn)在可以刪除兩個設(shè)備內(nèi)容:

DeleteDC (hdcMem) ;        DeleteDC (hdc) ;        

這樣,我們就獲得了一個位圖,該位圖上有Arial字體的字符串「Arial」。

當我們需要縮放字體以適應(yīng)不同顯示分辨率或縱橫比時,內(nèi)存設(shè)備內(nèi)容也能解決問題。在GRAFMENU程序中,我建立了四個位圖,這些位圖只適用于系統(tǒng)字體高8像素、寬4像素的顯示。對于其它尺 寸的系統(tǒng)字體,只能縮放位圖。GRAFMENU中的StretchBitmap函數(shù)完成此功能。

第一步是獲得顯示的設(shè)備內(nèi)容,然后取得系統(tǒng)字體的文字規(guī)格,接下來建立兩個內(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獲得位圖的大?。?/p>

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)字體大小來修改某些字段:

bm2 = bm1 ;        bm2.bmWidth                = (tm.tmAveCharWidth * bm2.bmWidth)  / 4 ;        bm2.bmHeight               = (tm.tmHeight       * bm2.bmHeight) / 8 ;        bm2.bmWidthBytes           = ((bm2.bmWidth + 15) / 16) * 2 ;        

下一個位圖帶有句柄hBitmap2,可以根據(jù)動態(tài)的尺寸建立:

hBitmap2 = CreateBitmapIndirect (&bm2) ;        

然后將這兩個位圖選進兩個內(nèi)存設(shè)備內(nèi)容中:

SelectObject (hdcMem1, hBitmap1) ;        SelectObject (hdcMem2, hBitmap2) ;        

我們想把第一個位圖復(fù)制給第二個位圖,并在此程序中進行拉伸。這包括StretchBlt呼叫:

StretchBlt (hdcMem2, 0, 0, bm2.bmWidth, bm2.bmHeight,                                          hdcMem1, 0, 0, bm1.bmWidth, bm1.bmHeight, SRCCOPY) ;        

現(xiàn)在第二幅圖適當?shù)乜s放了,我們可將其用到菜單中。剩下的清除工作很簡單:

DeleteDC (hdcMem1) ;        DeleteDC (hdcMem2) ;        DeleteObject (hBitmap1) ;        

在建造菜單時,GRAFMENU中的CreateMyMenu函數(shù)呼叫了StretchBitmap和GetBitmapFont函數(shù)。GRAFMENU在資源文件中定義了兩個菜單,在選擇「File」和「Edit」選項時會彈出這兩個菜單。函數(shù) 開始先取得一個空菜單的句柄:

hMenu = CreateMenu () ;        

從資源文件加載「File」的彈出式菜單(包括四個選項:「New」、「Open」、「Save」和「Save as」):

hMenuPopup = LoadMenu (hInstance, TEXT ("MenuFile")) ;        

從資源文件還加載了包含「FILE」的位圖,并用StretchBitmap進行了拉伸:

hBitmapFile = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapFile"))) ;        

位圖句柄和彈出式菜單句柄都是AppendMenu呼叫的參數(shù):

AppendMenu (hMenu, MF_BITMAP | MF_POPUP, hMenuPopup, (PTSTR) (LONG)        hBitmapFile) ;        

「Edit」菜單類似程序如下:

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通過呼叫SetMenu,完成了窗口菜單的建立工作。

GRAFMENU還改變了AddHelpToSys函數(shù)中的系統(tǒng)菜單。此函數(shù)首先獲得一個系統(tǒng)菜單句柄:

hMenu = GetSystemMenu (hwnd, FALSE) ;        

這將載入「HELP」位圖,并將其拉伸到適當尺寸:

hBitmapHelp = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapHelp"))) ;        

這將給系統(tǒng)菜單添加一條分隔線和拉伸的位圖:

AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ;        AppendMenu (hMenu, MF_BITMAP, IDM_HELP, (PTSTR)(LONG) hBitmapHelp) ;        

GRAFMENU在退出之前呼叫一個函數(shù)來清除并刪除所有位圖。

下面是在菜單中使用位圖的一些注意事項。

在頂層菜單中,Windows調(diào)整菜單列的高度以適應(yīng)最高的位圖。其它位圖(或字符串)是根據(jù)菜單列的頂端對齊的。如果在頂層菜單中使用了位圖,那么從使用常數(shù)SM_CYMENU的GetSystemMetrics得 到的菜單列大小將不再有效。

執(zhí)行GRAFMENU期間可以看到:在彈出式菜單中,您可使用帶有位圖菜單項的勾選標記,但勾選標記是正常尺寸。如果不滿意,您可以建立一個自訂的勾選標記,并使用SetMenuItemBitmaps。

在菜單中使用非文字(或者使用非系統(tǒng)字體的文字)的另一種方法是「擁有者繪制」菜單。

菜單的鍵盤接口是另一個問題。當菜單含有文字時,Windows會自動添加鍵盤接口。要選擇一個菜單項,可以使用Alt與字符串中的一個字母的組合鍵。而一旦在菜單中放置了位圖,就刪除了鍵盤 接口。即使位圖表達了一定的含義,但Windows并不知道。

目前我們可以使用WM_MENUCHAR消息。當您按下Alt和與菜單項不相符的一個字符鍵的組合鍵時,Windows將向您的窗口消息處理程序發(fā)送一個WM_MENUCHAR消息。GRAFMENU需要截取WM_MENUCHAR消息 并檢查wParam的值(即按鍵的ASCII碼)。如果這個值對應(yīng)一個菜單項,那么向Windows傳回雙字組:其中高字組為2,低字組是與該鍵相關(guān)的菜單項索引值。然后由Windows處理余下的事。

非矩形位圖圖像

位圖都是矩形,但不需要都顯示成矩形。例如,假定您有一個矩形位圖圖像,但您卻想將它顯示成橢圓形。

首先,這聽起來很簡單。您只需將圖像加載Visual C++ Developer Studio或者Windows的「畫圖」程序,然后用白色的畫筆將圖像四周畫上白色。這時將獲得一幅橢圓形的圖像,而橢圓的外面就 成了白色。只有當背景色為白色時此位圖才能正確顯示,如果在其它背景色上顯示,您就會發(fā)現(xiàn)橢圓形的圖像和背景之間有一個白色的矩形。這種效果不好。

有一種非常通用的技術(shù)可解決此類問題。這種技術(shù)包括「屏蔽(mask)」位圖和一些位映像操作。屏蔽是一種單色位圖,它與您要顯示的矩形位圖圖像尺寸相同。每個屏蔽的像素都對應(yīng)位圖圖像 的一個像素。屏蔽像素是1(白色),對應(yīng)著位圖像素顯示;是0(黑色),則顯示背景色。(或者屏蔽位圖與此相反,這根據(jù)您使用的位映像操作而有一些相對應(yīng)的變化。)

讓我們看看BITMASK程序是如何實作這一技術(shù)的。如程序14-9所示。

程序14-9 BITMASK
        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位。不過,另外制作個BITMASK只是因為此文件的內(nèi)容是任何東西都可以。

注意,BITMASK將窗口背景設(shè)為亮灰色。這樣就確保我們能正確地屏蔽位圖,而不只是將其涂成白色。

下面讓我們看一下WM_CREATE的處理程序:BITMASK用LoadBitmap函數(shù)獲得hBitmapImag變量中原始圖像的句柄。用GetObject函數(shù)可取得位圖的寬度高度。然后將位圖句柄選進句柄為hdcMemImag的 內(nèi)存設(shè)備內(nèi)容中。

程序建立的下一個單色位圖與原來的圖大小相同,其句柄儲存在hBitmapMask,并選進句柄為hdcMemMask的內(nèi)存設(shè)備內(nèi)容中。在內(nèi)存設(shè)備內(nèi)容中,使用GDI函數(shù),屏蔽位圖就涂成了黑色背景和一個 白色的橢圓:

SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ;        Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;        SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ;        Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;        

因為這是一個單色的位圖,所以黑色區(qū)域的位是0,而白色區(qū)域的位是1。

然后BitBlt呼叫就按此屏蔽修改了原圖像:

BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap,                                  hdcMemMask, 0, 0, SRCAND) ;        

SRCAND位映像操作在來源位(屏蔽位圖)和目的位(原圖像)之間執(zhí)行了位AND操作。只要屏蔽位圖是白色,就顯示目的;只要屏蔽是黑色,則目的就也是黑色?,F(xiàn)在原圖像中就形成了一個黑色包 圍的橢圓區(qū)域。

現(xiàn)在讓我們看一下WM_PAINT處理程序。此程序同時改變了選進內(nèi)存設(shè)備內(nèi)容中的圖像位圖和屏蔽位圖。兩次BitBlt呼叫完成了這個魔術(shù),第一次在窗口上執(zhí)行屏蔽位圖的BitBlt:

BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326) ;        

這里使用了一個沒有名稱的位映像操作。邏輯運算子是D & ~S?;貞泚碓矗雌帘挝粓D-是黑色(位值0)包圍的一個白色(位值1)橢圓。位映像操作首先將來源反色,也就是改成白色包圍的黑色橢圓。然后位操作在這個已轉(zhuǎn)換的來源和目的(即窗口上 )之間執(zhí)行位AND操作。當目的和位值1「AND」時保持不變;與位值0「AND」時,目的將變黑。因此,BitBlt操作將在窗口上畫一個黑色的橢圓。

第二次的BitBlt呼叫則在窗口中繪制圖像位圖:

BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT) ;        

位映像操作在來源和目的之間執(zhí)行位「OR」操作。由于來源位圖的外面是黑色,因此保持目的不變;而在橢圓區(qū)域內(nèi),目的是黑色,因此圖像就原封不動地復(fù)制了過來。執(zhí)行結(jié)果如圖14-9所示。

注意事項:

有時您需要一個很復(fù)雜的屏蔽-例如,抹去原始圖像的整個背景。您將需要在畫圖程序中手工建立然后將其儲存到成文件。


 

圖14-9 BITMASK的屏幕顯示

如果正在為Windows NT/XP 編寫類似的應(yīng)用程序,那么您可以使用與MASKBIT程序類似的MaskBlt函數(shù),而只需要更少的函數(shù)呼叫。Windows NT還包括另一個類似BitBlt的函數(shù),Windows 98不支持該函數(shù)。此函數(shù)是PlgBlt(「平行四邊形位塊移動:parallelogram blt」)。這個函數(shù)可以對圖像進行旋轉(zhuǎn)或者傾斜位圖圖像。

最后,如果在您的機器上執(zhí)行BITMASK程序,您就只會看見黑色、白色和兩個灰色的陰影,這是因為您執(zhí)行的顯示模式是16色或256色。對于16色模式,顯示效果無法改進,但在256色模式下可以 改變調(diào)色盤以顯示灰階。

簡單的動畫

小張的位圖顯示起來非常快,因此可以將位圖和Windows定時器聯(lián)合使用,來完成一些基本的動畫。

現(xiàn)在開始這個彈球程序。

BOUNCE程序,如程序14-10所示,產(chǎn)生了一個在窗口顯示區(qū)域彈來彈去的小球。該程序利用定時器來控制小球的行進速度。小球本身是一幅位圖,程序首先通過建立位圖來建立小球,將其選進內(nèi)存 設(shè)備內(nèi)容,然后呼叫一些簡單的GDI函數(shù)。程序用BitBlt從一個內(nèi)存設(shè)備內(nèi)容將這個位圖小球畫到顯示器上。

程序14-10 BOUNCE
        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每次收到一個WM_SIZE消息時都重畫小球。這就需要與視頻顯示器兼容的內(nèi)存設(shè)備內(nèi)容:

hdcMem = CreateCompatibleDC (hdc) ;        

小球的直徑設(shè)為窗口顯示區(qū)域高度或?qū)挾戎休^短者的十六分之一。不過,程序構(gòu)造的位圖卻比小球大:從位圖中心到位圖四個邊的距離是小球半徑的1.5倍:

hBitmap = CreateCompatibleBitmap (hdc, cxTotal, cyTotal) ;        

將位圖選進內(nèi)存設(shè)備內(nèi)容后,整個位圖背景設(shè)成白色:

Rectangle (hdcMem, -1, -1, xTotal + 1, yTotal + 1) ;        

那些不固定的坐標使矩形邊框在位圖之外著色。一個對角線開口的畫刷選進內(nèi)存設(shè)備內(nèi)容,并將小球畫在位圖的中央:

Ellipse (hdcMem, xMove, yMove, xTotal - xMove, yTotal - yMove) ;        

當小球移動時,小球邊界的空白會有效地刪除前一時刻的小球圖像。在另一個位置重畫小球只需在BitBlt呼叫中使用SRCCOPY的ROP代碼:

BitBlt (hdc, xCenter - cxTotal / 2, yCenter - cyTotal / 2, cxTotal, cyTotal,                          hdcMem, 0, 0, SRCCOPY) ;        

BOUNCE程序只是展示了在顯示器上移動圖像的最簡單的方法。在一般情況下,這種方法并不能令人滿意。如果您對動畫感興趣,那么除了在來源和目的之間執(zhí)行或操作以外,您還應(yīng)該研究其它的 ROP代碼(例如SRCINVERT)。其它動畫技術(shù)包括Windows調(diào)色盤(以及AnimatePalette函數(shù))和CreateDIBSection函數(shù)。對于更高級的動畫您只好放棄GDI而使用DirectX接口了。

窗口外的位圖 

SCRAMBLE程序,如程序14-11所示,編寫非常粗糙,我本來不應(yīng)該展示這個程序,但它示范了一些有趣的技術(shù),而且在交換兩個顯示矩形內(nèi)容的BitBlt操作的程序中,用內(nèi)存設(shè)備內(nèi)容作為臨時儲存 空間。

程序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沒有窗口消息處理程序。在WinMain中,它首先呼叫帶有桌面窗口句柄的LockWindowUpdate。此函數(shù)暫時防止其它程序更新屏幕。然后SCRAMBLE通過呼叫帶有參數(shù)DCX_LOCKWINDOWUPDATE的 GetDCEx來獲得整個屏幕的設(shè)備內(nèi)容。這樣就只有SCRAMBLE可以更新屏幕了。

然后SCRAMBLE確定全屏幕的尺寸,并將長寬分別除以10。程序用這個尺寸(名稱是cx和cy)來建立一個位圖,并將該位圖選進內(nèi)存設(shè)備內(nèi)容。

使用C語言的rand函數(shù),SCRAMBLE計算出四個隨機值(兩個坐標點)作為cx和cy的倍數(shù)。程序透過三次呼叫BitBlt函數(shù)來交換兩個矩形塊中顯示的內(nèi)容。第一次將從第一個坐標點開始的矩形復(fù)制到 內(nèi)存設(shè)備內(nèi)容。第二次BitBlt將從第二坐標點開始的矩形復(fù)制到第一點開始的位置。第三次將內(nèi)存設(shè)備內(nèi)容中的矩形復(fù)制到第二個坐標點開始的區(qū)域。

此程序?qū)⒂行У亟粨Q顯示器上兩個矩形中的內(nèi)容。SCRAMBLE執(zhí)行300次交換,這時的屏幕顯示肯定是一團糟。但不用擔心,因為SCRAMBLE記得是怎么把顯示弄得這樣一團糟的,接著在退出前它會按 相反的次序恢復(fù)原來的桌面顯示(鎖定屏幕前的畫面)!

您也可以用內(nèi)存設(shè)備內(nèi)容將一個位圖復(fù)制給另一個位圖。例如,假定您要建立一個位圖,該位圖只包含另一個位圖左上角的圖形。如果原來的圖像句柄為hBitmap,那么您可以將其尺寸復(fù)制到一個 BITMAP型態(tài)的結(jié)構(gòu)中:

GetObject (hBitmap, sizeof (BITMAP), &bm) ;        

然后建立一個未初始化的新位圖,該位圖的尺寸是原來圖的1/4:

hBitmap2 = CreateBitmap (  bm.bmWidth / 2, bm.bmHeight / 2,                                bm.bmPlanes, bm.bmBitsPixel, NULL) ;        

現(xiàn)在建立兩個內(nèi)存設(shè)備內(nèi)容,并將原來位圖和新位圖選分別進這兩個內(nèi)存設(shè)備內(nèi)容:

hdcMem1 = CreateCompatibleDC (hdc) ;        hdcMem2 = CreateCompatibleDC (hdc) ;        SelectObject (hdcMem1, hBitmap) ;        SelectObject (hdcMem2, hBitmap2) ;        

最后,將第一個位圖的左上角復(fù)制給第二個:

BitBlt (    hdcMem2, 0, 0, bm.bmWidth / 2, bm.bmHeight / 2,                                  hdcMem1, 0, 0, SRCCOPY) ;        

剩下的只是清除工作:

DeleteDC (hdcMem1) ;        DeleteDC (hdcMem2) ;        DeleteObject (hBitmap) ;        

BLOWUP.C程序,如圖14-21所示,也用窗口更新鎖定來在程序窗口之外顯示一個捕捉的矩形。此程序允許使用者用鼠標圈選屏幕上的矩形區(qū)域,然后BLOWUP將該區(qū)域的內(nèi)容復(fù)制到位圖。在WM_PAINT 消息處理期間,位圖復(fù)制到程序的顯示區(qū)域,必要時將拉伸或壓縮。(參見程序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 ;將用戶坐標轉(zhuǎn)換成屏幕坐標
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 ;如果此時還沒有開始抓圖
invoke GetDesktopWindow ;則取得整個窗口的handle
mov hwndScr,eax
;invoke LockWindowUpdate,eax ;鎖定指定窗口,禁止它更新
.if eax!=0
mov bCapturing,TRUE ;標記開始抓圖
invoke SetCapture,hwnd ;在指定窗口里設(shè)置鼠標捕獲
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)開始抓圖
mov bBlocking,TRUE ;設(shè)置抓圖區(qū)域標志

mov eax,lParam
and eax,0FFFFh
mov ptBeg.x,eax

mov eax,lParam
shr eax,16 
mov ptBeg.y,eax ;鼠標的坐標

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顯示的一個范例

由于鼠標攔截的限制,所以開始使用BLOWUP時會有些困難,需要逐漸適應(yīng)。下面是使用本程序的方法:

  1. 在BLOWUP顯示區(qū)域按下鼠標左鍵不放,鼠標指針會變成「+」字型。
  2. 繼續(xù)按住左鍵,將鼠標移到屏幕上的任何其它位置。鼠標光標的位置就是您要圈選的矩形區(qū)域的左上角。
  3. 繼續(xù)按住左鍵,按下鼠標右鍵,然后拖動鼠標到您要圈選的矩形區(qū)域的右下角。釋放鼠標左鍵和右鍵。(釋放鼠標左、右鍵次序無關(guān)緊要。)

鼠標光標恢復(fù)成箭頭狀,這時您圈選的矩形區(qū)域已復(fù)制到了BLOWUP的顯示區(qū)域,并作了適當?shù)膲嚎s或拉伸變化。

如果您從右上角到左下角選取的話,BLOWUP將顯示矩形區(qū)域的鏡像。如果從左下到右上角選取,BLOWUP將顯示顛倒的圖像。如果從右上角至左上角選取,程序?qū)⒕C合兩種效果。

BLOWUP還 包含將位圖復(fù)制到剪貼簿,以及將剪貼簿中的位圖復(fù)制到程序的處理功能。BLOWUP處理WM_INITMENUPOPUP消息來啟用或禁用「Edit」菜單中的不同選項,并通過WM_COMMAND消息來處理這些菜單項。您應(yīng) 該對這些程序代碼的結(jié)構(gòu)比較熟悉,因為它們與第十二章中的復(fù)制和粘貼文字項目的處理方式在本質(zhì)上是一樣的。

不過,對于位圖,剪貼簿對象不是整體句柄而是位圖句柄。當您使用CF_BITMAP時, GetClipboardData函數(shù)傳回一個HBITMAP對象,而且SetClipboardData函數(shù)接收一個HBITMAP對象。如果您想將位圖傳送給剪貼簿又想保留副本以供程序本身使用,那么您必須復(fù)制位圖。同樣,如 果您從剪貼簿上粘貼了一幅位圖,也應(yīng)該做一個副本。BLOWUP中的CopyBitmap函數(shù)是通過取得現(xiàn)存位圖的BITMAP結(jié)構(gòu),并在CreateBitmapIndirect函數(shù)中用這個結(jié)構(gòu)建立一個新位圖來完成此項操作的。( 變量名的后綴Src和Dst分別代表「來源」和「目的」。) 兩個位圖都被選進內(nèi)存設(shè)備內(nèi)容,而且通過呼叫BitBlt來復(fù)制位圖內(nèi)容。(另一種復(fù)制位的方法,可以先按位圖大小配置一塊內(nèi)存,然后為來源位圖呼叫GetBitmapBits,為目的位圖呼叫 SetBitmapBits。)

我發(fā)現(xiàn)BLOWUP對于檢查Windows及其應(yīng)用程序中大量分散的小位圖和圖片非常有用

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
把我的第一個匯編程序(QQ自動群發(fā)器)源碼與和我一樣初級的編程愛好者共享
【新提醒】獲取并顯示當前內(nèi)存使用情況(支持4G以上內(nèi)存),Win32匯編語言案例解析,典型案例解析(VIP免費),魚C論壇
鼠標屏幕取詞的原碼
Win32匯編基礎(chǔ)教程
【原創(chuàng)】Nasm
WIN32匯編: 27.工具提示控件
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服