Win32汇编图形操作GDI原理14页word文档.docx

上传人:b****6 文档编号:13593633 上传时间:2023-06-15 格式:DOCX 页数:16 大小:27.86KB
下载 相关 举报
Win32汇编图形操作GDI原理14页word文档.docx_第1页
第1页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第2页
第2页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第3页
第3页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第4页
第4页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第5页
第5页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第6页
第6页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第7页
第7页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第8页
第8页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第9页
第9页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第10页
第10页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第11页
第11页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第12页
第12页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第13页
第13页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第14页
第14页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第15页
第15页 / 共16页
Win32汇编图形操作GDI原理14页word文档.docx_第16页
第16页 / 共16页
亲,该文档总共16页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Win32汇编图形操作GDI原理14页word文档.docx

《Win32汇编图形操作GDI原理14页word文档.docx》由会员分享,可在线阅读,更多相关《Win32汇编图形操作GDI原理14页word文档.docx(16页珍藏版)》请在冰点文库上搜索。

Win32汇编图形操作GDI原理14页word文档.docx

Win32汇编图形操作GDI原理14页word文档

Win32汇编图形操作GDI原理

课本、报刊杂志中的成语、名言警句等俯首皆是,但学生写作文运用到文章中的甚少,即使运用也很难做到恰如其分。

为什么?

还是没有彻底“记死”的缘故。

要解决这个问题,方法很简单,每天花3-5分钟左右的时间记一条成语、一则名言警句即可。

可以写在后黑板的“积累专栏”上每日一换,可以在每天课前的3分钟让学生轮流讲解,也可让学生个人搜集,每天往笔记本上抄写,教师定期检查等等。

这样,一年就可记300多条成语、300多则名言警句,日积月累,终究会成为一笔不小的财富。

这些成语典故“贮藏”在学生脑中,自然会出口成章,写作时便会随心所欲地“提取”出来,使文章增色添辉。

世界上只有一种失败,那就是半途而废。

~~天道酬勤。

~~苦我心志,劳我筋骨。

~~…

要练说,得练看。

看与说是统一的,看不准就难以说得好。

练看,就是训练幼儿的观察能力,扩大幼儿的认知范围,让幼儿在观察事物、观察生活、观察自然的活动中,积累词汇、理解词义、发展语言。

在运用观察法组织活动时,我着眼观察于观察对象的选择,着力于观察过程的指导,着重于幼儿观察能力和语言表达能力的提高。

Win32汇编--图形操作--GDI原理

“教书先生”恐怕是市井百姓最为熟悉的一种称呼,从最初的门馆、私塾到晚清的学堂,“教书先生”那一行当怎么说也算是让国人景仰甚或敬畏的一种社会职业。

只是更早的“先生”概念并非源于教书,最初出现的“先生”一词也并非有传授知识那般的含义。

《孟子》中的“先生何为出此言也?

”;《论语》中的“有酒食,先生馔”;《国策》中的“先生坐,何至于此?

”等等,均指“先生”为父兄或有学问、有德行的长辈。

其实《国策》中本身就有“先生长者,有德之称”的说法。

可见“先生”之原意非真正的“教师”之意,倒是与当今“先生”的称呼更接近。

看来,“先生”之本源含义在于礼貌和尊称,并非具学问者的专称。

称“老师”为“先生”的记载,首见于《礼记?

曲礼》,有“从于先生,不越礼而与人言”,其中之“先生”意为“年长、资深之传授知识者”,与教师、老师之意基本一致。

Windows是基于图形界面的,所以在Win32编程中,图形操作是最常用的操作。

GDI的意义在于将程序对图形界面的操作和硬件设备隔绝开来,在程序中可以将所有的图形设备都看成是虚拟设备,包括视频显示器和打印机等,然后通过GDI函数用同样的方法去操作它们,由Windows负责将函数调用转化成针对具体硬件的操作。

