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

显示和打印

位图是用来看的。在这一节中,我们看一看Windows在视频显示器上或打印页面上支持显示DIB的两个函数。要得到更好的性能,您可以使用一种兜圈子的方法来显示位图,我会在本章的后面讨论该方法,但先研究这两个函数会好一些。

这两个函数称为SetDIBitsToDevice(发音为「set dee eye bits to device」)和StretchDIBits (发音为「stretch dee eye bits」)。每个函数都使用储存在内存中的DIB并能显示整个DIB或它的矩形部分。当使用SetDIBitsToDevice时,以图素为单位所显示映像的大小与DIB的图素大小相同。例如,一个640×480的DIB会占据整个标准的VGA屏幕,但在300dpi的激光打印机上它只有约2.1×1.6英寸。StretchDIBits能延伸和缩小DIB尺寸的行和列从而在输出设备上显示一个特定的大小。

了解DIB

当呼叫两个函数之一来显示DIB时,您需要几个关于图像的信息。正如我前面说过的,DIB文件包含下列部分:


 

DIB文件能被加载内存。如果除了文件表头外,整个文件被储存在内存的连续区块中,指向该内存块开始处(也就是信息表头的开头)的指标被称为指向packed DIB的指标(见下图)。


 

这是通过剪贴簿传输DIB时所用的格式,并且也是您从DIB建立画刷时所用的格式。因为整个DIB由单个指标(如pPackedDib)引用,所以packed DIB是在内存中储存DIB的方便方法,您可以把指标定义为指向BYTE的指标。使用本章前面所示的结构定义,能得到所有储存在DIB内的信息,包括色彩对照表和个别图素位。

然而,要想得到这么多信息,还需要一些程序代码。例如,您不能通过以下叙述简单地取得DIB的图素宽度:

iWidth = ((PBITMAPINFOHEADER) pPackedDib)->biWidth ;
        

DIB有可能是OS/2兼容格式的。在那种格式中,packed DIB以BITMAPCOREHEADER结构开始,并且DIB的图素宽度和高度以16位WORD,而不是32位LONG储存。因此,首先必须检查DIB是否为旧的格式,然后进行相对应的操作:

if (((PBITMAPCOREHEADER) pPackedDib)->bcSize == sizeof (BITMAPCOREHEADER))
        
           iWidth = ((PBITMAPCOREHEADER) pPackedDib)->bcWidth ;
        
else
        
           iWidth = ((PBITMAPINFOHEADER) pPackedDib)->biWidth ;
        

当然,这不很糟,但它不如我们所喜好的清晰。

现在有一个很有趣的实验:给定一个指向packed DIB的指标,我们要找出位于坐标(5,27)的图素值。即使假定DIB不是OS/2兼容的格式,您也需要了解DIB的宽度、高度和位数。您需要计算每一列图素的位组长度,确定色彩对照表内的项目数,以及色彩对照表是否包括三个32位的颜色屏蔽。您还需检查DIB是否被压缩,在这种情况下图素是不能直接由地址得到的。

如果您需要直接存取所有的DIB图素(就像许多图形处理工作一样),这可能会增加一点处理时间。由于这个原因,储存一个指向packed DIB的指标就很方便了,不过这并不是一种有效率的解决方式。另一个漂亮的解决方法是为DIB定义一个包含足够成员数据的C++类别,从而允许快速随机地存取DIB图素。然而,我曾经答应读者在本书内无需了解C++,我将在下一章说明一个C的解决方法。

对于SetDIBitsToDevice和StretchDIBits函数,需要的信息包括一个指向DIB的BITMAPINFO结构的指针。您应回想起,BITMAPINFO结构由BITMAPINFOHEADER结构和色彩对照表组成。因此这仅是一个指向packed DIB的指标。

