见招拆招《Windows程序设计》(十三) 第三部分
相关的例子:下载>>> 作者:Zoologist  于2009-3-17上传 

在处理「File Open」菜单命令期间,在SEQDISP.C内的所有文件I/O都会发生。在处理WM_COMMAND的最后,程序进入读取单行图素并用SetDIBitsToDevice显示该行图素的循环。整个DIB储存在内存中以便在处理WM_PAINT期间也能显示它。

缩放到合适尺寸

SetDIBitsToDevice完成了将DIB的图素对点送入输出设备的显示程序。这对于打印DIB用处不大。打印机的分辨率越高,得到的图像就越小,您最终会得到如邮票大小的图像。

要通过缩小或放大DIB,在输出设备上以特定的大小显示它,可以使用StretchDIBits:

iLines = StretchDIBits (
        
                                  hdc,           // device context handle
        
                                  xDst,         // x destination coordinate
        
                                  yDst,          // y destination coordinate
        
                                  cxDst,         // destination rectangle width
        
                                  cyDst,         // destination rectangle height
        
                                xSrc,          // x source coordinate
        
                                  ySrc,          // y source coordinate
        
                                  cxSrc,         // source rectangle width
        
                                  cySrc,         // source rectangle height
        
                                  pBits,         // pointer to DIB pixel bits
        
                                  pInfo,         // pointer to DIB information
        
                                  fClrUse,       // color use flag
        
                                  dwRop) ;      // raster operation
        

函数参数除了下列三个方面,均与SetDIBitsToDevice相同。

还有另一个更细微的差别。如果查看SetDIBitsToDevice的声明,您会发现cxSrc和cySrc是DWORD,这是32位无正负号长整数型态。在StretchDIBits中,cxSrc和cySrc(以及cxDst和cyDst)定义为带正负号的整数型态,这意味着它们可以为负数,实际上等一下就会看到,它们确实能为负数。如果您已经开始检查是否别的参数也可以为负数,就让我声明一下:在两个函数中,xSrc和ySrc均定义为int,但这是错的,这些值始终是非负数。

DIB内的来源矩形被映射到目的矩形的坐标显示如表15-4所示。

表15-4

来源矩形

目的矩形

(xSrc, ySrc)

(xDst, yDst + cyDst - 1)

(xSrc + cxSrc - 1, ySrc)

(xDst + cxDst - 1, yDst + cyDst - 1)

(xSrc, ySrc + cySrc - 1)

(xDst, yDst)

(xSrc + cxSrc - 1, ySrc + cySrc - 1)

(xDst + cxDst - 1, yDst)

右列中的-1项是不精确的,因为放大的程度(以及映像方式和其它变换)能产生略微不同的结果。

例如,考虑一个2×2的DIB,这里StretchDIBits的xSrc和ySrc参数均为0,cxSrc和cySrc均为2。假定我们显示到的设备内容具有MM_TEXT映像方式并且不进行变换。如果xDst和yDst均为0,cxDst和cyDst均为4,那么我们将以倍数2放大DIB。每个来源图素(x,y)将映射到下面所示的四个目的图素上:

(0,0) --> (0,2) and (1,2) and (0,3) and (1,3)
        
(1,0) --> (2,2) and (3,2) and (2,3) and (3,3)
        
(0,1) --> (0,0) and (1,0) and (0,1) and (1,1)
        
(1,1) --> (2,0) and (3,0) and (2,1) and (3,1)
        

上表正确地指出了目的的角,(0,3)、(3,3)、(0,0)和(3,0)。在其它情况下,坐标可能是个大概值。

目的设备内容的映像方式对SetDIBitsToDevice的影响仅是由于xDst和yDst是逻辑坐标。StretchDIBits完全受映像方式的影响。例如,如果您设定了y值向上递增的一种度量映像方式,DIB就会颠倒显示。

您可以通过把cyDst设定为负数来弥补这种情况。实际上,您可以将任何参数的宽度和高度变为负值来水平或垂直翻转DIB。在MM_TEXT映像方式下,如果cySrc和cyDst符号相反,DIB会沿着水平轴翻转并颠倒显示。如果cxSrc和cxDst符号相反,DIB会沿着垂直轴翻转并显示它的镜面图像。

