见招拆招《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。执行最接近色彩搜寻包括从一种颜色到其它颜色集合中找寻最短距离。幸运的是,在RGB颜色立方体中「比较」距离时,并不需要计算平方根部分。但是需转换的每个图素必须与设备的所有颜色相比较以发现最接近的颜色。这是个工作量相当大的工作。(尽管在8位设备上显示8位DIB也得进行最接近色彩搜寻,但它不必对每个图素都进行,它仅需对DIB色彩对照表中的每种颜色进行寻找。)

正是由于以上原因,应该避免使用SetDIBitsToDevice或StretchDIBits在8位视频显示卡上显示16位、24位或32位DIB。DIB应转换为8位DIB,或者8位DDB,以求得更好的显示效能。实际上,您可以经由将DIB转换为DDB并使用BitBlt和StretchBlt显示图像,来加快显示任何DIB的速度。

如果在8位视频显示器上执行Windows(或仅仅切换到8位模式来观察在显示True-ColorDIB时的效能变化),您可能会注意到另一个问题:DIB不会使用所有颜色来显示。任何在8位视频显示器上的DIB刚好限制在以20种颜色显示。如何获得多于20种颜色是「调色盘管理器」的任务,这将在下一章提到。

最后,如果在同一台机器上执行Windows 98和Windows NT,您可能会注意到:对于同样的显示模式,Windows NT显示大型DIB花费的时间较长。这是Windows NT的客户/服务器体系结构的结果,它使大量数据在传输给API函数时耗费更多时间。解决方法是将DIB转换为DDB。而我等一下将谈到的CreateDIBSection函数对这种情况特别有用。

DIB 和 DDB 的结合

您可以做许多事情去发掘DIB的格式,并呼叫两个DIB绘图函数:SetDIBitsToDevice和StretchDIBits。您可以直接存取DIB中的各个位、字节和图素,且一旦您有了一堆能让您以结构化的方式检查和更改数据的函数,您要怎么处理DIB就没人管了。

实际上,我们发现还是有一些限制。在上一章,我们了解了使用GDI函数在DDB上绘制图像的方法。到目前为止,还没有在DIB上绘图的方法。另一个问题是SetDIBitsToDevice和StretchDIBits没有BitBlt和StretchBlt速度快,尤其在Windows NT环境下以及执行许多最接近颜色搜寻(例如,在8位视频卡上显示24位DIB)时。

因此,在DIB和DDB之间进行转换是有好处的。例如,如果我们有一个需要在屏幕上显示许多次的DIB,那么把DIB转换为DDB就很有意义,这样我们就能够使用快速的BitBlt和StretchBlt函数来显示它了。

从DIB建立DDB

从DIB中建立GDI位图对象可能吗?基本上我们已经知道了方法:如果有DIB,您就能够使用CreateCompatibleBitmap来建立与DIB大小相同并与视频显示器兼容的GDI位图对象。然后将该位图对象选入内存设备内容并呼叫SetDIBitsToDevice在那个内存DC上绘图。结果就是DDB具有与DIB相同的图像,但具有与视频显示器兼容的颜色组织。

您也可以通过呼叫CreateDIBitmap用几个步骤完成上述工作。函数的语法为:

hBitmap = CreateDIBitmap (
        
                  hdc,               // device context handle
        
                                        pInfoHdr,              // pointer to DIB information header
        
                    fInit,      // 0 or CBM_INIT
        
                    pBits,      // pointer to DIB pixel bits
        
                    pInfo,      // pointer to DIB information
        
                    fClrUse) ;  // color use flag
        

请注意pInfoHdr和pInfo这两个参数,它们分别定义为指向BITMAPINFOHEADER结构和BITMAPINFO结构的指针。正如我们所知,BITMAPINFO结构是后面紧跟色彩对照表的BITMAPINFOHEADER结构。我们一会儿会看到这种区别所起的作用。最后一个参数是DIB_RGB_ COLORS(等于0)或DIB_PAL_COLORS,它们在SetDIBitsToDevice函数中使用。下一章我将讨论更多这方面的内容。

