友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
八万小说网 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

windows环境下32位汇编语言程序设计-第29部分

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




; 注册窗口类

;********************************************************************

                        invoke  RtlZeroMemory;addr @stWndClass;sizeof @stWndClass

                        invoke  LoadIcon;hInstance;ICO_MAIN

                        mov     @stWndClass。hIcon;eax

                        mov     @stWndClass。hIconSm;eax

                        invoke  LoadCursor;0;IDC_ARROW

                        mov     @stWndClass。hCursor;eax

                        push        hInstance

                        pop     @stWndClass。hInstance

                        mov     @stWndClass。cbSize;sizeof WNDCLASSEX

                        mov     @stWndClass。style;CS_HREDRAW or CS_VREDRAW

                        mov     @stWndClass。lpfnWndProc;offset _ProcWinMain

                        mov     @stWndClass。hbrBackground;COLOR_WINDOW + 1

                        mov     @stWndClass。lpszClassName;offset szClassName

                        invoke  RegisterClassEx;addr @stWndClass

;********************************************************************

; 建立并显示窗口

;********************************************************************

                        invoke  CreateWindowEx;WS_EX_CLIENTEDGE;

                                offset szClassName;offset szClassName;

                                WS_OVERLAPPEDWINDOW;

                                100;100;250;270;

                                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

下面简单分析一下程序的结构。

程序首先用标准的方法建立了一个窗口,在窗口的初始化消息WM_CREATE中用SetTimer建立了一个周期为1秒的定时器,用来在窗口的客户区中绘画时钟。这个定时器在WM_CLOSE消息中用KillTimer函数撤销。在定时器消息中,程序用InvalidateRect函数让整个客户区失效,相当于让Windows在消息循环中放入一条WM_PAINT消息,整个时钟的绘画在WM_PAINT消息中完成。

在WM_PAINT消息中程序用标准的方法调用BeginPaint函数获取窗口客户区的hDC,以便在上面绘画时钟,在消息返回的时候用EndPaint函数释放hDC,两个函数的中间,程序把hDC传给_ShowTime子程序,由这个子程序完成整个绘画工作。

在第6章中已经讲到:获取系统时间不能依赖于WM_TIMER消息的计数,所以在_ShowTime子程序的开始,程序调用GetLocalTime来获取当前的系统时间,并根据这个时间来绘画时钟的时、分、秒指针。由于绘画的过程很快,所以整个程序的结构使用前面图7。1中所示的A结构,也就是每次有WM_PAINT消息的时候,程序总是重画整个客户区,所以读者在速度比较慢的计算机上运行这个程序时,可能会看到有个闪烁的过程,因为程序每次总是先将整个客户区清除成背景色(InvalidateRect函数最后的TRUE参数要求Windows在发送WM_PAINT消息前清除客户区),然后绘画四周的刻度,最后画上指针。绘画刻度是由_DrawDot子程序完成的,绘画指针是由_DrawLine子程序完成的。

GetLocalTime后面的_CalcClockParam子程序根据客户区的尺寸计算时钟尺寸参数,它比较客户区高度和宽度,以其中的较小值用做时钟的直径,计算得到的圆心最后存放于全局变量dwCenterX和dwCenterY中,计算得到的半径存放于dwRadius中。

程序中有两个公用的子程序:_CalcX和_CalcY,它们用来计算角度对应的坐标,如图7。5所示,时钟0点时间是从垂直方向开始的,以时间值为角度配合Windows的默认坐标系,对应某个时间点(x,y),x应该是圆心x加上角度的正弦值乘以半径,y应该是圆心y减去角度的余弦值乘以半径。_CalcX和_CalcY输入的参数是角度_dwDegree和半径_dwRadius。子程序中使用80x86的协处理器指令,首先将角度值换算成弧度值——乘以π并除以180,然后用上面分析的公式进行浮点计算并将结果返回。


图7。5  时钟程序的坐标计算

在接下来的内容中,先介绍一些绘画操作的背景知识。

7。2。1  画笔和画刷

GDI中的绘画函数有3大类:画点、画线和画填充区域。使用过Photoshop等图形软件的读者一定知道,在画线之前需要选择一种画笔,这样画出来的线条都是基于这种画笔的;同样,填充一个区域之前需要选择一种画刷,这样整个填充区域将重复使用这个画刷的颜色或图案。

GDI中也有同样的画笔和画刷的概念,画笔、画刷以及其他一些GDI中要使用的东西,包括字体、区域、路径、图案和位图统称GDI中的“对象”,通过SelectObject函数可以指定一个DC当前使用的对象对应哪个对象句柄,称为“当前对象”,当设置了一个当前对象的时候,以后和这种对象相关的函数都将使用当前对象,直到再次用SelectObject选择新的对象为止。比如选择了新的画笔后,以后所有画线函数画出来的线条样式都是由这个画笔决定的,而选择了新的画刷后,则所有填充函数填充的样式都将使用这个画刷。