下面是总结这些内容的表达式,xMM和yMM指出映像方式的方向,如果x值向右增长,则xMM值为1;如果x值向左增长,则值为-1。同样,如果y值向下增长,则yMM值为1;如果y值向上增长,则值为-1。Sign函数对于正值传回TURE,对于负值传回FALSE。

if (!Sign (xMM × cxSrc × cxDst))
        
    DIB is flipped on its vertical axis (mirror image)
        
if (!Sign (yMM × cySrc × cyDst))
        
           DIB is flipped on its horizontal axis (upside down)
        

若有疑问,请查阅表15-4

程序15-5 SHOWDIB以实际尺寸显示DIB、放大至显示区域窗口的大小、打印DIB以及把DIB传输到剪贴簿。

程序15-5 SHOWDIB
        
SHOWDIB2.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib comdlg32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FILE_OPEN equ 40001
IDM_SHOW_NORMAL equ 40002
IDM_SHOW_CENTER equ 40003
IDM_SHOW_STRETCH equ 40004
IDM_SHOW_ISOSTRETCH equ 40005
IDM_FILE_PRINT equ 40006
IDM_EDIT_COPY equ 40007
IDM_EDIT_CUT equ 40008
IDM_EDIT_DELETE equ 40009
IDM_FILE_SAVE equ 40010

.DATA
szAppName TCHAR "ShowDib2",0
szString TCHAR "ShowDib2: Printing",0
diGloble DOCINFO <sizeof (DOCINFO),offset szString,0,0,0>
printdlg PRINTDLG <sizeof (PRINTDLG),,, ,,, ,,, ,,, >
wShow DWORD IDM_SHOW_NORMAL
.DATA?
hInstance HINSTANCE ?
pbmfh DWORD ? ;指向 BITMAPFILEHEADER 结构体的指针
pbmi DWORD ? ;指向 BITMAPINFO 结构体的指针
pBits DWORD ? ;指向一个 BYTE
cxClient DWORD ?
cyClient DWORD ?
cxDib DWORD ?
cyDib DWORD ?
szFileName db MAX_PATH dup(?)
szTitleName db MAX_PATH dup(?)
ofn OPENFILENAME <?>

;DOCINFOA STRUCT
; cbSize DWORD ?
; lpszDocName DWORD ?
; lpszOutput DWORD ?
; lpszDatatype DWORD ?
; fwType DWORD ?
;DOCINFOA ENDS

;BITMAPFILEHEADER STRUCT
; bfType WORD ?
; bfSize DWORD ?
; bfReserved1 WORD ?
; bfReserved2 WORD ?
; bfOffBits DWORD ?
;BITMAPFILEHEADER ENDS

;BITMAPINFO STRUCT
; bmiHeader BITMAPINFOHEADER <>
; bmiColors RGBQUAD <>
;BITMAPINFO ENDS

;BITMAPINFOHEADER STRUCT
; biSize DWORD ?
; biWidth DWORD ?
; biHeight DWORD ?
; biPlanes WORD ?
; biBitCount WORD ?
; biCompression DWORD ?
; biSizeImage DWORD ?
; biXPelsPerMeter DWORD ?
; biYPelsPerMeter DWORD ?
; biClrUsed DWORD ?
; biClrImportant DWORD ?
;BITMAPINFOHEADER ENDS

;BITMAPCOREHEADER STRUCT
; bcSize DWORD ?
; bcWidth WORD ?
; bcHeight WORD ?
; bcPlanes WORD ?
; bcBitCount WORD ?
;BITMAPCOREHEADER ENDS

.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 hAccel :HACCEL
LOCAL hWnd :HWND
LOCAL msg :MSG
LOCAL wndclass :WNDCLASSEX

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,hInst,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("Show DIB #2"),
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

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

ShowDib proc hdc:HDC, pbmiSD:DWORD, pBitsSD:DWORD, cxDibSD:DWORD, cyDibSD:DWORD,cxClientSD:DWORD, cyClientSD:DWORD, wShowSD:DWORD
;pbmi指向BITMAPINFO结构体 pBits指向Byte