理解Windows中位图函数的作用是很重要的。不要考虑CreateDIBitmap函数的名称,它不建立与「设备无关的位图」,它从设备无关的规格中建立「设备相关的位图」。注意该函数传回GDI位图对象的句柄,CreateBitmap、CreateBitmapIndirect和CreateCompatibleBitmap也与它一样。

呼叫CreateDIBitmap函数最简单的方法是:

hBitmap = CreateDIBitmap (NULL, pbmih, 0, NULL, NULL, 0) ;
        

唯一的参数是指向BITMAPINFOHEADER结构(不带色彩对照表)的指标。在这个形式中,函数建立单色GDI位图对象。第二种简单的方法是:

hBitmap = CreateDIBitmap (hdc, pbmih, 0, NULL, NULL, 0) ;
        

在这个形式中,函数建立了与设备内容兼容的DDB,该设备内容由hdc参数指出。到目前为止,我们都是透过CreateBitmap(建立单色位图)或CreateCompatibleBitmap(建立与视频显示器兼容的位图)来完成一些工作。

在CreateDIBitmap的这两个简化模式中,图素还未被初始化。如果CreateDIBitmap的第三个参数是CBM_INIT,Windows就会建立DDB并使用最后三个参数初始化位图位。pInfo参数是指向包括色彩对照表的BITMAPINFO结构的指针。pBits参数是指向由BITMAPINFO结构指出的色彩格式中的位数组的指针,根据色彩对照表这些位被转换为设备的颜色格式,这与SetDIBitsToDevice的情况相同。实际上,整个CreateDIBitmap函数可以用下列程序代码来实作:

HBITMAP CreateDIBitmap (   HDC hdc, CONST BITMAPINFOHEADER * pbmih,
        
                      DWORD fInit, CONST VOID * pBits,
        
                       CONST BITMAPINFO * pbmi, UINT fUsage)
        
{
        
           HBITMAP       hBitmap ;
        
           HDC                   hdc ;
        
           int                   cx, cy, iBitCount ;
        
           if (pbmih->biSize == sizeof (BITMAPCOREHEADER))
        
    {
        
                          cx   = ((PBITMAPCOREHEADER) pbmih)->bcWidth ;
        
                          cy   = ((PBITMAPCOREHEADER) pbmih)->bcHeight ;
        
                          iBitCount     = ((PBITMAPCOREHEADER) pbmih)->bcBitCount ;
        
           }
        
           else
        
    {
        
                          cx  = pbmih->biWidth ;
        
                          cy  = pbmih->biHeight ;
        
                         iBitCount     = pbmih->biBitCount ;
        
   }
        
           if (hdc)
        
                          hBitmap = CreateCompatibleBitmap (hdc, cx, cy) ;
        
           else
        
                          hBitmap = CreateBitmap (cx, cy, 1, 1, NULL) ;
        
           if     (fInit == CBM_INIT)
        
           {
        
                          hdcMem = CreateCompatibleDC (hdc) ;
        
                          SelectObject (hdcMem, hBitmap) ;
        
                          SetDIBitsToDevice (   hdcMem, 0, 0, cx, cy, 0, 0, 0 cy,
        
                   pBits, pbmi, fUsage) ;
        
                          DeleteDC (hdcMem) ;
        
    }
        

return hBitmap ;
        
}
        

如果仅需显示DIB一次,并担心SetDIBitsToDevice显示太慢,则呼叫CreateDIBitmap并使用BitBlt或StretchBlt来显示DDB就没有什么意义。因为SetDIBitsToDevice和CreateDIBitmap都执行颜色转换,这两个工作会占用同样长的时间。只有在多次显示DIB时(例如在处理WM_PAINT消息时)进行这种转换才有意义。

程序15-6 DIBCONV展示了利用SetDIBitsToDevice把DIB文件转换为DDB的方法。

程序15-6 DIBCONV
        
DIBCONV.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

