windows环境下32位汇编语言程序设计-第43节
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
mov @stWndClass。hbrBackground;COLOR_WINDOW + 1
mov @stWndClass。lpszClassName;offset szClassName
invoke RegisterClassEx;addr @stWndClass
;********************************************************************
; 建立并显示窗口
;********************************************************************
invoke CreateWindowEx;NULL;
offset szClassName;offset szClassName;
WS_POPUP or WS_SYSMENU;
100;100;CLOCK_SIZE;CLOCK_SIZE;
NULL;NULL;hInstance;NULL
Mov hWinMain;eax
invoke ShowWindow;hWinMain;SW_SHOWNORMAL
invoke UpdateWindow;hWinMain
;********************************************************************
; 消息循环
;********************************************************************
。while TRUE
invoke GetMessage;addr @stMsg;NULL;0;0
。break 。if eax 0
invoke TranslateMessage;addr @stMsg
invoke DispatchMessage;addr @stMsg
。endw
ret
_WinMain endp
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
start:
call _WinMain
invoke ExitProcess;NULL
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
end start
首先简单分析一下程序的结构,主程序中先用RegisterClassEx注册窗口类,再用CreateWindowEx建立窗口的过程,这是标准的创建窗口的代码,惟一的特殊之处是建立窗口使用的窗口风格是WS_POPUP或WS_SYSMENU,这样建立的窗口没有标题栏,以便以后实现圆形的窗口。
程序中的坐标算法和7。2节的Clock。asm是一样的,所以有些子程序沿用了上一个程序中的相关子程序,如计算坐标的_CalcX和_CalcY,绘画指针的_DrawLine子程序等。
在窗口过程的WM_CREATE消息中,程序用CreatePopupMenu创建了一个Popup菜单,用来在按下右键的时候弹出该菜单,并多次调用AppendMenu创建需要的菜单项(相关内容见第5章的“菜单”一节)。然后,程序用CreateEllipticRgn建立一个区域,并用SetWindowRgn函数把窗口设置成圆形,这两个函数在本章的最后一节介绍。接下来,程序用SetWindowPos函数把窗口设置为“总在最前面”。在WM_CREATE消息的最后,程序调用_CreateBackGround和_CreateClockPic创建背景位图和时钟位图,这是这一节中要详细介绍的内容。和前面简单的时钟程序类似,本程序也建立了一个周期为1s的定时器用来刷新屏幕。
在选择菜单后的WM_MAND消息中,程序调用_DeleteBackGround子程序先删除原有的位图,再调用_CreateBackGround和_CreateClockPic子程序产生新的背景位图和时钟位图,最后调用InvalidateRect函数产生WM_PAINT消息重新绘画客户区。
程序有个特殊之处是对WM_LBUTTONDOWN消息的处理,由于建立的窗口没有标题栏,所以无法用拖动标题栏的方法移动窗口,为了在窗口客户区拖动鼠标就能够移动窗口,必须欺骗Windows,让它认为是在拖动标题栏,所以在WM_LBUTTONDOWN消息中,程序发送位置为HTCAPTION的WM_NCLBUTTONDOWN消息,这样就相当于告诉Windows用户按下的是标题栏。头尾处的两个SetCursor函数是为了按下鼠标时让光标变成一个抓住东西的手的形状。
整个程序的结构采用图7。1中的B结构,也就是说窗口客户区的绘画代码和产生时钟位图的代码是分开的,程序中建立了两个位图当做缓冲数据,第一个位图是背景位图,在_CreateBackGround子程序中建立,只有在程序初始化以及在菜单中选择了不同的背景和边框后才需要调用这个子程序,以便建立新的背景图片;第二个位图是要输出到屏幕的时钟位图,它在_CreateClockPic子程序中建立,时钟位图是通过将背景位图拷贝过来,再根据当前时间画上指针得到的,程序在WM_TIMER消息中每秒重画一次新的时钟图片,并用InvalidateRect函数产生一个WM_PAINT消息将这个位图拷贝到屏幕上,当其他原因产生WM_PAINT消息时,程序并不从头开始产生背景位图和时钟位图等图片,而是直接从时钟图片中拷贝数据到屏幕上。
好了,接下来继续分析_CreateBackGround和_CreateClockPic子程序是如何对位图进行处理的。
7。3。2 创建和使用位图
所有绘图函数的操作对象都是“设备环境”,对位图操作也不例外。为了对位图使用GDI函数,需要使用CreatepatibleDC函数为位图建立一个DC,然后使用SelectObject函数将位图选入这个DC中,这样,所有对这个DC的绘图操作实际上就是在操作这个位图。每一个需要操作的位图都需要单独为它创建一个DC。
程序中常常使用在资源中预定义的位图,但也有使用未初始化的位图的情况,如例子程序的背景位图和时钟位图一开始就是未初始化的,它们是程序开始运行后才被创建的。
为了建立一个未初始化的位图,可以使用以下的函数:
invoke CreatepatibleBitmap,hDC,dwWidth,dwHeight
mov hBitmap1;eax ;方法1
invoke CreateBitmap,dwWidth,dwHeight,dwPlanes,dwBitsPerPel,NULL
mov hBitmap2;eax ;方法2
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第7章 图形操作
7。3 创建和使用位图(5)
创建一个位图需要的参数是高度、宽度以及颜色深度,要创建位图必须得知这些参数。使用CreatepatibleBitmap创建位图的时候,参数中有一个hDC,这是个参考hDC,也就是说,新位图的颜色深度和hDC对应的“设备环境”的颜色深度相同(注意:有个hDC参数的意思并不是将创建的位图选入这个hDC中)。CreateBitmap函数则直接在参数dwPlanes和dwBitsPerPel中指定了颜色深度。两个函数的dwWidth和dwHeight参数指定创建的位图的宽度和高度。
在例子程序的_CreateBackGround子程序中,为了建立背景图片和时钟图片,需要建立两个未初始化的位图和操作它们的DC,所以程序一开始用GetDC函数获取主窗口的hDC来当做参考DC,然后用CreatepatibleDC函数建立了两个DC(句柄放在全局变量hDcBack和hDcClock中),并用CreatepatibleBitmap建立了两个位图(句柄放入hBmpBack和hBmpClock中),接下来用SelectObject将这两个位图选入新建的hDC中。
创建背景图片的过程中还要用到资源中的背景图片、边框图片和边框的遮掩图片,对于这些图片,程序用LoadBitmap函数装入,并使用CreatepatibleDC为每个图片建立一个DC。
对于不再使用的位图,要用DeleteObject函数将它们删除。所以在子程序的最后,使用DeleteObject函数将临时使用的位图句柄删除,并使用DeleteDC将操作这些位图的hDC删除。
操作未初始化位图需要用到CreatepatibleDC和CreatepatibleBitmap函数,初学者常犯的错误是用CreatepatibleDC返回的hDC当做CreatepatibleBitmap函数的参考hDC,这样的结果是建立的位图是单色的,正确的做法是两个函数的参考hDC都使用窗口客户区的hDC。
7。3。3 使用设备无关位图
设备无关位图简称为DIB,这在5。3。1小节中已经有所介绍。DIB一般是存放在磁盘上的以bmp为扩展名的位图文件,使用DIB的关键是如何将DIB中的数据转化为一个内存中的位图并返回一个位图句柄。
bmp文件的文件结构是这样定义的:文件的开始是一个BITMAPFILEHEADER结构,这个结构定义如下:
BITMAPFILEHEADER STRUCT
bfType WORD ? ;文件标识,必须是“BM”
bfSize DWORD ? ;文件长度
bfReserved1 WORD ? ;0
bfReserved2 WORD ? ;0
bfOffBits DWORD ? ;位图像素数据在文件中的起始位置
BITMAPFILEHEADER ENDS
BITMAPFILEHEADER结构的后面要么是BITMAPCOREHEADER结构,要么是BITMAPINFOHEADER结构和索引色表,这两种结构的定义如下:
BITMAPCOREHEADER STRUCT
bcSize DWORD ? ;本结构长度
bcWidth WORD ? ;位图宽度
bcHeight WORD ? ;位图高度
bcPlanes WORD ? ;位图的色平面数
bcBitCount WORD ? ;位图的颜色深度
BITMAPCOREHEADER ENDS
BITMAPINFOHEADER STRUCT
bcSize DWORD ? ;本结构长度
bcWidth WORD ? ;位图宽度
bcHeight WORD ? ;位图高度
bcPlanes WORD ? ;位图的色平面数
bcBitCount WORD ? ;位图的颜色深度
bipression DWORD ? ;位图的压缩方式
biSizeImage DWORD ? ;图形尺寸
biXPelsPerMeter DWORD ? ;图形x方向分辨率,单位是像素/米
biYPelsPerMeter DWORD ? ;图形y方向分辨率,单位是像素/米
biClrUsed DWORD ?
biClrImportant DWORD ?
BITMAPINFOHEADER ENDS
这两个数据结构主要包含了位图的一些参数,在这些数据结构的后面,就是位图的像素数据了,整个bmp文件就由这3部分组成。
要使用DIB,可以首先将整个文件读到内存中,然后从这些数据结构中得知位图的各种参数,最后使用SetDIBitsToDevice函数将位图数据复制到一个hDC中,如果这个hDC对应一个未初始化的位图,那么就相当于得到了包含磁盘bmp位图数据的位图句柄,并且可以在任何地方使用它。当然,在这以后可以将读入文件数据的内存释放掉。
SetDIBitsToDevice函数的用法是:
invoke SetDIBitsToDevice;hDC;xDest;yDest;
dwWidth;dwHeight;xSrc;ySrc;uStartScan;cScanLines;
lpvBits;lpbmi;fuColorUse
hDC是目的DC的句柄,xDest和yDest指定了位图复制到hDC的左上角位置,dwWidth和dwHeight指定了要复制的宽度和高度,xSrc和ySrc指定DIB中要复制的左上角位置,uStartScan和cScanLines指定开始复制的扫描线和要复制的扫描线数,最后,lpvBits指向DIB中的像素数据部分,lpbmi指向DIB中的BITMAPINFO或BITMAPCOREINFO结构,fuColorUse指定了DIB中数据的类型,用DIB_RGB_COLORS表示数据是RGB类型的。
子程序_CreateDIBitmap分析一个DIB文件的参数并返回包含整个DIB位图数据的位图句柄,读者可以在任何地方使用这个位图句柄。子程序的输入参数_hWnd用来获取参考hDC的窗口句柄,_lpFileData是将DIB文件整个读入内存后的内存指针。代码如下:
_CreateDIBitmap proc _hWnd;_lpFileData
local @lpBitmapInfo;@lpBitmapBits
local @dwWidth;@dwHeight
local @hDc;@hBitmap
pushad
mov @hBitmap;0
mov esi;_lpFileData
mov eax;BITMAPFILEHEADER。bfOffBits 'esi'
add eax;e