SelectObject函数的用法是:

    invoke  SelectObject,hDC,hGDIObject

    mov     hOldObject;eax

其中参数hGDIObject就是对象的句柄,它可以是位图句柄、画笔句柄、画刷句柄、字体句柄或区域句柄,函数会根据句柄的种类自动替换原有的对象,并将原来使用的对象句柄返回(当对象类型是区域的时候除外),如果DC中原来没有设置当前对象,那么函数的返回值是GDI_ERROR或NULL。



 
来源:电子工业出版社 作者:罗云彬 上一页         回书目         下一页          
上一页         回书目         下一页          
  


第7章 图形操作


7。2 绘 制 图 形(4)

    
1。 使用预定义的画笔和画刷

Windows预定义了一些常用的画笔和画刷,在程序中可以用GetStockObject来获取它们的句柄,Stock的中文含义是“常备的、库存的”,所以这个函数字面上的意思就是“获取常用的对象”,注意并没有类似于GetStockPen或GetStockBrush之类的函数,所有获取常用对象的操作统一使用GetStockObject函数。

GetStockObject函数的用法是:

    invoke  GetStockObject,fnObject

    mov     hObject;eax

fnObject参数是预定义的对象类型,可以是表7。2所示的取值。

表7。2  GDI中的常用对象

预 定 义 值
 说    明
 
BLACK_PEN

WHITE_PEN

NULL_PEN

BLACK_BRUSH

DKGRAY_BRUSH

GRAY_BRUSH

LTGRAY_BRUSH

WHITE_BRUSH

HOLLOW_BRUSH或NULL_BRUSH

ANSI_FIXED_FONT

ANSI_VAR_FONT

DEFAULT_GUI_FONT(Win95)

OEM_FIXED_FONT

SYSTEM_FONT

DEFAULT_PALETTE
 黑色画笔

白色画笔

空画笔

黑色画刷

深灰色画刷

灰色画刷

浅灰色画刷

白色画刷

空画刷

等宽系统字体

不等宽系统字体

默认系统字体(用于菜单、对话框等)

OEM等宽字体

默认系统字体(用于菜单、对话框等)

默认图案
 

NULL_PEN和NULL_BRUSH是空画笔和空画刷,之所以有空的对象,是因为绘制填充区域的函数同时用到了画笔和画刷——绘制的外框使用当前画笔,中间用当前画刷填充。使用空对象可以有机会画出没有边框线只有填充图案,或者只有边框线而不填充的区域来。

用GetStockObject函数得到对象句柄以后,就可以用SelectObject函数将对象句柄设置到DC中了。例子文件Clock。asm中的_ShowTime函数中用GetStockObject函数获取了一个BLACK_BRUSH画刷,用来绘画时钟的刻度。

2。 使用自定义的画笔和画刷

使用GetStockObject函数得到的对象是最“简陋”的,如画笔只能是白色或黑色的宽度为1像素的实线,画刷只能是白色、黑色和有限的几种灰色色块。要想使用彩色的、多种多样风格的画笔和画刷,就必须用自定义的方法。

创建自定义的画笔可以使用CreatePen,ExtCreatePen或CreatePenIndirect函数,CreatePen函数的使用方法是:

    invoke  CreatePen,fnPenStyle,dwWidth,dwColor

    mov     hPen,eax

fnPenStyle参数是画笔风格,它可以是两种实线风格PS_SOLID,PS_INSIDEFRAME或空画笔PS_NULL,以及几种虚线风格PS_DASH,PS_DOT,PS_DASHDOT或PS_DASHDOTDOT。它们对应的线条如图7。6所示,图中从上到下分别是PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASHDOTDOT和PS_INSIDEFRAME风格的线条,几种虚线的风格很好记,只要记得“点”就是DOT,“划”就是DASH就可以了,如PS_DASHDOTDOT风格就是由“划、点、点”重复组成的虚线。

PS_SOLID和PS_INSIDEFRAME风格的画笔使用的都是实线线条,它们之间的区别在于当画笔的宽度大于1像素。在使用区域绘画函数的时候,PS_SOLID线条会居中画于边线上,而PS_INSIDEFRAME线条会全部画在边线里面,它的宽度会向区域的内部扩展,所以它的名称是InsideFrame。


图7。6  几种自定义画笔风格

CreatePen 函数的dwWidth参数定义了画笔的宽度,单位是DC坐标映射方法中定义的逻辑单位,如果这个参数使用NULL,那么函数会使用1像素的宽度。宽度参数会影响到风格参数:当宽度大于1的时候,画笔风格不能使用虚线,这时候即使指定了虚线风格,函数也会自动使用PS_SOLID风格。dwColor参数指定了画笔的颜色。

例子源代码的_ShowTime子程序中用不同宽度的线条来绘画时、分、秒指针,绘画前就使用CreatePen函数创建了不同宽度的画笔。