.DATA
szAppName TCHAR "DibConv",0
.DATA?
hInstance HINSTANCE ?
hBitmap HBITMAP ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
szFileName db MAX_PATH dup(?)
szTitleName db MAX_PATH dup(?)

;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 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("DIB to DDB Conversion"),
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


CreateBitmapObjectFromDibFile proc hdc:HDC, FileName:DWORD
;FileName 是传过来的地址
LOCAL pbmfh:DWORD ;pbmfh指向BITMAPFILEHEADER结构体
LOCAL bSuccess:BOOL
LOCAL dwFileSize, dwHighSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE
LOCAL hBitmapCB:HBITMAP

; Open the file: read access, prohibit write access
invoke CreateFile,FileName, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax

.if (hFile == INVALID_HANDLE_VALUE)
mov eax,NULL
ret
.endif

; Read in the whole file
invoke GetFileSize,hFile,addr dwHighSize
mov dwFileSize,eax

.if (dwHighSize!=0)
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwFileSize ;malloc,dwFileSize
mov pbmfh,eax ;pbmfh = malloc (dwFileSize) ;

.if (pbmfh==0)
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif

invoke ReadFile,hFile, pbmfh, dwFileSize,addr dwBytesRead, NULL
mov bSuccess,eax

invoke CloseHandle,hFile

; Verify the file
mov esi,pbmfh
mov ax,[esi]
mov ebx,[esi+2]
mov ecx,dwBytesRead

.if (bSuccess==0) || ( ecx!= dwFileSize) || (ax != "MB") || (ebx != dwFileSize) ;注意 “BM”需要调整为“MB”
invoke LocalFree,pbmfh
mov eax,NULL
ret
.endif

; Create the DDB
mov esi,pbmfh
mov edi,esi
add edi,sizeof (BITMAPFILEHEADER) ;edi = (BITMAPINFO *) (pbmfh + 1) ;

mov eax,esi ;esi中的已经是结构体的地址了
mov ecx,[eax+10]
add ecx,eax ;ecx = (BYTE *) pbmfh + pbmfh->bfOffBits ;

mov esi,pbmfh
mov ebx,esi
add ebx,sizeof (BITMAPINFO )
invoke CreateDIBitmap,hdc,edi,CBM_INIT,ecx,edi,DIB_RGB_COLORS

mov hBitmapCB,eax
invoke LocalFree,pbmfh

mov eax,hBitmapCB
ret
CreateBitmapObjectFromDibFile endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bitmap:BITMAP
LOCAL hdc, hdcMem:HDC
LOCAL ps:PAINTSTRUCT

.if uMsg == WM_CREATE
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
lea eax,szFileName
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
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_COMMAND
mov eax,wParam
and eax,0FFFFh
.if ax == IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif

; If there's an existing DIB, delete it
.if (hBitmap!=0)
invoke DeleteObject,hBitmap
mov hBitmap,NULL
.endif

; Create the DDB from the DIB
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax

invoke ShowCursor,TRUE

invoke GetDC,hwnd
mov hdc,eax

invoke CreateBitmapObjectFromDibFile,hdc,addr szFileName
mov hBitmap,eax
invoke ReleaseDC,hwnd, hdc
invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

; Invalidate the client area for later update
invoke InvalidateRect,hwnd, NULL, TRUE
.if (hBitmap == NULL)
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif

xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if (hBitmap!=0)
invoke GetObject,hBitmap, sizeof (BITMAP),addr bitmap
invoke CreateCompatibleDC,hdc
mov hdcMem,eax

invoke SelectObject,hdcMem, hBitmap
invoke BitBlt,hdc,0, 0, bitmap.bmWidth, bitmap.bmHeight,hdcMem, 0, 0, SRCCOPY
invoke DeleteDC,hdcMem
.endif
invoke EndPaint,hdc,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

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

#define IDM_FILE_OPEN 40001

// Menu

DIBCONV MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&Open", IDM_FILE_OPEN

END

END