mov eax,wShowSD
.if ax==IDM_SHOW_NORMAL
invoke SetDIBitsToDevice,hdc, 0, 0, cxDibSD, cyDibSD, 0, 0,0, cyDibSD, pBitsSD, pbmiSD, DIB_RGB_COLORS
ret
.elseif ax==IDM_SHOW_CENTER
mov ebx,cxClient
sub ebx,cxDibSD
shr ebx,1
mov ecx,cyClient
sub ecx,cyDibSD
shr ecx,1
invoke SetDIBitsToDevice,hdc, ebx,ecx,cxDibSD, cyDibSD, 0, 0, 0, cyDibSD, pBitsSD, pbmiSD, DIB_RGB_COLORS
ret
.elseif ax==IDM_SHOW_STRETCH
invoke SetStretchBltMode,hdc, COLORONCOLOR
invoke StretchDIBits,hdc,0, 0, cxClientSD, cyClientSD, 0, 0, cxDibSD, cyDibSD,pBitsSD, pbmiSD, DIB_RGB_COLORS, SRCCOPY
ret
.elseif ax==IDM_SHOW_ISOSTRETCH
invoke SetStretchBltMode,hdc, COLORONCOLOR
invoke SetMapMode,hdc, MM_ISOTROPIC
invoke SetWindowExtEx,hdc, cxDibSD, cyDibSD, NULL
invoke SetViewportExtEx,hdc, cxClientSD, cyClientSD, NULL
mov eax,cxDibSD
shr eax,1
mov ebx,cyDibSD
shr ebx,1
invoke SetWindowOrgEx,hdc, eax, ebx, NULL
mov eax,cxClientSD
shr eax,1
mov ebx,cyClientSD
shr ebx,1
invoke SetViewportOrgEx ,hdc, eax, ebx, NULL
invoke StretchDIBits,hdc,0, 0, cxDibSD, cyDibSD, 0, 0, cxDibSD, cyDibSD,pBitsSD, pbmiSD, DIB_RGB_COLORS, SRCCOPY
ret
.endif
ShowDib endp