函数也需要一个指向图素位的指针。尽管程序代码写得很不漂亮,但这个指针还是可以从信息表头内的信息推出。注意,当您存取BITMAPFILEHEADER结构的bfOffBits字段时,这个指标能很容易地计算出。bfOffBits字段指出了从DIB文件的开头到图素位的偏移量。您可以简单地把此偏移量加到BITMAPINFO指标中,然后减去BITMAPFILEHEADER结构的大小。然而,当您从剪贴簿上得到指向packed DIB的指标时,这并不起作用,因为没有BITMAPFILEHEADER结构。

此图表显示了两个所需的指标:


 

SetDIBitsToDevice和StretchDIBits函数需要两个指向DIB的指标,因为这两个部分不在连续的内存块内。您可能有如下所示的两块内存:


 

确实,把DIB分成两个内存块是很有用的,只是我们更喜欢与整个DIB储存在单个内存块的packed DIB打交道。

除了这两个指标,SetDIBitsToDevice和StretchDIBits函数通常也需要DIB的图素宽度和高度。如只想显示DIB的一部分,就不必明确地知道这些值,但它们会定义您在DIB图素位数组内定义的矩形的上限。

点对点图素显示

SetDIBitsToDevice函数显示没有延伸和缩小的DIB。DIB的每个图素对应到输出设备的一个图素上,而且DIB中的图像一定会被正确显示出来-也就是说,图像的顶列在上方。任何会影响设备内容的坐标转换都影响了显示DIB的开始位置,但不影响显示出来的图片大小和方向。该函数如下:

iLines =    SetDIBitsToDevice (
        
                                  hdc,           // device context handle
        
                                  xDst,          // x destination coordinate
        
                                yDst,          // y destination coordinate
        
                                  cxSrc,         // source rectangle width
        
                                  cySrc,         // source rectangle height
        
                                  xSrc,          // x source coordinate
        
                                  ySrc,          // y source coordinate
        
                                  yScan,         // first scan line to draw
        
                                  cyScans,       // number of scan lines to draw
        
                                  pBits,         // pointer to DIB pixel bits
        
                                  pInfo,         // pointer to DIB information
        
                                 fClrUse) ;     // color use flag
        

不要对参数的数量感到厌烦,在多数情况下,函数用起来比看起来要简单。不过在其它用途上来说,它的用法真的是乱七八糟,不过我们将学会怎么用它。

和GDI显示函数一样,SetDIBitsToDevice的第一个参数是设备内容句柄,它指出显示DIB的设备。下面两个参数xDst和yDst,是输出设备的逻辑坐标,并指出了显示DIB图像左上角的坐标(「上端」指的是视觉上的上方,并不是DIB图素的第一行)。注意,这些都是逻辑坐标,因此它们附属于实际上起作用的任何坐标转换方式或-在Windows NT的情况下-设定的任何空间转换。在内定的MM_TEXT映像方式下,可以把这些参数设为0,从显示平面上向左向上显示DIB图像。

您可以显示整个DIB图像或仅显示其中的一部分,这就是后四个参数的作用。但是DIB图素数据的由上而下的方向产生了许多误解,待会儿会谈到这些。现在应该清楚当显示整个DIB时,应把xSrc和ySrc设定为0,并且cxSrc和cySrc应分别等于DIB的图素宽度和高度。注意,因为BITMAPINFOHEADER结构的biHeight字段对于由上而下的DIB来说是负的,cySrc应设定为biHeight字段的绝对值。

此函数的文件(/Platform SDK/Graphics and Multimedia Services/GDI/Bitmaps/Bitmap Reference/Bitmap Functions/SetDIBitsToDevice)中说xSrc、ySrc、cxSrc和cySrc参数是逻辑单位。这是不正确的,它们是图素的坐标和尺寸。对于DIB内的图素,拥有逻辑坐标和单位是没有什么意义的。而且,不管是什么映像方式,在输出设备上显示的DIB始终是cxSrc图素宽和cySrc图素高。

现在先不详细讨论这两个参数yScan和cyScan。这些参数在您从磁盘文件或通过调制解调器读取数据时,透过每次显示DIB的一小部分减少对内存的需求。通常,yScan设定为0,cyScan设定为DIB的高度。