DIBCONV.ASM 本身就是完整的,并不需要前面的文件。在对它仅有的菜单命令(「File Open」)的响应中,WndProc呼叫程序的CreateBitmapObjectFromDibFile函数。此函数将整个文件读入内存并将指向内存块的指针传递给CreateDIBitmap函数,函数传回位图的句柄,然后包含DIB的内存块被释放。在WM_PAINT消息处理期间,WndProc将位图选入兼容的内存设备内容并使用BitBlt(不是SetDIBitsToDevice)在显示区域显示位图。它通过使用位图句柄呼叫带有BITMAP结构的GetObject函数来取得位图的宽度和高度。

在从CreateDIBitmap建立位图时不必初始化DDB图素位,之后您可以呼叫SetDIBits初始化图素位。该函数的语法如下:

iLines = SetDIBits (
        
                          hdc,          // device context handle
        
                          hBitmap,   // bitmap handle
        
                          yScan,      // first scan line to convert
        
                          cyScans,   // number of scan lines to convert
        
                          pBits,        // pointer to pixel bits
        
                          pInfo,        // pointer to DIB information
        
                         fClrUse) ;  // color use flag
        

函数使用了BITMAPINFO结构中的色彩对照表把位转换为设备相关的格式。只有在最后一个参数设定为DIB_PAL_COLORS时,才需要设备内容句柄。

从DDB到DIB

与SetDIBits函数相似的函数是GetDIBits,您可以使用此函数把DDB转化为DIB:

int WINAPI GetDIBits (
        
                  hdc,                  // device context handle
        
                  hBitmap,      // bitmap handle
        
                  yScan,        // first scan line to convert
        
                  cyScans,      // number of scan lines to convert
        
                  pBits,                // pointer to pixel bits (out)
        
                  pInfo,                // pointer to DIB information (out)
        
                  fClrUse) ;    // color use flag
        

然而,此函数产生的恐怕不是SetDIBits的反运算结果。在一般情况下,如果使用CreateDIBitmap和SetDIBits将DIB转换为DDB,然后使用GetDIBits把DDB转换回DIB,您就不会得到原来的图像。这是因为在DIB被转换为设备相关的格式时,有一些信息遗失了。遗失的信息数量取决于进行转换时Windows所执行的显示模式。

您可能会发现没有使用GetDIBits的必要性。考虑一下:在什么环境下您的程序发现自身带有位图句柄,但没有用于在起始的位置建立位图的数据?剪贴簿?但是剪贴簿为DIB提供了自动的转换。GetDIBits函数的一个例子是在捕捉屏幕显示内容的情况下,例如第十四章中BLOWUP程序所做的。我不示范这个函数,但在Microsoft网站的Knowledge Base文章Q80080中有一些信息。

DIB区块

我希望您已经对设备相关和设备无关位图的区别有了清晰的概念。DIB能拥有几种色彩组织中的一种,DDB必须是单色的或是与真实输出设备相同的格式。DIB是一个文件或内存块;DDB是GDI位图对象并由位图句柄表示。DIB能被显示或转换为DDB并转换回DIB,但是这里包含了设备无关位和设备相关位之间的转换程序。

现在您将遇到一个函数,它打破了这些规则。该函数在32位Windows版本中发表,称为CreateDIBSection,语法为:

hBitmap = CreateDIBSection (
        
                   hdc,           // device context handle
        
                   pInfo,         // pointer to DIB information
        
                  fClrUse,     // color use flag
        
                  ppBits,      // pointer to pointer variable
        
                  hSection,     // file-mapping object handle
        
                  dwOffset) ;   // offset to bits in file-mapping object
        

CreateDIBSection是Windows API中最重要的函数之一(至少在使用位图时),然而您会发现它很深奥并难以理解。

让我们从它的名称开始,我们知道DIB是什么,但「DIB section」到底是什么呢?当您第一次检查CreateDIBSection时,可能会寻找该函数与DIB区块工作的方式。这是正确的,CreateDIBSection所做的就是建立了DIB的一部分(位图图素位的内存块)。