如果需要创建更复杂的画笔,可以使用ExtCreatePen函数。这个函数除了有CreatePen的全部功能外,还可以让用户自己定义线条的样子,这样可以不必限制于上面的点点划划了。函数的用法读者可以参考函数手册。

创建自定义画刷可以使用的函数有:CreateSolidBrush,CreateHatchBrush,CreatePatternBrush和CreateBrushIndirect。

CreateSolidBrush创建单色的画刷:

    invoke  CreateSolidBrush,dwColor

    mov     hBrush,eax

要输入的惟一参数是画刷的颜色。而CreateHatchBrush可以创建几种预定义图案的画刷:

    invoke  CreateHatchBrush,iHatchStyle,dwColor

    mov     hBrush,eax

dwColor指定了图案线条的颜色,iHatchStyle定义了不同的图案线条,这些图案线条实际上是以8×8的位图重复铺开组成的,iHatchStyle的定义值可以是HS_BDIAGONAL,HS_CROSS,HS_DIAGCROSS,HS_FDIAGONAL,HS_HORIZONTAL和HS_VERTICAL,这6种图案的花样在图7。7中从左到右排列显示。


图7。7  CreateSolidBrush中的画刷图案

如果这些简单的图案不能满足使用要求,CreatePatternBrush是个很好的选择:

    invoke  CreatePatternBrush,hBitmap

    mov     hBrush,eax

这个函数用一个位图当做画刷的图案,当要绘画的区域大于位图尺寸的时候,位图被重复铺开,就像HTML文件中的背景图案一样。读者可以尝试一下用一幅做网页文件背景的位图创建一个位图画刷,并且在RegisterClassEx时在WNDCLASSEX结构中的hbrBackground字段中使用这个画刷,这样创建出来的窗口背景会和网页背景一样华丽!读者可以参考所附光盘的Chapter07TestObject目录中的源代码。

对于自定义的画笔和画刷,还有其他自定义的对象,在不再需要的时候必须使用DeleteObject函数删除,但是要注意:当对象还是一个DC的当前对象的时候不要将它删除,在删除前应该确定DC中已经选入了其他的对象。与之相反,用GetStockObject获取的预定义对象使用后不需要删除,但是对它们调用DeleteObject也没有关系,因为它们不会被真正删除。由于SelectObject返回值就是DC原来使用的对象句柄,所以删除对象的一个好时机就是当SelectObject返回的时候,如例子程序的_ShowTime子程序中用的:

    invoke  CreatePen;PS_SOLID;2;0

    invoke  SelectObject;_hDC;eax

        invoke  DeleteObject;eax

SelectObject将CreatePen创建的画笔句柄选入DC,返回值eax就是以前使用的画笔句柄,这个句柄不再使用了,所以可以在下面用DeleteObject直接删除,而这次建立的画笔可以在下次执行SelectObject后用同样的方法删除。

7。2。2  绘制像素点

在DC上绘制像素点是绘图最基本的操作,使用的方法是:

    invoke  SetPixel,hDC,dwX,dwY,dwColor

SetPixel函数在hDC的dwX、dwY位置以dwColor为颜色画上一个像素点,如果需要获取hDC中某个像素点当前的颜色值,那么可以使用GetPixel函数:

    invoke  GetPixel,hDC,dwX,dwY

    mov     dwColor,eax

虽然绘画像素是最基本的绘图操作方法,但是在程序中一般很少使用SetPixel函数,因为它的开销太大了,只适合用在需要少量绘画像素的地方,如果要绘画一个线条或者整个区域,那么最好使用画线函数或者填充函数,因为这些函数是在驱动程序级别上完成的,所有的硬件加速功能都可以用上。

图形处理前最基本的步骤是获取像素,但也不应该用GetPixel函数来获取一大块的像素数据,理由是同样的。如果要分析整个区域的像素数据,最好的办法就是用GetDIBits函数将全部数据拷贝到内存中再进行处理。



 
来源:电子工业出版社 作者:罗云彬 上一页         回书目         下一页          
上一页         回书目         下一页          
  


第7章 图形操作


7。2 绘 制 图 形(5)

    
7。2。3  绘制图形

GDI的图形绘制函数主要有绘制线条和填充区域两大类。绘制线条的函数以当前画笔绘制线条;绘制填充区域的函数以当前画笔绘制边线,并以当前画刷填充中间的区域。

1。 绘制线条

绘制线条的函数有画直线的LineTo,画多条直线的Polyline和PolylineTo,画贝塞儿曲线的PolyBezier和PolyBezierTo,画弧线的Arc和ArcTo。

DC的数据结构中有一个“当前点”,LineTo函数就是从当前点画一条直线到参数中指定的点,并把参数中指定的点设置为新的当前点。画线函数中所有以To结尾的函数都是从当前点开始绘画的,如LineTo,PolylineTo,PolyBezierTo和ArcTo,这些函数在绘画结束后会把绘制的最后一
返回目录 上一页 下一页 回到顶部 7 6
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!