只要一个设备提供了和Windows兼容的驱动程序,它就可以被看做是一个标准的设备。

以前在DOS系统下写应用程序的时候,如果要进行图形操作,那么就要考虑到市场上每种显示卡的不同,否则在装配某种显卡的计算机上就可能无法正常运行,对汇编程序员来说,这真是一个恶梦。

在Win32编程中,正是GDI函数让这个恶梦成为历史。

GDI函数全部包括在GDI32.DLL中,在编程的时候,注意要在源程序的开头加上相应的包含语句:

includegdi32.incincludelibgdi32.lib

和GDI相关的内容真是太庞大了,只要查看一下gdi32.inc文件就可以发现,函数的总数达到了300多个,和GDI相关的数据结构也非常多,为了能了解GDI的原理和基本的使用方法:

归纳起来,GDI操作可以从3个方面去了解--When,Where和HowWhen--指的是进行图形操作的时机,究竟什么时刻最适合程序进行图形操作呢?

--"GDI程序的结构"

Where--指的是图形该往哪里画,既然Windows隔离了硬件图形设备,那么该把什么地方当做"下笔"的地方呢?

--"设备环境"

How--了解了上面两个问题后,最后还要知道"如何画",这就涉及如何使用大部分GDI函数的问题了。

一、GDI程序的结构

1、客户区的刷新

正如上面所说的,这里讨论的是"When"的问题,读者可能会问:

为什么会有这个问题,如果要向窗口输出图形,程序想在什么时候输出那就是什么时候,难道这个时刻还有规定不成?

在DOS操作系统中编程的时候,程序把文字或图形输出到屏幕,在输出新的内容之前,这些内容总是保留在屏幕原处,这些内容会被意外覆盖的唯一情况是激活一个TSR程序,但TSR程序在退出之前有义务恢复原来的屏幕,如果它无法恢复屏幕的内容,那么这是它的责任,我们不会在自己的程序中去考虑屏幕内容会无缘无故消失这种情况,所以可以把屏幕看成是应用程序私有的。

如果程序输出的内容过多,如用dir显示一个含有很多文件的目录,用户根本无法看清快速上翻的屏幕,这时程序可以设计一个参数来暂停一下,如dir/p。

这已经是DOS程序最"体贴"的做法了,如果用户想回过头去看已经滚出屏幕的内容,那可对不起,只能再执行一遍了!

所以对DOS程序来说,程序想在什么时候输出信息那就是什么时候,根本不存在When这个问题。

但在Windows操作系统中,屏幕是多个程序"公用"的,用户程序不要指望输出到窗口中的内容经过一段时间后还会保留在那里,它们可能被别的东西覆盖,如其他窗口、鼠标箭头或下拉的菜单等。

在Windows中,恢复被覆盖内容的责任大部分属于用户程序自己,理由很简单:

Windows是个多任务的操作系统,假如程序B覆盖了程序A的窗口内容,覆盖掉的内容由程序B负责恢复的话,它就必须保存它覆盖掉的内容,但是在它将保存的内容恢复之前,程序A也在运行,并可能在程序B恢复以前已经向它自己的窗口输出新的内容,结果当程序B恢复它保存的窗口内容时,保存的内容可能是过时的(而DOS的情况就不同,TSR程序激活的时候,用户程序是被挂起的),所以最好的办法就是让程序A自己来决定如何恢复。

Windows系统采用的方法是:

当Windows检测到窗口被覆盖的地方需要恢复的时候,它会向用户程序发送一个WM_PAINT消息,消息中包括了需要恢复的区域,然后由用户程序来决定如何恢复被覆盖的内容。

如果程序因为忙于处理其他事务以至于无法及时响应WM_PAINT消息,那么窗口客户区原先被覆盖的地方可能会被Windows暂时画成一块白色(或者背景色)的矩形,或者根本就是保留被覆盖时的情形,直到程序有时间去响应WM_PAINT消息为止。