现在我们看一下传回值,它是GDI位图对象的句柄,这个传回值可能是该函数呼叫最会拐人的部分。传回值似乎暗示着CreateDIBSection在功能上与CreateDIBitmap相同。事实上,它只是相似但完全不同。实际上,从CreateDIBSection传回的位图句柄与我们在本章和 上一章遇到的所有位图建立函数传回的位图句柄在本质上不同。

一旦理解了CreateDIBSection的真实特性,您可能觉得奇怪为什么不把传回值定义得有所区别。您也可能得出结论:CreateDIBSection应该称之为CreateDIBitmap,并且如同我前面所指出的CreateDIBitmap应该称之为CreateDDBitmap。

首先让我们检查一下如何简化CreateDIBSection,并正确地使用它。首先,把最后两个参数hSection和dwOffset,分别设定为NULL和0,我将在本章最后讨论这些参数的用法。第二,仅在fColorUse参数设定为DIB_ PAL_COLORS时,才使用hdc参数,如果fColorUse为DIB_RGB_COLORS(或0),hdc将被忽略(这与CreateDIBitmap不同,hdc参数用于取得与DDB兼容的设备的色彩格式)。

因此,CreateDIBSection最简单的形式仅需要第二和第四个参数。第二个参数是指向BITMAPINFO结构的指针,我们以前曾使用过。我希望指向第四个参数的指标定义的指标不会使您困惑,它实际上很简单。

假设要建立每图素24位的384×256位DIB,24位格式不需要色彩对照表,因此它是最简单的,所以我们可以为BITMAPINFO参数使用BITMAPINFOHEADER结构。

您需要定义三个变量:BITMAPINFOHEADER结构、BYTE指针和位图句柄:

BITMAPINFOHEADER   bmih ;
        
BYTE                       * pBits ;
        
HBITMAP                    hBitmap ;
        

现在初始化BITMAPINFOHEADER结构的字段

bmih->biSize              = sizeof (BITMAPINFOHEADER) ;
        
bmih->biWidth              = 384 ;
        
bmih->biHeight             = 256 ;
        
bmih->biPlanes             = 1 ;
        
bmih->biBitCount           = 24 ;
        
bmih->biCompression        = BI_RGB ;
        
bmih->biSizeImage          = 0 ;
        
bmih->biXPelsPerMeter      = 0 ;
        
bmih->biYPelsPerMeter      = 0 ;
        
bmih->biClrUsed            = 0 ;
        
bmih->biClrImportant       = 0 ;
        

在基本准备后,我们呼叫该函数:

hBitmap = CreateDIBSection (NULL, (BITMAPINFO *)  &bmih, 0, &pBits, NULL, 0) ;
        

注意,我们为第二个参数赋予BITMAPINFOHEADER结构的地址。这是常见的,但一个BYIE指针pBits的地址,就不常见了。这样,第四个参数是函数需要的指向指标的指标。

这是函数呼叫所做的:CreateDIBSection检查BITMAPINFOHEADER结构并配置足够的内存块来加载DIB图素位。(在这个例子里,内存块的大小为384×256×3字节。)它在您提供的pBits参数中储存了指向此内存块的指针。函数传回位图句柄,正如我说的,它与CreateDIBitmap和其它位图建立函数传回的句柄不一样。

然而,我们还没有做完,位图图素是未初始化的。如果正在读取DIB文件,可以简单地把pBits参数传递给ReadFile函数并读取它们。或者可以使用一些程序代码「人工」设定。

程序15-7 DIBSECT除了呼叫CreateDIBSection而不是CreateDIBitmap之外,与DIBCONV程序相似。

程序15-7 DIBSECT
        
DIBSECT.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

.DATA
szAppName TCHAR "DIBsect",0
.DATA?
hInstance HINSTANCE ?
hBitmap HBITMAP ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
szFileName db MAX_PATH dup(?)
szTitleName db MAX_PATH dup(?)

;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 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("DIB to DDB Conversion"),
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