pBits参数是指向DIB图素位的指针。pInfo参数是指向DIB的BITMAPINFO结构的指针。虽然BITMAPINFO结构的地址与BITMAPINFOHEADER结构的地址相同,但是SetDIBitsToDevice结构被定义为使用BITMAPINFO结构,暗示着:对于1位、4位和8位DIB,位图信息表头后必须跟着色彩对照表。尽管pInfo参数被定义为指向BITMAPINFO结构的指针,它也是指向BITMAPCOREINFO、BITMAPV4HEADER或BITMAPV5HEADER结构的指针。

最后一个参数是DIB_RGB_COLORS或DIB_PAL_COLORS,在WINGDI.H内分别定义为0和1。如果您使用DIB_RGB_COLORS,这意味着DIB包含了色彩对照表。DIB_PAL_COLORS旗标指出,DIB内的色彩对照表已经被指向在设备内容内选定并识别的调色盘的16位索引代替。在下一章我们将学习这个选项。现在先使用DIB_RGB_COLORS,或者是0。

SetDIBitsToDevice函数传回所显示的扫描行的数目。

因此,要呼叫SetDIBitsToDevice来显示整个DIB图像,您需要下列信息:

然后用下列方法呼叫SetDIBitsToDevice:

SetDIBitsToDevice (
        
           hdc,          // device context handle
        
           xDst,         // x destination coordinate
        
   yDst,         // y destination coordinate
        
           cxDib,        // source rectangle width
        
           cyDib,        // source rectangle height
        
   0,            // x source coordinate
        
   0,            // y source coordinate
        
           0,            // first scan line to draw
        
   cyDib,        // number of scan lines to draw
        
   pBits,        // pointer to DIB pixel bits
        
   pInfo,   // pointer to DIB information
        
           0) ;          // color use flag
        

因此,在DIB的12个参数中,四个设定为0,一个是重复的。

程序15-2 SHOWDIB1通过使用SetDIBitsToDevice函数显示DIB。

程序15-2 SHOWDIB1
        
SHOWDIB1.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_FILE_SAVE equ 40002

.DATA
szAppName TCHAR "SHOWDIB1",0
.DATA?
hInstance HINSTANCE ?
pbmfh DWORD ? ;指向 BITMAPFILEHEADER 结构体的指针
pbmi DWORD ? ;指向 BITMAPINFO 结构体的指针
pBits DWORD ? ;指向一个 BYTE
cxClient DWORD ?
cyClient DWORD ?
cxDib DWORD ?
cyDib DWORD 0?
szFileName db MAX_PATH dup(?)
szTitleName db MAX_PATH dup(?)
ofn OPENFILENAME <?>

;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 #1"),
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

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

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

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

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bSuccess:BOOL
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT

.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
invoke EnableMenuItem,wParam, IDM_FILE_SAVE, eax
xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov eax,wParam
and eax,0FFFFh
.if eax == 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 eax == 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
.endif
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if pbmfh!=0

invoke SetDIBitsToDevice,hdc,0,0,cxDib,cyDib,0, 0,0,cyDib,pBits,pbmi,DIB_RGB_COLORS
.if eax==0
invoke Beep,1000,1000
.endif
.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


SHOWDIB1.RC

#include "resource.h"

#define IDM_FILE_OPEN 40001

#define IDM_FILE_SAVE 40002

// Menu

SHOWDIB1 MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&Open...", IDM_FILE_OPEN

MENUITEM "&Save...", IDM_FILE_SAVE

END

END

         

DIBFILE.ASM 文件包含了显示「File Open」和「File Save」对话框的例程,以及把整个DIB文件(拥有BITMAPFILEHEADER结构)加载单个内存块的例程。程序也会将这样一个内存区写出到文件。

当在SHOWDIB1.ASM内执行「File Open」命令加载DIB文件后,程序计算内存块中BITMAPINFOHEADER结构和图素位的偏移量,程序也获得DIB的图素宽度和高度。所有信息都储存在静态变量中。在处理WM_PAINT消息处理期间,程序通过呼叫SetDIBitsToDevice显示DIB。