我们常常可以看到这种情况发生在死锁程序的客户区内,这就是因为死锁的程序无法响应WM_PAINT消息来恢复客户区造成的。

所以对于"When"这个问题,答案是:

程序应该在Windows要求的时候绘画客户区,也就是在收到WM_PAINT消息的时候。

如果程序需要主动刷新客户区,那么可以通过调用InvalidateRect等函数引发一条WM_PAINT消息,因为在WM_PAINT消息中刷新客户区的代码是必须存在的,所以用这种看似"舍近求远"的办法实际上可以节省一份重复的代码。

即使是在游戏程序这种"主动刷新"远远多于"被动刷新"的程序中,只要窗口有被其他东西覆盖的可能,那么这个原则就是适用的。

2、GDI程序的结构

对于Win32程序来说,WM_PAINT消息随时可能发生,这就意味着,程序再也不能像在DOS下一样输出结果后就不管了,反过来,程序在任何时刻都应该知道如何恢复整个或局部客户区中以前输出的内容。

如果程序的功能比较简单,可以将计算及刷新整个客户区的代码全部安排在WM_PAINT消息中完成,这样,每次当客户区的全部或部分需要被更新的时候,程序重新执行整个生成客户区屏幕数据的功能模块并刷新客户区。

这种结构适用于功能模块很短小且执行速度很快的情况,整个过程的时间最好不超过几百ms,否则,用户会在一个明显的等待时间后才看到程序把客户区中的"空洞"补上。

当生成屏幕数据的功能模块有些复杂的时候,就应该考虑采用如下结构,即功能模块和客户区刷新模块分别在不同的子程序中实现,功能模块单独用一个子程序完成,这个子程序可以由用户通过选择菜单项在WM_COMMAND消息中执行,也可以新建另外一个线和来完成,总之,它最后把计算结果放到一个缓冲区中,而每当客户区需要刷新时,程序在WM_PAINT消息中调用客户区刷新子程序,这个子程序从计算好的缓冲区中取出数据并输出到客户区中,由于单纯的屏幕刷新过程是很快的,所以用户根本来不及看到客户区中的空洞。

3、探讨WM_PAINT消息

当客户区被覆盖并重新显示的时候,Windows并不是在所有的的下都发送WM_PAINT消息,下面是几种不同的情况:

l当鼠标光标移过窗口客户区以及图标拖过客户区这两种情况,Windows总是自己保存被覆盖的区域并恢复它,并不需要发送WM_PAINT消息通知用户程序。

l当窗口客户区被自己的下拉式菜单覆盖,或者被自己弹出的对话框覆盖后,Windows会尝试保存被覆盖的区域并在以后恢复它,如果因为某种原因无法保存并恢复的话,Windows会发送一个WM_PAINT消息通知程序。

l别的情况造成窗口的一部分从不可见变到可见,如程序从最小化的状态恢复,其他的窗口覆盖客户区后移开,用户改变了窗口的大小不一和用户按动滚动条等,在这些情况下,Windows会向窗口发送WM_PAINT消息。

l一些函数会引发WM_PAINT消息,如UpdateWindow,InvalidateRect以及InvalidateRgn函数等。

窗口过程收到WM_PAINT消息后,并不代表整个客户区都需要被刷新,有可能客户区被覆盖的区域只有一小块,这个区域就叫做"无效区域",程序只需要更新这个区域。

和WM_TIMER消息类似,WM_PAINT消息也是一个低级别的消息,虽然它不会像WM_TIMER消息一样被丢弃,但Windows总是在消息循环空的时候才把WM_PAINT放入其中,实际上,Windows为每个窗口维护一个"绘图信息结构",无效区域的坐标就在其中,每当消息循环空的时候,如果Windows发现存在一个无效区域,就会放入一个WM_PAINT消息。