DibFileInitialize proc hwnd:HWND
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT ("Bitmap Files (*.BMP)\0*.bmp\0\0All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
mov eax,NULL
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ; // Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,0
mov ofn.Flags,eax ; Set in Open and Close functions
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax
ret
DibFileInitialize endp

DibFileOpenDlg proc hwnd:HWND,pstrFileName:PTSTR,pstrTitleName:PTSTR
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,pstrFileName
mov ofn.lpstrFile,eax
mov eax,pstrTitleName
mov ofn.lpstrFileTitle,eax
mov eax,0
mov ofn.Flags,eax
invoke GetOpenFileName,addr ofn
ret
DibFileOpenDlg endp

DibLoadImage proc pstrFileName:PTSTR
LOCAL bSuccess:BOOL
LOCAL dwFileSize, dwHighSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE
LOCAL pbmfhDLI:DWORD ;指向 BITMAPFILEHEADER 结构体的指针

invoke CreateFile,pstrFileName, GENERIC_READ, FILE_SHARE_READ,NULL,OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,NULL
mov hFile,eax

.if hFile == INVALID_HANDLE_VALUE
xor eax,eax
ret
.endif

invoke GetFileSize,hFile,addr dwHighSize
mov dwFileSize,eax

.if dwHighSize!=0
invoke CloseHandle,hFile
xor eax,eax
ret
.endif


invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwFileSize ;malloc,dwFileSize
mov pbmfhDLI,eax

.if pbmfhDLI==0
invoke CloseHandle,hFile
xor eax,eax
ret
.endif

invoke ReadFile,hFile, pbmfhDLI, dwFileSize,addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile

mov esi,pbmfhDLI
mov ax,[esi]
mov ebx,[esi+2]
mov ecx,dwBytesRead
.if (bSuccess==0) || ( ecx!= dwFileSize) || (ax != "MB") || (ebx != dwFileSize) ;注意 “BM”需要调整为“MB”
invoke LocalFree,pbmfhDLI
mov eax,NULL
ret
.endif
mov eax,pbmfhDLI
ret
DibLoadImage endp

DibSaveImage proc pstrFileName:PTSTR, pbmfhDSI:DWORD ;指向BITMAPFILEHEADER结构体的指针
LOCAL bSuccess:BOOL
LOCAL dwBytesWritten:DWORD
LOCAL hFile:HANDLE
invoke CreateFile,pstrFileName, GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL

mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,FALSE
ret
.endif

mov esi,pbmfhDSI
mov ebx,[esi+2]
invoke WriteFile,hFile, pbmfhDSI, ebx,addr dwBytesWritten, NULL
mov bSuccess,eax
invoke CloseHandle,hFile
mov esi,pbmfhDSI
mov ebx,[esi+2]
.if (bSuccess==0)||(ebx!=dwBytesWritten)
invoke DeleteFile,pstrFileName
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DibSaveImage endp

DibFileSaveDlg proc hwnd:HWND,pstrFileName:PTSTR,pstrTitleName:PTSTR
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,pstrFileName
mov ofn.lpstrFile,eax
mov eax,pstrTitleName
mov ofn.lpstrFileTitle,eax
mov eax,OFN_OVERWRITEPROMPT
mov ofn.Flags,eax
invoke GetSaveFileName,addr ofn
ret
DibFileSaveDlg 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

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bSuccess:BOOL
LOCAL hdc, hdcPrn:HDC
LOCAL hGlobal:HGLOBAL
LOCAL hMenu:HMENU
LOCAL cxPage, cyPage, iEnable :DWORD
LOCAL ps:PAINTSTRUCT
LOCAL pGlobal:DWORD ;指向Byte的指针

.if uMsg == WM_CREATE
invoke DibFileInitialize,hwnd
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
shl eax,16
shr eax,16
mov cxClient,eax
mov eax,lParam
shr eax,16
mov cyClient,eax
xor eax,eax
ret
.elseif uMsg == WM_INITMENUPOPUP
.if pbmfh!=0
mov eax,MF_ENABLED
.else
mov eax,MF_GRAYED
.endif
mov iEnable,eax
invoke EnableMenuItem,wParam, IDM_FILE_SAVE, iEnable
invoke EnableMenuItem,wParam, IDM_FILE_PRINT, iEnable
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
invoke GetMenu,hwnd
mov hMenu,eax

mov eax,wParam
and eax,0FFFFh
.if ax == IDM_FILE_OPEN
; Show the File Open dialog box
invoke DibFileOpenDlg,hwnd,addr szFileName,addr szTitleName
.if eax==0
xor eax,eax
ret
.endif

; If there's an existing DIB, free the memory
.if pbmfh!=0
invoke LocalFree,pbmfh
mov pbmfh,NULL
.endif
; Load the entire DIB into memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE

invoke DibLoadImage,addr szFileName
mov pbmfh,eax
invoke ShowCursor,FALSE

invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

; Invalidate the client area for later update
invoke InvalidateRect,hwnd, NULL, TRUE

.if (pbmfh == NULL)
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, 0
xor eax,eax
ret
.endif
; Get pointers to the info structure & the bits
mov esi,pbmfh
mov edi,esi
add edi,sizeof (BITMAPFILEHEADER)
mov pbmi,edi ;pbmi = (BITMAPINFO *) (pbmfh + 1) ;

mov eax,esi ;esi中的已经是结构体的地址了
mov ecx,[eax+10]
add ecx,eax
;pBits = (BYTE *) pbmfh + pbmfh->bfOffBits ;
mov pBits,ecx
; Get the DIB width and height
mov esi,pbmi
mov eax,[esi] ;pbmi->bmiHeader.biSize == sizeof (BITMAPCOREHEADER)

.if eax==sizeof (BITMAPCOREHEADER)
;cxDib = ((BITMAPCOREHEADER *) pbmi)->bcWidth ;
;cyDib = ((BITMAPCOREHEADER *) pbmi)->bcHeight ;
mov esi,pbmi
xor eax,eax
mov eax,[esi+4]
mov cxDib,eax
mov eax,[esi+6]
mov cyDib,eax
.else
;cxDib = pbmi->bmiHeader.biWidth ;
;cyDib = abs(pbmi->bmiHeader.biHeight) ;
mov esi,pbmi
mov eax,[esi+4]
mov cxDib,eax
mov eax,[esi+8]
cmp eax,0
jg Glt
neg eax
Glt:
mov cyDib,eax
.endif

xor eax,eax
ret
.elseif ax == IDM_FILE_SAVE
; Show the File Save dialog box
invoke DibFileSaveDlg,hwnd,addr szFileName,addr szTitleName
.if eax==0
xor eax,eax
ret
.endif
; Save the DIB to memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE

invoke DibSaveImage,addr szFileName, pbmfh
mov bSuccess,eax
invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if bSuccess==0
invoke MessageBox,hwnd, CTEXT ("Cannot save DIB file"),addr szAppName, 0
xor eax,eax
ret
.endif
.elseif ax == IDM_FILE_PRINT
.if (pbmfh==0)
xor eax,eax
ret
.endif
;Get printer DC
mov eax,PD_RETURNDC or PD_NOPAGENUMS or PD_NOSELECTION
mov printdlg.Flags,eax

invoke PrintDlg,addr printdlg
.if eax==0
xor eax,eax
ret
.endif
mov eax,printdlg.hDC
mov hdcPrn ,eax
.if (eax==NULL)
invoke MessageBox,hwnd, CTEXT ("Cannot obtain Printer DC"),addr szAppName, MB_ICONEXCLAMATION or MB_OK
xor eax,eax
ret
.endif

; Check whether the printer can print bitmaps
invoke GetDeviceCaps,hdcPrn, RASTERCAPS
and eax,RC_BITBLT
.if eax==0
invoke DeleteDC,hdcPrn
invoke MessageBox,hwnd, CTEXT ("Printer cannot print bitmaps"),addr szAppName, MB_ICONEXCLAMATION or MB_OK
xor eax,eax
ret
.endif
; Get size of printable area of page

invoke GetDeviceCaps,hdcPrn, HORZRES
mov cxPage,eax
invoke GetDeviceCaps,hdcPrn, VERTRES
mov cyPage,eax

mov bSuccess,FALSE

; Send the DIB to the printer
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE


invoke StartDoc,hdcPrn,addr diGloble
mov ebx,eax
invoke StartPage,hdcPrn
cmp ebx,0
jl GoE
cmp eax,0

invoke ShowDib,hdcPrn, pbmi, pBits, cxDib, cyDib,cxPage, cyPage, wShow
invoke EndPage,hdcPrn
cmp eax,0
jl Gll
mov bSuccess,TRUE
invoke EndDoc,hdcPrn
Gll:
GoE:
invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax
invoke DeleteDC,hdcPrn
.if (bSuccess==0)
invoke MessageBox,hwnd, CTEXT ("Could not print bitmap"),addr szAppName, MB_ICONEXCLAMATION or MB_OK
xor eax,eax
ret
.endif
.elseif (ax==IDM_EDIT_COPY) || (ax==IDM_EDIT_CUT)
.if (pbmfh==0)
xor eax,eax
ret
.endif
; Make a copy of the packed DIB
mov esi,pbmfh
mov eax,[esi+2]
sub eax,sizeof (BITMAPFILEHEADER)
invoke GlobalAlloc,GHND or GMEM_SHARE,eax
mov hGlobal,eax
invoke GlobalLock,hGlobal
mov pGlobal,eax

mov esi,pbmfh
mov eax,esi
add eax,sizeof (BITMAPFILEHEADER)
mov ebx,[esi+2]
sub ebx,sizeof (BITMAPFILEHEADER)
;mov eax,[ebx]
invoke CopyMemory,pGlobal, eax,ebx
invoke GlobalUnlock,hGlobal
;下面还有问题需要继续调试
;invoke Beep,100,1000
; Transfer it to the clipboard
invoke OpenClipboard,hwnd
invoke EmptyClipboard
invoke SetClipboardData,CF_DIB, hGlobal
invoke CloseClipboard

mov eax,wParam
.if (ax == IDM_EDIT_COPY)
xor eax,eax
ret
.endif

; fall through if IDM_EDIT_CUT
jmp GoThrough
.elseif ax==IDM_EDIT_DELETE
GoThrough:
.if (pbmfh!=0)
invoke LocalFree,pbmfh
mov pbmfh,NULL
invoke InvalidateRect,hwnd, NULL, TRUE
.endif
xor eax,eax
ret
.elseif (ax==IDM_SHOW_NORMAL) ||(ax==IDM_SHOW_CENTER)||(ax==IDM_SHOW_STRETCH)||(ax==IDM_SHOW_ISOSTRETCH)
invoke CheckMenuItem,hMenu, wShow, MF_UNCHECKED
mov eax,wParam
and eax,0FFFFh
mov wShow,eax
invoke CheckMenuItem,hMenu, wShow, MF_CHECKED
invoke InvalidateRect,hwnd, NULL, TRUE

xor eax,eax
ret
.endif
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if pbmfh!=0
invoke ShowDib,hdc, pbmi, pBits, cxDib, cyDib,cxClient, cyClient, wShow
.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
.if pbmfh!=0
invoke LocalFree,pbmfh
.endif

invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif

invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

SHOWDIB2.RC (摘录)
        
#include "resource.h"

#define IDM_FILE_OPEN 40001

#define IDM_SHOW_NORMAL 40002

#define IDM_SHOW_CENTER 40003

#define IDM_SHOW_STRETCH 40004

#define IDM_SHOW_ISOSTRETCH 40005

#define IDM_FILE_PRINT 40006

#define IDM_EDIT_COPY 40007

#define IDM_EDIT_CUT 40008

#define IDM_EDIT_DELETE 40009

#define IDM_FILE_SAVE 40010


// Menu

SHOWDIB2 MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&Open...\tCtrl+O",IDM_FILE_OPEN

MENUITEM "&Save...\tCtrl+S", IDM_FILE_SAVE

MENUITEM SEPARATOR

MENUITEM "&Print\tCtrl+P", IDM_FILE_PRINT

END

POPUP "&Edit"

BEGIN

MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT

MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY

MENUITEM "&Delete\tDelete", IDM_EDIT_DELETE

END

POPUP "&Show"

BEGIN

MENUITEM "&Actual Size", IDM_SHOW_NORMAL, CHECKED

MENUITEM "&Center", IDM_SHOW_CENTER

MENUITEM "&Stretch to Window", IDM_SHOW_STRETCH

MENUITEM "Stretch &Isotropically", IDM_SHOW_ISOSTRETCH

END

END

/////////////////////////////////////////////////////////////////////////////

// Accelerator

SHOWDIB2 ACCELERATORS DISCARDABLE

BEGIN

"C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT

"O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT

"P", IDM_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT

"S", IDM_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT

VK_DELETE, IDM_EDIT_DELETE, VIRTKEY, NOINVERT

"X", IDM_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT

END

有意思的是ShowDib函数,它依赖于菜单选择以四种不同的方式之一在程序的显示区域显示DIB。可以使用SetDIBitsToDevice从显示区域的左上角或在显示区域的中心显示DIB。程序也有两个使用StretchDIBits的选项,DIB能放大填充整个显示区域。在此情况下它可能会变形,或它能等比例显示,也就是说不会变形。

把DIB复制到剪贴簿包括:在整体共享内存中制作packed DIB内存块的副本。剪贴簿数据型态为CF_DIB。程序没有列出从剪贴簿复制DIB的方法,因为在仅有指向packed DIB的指标的情况下这样做需要更多步骤来确定图素位的偏移量。我将在下一章的末尾示范如何做到这点的方法。

附件中还有一个 SHOWDIB2prb.asm 程序,这个程序有一个BUG,就是当你选择显示模式之后,菜单中前面的小对号就会不翼而飞。请尝试调试吧!

色彩转换、调色盘和显示效能

记得在虎豹小霸王编剧William Goldman的另一出电影剧本《All the President's Men》中,Deep Throat告诉Bob Woodward揭开水门秘密的关键是「跟着钱走」。那么在位图显示中获得高级性能的关键就是「跟着图素位走」以及理解色彩转换发生的时机。DIB是设备无关的格式,视频显示器内存几乎总是与图素格式不同。在SetDIBitsToDevice或StretchDIBits函数呼叫期间,每个图素(可能有几百万个)必须从设备无关的格式转换成设备相关格式。

在许多情况下,这种转换是很繁琐的。例如,在24位视频显示器上显示24位DIB,显示驱动程序最多是切换红、绿、蓝的字节顺序而已。在24位设备上显示16位DIB就需要位的搬移和修剪了。在24位设备上显示4位或8位DIB要求在DIB色彩对照表内查找DIB图素位,然后对字节重新排列。

但是要在4位或8位视频显示器上显示16位、24位或32位DIB时,会发生什么事情呢?一种完全不一样的颜色转换发生了。对于DIB内的每个图素,设备驱动程序必须在图素和显示器上可用的颜色之间「找寻最接近的色彩」,这包括循环和计算。(GDI函数GetNearestColor进行「最接近色彩搜寻」。)

整个RGB色彩的三维数组可用立方体表示。曲线内任意两点之间的距离是:


 

在这里两个颜色是R1G1B1和R2G2B2。