分类:
2009-07-19 13:44:25
相关的例子: | 作者:Zoologist 于2008-12-15上传 |
位图中的画刷
BRICKS系列的最后一个项目是BRICKS3,如程序14-5所示。乍看此程序,您可能会有这种感觉:程序代码哪里去了呢?
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文件,而且窗口看上去也相同。
正如您看到的一样,窗口消息处理程序没有更多的内容。BRICKS3实际上使用砖块图案作为窗口类别背景画刷,它在WNDCLASS结构的hbrBackground字段中定义。
您现在可能猜想GDI画刷是很小的位图,通常是8个像素见方。如果将LOGBRUSH结构的lbStyle字段设定为BS_PATTERN,然后呼叫CreatePatternBrush或CreateBrushIndirect,您就可以在位图外面 来建立画刷了。此位图至少是宽高各8个像素。如果再大,Windows 98将只使用位图的左上角作为画刷。而Windows NT不受此限制,它会使用整个位图。
请记住,画刷和位图都是GDI对象,而且您应该在程序终止前删除您在程序中建立画刷和位图。如果您依据位图建立画刷,那么在用画刷画图时,Windows将复制位图位到画刷所绘制的区域内。呼 叫CreatePatternBrush(或者CreateBrushIndirect)之后,您可以立即删除位图而不会影响到画笔。类似地,您也可以删除画刷而不会影响到您选进的原始位图。注意,BRICKS3在建立画刷后删除了位 图,并在程序终止前删除了画刷。
绘制位图
在窗口中绘图时,我们已经将位图当成绘图来源使用过了。这要求先将位图选进内存设备内容,并呼叫BitBlt或者StretchBlt。您也可以用内存设备内容句柄作为所有实际呼叫的GDI函数中的第一 参数。内存设备内容的动作与实际的设备内容相同,除非显示平面是位图。
程序14-6所示的HELLOBIT程序展示了此项技术。程序在一个小位图上显示了字符串「Hello, world!」,然后从位图到程序显示区域执行BitBlt或StretchBlt(依照选择的菜单选项而定)。
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
.endif
mov eax,cyBitmap
add y,eax
mov
eax,y
.if eax
.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确定字符串的像素尺寸开始。这些尺寸将成为与视频显示兼容的位图尺寸。当此位图被选进内存设备内容(也与视频显示兼容)后,再呼叫TextOut将文字显示在 位图上。内存设备内容在程序执行期间保留。在处理WM_DESTROY信息期间,HELLOBIT删除了位图和内存设备内容。
HELLOBIT中的一条菜单选项允许您显示位图尺寸,此尺寸或者是显示区域中水平和垂直方向平铺的实际尺寸,或者是缩放成显示区域大小的尺寸,如图14-4所示。正与您所见到的一样,这不是显 示大尺寸字符的好方法!它只是小字体的放大版,并带有放大时产生的锯齿线。
图14-4 HELLOBIT的屏幕显示 |
您可能想知道一个程序,例如HELLOBIT,是否需要处理WM_DISPLAYCHANGE消息。只要使用者(或者其它应用程序)修改了视频显示大小或者颜色深度,应用程序就接收到此讯息。其中颜色深度的 改变会导致内存设备内容和视频设备内容不兼容。但这并不会发生,因为当显示模式修改后,Windows自动修改了内存设备内容的颜色分辨率。选进内存设备内容的位图仍然保持原样,但不会造成任何问 题。
阴影位图
在内存设备内容绘图(也就是位图)的技术是执行「阴影位图(shadow bitmap)」的关键。此位图包含窗口显示区域中显示的所有内容。这样,对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
.endif
mov
esi,pcxBitmap
mov [esi],eax
mov esi,pcyBitmap
mov eax,[esi]
.if eax
.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 ;这个地方是我设计用来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中画线,请按下鼠标左键并拖动鼠标。要擦掉画过的东西(更确切地说,是画白线),请按下鼠标右键并拖动鼠标。要清空整个窗口,请…结束程序,然后重新加载,一切从头再来 。图14-5中显示的SKETCH程序图样表达了对频果公司的麦金塔计算机早期广告的敬意。
图14-5 SKETCH的屏幕显示 |
此阴影位图应多大?在本程序中,它应该大到能包含最大化窗口的整个显示区域。这一问题很容易根据GetSystemMetrics信息计算得出,但如果使用者修改了显示设定后再显示,进而扩大了最大 化时窗口的尺寸,这时将发生什么呢?SKETCH程序在EnumDisplaySettings函数的帮助下解决了此问题。此函数使用DEVMODE结构来传回全部有效视频显示模式的信息。第一次呼叫此函数时,应将 EnumDisplaySettings的第二参数设为0,以后每次呼叫此值都增加。EnumDisplaySettings传回FALSE时完成。
与此同时,SKETCH将建立一个阴影位图,它比目前视频显示模式的表面还多四倍,而且需要几兆字节的内存。由于如此,SKETCH将检查位图是否建立成功了,如果没有建立,就从WM_CREATE传回-1 ,以表示错误。
在WM_MOUSEMOVE消息处理期间,按下鼠标左键或者右键,并在内存设备内容和显示区域设备内容中画线时,SKETCH拦截鼠标。如果画线方式更复杂的话,您可能想在一个函数中实作,程序将呼叫 此函数两次-一次画在视频设备内容上,一次画在内存设备内容上。
下面是一个有趣的实验:使SKETCH窗口小于全画面尺寸。随着鼠标左键的按下,将鼠标拖出窗口的右下角。因为SKETCH拦截鼠标,所以它继续接收并处理WM_MOUSEMOVE消息。现在扩大窗口,您将 看到阴影位图包含您在SKETCH窗口外所画的内容。
在菜单中使用位图
您也可以用位图在菜单上显示选项。如果您联想起菜单中文件夹、剪贴簿和资源回收筒的图片,那么不要再想那些图片了。您应该考虑一下,菜单上显示位图对画图程序用途有多大,想象一下在 菜单中使用不同字体和字体大小、线宽、阴影图案以及颜色。
GRAFMENU是展示图形菜单选项的范例程序。此程序顶层菜单如图14-6所示。放大的字母来自于40×16像素的单色位图文件,该文件在Visual C++ Developer Studio建立。从菜单上选择「FONT」将弹出三个选择项-「Courier New」、「 Arial」和「Times New Roman」。它们是标准的Windows TrueType字体,并且每一个都按其相关的字体显示,如图14-7所示。这些位图在程序中用内存设备内容建立。
图14-6 GRAFMENU程序的顶层菜单 |
图14-7 GRAFMENU程序弹出的「FONT」菜单 |
最后,在拉下系统菜单时,您将获得一些「辅助」信息,用「HELP」表示了新使用者的在线求助项目(参见图14-8)。此64×64像素的单色位图是在Developer Studio中建立的。
图14-8 GRAFMENU程序系统菜单 |
GRAFMENU程序,包括四个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。位图有两个来源:可以在Visual C++ Developer Studio建立位图,包括资源脚本中的位图文件,并在程序使用LoadBitmap时将位图资源加载到内存,然后呼叫AppendMenu或InsertMenu将位图附加到菜单上。但是用这种方法会有一些问题:位图 不适于所有显示模式的分辨率和纵横比;有时您需要缩放加载的位图以解决此问题。另一种方法是:在程序内部建立位图,并将它选进内存设备内容,画出来,然后再附加到菜单中。
GRAFMENU中的GetBitmapFont函数的参数为0、1或2,传回一个位图句柄。此位图包含字符串「Courier New」、「Arial」或「Times New Roman」,而且字体是各自对应的字体,大小是正常系统字体的两倍。让我们看看GetBitmapFont是怎么做的。(下面的程序代码与GRAFMENU.C文件中的有些不同。为了清楚起见,我用「Arial」字 体相应的值代替了引用szFaceName数组。)
第一步是用TEXTMETRIC结构来确定目前系统字体的大小,并建立一个与目前屏幕兼容的内存设备内容:
hdc = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ; GetTextMetrics (hdc, &tm) ; hdcMem = CreateCompatibleDC (hdc) ;
CreateFont函数建立了一种逻辑字体,该字体高是系统字体的两倍,而且逻辑名称为「Arial」:
hFont = CreateFont (2 * tm.tmHeight, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TEXT ("Arial")) ;
从内存设备内容中选择该字体,然后储存内定字体句柄:
hFont = (HFONT) SelectObject (hdcMem, hFont) ;
现在,当我们向内存设备内容写一些文字时,Windows就会使用选进设备内容的TrueType Arial字体了。
但这个内存设备内容最初只有一个单像素单色设备平面。我们必须建立一个足够大的位图以容纳我们所要显示的文字。通过GetTextExtentPoint32函数,可以取得文字的大小,而用CreateBitmap 可以根据这些尺寸来建立位图:
GetTextExtentPoint32 (hdcMem, TEXT ("Arial"), 5, &size) ; hBitmap = CreateBitmap (size.cx, size.cy, 1, 1, NULL) ; SelectObject (hdcMem, hBitmap) ;
现在这个设备内容是一个单色的显示平面,大小也是严格的文字尺寸。我们现在要做的就是书写文字:
TextOut (hdcMem, 0, 0, TEXT ("Arial"), 5) ;
除了清除,所有的工作都完成了。要清除,我们可以用SelectObject将系统字体(带有句柄hFont)重新选进设备内容,然后删除SelectObject传回的前一个字体句柄,也就是Arial字体句柄:
DeleteObject (SelectObject (hdcMem, hFont)) ;
现在可以删除两个设备内容:
DeleteDC (hdcMem) ; DeleteDC (hdc) ;
这样,我们就获得了一个位图,该位图上有Arial字体的字符串「Arial」。
当我们需要缩放字体以适应不同显示分辨率或纵横比时,内存设备内容也能解决问题。在GRAFMENU程序中,我建立了四个位图,这些位图只适用于系统字体高8像素、宽4像素的显示。对于其它尺 寸的系统字体,只能缩放位图。GRAFMENU中的StretchBitmap函数完成此功能。
第一步是获得显示的设备内容,然后取得系统字体的文字规格,接下来建立两个内存设备内容:
hdc = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ; GetTextMetrics (hdc, &tm) ; hdcMem1 = CreateCompatibleDC (hdc) ; hdcMem2 = CreateCompatibleDC (hdc) ; DeleteDC (hdc) ;
传递给函数的位图句柄是hBitmap1。程序能用GetObject获得位图的大小:
GetObject (hBitmap1, sizeof (BITMAP), (PSTR) &bm1) ;
此操作将尺寸复制到BITMAP型态的结构bm1中。结构bm2等于结构bm1,然后根据系统字体大小来修改某些字段:
bm2 = bm1 ; bm2.bmWidth = (tm.tmAveCharWidth * bm2.bmWidth) / 4 ; bm2.bmHeight = (tm.tmHeight * bm2.bmHeight) / 8 ; bm2.bmWidthBytes = ((bm2.bmWidth + 15) / 16) * 2 ;
下一个位图带有句柄hBitmap2,可以根据动态的尺寸建立:
hBitmap2 = CreateBitmapIndirect (&bm2) ;
然后将这两个位图选进两个内存设备内容中:
SelectObject (hdcMem1, hBitmap1) ; SelectObject (hdcMem2, hBitmap2) ;
我们想把第一个位图复制给第二个位图,并在此程序中进行拉伸。这包括StretchBlt呼叫:
StretchBlt (hdcMem2, 0, 0, bm2.bmWidth, bm2.bmHeight, hdcMem1, 0, 0, bm1.bmWidth, bm1.bmHeight, SRCCOPY) ;
现在第二幅图适当地缩放了,我们可将其用到菜单中。剩下的清除工作很简单:
DeleteDC (hdcMem1) ; DeleteDC (hdcMem2) ; DeleteObject (hBitmap1) ;
在建造菜单时,GRAFMENU中的CreateMyMenu函数呼叫了StretchBitmap和GetBitmapFont函数。GRAFMENU在资源文件中定义了两个菜单,在选择「File」和「Edit」选项时会弹出这两个菜单。函数 开始先取得一个空菜单的句柄:
hMenu = CreateMenu () ;
从资源文件加载「File」的弹出式菜单(包括四个选项:「New」、「Open」、「Save」和「Save as」):
hMenuPopup = LoadMenu (hInstance, TEXT ("MenuFile")) ;
从资源文件还加载了包含「FILE」的位图,并用StretchBitmap进行了拉伸:
hBitmapFile = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapFile"))) ;
位图句柄和弹出式菜单句柄都是AppendMenu呼叫的参数:
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函数可以构造这三种不同字体的弹出式菜单:
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函数中的系统菜单。此函数首先获得一个系统菜单句柄:
hMenu = GetSystemMenu (hwnd, FALSE) ;
这将载入「HELP」位图,并将其拉伸到适当尺寸:
hBitmapHelp = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapHelp"))) ;
这将给系统菜单添加一条分隔线和拉伸的位图:
AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ; AppendMenu (hMenu, MF_BITMAP, IDM_HELP, (PTSTR)(LONG) hBitmapHelp) ;
GRAFMENU在退出之前呼叫一个函数来清除并删除所有位图。
下面是在菜单中使用位图的一些注意事项。
在顶层菜单中,Windows调整菜单列的高度以适应最高的位图。其它位图(或字符串)是根据菜单列的顶端对齐的。如果在顶层菜单中使用了位图,那么从使用常数SM_CYMENU的GetSystemMetrics得 到的菜单列大小将不再有效。
执行GRAFMENU期间可以看到:在弹出式菜单中,您可使用带有位图菜单项的勾选标记,但勾选标记是正常尺寸。如果不满意,您可以建立一个自订的勾选标记,并使用SetMenuItemBitmaps。
在菜单中使用非文字(或者使用非系统字体的文字)的另一种方法是「拥有者绘制」菜单。
菜单的键盘接口是另一个问题。当菜单含有文字时,Windows会自动添加键盘接口。要选择一个菜单项,可以使用Alt与字符串中的一个字母的组合键。而一旦在菜单中放置了位图,就删除了键盘 接口。即使位图表达了一定的含义,但Windows并不知道。
目前我们可以使用WM_MENUCHAR消息。当您按下Alt和与菜单项不相符的一个字符键的组合键时,Windows将向您的窗口消息处理程序发送一个WM_MENUCHAR消息。GRAFMENU需要截取WM_MENUCHAR消息 并检查wParam的值(即按键的ASCII码)。如果这个值对应一个菜单项,那么向Windows传回双字组:其中高字组为2,低字组是与该键相关的菜单项索引值。然后由Windows处理余下的事。
非矩形位图图像
位图都是矩形,但不需要都显示成矩形。例如,假定您有一个矩形位图图像,但您却想将它显示成椭圆形。
首先,这听起来很简单。您只需将图像加载Visual C++ Developer Studio或者Windows的「画图」程序,然后用白色的画笔将图像四周画上白色。这时将获得一幅椭圆形的图像,而椭圆的外面就 成了白色。只有当背景色为白色时此位图才能正确显示,如果在其它背景色上显示,您就会发现椭圆形的图像和背景之间有一个白色的矩形。这种效果不好。
有一种非常通用的技术可解决此类问题。这种技术包括「屏蔽(mask)」位图和一些位映像操作。屏蔽是一种单色位图,它与您要显示的矩形位图图像尺寸相同。每个屏蔽的像素都对应位图图像 的一个像素。屏蔽像素是1(白色),对应着位图像素显示;是0(黑色),则显示背景色。(或者屏蔽位图与此相反,这根据您使用的位映像操作而有一些相对应的变化。)
让我们看看BITMASK程序是如何实作这一技术的。如程序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文件是原作者侄子的一幅黑白数字照片,宽200像素,高320像素,每像素8位。不过,另外制作个BITMASK只是因为此文件的内容是任何东西都可以。
注意,BITMASK将窗口背景设为亮灰色。这样就确保我们能正确地屏蔽位图,而不只是将其涂成白色。
下面让我们看一下WM_CREATE的处理程序:BITMASK用LoadBitmap函数获得hBitmapImag变量中原始图像的句柄。用GetObject函数可取得位图的宽度高度。然后将位图句柄选进句柄为hdcMemImag的 内存设备内容中。
程序建立的下一个单色位图与原来的图大小相同,其句柄储存在hBitmapMask,并选进句柄为hdcMemMask的内存设备内容中。在内存设备内容中,使用GDI函数,屏蔽位图就涂成了黑色背景和一个 白色的椭圆:
SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ; Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ; SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ; Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;
因为这是一个单色的位图,所以黑色区域的位是0,而白色区域的位是1。
然后BitBlt呼叫就按此屏蔽修改了原图像:
BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap, hdcMemMask, 0, 0, SRCAND) ;
SRCAND位映像操作在来源位(屏蔽位图)和目的位(原图像)之间执行了位AND操作。只要屏蔽位图是白色,就显示目的;只要屏蔽是黑色,则目的就也是黑色。现在原图像中就形成了一个黑色包 围的椭圆区域。
现在让我们看一下WM_PAINT处理程序。此程序同时改变了选进内存设备内容中的图像位图和屏蔽位图。两次BitBlt呼叫完成了这个魔术,第一次在窗口上执行屏蔽位图的BitBlt:
BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326) ;
这里使用了一个没有名称的位映像操作。逻辑运算子是D & ~S。回忆来源-即屏蔽位图-是黑色(位值0)包围的一个白色(位值1)椭圆。位映像操作首先将来源反色,也就是改成白色包围的黑色椭圆。然后位操作在这个已转换的来源和目的(即窗口上 )之间执行位AND操作。当目的和位值1「AND」时保持不变;与位值0「AND」时,目的将变黑。因此,BitBlt操作将在窗口上画一个黑色的椭圆。
第二次的BitBlt呼叫则在窗口中绘制图像位图:
BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT) ;
位映像操作在来源和目的之间执行位「OR」操作。由于来源位图的外面是黑色,因此保持目的不变;而在椭圆区域内,目的是黑色,因此图像就原封不动地复制了过来。执行结果如图14-9所示。
注意事项:
有时您需要一个很复杂的屏蔽-例如,抹去原始图像的整个背景。您将需要在画图程序中手工建立然后将其储存到成文件。
图14-9 BITMASK的屏幕显示 |
如果正在为Windows NT/XP 编写类似的应用程序,那么您可以使用与MASKBIT程序类似的MaskBlt函数,而只需要更少的函数呼叫。Windows NT还包括另一个类似BitBlt的函数,Windows 98不支持该函数。此函数是PlgBlt(「平行四边形位块移动:parallelogram blt」)。这个函数可以对图像进行旋转或者倾斜位图图像。
最后,如果在您的机器上执行BITMASK程序,您就只会看见黑色、白色和两个灰色的阴影,这是因为您执行的显示模式是16色或256色。对于16色模式,显示效果无法改进,但在256色模式下可以 改变调色盘以显示灰阶。
简单的动画
小张的位图显示起来非常快,因此可以将位图和Windows定时器联合使用,来完成一些基本的动画。
现在开始这个弹球程序。
BOUNCE程序,如程序14-10所示,产生了一个在窗口显示区域弹来弹去的小球。该程序利用定时器来控制小球的行进速度。小球本身是一幅位图,程序首先通过建立位图来建立小球,将其选进内存 设备内容,然后呼叫一些简单的GDI函数。程序用BitBlt从一个内存设备内容将这个位图小球画到显示器上。
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消息时都重画小球。这就需要与视频显示器兼容的内存设备内容:
hdcMem = CreateCompatibleDC (hdc) ;
小球的直径设为窗口显示区域高度或宽度中较短者的十六分之一。不过,程序构造的位图却比小球大:从位图中心到位图四个边的距离是小球半径的1.5倍:
hBitmap = CreateCompatibleBitmap (hdc, cxTotal, cyTotal) ;
将位图选进内存设备内容后,整个位图背景设成白色:
Rectangle (hdcMem, -1, -1, xTotal + 1, yTotal + 1) ;
那些不固定的坐标使矩形边框在位图之外着色。一个对角线开口的画刷选进内存设备内容,并将小球画在位图的中央:
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程序只是展示了在显示器上移动图像的最简单的方法。在一般情况下,这种方法并不能令人满意。如果您对动画感兴趣,那么除了在来源和目的之间执行或操作以外,您还应该研究其它的 ROP代码(例如SRCINVERT)。其它动画技术包括Windows调色盘(以及AnimatePalette函数)和CreateDIBSection函数。对于更高级的动画您只好放弃GDI而使用DirectX接口了。
窗口外的位图
SCRAMBLE程序,如程序14-11所示,编写非常粗糙,我本来不应该展示这个程序,但它示范了一些有趣的技术,而且在交换两个显示矩形内容的BitBlt操作的程序中,用内存设备内容作为临时储存 空间。
程序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 ;数组和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 ;数组和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。此函数暂时防止其它程序更新屏幕。然后SCRAMBLE通过呼叫带有参数DCX_LOCKWINDOWUPDATE的 GetDCEx来获得整个屏幕的设备内容。这样就只有SCRAMBLE可以更新屏幕了。
然后SCRAMBLE确定全屏幕的尺寸,并将长宽分别除以10。程序用这个尺寸(名称是cx和cy)来建立一个位图,并将该位图选进内存设备内容。
使用C语言的rand函数,SCRAMBLE计算出四个随机值(两个坐标点)作为cx和cy的倍数。程序透过三次呼叫BitBlt函数来交换两个矩形块中显示的内容。第一次将从第一个坐标点开始的矩形复制到 内存设备内容。第二次BitBlt将从第二坐标点开始的矩形复制到第一点开始的位置。第三次将内存设备内容中的矩形复制到第二个坐标点开始的区域。
此程序将有效地交换显示器上两个矩形中的内容。SCRAMBLE执行300次交换,这时的屏幕显示肯定是一团糟。但不用担心,因为SCRAMBLE记得是怎么把显示弄得这样一团糟的,接着在退出前它会按 相反的次序恢复原来的桌面显示(锁定屏幕前的画面)!
您也可以用内存设备内容将一个位图复制给另一个位图。例如,假定您要建立一个位图,该位图只包含另一个位图左上角的图形。如果原来的图像句柄为hBitmap,那么您可以将其尺寸复制到一个 BITMAP型态的结构中:
GetObject (hBitmap, sizeof (BITMAP), &bm) ;
然后建立一个未初始化的新位图,该位图的尺寸是原来图的1/4:
hBitmap2 = CreateBitmap ( bm.bmWidth / 2, bm.bmHeight / 2, bm.bmPlanes, bm.bmBitsPixel, NULL) ;
现在建立两个内存设备内容,并将原来位图和新位图选分别进这两个内存设备内容:
hdcMem1 = CreateCompatibleDC (hdc) ; hdcMem2 = CreateCompatibleDC (hdc) ; SelectObject (hdcMem1, hBitmap) ; SelectObject (hdcMem2, hBitmap2) ;
最后,将第一个位图的左上角复制给第二个:
BitBlt ( hdcMem2, 0, 0, bm.bmWidth / 2, bm.bmHeight / 2, hdcMem1, 0, 0, SRCCOPY) ;
剩下的只是清除工作:
DeleteDC (hdcMem1) ; DeleteDC (hdcMem2) ; DeleteObject (hBitmap) ;
BLOWUP.C程序,如图14-21所示,也用窗口更新锁定来在程序窗口之外显示一个捕捉的矩形。此程序允许使用者用鼠标圈选屏幕上的矩形区域,然后BLOWUP将该区域的内容复制到位图。在WM_PAINT 消息处理期间,位图复制到程序的显示区域,必要时将拉伸或压缩。(参见程序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 ;将用户坐标转换成屏幕坐标
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 ;在指定窗口里设置鼠标捕获
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 ;如果已经开始抓图
mov bBlocking,TRUE
;设置抓图区域标志
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时会有些困难,需要逐渐适应。下面是使用本程序的方法:
鼠标光标恢复成箭头状,这时您圈选的矩形区域已复制到了BLOWUP的显示区域,并作了适当的压缩或拉伸变化。
如果您从右上角到左下角选取的话,BLOWUP将显示矩形区域的镜像。如果从左下到右上角选取,BLOWUP将显示颠倒的图像。如果从右上角至左上角选取,程序将综合两种效果。
BLOWUP还 包含将位图复制到剪贴簿,以及将剪贴簿中的位图复制到程序的处理功能。BLOWUP处理WM_INITMENUPOPUP消息来启用或禁用「Edit」菜单中的不同选项,并通过WM_COMMAND消息来处理这些菜单项。您应 该对这些程序代码的结构比较熟悉,因为它们与中的复制和粘贴文字项目的处理方式在本质上是一样的。
不过,对于位图,剪贴簿对象不是整体句柄而是位图句柄。当您使用CF_BITMAP时, GetClipboardData函数传回一个HBITMAP对象,而且SetClipboardData函数接收一个HBITMAP对象。如果您想将位图传送给剪贴簿又想保留副本以供程序本身使用,那么您必须复制位图。同样,如 果您从剪贴簿上粘贴了一幅位图,也应该做一个副本。BLOWUP中的CopyBitmap函数是通过取得现存位图的BITMAP结构,并在CreateBitmapIndirect函数中用这个结构建立一个新位图来完成此项操作的。( 变量名的后缀Src和Dst分别代表「来源」和「目的」。) 两个位图都被选进内存设备内容,而且通过呼叫BitBlt来复制位图内容。(另一种复制位的方法,可以先按位图大小配置一块内存,然后为来源位图呼叫GetBitmapBits,为目的位图呼叫 SetBitmapBits。)
我发现BLOWUP对于检查Windows及其应用程序中大量分散的小位图和图片非常有用。
|