无效区域的坐标并不附带在WM_PAINT消息的参数中,在程序中有其他方法可以获取,WM_PAINT消息只是通知程序有个区域需要更新而已,所以Windows也不会同时将两条WM_PAINT消息放入消息循环,当Windows要放入一条WM_PAINT消息的时候,如果发现已存在一个无效区域了,那么它只需要把新旧两个无效区域合并计算出一个新的无效区域就可以了,消息循环中还是只需要一条WM_PAINT消息。

由于存在"无效区域"这样一个东西,所以程序在WM_PAINT消息中对客户区刷新完毕后工作并没有结束,如果不使无效区域变得有效,Windows会在下一轮消息循环中继续放入一个WM_PAINT消息。

当Windows检查"绘图信息结构"的时候发现没有了无效区域,也就不会继续发送WM_PAINT消息了。

WM_PAINT消息的处理流程一般是:

.ifeax==WM_PAINT;eax为uMsginvokeBeginPaint,hWnd,addrstPS

;刷新客户区的代码

invokeEndPaint,hWnd,addrstPSxoreax,eaxret

读者可以发现中间并没有调用ValidateRect来使无效区域变得有效,这是因为BeginPaint函数和EndPaint函数隐含有这个功能,如果不是以BeginPaint/EndPaint当做消息处理代码的头尾的话,那么在WM_PAINT消息返回的时候就必须调用ValidateRect函数。

BeginPaint函数的第二个参数是一个绘图信息结构的缓冲区地址,Windows会在这里返回绘图信息结构,结构中包含了无效区域的位置和大小,绘图信息结构的定义如下:

PAINTSTRUCTSTRUCThdcDWORD?

fEraseDWORD?

rcPaintRECTfRestoreDWORD?

fIncUpdateDWORD?