CreateDIBsectionFromDibFile proc hdc:HDC, FileName:DWORD
;FileName 是传过来的地址
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;pbmi指向BITMAPINFO结构体
LOCAL pBits:DWORD ;pBits指向Byte
LOCAL pbmfh:DWORD ;pbmfh指向BITMAPFILEHEADER结构体
LOCAL bSuccess:BOOL
LOCAL dwInfoSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE
LOCAL hBitmapCB:HBITMAP

; Open the file: read access, prohibit write access
invoke CreateFile,FileName, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax

.if (hFile == INVALID_HANDLE_VALUE)
mov eax,NULL
ret
.endif

; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile, addr bmfh, sizeof(BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov ax,bmfh.bfType
mov ecx,dwBytesRead

.if (bSuccess==0) || ( ecx!= sizeof (BITMAPFILEHEADER)) || (ax != "MB") ;注意 “BM”需要调整为“MB”
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif

; Allocate memory for the BITMAPINFO structure & read it in
mov eax,bmfh.bfOffBits
sub eax,sizeof (BITMAPFILEHEADER)
mov dwInfoSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwInfoSize ;malloc,dwFileSize
mov pbmi,eax ;pbmi = malloc (dwInfoSize) ;

invoke ReadFile,hFile, pbmi, dwInfoSize,addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
.if (bSuccess==0) || ( eax!= dwInfoSize)
invoke LocalFree,pbmi
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif

; Create the DIB Section
invoke CreateDIBSection,NULL, pbmi, DIB_RGB_COLORS,addr pBits, NULL, 0
mov hBitmapCB,eax
.if (hBitmapCB == NULL)
invoke LocalFree,pbmi
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif

; Read in the bitmap bits
mov ebx,bmfh.bfSize
sub ebx,bmfh.bfOffBits
invoke ReadFile,hFile, pBits, ebx,addr dwBytesRead, NULL
invoke LocalFree,pbmi
invoke CloseHandle,hFile
mov eax,hBitmapCB
ret
CreateDIBsectionFromDibFile endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bitmap:BITMAP
LOCAL hdc, hdcMem:HDC
LOCAL ps:PAINTSTRUCT

.if uMsg == WM_CREATE
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
lea eax,szFileName
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
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_COMMAND
mov eax,wParam
and eax,0FFFFh
.if ax == IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif

; If there's an existing DIB, delete it
.if (hBitmap!=0)
invoke DeleteObject,hBitmap
mov hBitmap,NULL
.endif

; Create the DDB from the DIB
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax

invoke ShowCursor,TRUE

invoke GetDC,hwnd
mov hdc,eax

invoke CreateDIBsectionFromDibFile ,hdc,addr szFileName
mov hBitmap,eax
invoke ReleaseDC,hwnd, hdc
invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

; Invalidate the client area for later update
invoke InvalidateRect,hwnd, NULL, TRUE
.if (hBitmap == NULL)
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif

xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if (hBitmap!=0)
invoke GetObject,hBitmap, sizeof (BITMAP),addr bitmap
invoke CreateCompatibleDC,hdc
mov hdcMem,eax

invoke SelectObject,hdcMem, hBitmap
invoke BitBlt,hdc,0, 0, bitmap.bmWidth, bitmap.bmHeight,hdcMem, 0, 0, SRCCOPY
invoke DeleteDC,hdcMem
.endif
invoke EndPaint,hdc,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

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

#define IDM_FILE_OPEN 40001

// Menu

DIBsect MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&Open", IDM_FILE_OPEN

END

END

注意DIBCONV中的CreateBitmapObjectFromDibFile函数和DIBSECT中的CreateDIbsectionFromDibFile函数之间的区别。DIBCONV读入整个文件,然后把指向DIB内存块的指针传递给CreateDIBitmap函数。DIBSECT首先读取BITMAPFILEHEADER结构中的信息,然后确定BITMAPINFO结构的大小,为此配置内存,并在第二个ReadFile呼叫中将它读入内存。然后,函数把指向BITMAPINFO结构和指针变量pBits的指针传递给CreateDIBSection。函数传回位图句柄并设定pBits指向函数将要读取DIB图素位的内存块。

pBits指向的内存块归系统所有。当通过呼叫DeleteObject删除位图时,内存会被自动释放。然而,程序能利用该指针直接改变DIB位。当应用程序透过API传递海量存储器块时,只要系统拥有这些内存块,在WINDOWS NT下就不会影响速度。

我之前曾说过,当在视频显示器上显示DIB时,某些时候必须进行从设备无关图素到设备相关图素的转换,有时这些格式转换可能相当费时。来看一看三种用于显示DIB的方法:

再读一下上面这些叙述,确定您不会误解它的意思。这是从CreateDIBSection传回的位图句柄不同于我们所遇到的其它位图句柄的一个地方。此位图句柄实际上指向储存在内存中由系统维护但应用程序能存取的DIB。在需要的时候,DIB会转化为特定的色彩格式,通常是在用BitBlt或StretchBlt显示位图时。

您也可以将位图句柄选入内存设备内容并使用GDI函数来绘制。在 pBits 变量指向的DIB图素内将反映出结果。因为Windows NT下的GDI函数分批呼叫,在内存设备背景上绘制之后和「人为」的存取位之前会呼叫GdiFlush。

在DIBSECT,我们清除pBits变量,因为程序不再需要这个变量了。您会使用CreateDIBSection的主要原因在于您有需要直接更改位值。在CreateDIBSection呼叫之后似乎就没有别的方法来取得位指针了。

DIB区块的其它区别

从CreateDIBitmap传回的位图句柄与函数的hdc参数引用的设备有相同的平面和图素字节织。您能通过具有BITMAP结构的GetObject呼叫来检验这一点。

CreateDIBSection就不同了。如果以该函数传回的位图句柄的BITMAP结构呼叫GetObject,您会发现位图具有的色彩组织与BITMAPINFOHEADER结构的字段指出的色彩组织相同。您能将这个句柄选入与视频显示器兼容的内存设备内容。这与前面关于DDB的内容相矛盾,但这也就是我说此DIB区块位图句柄不同的原因。

另一个奇妙之处是:你可能还记得,DIB中图素数据行的位组长度始终是4的倍数。GDI位图对象中行的位组长度,就是使用GetObject从BITMAP结构的bmWidthBytes字段中得到的长度,始终是2的倍数。如果用每图素24位和宽度2图素设定BITMAPINFOHEADER结构并随后呼叫GetObject,您就会发现bmWidthBytes字段是8而不是6。

使用从CreateDIBSection传回的位图句柄,也可以使用DIBSECTION结构呼叫GetObject:

GetObject (hBitmap, sizeof (DIBSECTION), &dibsection) ;
        

此函数不能处理其它位图建立函数传回的位图句柄。DIBSECTION结构定义如下:

typedef struct tagDIBSECTION  // ds
        
{
        
           BITMAP                        dsBm ;                // BITMAP structure
        
           BITMAPINFOHEADER      dsBmih ;                  // DIB information header
        
           DWORD                        dsBitfields [3] ;     // color masks
        
          HANDLE                        dshSection ;          // file-mapping object handle
        
           DWORD                         dsOffset ;            // offset to bitmap bits
        
}
        
DIBSECTION, * PDIBSECTION ;
        

此结构包含BITMAP结构和BITMAPINFOHEADER结构。最后两个字段是传递给CreateDIBSection的最后两个参数,等一下将会讨论它们。

DIBSECTION结构中包含除了色彩对照表以外有关位图的许多内容。当把DIB区块位图句柄选入内存设备内容时,可以通过呼叫GetDIBColorTable来得到色彩对照表:

hdcMem = CreateCompatibleDC (NULL) ;
        
SelectObject (hdcMem, hBitmap) ;
        
GetDIBColorTable (hdcMem, uFirstIndex, uNumEntries, &rgb) ;
        
DeleteDC (hdcMem) ;
        

同样,您可以通过呼叫SetDIBColorTable来设定色彩对照表中的项目。

文件映像选项

我们还没有讨论CreateDIBSection的最后两个参数,它们是文件映像对象的句柄和文件中位图位开始的偏移量。文件映像对象使您能够像文件位于内存中一样处理文件。也就是说,可以通过使用内存指针来存取文件,但文件不需要整个加载内存中。

在大型DIB的情况下,此技术对于减少内存需求是很有帮助的。DIB图素位能够储存在磁盘上,但仍然可以当作位于内存中一样进行存取,虽然会影响程序执行效能。问题是,当图素位实际上储存在磁盘上时,它们不可能是实际DIB文件的一部分。它们必须位于其它的文件内。

为了展示这个程序,下面显示的函数除了不把图素位读入内存以外,与DIBSECT中建立DIB区块的函数很相似。然而,它提供了文件映像对象和传递给CreateDIBSection函数的偏移量:

HBITMAP CreateDIBsectionMappingFromFile (PTSTR szFileName)
        
{
        
           BITMAPFILEHEADER              bmfh ;
        
           BITMAPINFO                    *      pbmi ;
        
           BYTE                          *      pBits ;
        
           BOOL                          bSuccess ;
        
           DWORD                         dwInfoSize, dwBytesRead ;
        
           HANDLE                        hFile, hFileMap ;
        
           HBITMAP                       hBitmap ;
        
           hFile = CreateFile (szFileName, GENERIC_READ | GENERIC_WRITE,
        
                                  0,            // No sharing!
        
                                  NULL, OPEN_EXISTING, 0, NULL) ;
        

           if (hFile == INVALID_HANDLE_VALUE)
        
                          return NULL ;
        
           bSuccess = ReadFile ( hFile, &bmfh, sizeof (BITMAPFILEHEADER),
        
                                  &dwBytesRead, NULL) ;
        

           if (!bSuccess || (dwBytesRead != sizeof (BITMAPFILEHEADER))       
        
                          || (bmfh.bfType != * (WORD *) "BM"))
        
           {
        
                          CloseHandle (hFile) ;
        
                          return NULL ;
        
           }
        
           dwInfoSize = bmfh.bfOffBits - sizeof (BITMAPFILEHEADER) ;
        
           pbmi = malloc (dwInfoSize) ;
        
           bSuccess = ReadFile (hFile, pbmi, dwInfoSize, &dwBytesRead, NULL) ;
        

           if (!bSuccess || (dwBytesRead != dwInfoSize))
        
           {
        
                          free (pbmi) ;
        
                          CloseHandle (hFile) ;
        
                          return NULL ;
        
           }
        
           hFileMap = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL) ;
        
           hBitmap = CreateDIBSection (  NULL, pbmi, DIB_RGB_COLORS, &pBits,hFileMap, bmfh.bfOffBits) ;
        
    free (pbmi) ;
        
    return hBitmap ;
        
}
        

啊哈!这个程序不会动。CreateDIBSection的文件指出「dwOffset [函数的最后一个参数]必须是DWORD大小的倍数」。尽管信息表头的大小始终是4的倍数并且色彩对照表的大小也始终是4的倍数,但位图文件表头却不是,它是14字节。因此bmfh.bfOffBits永远不会是4的倍数。

总结

如果您有小型的DIB并且需要频繁地操作图素位,您可以使用SetDIBitsToDevice和StretchDIBits来显示它们。然而,对于大型的DIB,此技术会遇到显示效能的问题,尤其在8位视频显示器上和Windows NT环境下。

您可以使用CreateDIBitmap和SetDIBits把DIB转化为DDB。现在,显示位图可以使用快速的BitBlt和StretchBlt函数来进行了。然而,您不能直接存取这些与设备无关的图素位。

CreateDIBSection是一个很好的折衷方案。在Windows NT下通过BitBlt和StretchBlt使用位图句柄比使用SetDIBitsToDevice和StretchDIBits(但没有DDB的缺陷)会得到更好的效能。您仍然可以存取DIB图素位。



<<<上一篇
欢迎访问AoGo汇编小站:http://www.aogosoft.com