当然,SHOWDIB1还缺少一些功能。例如,如果DIB对显示区域来说太大,则没有滚动条可用来移动查看。在后面将修改这些缺陷。

DIB的颠倒世界

我们将得到一个重要的教训,它不仅在生活中重要,而且在操作系统的应用程序接口的设计中也重要。这个教训是:覆水难收。

回到OS/2 Presentation Manager那由下而上的DIB图素位的定义处,这样的定义是有点道理的,因为PM内的任何坐标系都有一个内定的左下角原点。例如:在PM窗口内,内定的(0,0)原点是窗口的左下角。(如果您觉得这很古怪,很多人和您的感觉一样。如果您不觉得古怪,那您可能是位数学家。)位图的绘制函数也根据左下角指定目的地。

因此,在OS/2内如果给位图指定了目的坐标(0,0),则图像将从窗口的左下角向上向右显示,如图15-1所示。


 

图15-1 在OS/2中以(0,0)为目的点显示的位图

在够慢的机器上,您能实际看到计算机由下而上绘制位图。

尽管OS/2坐标系统显得很古怪,但它的优点是高度的一致。位图的 (0,0)原点是位图文件中第一行的第一个图素,并且此图素被映像到在位图绘制函数中指定的目的坐标上。

Windows存在的问题是不能保持内部的一致性。当您只要显示整个DIB图像中的一小块矩形时,就要使用参数xSrc、ySrc、cxSrc和cySrc。这些来源坐标和大小与DIB数据的第一行(图像的最后一行)相关。这方面与OS/2相似,与OS/2不同的是,Windows在目的坐标上显示图像的顶列。因此,如果显示整个DIB图像,显示在(xDst,yDst)的图素是位于坐标(0,cyDib - 1)处的图素。DIB数据的最后一列就是图形的顶列。如果仅显示图像的一部分,则在(xDst,yDst)显示的图素是位于坐标(xSrc, ySrc + cySrc - 1)处的DIB图素。

图15-2显示的图表将帮助您理解这方面的内容。您可以把下面显示的DIB当成是储存在内存中的-就是说,上下颠倒。坐标的原点与DIB图素数据的第一个位是一致的。SetDIBitsToDevice的xSrc参数是以DIB的左边为基准,并且cxSrc是xSrc右边的图像宽度,这很直观。ySrc参数以DIB数据的首列(也就是图像的底部)为基准,并且cySrc是从ySrc到数据的末列(图像的顶端)的图像高度。


 

图15-2 正常DIB(由下而上)的坐标

如果目的设备内容具有使用MM_TEXT映像方式的内定图素坐标,来源矩形和目的矩形角落坐标之间的关系显示在表15-3中。

表15-3

来源矩形

目的矩形

(xSrc, ySrc)

(xDst, yDst + cySrc - 1)

(xSrc + cxSrc - 1, ySrc)

(xDst + cxSrc - 1, yDst + cySrc - 1)

(xSrc, ySrc + cySrc - 1)

(xDst, yDst)

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

(xDst + cxSrc - 1, yDst)

(xSrc,ySrc)不映射到(xDst,yDst),使得表格显得很混乱。在其它映像方式中,点(xSrc,ySrc + cySrc - 1)总是映像到逻辑点(xDst,yDst),图像也与MM_TEXT所显示的一样。

到目前为止,我们讨论了当BITMAPINFOHEADER结构的biHeight字段是正值时的正常情况。如果biHeight字段是负值,则DIB数据会以合理的由上而下的方式排列。您可能会认为这样将解决所有问题,如果您真地这样认为,那您就错了。

很明显地,有人会认为如果把DIB上下倒置,旋转每一行,然后给biHeight设定一个正值,它将像正常的由下而上的DIB一样操作,所有与DIB矩形相关的现存程序代码就不必修改。我认为这是一个合理的目的,但它忘记了一个事实,程序需要修改以处理由上而下的DIB,这样就不会使用一个负高度。