rgbReservedBYTE32dup(?

PAINTSTRUCTENDS

其中hdc字段是窗口的设备环境句柄,rcPaint字段是一个RECT结构,它指定了无效区域矩形的对角顶点,fErase字段如果为非零值,表示Windows在发送WM_PAINT消息前已经用背景色擦除了无效区域,后面3个字段是Windows内部使用的,应用程序不必去理会它们。

二、设备环境

解决了"When"的总是后,再考虑一下"Where"的问题。

在DOS操作系统中,向屏幕输出数据实际上是把输出内容拷贝到视频缓冲区中,如果在文本模式下显示信息,只需要把内容拷贝到B8000h处的内存中;显示图形信息,可以把图形数据拷贝到A0000h处的内存中。

在Windows中,GDI接口把程序和硬件分隔出来,在Win32编程中,再也不能通过直接向视频缓冲区拷贝数据的办法来显示信息了,那么,究竟该往哪里输出图形呢--这就是"Where"的问题。

答案是:

通过"设备环境"来输出图形。

1、什么是设备环境

在Windows中,所有与图形相关的操作都是用统一的方法来完成的(不然就不能称为"图形设备接口"了)。

不管是绘画屏幕上的一个窗口,还是把图形输出到打印机,或者对一幅位图进行绘画,使用的绘图函数都是相同的,为了实现方法上的统一,必须将所有的图形对象看成是一个虚拟的设备,这些设备可能有不同的属性,如黑白打印机和彩色屏幕的颜色深度是不同的,不同打印机的尺寸和分辨率可能是不同的,绘图仪只支持矢量而不支持位图等。

不同设备的不同属性就构成了一个绘图的"环境",就像DOS操作系统中把视频缓冲区当做图形操作的对象一样,这个绘图的"环境"就是Win32编程中图形操作的对象,把它叫做"设备环境"。

设备环境实际上是一个数据结构,结构中保存的就是设备的属性,当对设备环境进行图形操作的时候,Windows可以根据这些属性找到对应的设备进行相关的操作。

在实际使用中,通过"设备环境"可以操作的对象很广泛,除了可以是打印机或绘图仪等硬件设备外,也可以是窗口的客户区,包括大大小小的所有可以被称为窗口的按钮与控件等的客户区,也可以是一个位图。

总之,任何需要用到图形操作的东西都可以通过"设备环境"进行绘图。

为了更好地理解"设备环境"是什么,先来看一个例子:

//DcCopy.asm

.386

.modelflat,stdcalloptioncasemap:

none

;Include文件定义

includewindows.incincludegdi32.incincludelibgdi32.libincludeuser32.incincludelibuser32.libincludekernel32.incincludelibkernel32.libID_TIMERequ1

;数据段

.data?

hInstancedd?

hWin1dd?

hWin2dd?

.constszClass1db'SourceWindow',0szClass2db'DestWindow',0szCaption1db'请尝试用别的窗口覆盖本窗口!

',0szCaption2db'本窗口图像拷贝自另一窗口',0szTextdb'Win32Assembly,Simpleandpowerful!

',0

.code

;定时器过程

_ProcTimerproc_hWnd,uMsg,_idEvent,_dwTimelocal@hDc1,@hDc2local@stRect:

RECTinvokeGetDC,hWin1mov@hDc1,eaxinvokeGetDC,hWin2mov@hDc2,eaxinvokeGetClientRect,hWin1,addr@stRectinvokeBitBlt,@hDc2,0,0,@stRect.right,@stRect.bottom,@hDc1,0,0,SRCCOPYinvokeReleaseDC,hWin1,@hDc1invokeReleaseDC,hWin2,@hDc2ret_ProcTimerendp

;窗口过程

_ProcWinMainprocusesebxediesi,hWnd,uMsg,wParam,lParamlocal@stPs:

PAINTSTRUCTlocal@stRect:

RECTlocal@hDcmoveax,uMsgmovecx,hWnd

.ifeax==WM_PAINT&&ecx==hWin1invokeBeginPaint,hWnd,addr@stPsmov@hDc,eaxinvokeGetClientRect,hWnd,addr@stRectinvokeDrawText,@hDc,addrszText,-1,addr@stRect,DT_SINGLELINEorDT_CENTERorDT_VCENTERinvokeEndPaint,hWnd,addr@stPs

.elseifeax==WM_CLOSEinvokePostQuitMessage,NULLinvokeDestroyWindow,hWin1invokeDestroyWindow,hWin2

.elseinvokeDefWindowProc,hWnd,uMsg,wParam,lParamret

.endif

xoreax,eaxret_ProcWinMainendp_WinMainproclocal@stWndClass:

WNDCLASSEXlocal@stMsg:

MSGlocal@hTimerinvokeGetModuleHandle,NULLmovhInstance,eaxinvokeRtlZeroMemory,addr@stWndClass,sizeof@stWndClass

invokeLoadCursor,0,IDC_ARROWmov@stWndClass.hCursor,eaxpushhInstancepop@stWndClass.hInstancemov@stWndClass.cbSize,sizeofWNDCLASSEXmov@stWndClass.style,CS_HREDRAWorCS_VREDRAWmov@stWndClass.lpfnWndProc,offset_ProcWinMainmov@stWndClass.hbrBackground,COLOR_WINDOW+1mov@stWndClass.lpszClassName,offsetszClass1invokeRegisterClassEx,addr@stWndClassinvokeCreateWindowEx,WS_EX_CLIENTEDGE,offsetszClass1,offsetszCaption1,WS_OVERLAPPEDWINDOW,450,100,300,300,NULL,NULL,hInstance,NULLmovhWin1,eaxinvokeShowWindow,hWin1,SW_SHOWNORMALinvokeUpdateWindow,hWin1

mov@stWndClass.lpszClassName,offsetszClass2invokeRegisterClassEx,addr@stWndClassinvokeCreateWindowEx,WS_EX_CLIENTEDGE,offsetszClass2,offsetszCaption2,WS_OVERLAPPEDWINDOW,100,100,300,300,NULL,NULL,hInstance,NULLmovhWin2,eaxinvokeShowWindow,hWin2,SW_SHOWNORMALinvokeUpdateWindow,hWin2

;设置定时器

invokeSetTimer,NULL,NULL,100,addr_ProcTimermov@hTimer,eax

;消息循环

.whileTRUEinvokeGetMessage,addr@stMsg,NULL,0,0

.break.ifeax==0invokeTranslateMessage,addr@stMsginvokeDispatchMessage,addr@stMsg

.endw

;清除定时器

invokeKillTimer,NULL,@hTimerret_WinMainendpstart:

call_WinMaininvokeExitProcess,NULLendstart

这个程序的代码用到的大部分知识都是前面已经讲到的,在_WinMain中,用一个同样的窗口类建立了两个窗口,两个窗口属于同一个窗口类,所以它们的窗口过程都是_ProcWinMain,为了关闭任何一个窗口都可以结束程序,WM_CLOSE消息中用DestroyWindow函数摧毁了两个窗口。

程序设置了一个周期为100ms的定时器,Windows会每隔100ms调用_ProcTimer子程序。

在_ProcTimer中,将其中一个窗口的客户区拷贝到另一个窗口的客户区中,方法是通过GetDC获取窗口的DC句柄,并用BitBlt函数完成拷贝工作,所以在右边的窗口显示了一句"Win32Assembly,Simpleandpowerful!

",左边的窗口中也会出现这句话。

程序每100ms将右边窗口的客户区拷贝到左边的窗口客户区中,通过左边窗口的客户区就可以了解右边客户区的DC对应的究竟是什么内容。

通过左边窗口的变化可以惊奇地发现:

右边窗口客户区的内容并不是程序自己输出到客户区的那句文本,而是以客户区为矩形区域的屏幕上我们真正看到的东西,它竟然包括其他窗口覆盖在上面的东西。

这就意味着,扫雷游戏和纸牌游戏通过自己客户区对应的设备环境画图形,图形数据竟然画到了DcCopy窗口客户区对应的设备环境中。

这个例子验证了"设备环境"只是"环境"而不是"设备",它并不存储发给它的图形数据,图形数据透过它写到了它所描述的"设备"上,每个窗口客户区的"设备环境"对应的设备都是屏幕,但它们在位置上可能重叠,所以向一个窗口的客户区写数据相当于同时写了下层窗口的客户区。

为了让当前激活的窗口在视觉上保持在最上面,下层窗口向自己客户区写的内容首先要经过Windows的"过滤",只有没有被其他窗口覆盖掉的部分才真正被写到了屏幕上。

读者应该时刻提醒自己--"设备环境"只是一个环境,是设备属性的一组定义,程序输出的图形数据透过"设备环境"被定向到了具体的设备上,"设备环境"本身并不存储这些数据。

DeviceContext中Context的含义:

设备环境的上面是应用程序,下面是具体设备,而它是用来"联系上下关系"用的。

有人可能认为:

屏幕上的窗口就像放在桌面上的一张张纸,虽然一张纸可能暂时被另一张遮住,但纸上写的东西还是存在的,移开另一张纸就可以再次露出来。

但实际情况是:

桌面更像一个用粉笔写的公告黑板,一个窗口相当于划了一块空间写告示,写另一个告示的时候要把老告示的内容擦去一部分以便写新的内容,擦去的东西也就不存在了,如果要恢复老告示,那么必须把擦去的部分重新写上去。

2、获取设备环境句柄

要想对任何设备绘图,首先必须获取设备的"设备环境句柄"(hDC),几乎所有的GDI函数的操作目标都是hDC,在程序中得到一个hDC有几种方法。

最常用的方法是在WM_PAINT消息中用BeginPaint函数得到hDC,WM_PAINT消息的代码结构一般是:

.ifeax==WM_PAINT;eax为uMsginvokeBeginPaint,hWnd,addrstPS

;刷新客户区的代码

invokeEndPaint,hWnd,addrstPSxoreax,eaxretBegin

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 求职职场 > 面试

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2