而且,此决定的结果意味着由上而下的DIB的来源坐标在DIB数据的最后一列有一个原点,它也是图像的底列。这与我们遇到的情况完全不同。位于(0,0)原点的DIB图素不再是pBits指标引用的第一个图素,也不是DIB文件的最后一个图素,它位于两者之间。

图15-3显示的图表说明了在由上而下的DIB中指定矩形的方法,也是它储存在文件或内存中的样子。


 

图15-3 指定由上而下的DIB的坐标

无论如何,这个方案的实际优点是SetDIBitsToDevice函数的参数与DIB数据的方向无关。如果有显示了同一图像的两个DIB(一个由下而上,另一个由上而下。表示在两个DIB文件内的列顺序相反),您可以使用相同的参数呼叫SetDIBitsToDevice来选择显示图像的相同部分。

如程序15-3 APOLLO11中所示。

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

.DATA
szAppName TCHAR "Apollo11",0
.DATA?
hInstance HINSTANCE ?

pbmfh DWORD 2 dup(?) ;指向 BITMAPFILEHEADER 结构体的指针
pbmi DWORD 2 dup(?) ;指向 BITMAPINFO 结构体的指针
pBits DWORD 2 dup(?) ;指向BYTE
cxClient DWORD ?
cyClient DWORD ?
cxDib DWORD 2 dup(?)
cyDib DWORD 2 dup(?)

;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("Apollo 11"),
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

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

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

.if uMsg == WM_CREATE
;invoke Beep,1000,1000
invoke DibLoadImage,CTXT("Apollo11.bmp")
mov pbmfh[0],eax

invoke DibLoadImage,CTXT("Apollo11.bmp")
mov pbmfh[4],eax

.if (pbmfh[0] == NULL) || (pbmfh[4] == 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[0]
mov eax,esi
add eax,sizeof (BITMAPFILEHEADER)
mov pbmi[0],eax

mov esi,pbmfh[4]
mov eax,esi
add eax,sizeof (BITMAPFILEHEADER)
mov pbmi[4],eax
;mov eax,esi ;esi中的已经是结构体的地址了
mov esi,pbmfh[0]
mov eax,esi
mov ecx,[eax+10]
add ecx,eax
mov pBits[0],ecx
mov esi,pbmfh[4]
mov eax,esi
mov ecx,[eax+10]
add ecx,eax
mov pBits[4],ecx


;Get the DIB width and height (assume BITMAPINFOHEADER)
;Note that cyDib is the absolute value of the header value!!!
mov esi,pbmi[0]
mov eax,[esi+4]
mov cxDib[0],eax

mov esi,pbmi[4]
mov eax,[esi+4]
mov cxDib[4],eax

mov esi,pbmi[0]
mov eax,[esi+8]
mov cyDib[0],eax

mov esi,pbmi[4]
mov eax,[esi+8]
mov cyDib[4],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_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
mov ebx,cyClient
shr ebx,2
;Bottom-up DIB full size
invoke SetDIBitsToDevice,hdc, 0,ebx,cxDib[0],cyDib[0], 0, 0, 0,cyDib[0],pBits[0],pbmi[0],DIB_RGB_COLORS
;Bottom-up DIB partial
invoke SetDIBitsToDevice,hdc,240,ebx, 80, 166,80,60, 0,cyDib[0],pBits[0],pbmi[0],DIB_RGB_COLORS
;Top-down DIB full size
invoke SetDIBitsToDevice,hdc,340,ebx,cxDib[0],cyDib[0], 0, 0, 0,cyDib[0],pBits[0],pbmi[0],DIB_RGB_COLORS
;Top-down DIB partial
invoke SetDIBitsToDevice,hdc,580,ebx, 80, 166,80,60, 0,cyDib[4],pBits[4],pbmi[4],DIB_RGB_COLORS

invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
.if pbmfh[0]!=0
invoke LocalFree,pbmfh[0]
.endif
.if pbmfh[1]!=0
invoke LocalFree,pbmfh[1]
.endif
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif

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

 

程序加载了名为APOLLO11.BMP(由下而上版本)和APOLLOTD.BMP(由上而下版本)的两个DIB。它们都是220图素宽和240图素高。注意,在程序从表头信息结构中确定DIB的宽度和高度时,它使用abs函数得到biHeight字段的绝对值。当以全部大小或范围显示DIB时,不管显示位图的种类,xSrc、ySrc、cxSrc和cySrc坐标都是相同的。结果如图15-4所示。


 

图15-4 APOLLO11的屏幕显示

注意,「第一条扫描线」和「扫描线数目」参数保持不变,我将在以后简短说明。pBits参数也不变,不要只为了使它指向您需要显示的区域而试图更改pBits。

我在这个问题上花了这么多时间,并不是因为要让那些试图跟API定义中有问题的部分妥协的Windows程序写作者难堪,而是想让您不至于因为这个令人混淆的问题而紧张起来。这个问题之所以令人困惑,是因为它本身早就被搞混了。

我也想让您留意Windows文件中的某些叙述,例如对SetDIBitsToDevice,文件说:「由下而上DIB的原点是位图的左下角;由上而下DIB的原点是左上角」。这不仅模糊,而且是错误的。我可以用更好的方式来讲述:由下而上DIB的原点是位图图像的左下角,它是位图资料的第一列的第一个图素。由上而下DIB的原点也是位图图像的左下角,但在这种情况下,左下角是位图数据的最后一列的第一个图素。

如果要撰写存取DIB个别位的函数,问题会变的更糟。这应该与您为显示部分DIB映像而指定的坐标一致,我的解决方法是(我将在第十六章的DIB链接库中使用)以统一的手法参考DIB图素和坐标,就像在图像被正确显示时(0,0)原点所指的是DIB图像顶行的最左边的图素一样。

循序显示

拥有海量存储器能确保程序更容易地执行。要显示磁盘文件内的DIB,可以分为两个独立的工作:将DIB加载内存,然后显示它。

然而,您也可能在不把整个文件加载内存的情况下显示DIB。即使有足够的物理内存提供给DIB,把DIB移入内存也会迫使Windows的虚拟内存系统把内存中别的数据和程序代码移到磁盘上。如果DIB仅用于显示并立即从内存中消除,这就非常讨厌。

还有另一个问题:假设DIB位于例如软盘的慢速储存媒体上,或由调制解调器传输过来,或者来自扫描仪或视频截取程序取得图素数据的转换例程。您是否得等到整个DIB被加载内存后才显示它?还是从磁盘或电话线或扫描仪上得到DIB时,就开始显示它?

解决这些问题是SetDIBitsToDevice函数中yScan和cyScans参数的目的。要使用这个功能,需要多次呼叫SetDIBitsToDevice,大多数情况下使用同样的参数。然而对于每次呼叫,pBits参数指向位图图素总体排列的不同部分。yScans参数指出了pBits指向图素资料的行,cyScans参数是被pBits引用的行数。这大量地减少了内存需求。您仅需要为储存DIB的信息部分(BITMAPINFOHEADER结构和色彩对照表)和至少一行图素数据配置足够的内存。

例如,假设DIB有23行图素,您希望每次最多5行的分段显示这个DIB。您可能想配置一个由变量pInfo引用的内存块来储存DIB的BITMAPINFO部分,然后从文件中读取该DIB。在检查完此结构的字段后,能够计算出一行的位组长度。乘以5并配置该大小的另一个内存块(pBits)。现在读取前5行,呼叫您正常使用的函数,把yScan设定为0,把cyScans设定为5。现在从文件中读取下5行,这一次将yScan设定为5,继续将yScan设定为10,然后为15。最后,将最后3行读入pBits指向的内存块,并将yScan设定为20,将cyScans设定为3,以呼叫SetDIBitsToDevice。

现在有一个不好的消息。首先,使用SetDIBitsToDevice的这个功能要求程序的数据取得和数据显示元素之间结合得相当紧密。这通常是不理想的,因为您必须在获得数据和显示数据之间切换。首先,您将延缓整个程序;第二,SetDIBitsToDevice是唯一具有这个功能的位图显示函数。StretchDIBits函数不包括这个功能,因此您不能使用它以不同图素大小显示发表的DIB。您必须呼叫StretchDIBits多次,每次更改BITMAPINFOHEADER结构中的信息,并在屏幕的不同区域显示结果。

程序15-4 SEQDISP展示了这个功能的使用方法。

程序15-4 SEQDISP
        
SEQDISP.ASM
;下面这个程序我特地留下一处问题,读者可以尝试用多久找到问题所在?
;现象描述:我在绘制过称中加了一个Sleep做delay,但是发现它的“副作用”
;是绘制过程中如果发生了遮挡或者切换的话,图片就无法显示出。
;亲爱的读者,试试Debug吧

;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 "SeqDisp",0


.DATA?
hInstance HINSTANCE ?
pbmi DWORD ? ;指向 BITMAPINFO 结构体的指针
pBits DWORD ? ;指向一个 BYTE
cxClient DWORD ?
cyClient DWORD ?
cxDib DWORD ?
cyDib DWORD ?
cBits DWORD ?
ofn OPENFILENAME <?>
szFileName db MAX_PATH dup(?)
szTitleName db MAX_PATH dup(?)
szBuffer db MAX_PATH dup(?)

;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("DIB Sequential Display"),
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

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bmfh:DWORD ;指向 BITMAPFILEHEADER 的指针
LOCAL bSuccess, bTopDown:BOOL
LOCAL dwBytesRead:DWORD
LOCAL hFile:HANDLE
LOCAL hdc:HDC
LOCAL hMenu:HMENU
LOCAL iInfoSize, iBitsSize, iRowLength, y:DWORD
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
lea eax,szTitleName
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_COMMAND
invoke GetMenu,hwnd
mov hMenu,eax

mov eax,wParam
and eax,0FFFFh
.if eax == IDM_FILE_OPEN
; Display File Open dialog
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif
; Get rid of old DIB
.if pbmi!=0
invoke LocalFree,pbmi
mov pbmi,NULL
.endif
.if pBits!=0
invoke LocalFree,pBits
mov pBits,NULL
.endif

; Generate WM_PAINT message to erase background
invoke InvalidateRect,hwnd, NULL, TRUE
invoke UpdateWindow,hwnd

; Open the file
invoke CreateFile,addr szFileName, GENERIC_READ,FILE_SHARE_READ, NULL, OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax

.if (hFile == INVALID_HANDLE_VALUE)
invoke MessageBox,hwnd, CTEXT ("Cannot open file."),addr szAppName, MB_ICONWARNING or MB_OK
xor eax,eax
ret
.endif

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

.if (bSuccess==0) || (dwBytesRead != sizeof (BITMAPFILEHEADER))
invoke MessageBox,hwnd, CTEXT ("Cannot read file."),addr szAppName, MB_ICONWARNING or MB_OK
invoke CloseHandle,hFile
xor eax,eax
ret
.endif

mov esi,bmfh
mov ax,[esi]
; Check that it's a bitmap
.if (ax != "MB")
invoke MessageBox,hwnd,CTEXT ("File is not a bitmap."),addr szAppName, MB_ICONWARNING or MB_OK
invoke CloseHandle,hFile
xor eax,eax
ret
.endif

; Allocate memory for header and bits
mov esi,bmfh
mov eax,[esi+10]
sub eax,sizeof (BITMAPFILEHEADER)
mov iInfoSize,eax

mov eax,[esi+2]
sub eax,DWORD ptr [esi+10]
mov iBitsSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,iInfoSize
mov pbmi,eax
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,iBitsSize
mov pBits,eax

.if (pbmi == NULL) || (pBits == NULL)
invoke MessageBox,hwnd,CTEXT ("Cannot allocate memory."),addr szAppName, MB_ICONWARNING or MB_OK
.if pbmi!=NULL
invoke LocalFree,pbmi
.endif
.if pBits!=NULL
invoke LocalFree,pBits
.endif
invoke CloseHandle,hFile
xor eax,eax
ret
.endif

; Read in the Information Header
invoke ReadFile,hFile, pbmi, iInfoSize,addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
.if (bSuccess==0) || (eax != iInfoSize)
invoke MessageBox,hwnd, CTEXT ("Cannot read file."),addr szAppName, MB_ICONWARNING or MB_OK
.if pbmi!=NULL
invoke LocalFree,pbmi
.endif
.if pBits!=NULL
invoke LocalFree,pBits
.endif
invoke CloseHandle,hFile
xor eax,eax
ret
.endif

; Get the DIB width and height
mov eax,FALSE
mov bTopDown,eax

mov esi,pbmi
mov eax,[esi]

.if (eax == sizeof (BITMAPCOREHEADER))
xor eax,eax
mov ax,[esi+4]
mov cxDib,eax

mov ax,[esi+6]
mov cyDib,eax

mov ax,[esi+10]
mov cBits,eax

mov esi,pbmi
mov eax,[esi+8]
.else
cmp eax,0
jg GoL
mov eax,TRUE
mov bTopDown,eax
GoL:
mov esi,pbmi
mov eax,[esi+4]
mov cxDib,eax

mov eax,[esi+8]
cmp eax,0
jg GoG
neg eax
GoG:
mov cyDib,eax
; cyDib = abs (pbmi->bmiHeader.biHeight) ;
xor eax,eax
mov ax,[esi+14]
mov cBits,eax; cBits = pbmi->bmiHeader.biBitCount ;

mov esi,pbmi
mov eax,[esi+16]
.if (eax!= BI_RGB) && (eax != BI_BITFIELDS)
invoke MessageBox,hwnd, CTEXT ("File is compressed."),addr szAppName, MB_ICONWARNING or MB_OK
.if pbmi!=NULL
invoke LocalFree,pbmi
.endif
.if pBits!=NULL
invoke LocalFree,pBits
.endif

invoke CloseHandle,hFile
xor eax,eax
ret
.endif
.endif
; Get the row length
mov eax,cxDib
mov ecx,cBits
mul ecx
add eax,31
and eax,(Not 31)
shr eax,3
mov iRowLength,eax
;iRowLength = ((cxDib * cBits + 31) & ~31) >> 3 ;


; Read and display
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE

invoke GetDC,hwnd
mov hdc,eax

mov y,0
NextY:
mov eax,y
mov ecx,iRowLength
mul ecx
add eax,pBits
mov ebx,eax
invoke ReadFile,hFile, ebx,iRowLength,addr dwBytesRead, NULL
mov eax,y
mov ecx,iRowLength
mul ecx
add eax,pBits
mov ebx,eax
.if bTopDown==0
mov ecx,y
.else
mov ecx,cyDib
sub ecx,y
dec ecx
.endif
invoke SetDIBitsToDevice,hdc,0,0,cxDib,cyDib,0,0,ecx,1,\
ebx,pbmi,DIB_RGB_COLORS
invoke Sleep,10
inc y
mov eax,y
.if eax<cyDib
jmp NextY
.endif

invoke ReleaseDC,hwnd, hdc
invoke CloseHandle,hFile
invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax
xor eax,eax
ret
.endif
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if (pbmi==0)&&(pBits==0)
invoke SetDIBitsToDevice,hdc,0,0,cxDib,cyDib,0, 0,0,cyDib,pBits,pbmi,DIB_RGB_COLORS
.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
.if pbmi!=0
invoke LocalFree,pbmi
.endif
.if pBits==0
invoke LocalFree,pBits
.endif
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif


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


SEQDISP.RC 
#include "resource.h"

#define IDM_FILE_OPEN 40001

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

// Accelerator

SEQDISP ACCELERATORS DISCARDABLE

BEGIN

"O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT

END

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

// Menu

SEQDISP MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

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

END

END



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