各位,看一看,瞧一瞧,新出炉的...大拍卖喽!
第二章、在窗口中画图
精华浓缩:
Windows中负责图形输出的是GDI(即Graphic Device Interface,图形设备接口)。这是Windows与硬件无关的图形输出模式的体现。GDI建立在硬件抽象层(HAL)之上,屏蔽了不同输出设备之间的差异,从而为用户提供了一个统一的“标准输出设备”。但是,与DOS不同,Windows是多任务、进程独立的,每一个窗口都应该有一个独立的输出通道。这样,GDI又使用了一种简单的机制来保证在窗口中画图的不同程序之间能共享“设备”而又互不干扰。这个机制就是DC(Device Context,设备描述表)。
有人把DC比喻成画家的画室,这里有画布、画刷、画笔等等很多工具。
就画布而言,画布形式可以不同,是的,我可以在桌上(desktop)的纸上(window)画,也可以就画在桌面上,还可以画在墙上(管的着吗!^_^)。
为此,Windows MFC提供了四种不同的DC环境(封装为C++类),以标明不同的绘制权限,即:
CPaintDC, 用于在窗口客户区画图(仅限于在OnPaint处理函数中使用);
CClientDC, 也用于在窗口客户区画图(限于在OnPaint处理函数之外使用);
CWindowDC, 用于在窗口内任意地方画图,包括非客户区;
CMetaFileDC, 用于绘制GDI图元文件。
这些类都可以直接实例化,如:
CPaintDC dc(this);//this表示此DC所属窗口为当前窗口
创建了一个CPaintDC对象dc。
CWindowDC一般不常用,如果想在窗口非客户区画图,可借助OnNcPaint()处理函数捕获WM_NCPAINT消息。
刚才说了,DC中还有画刷、画笔等。这些都是DC的属性,可通过DC自身(调用其成员函数)获得。
DC属性包括文本颜色、背景颜色、映射模式、绘图模式、当前位置、当前画笔(刷)和当前字体等。
画笔(Pen)、画刷(Brush)都是独立的GDI对象,可通过CDC成员函数SelectObject()选入DC。同样操作方式的对象还有字体(Font)、位图(Bitmap)、调色板(Palette)和区域(Region)。
关于SelectObject():在创建好GDI对象之后,SelectObject()接收该对象指针,其返回值为先前选入DC的相同类型的对象指针(通常用来恢复DC用)。
Windows也预先定义了一些画笔、画刷、字体以及其他一些GDI对象。这些对象称为备用对象,用CGdiObject::SelectStockObject()选入。CGdiObject是表示GDI对象的CPen、CFont等类的基类。备用对象的属性说明可以查MSDN。
由CGdiObject派生类创建的画笔、画刷和其他对象都要占用内存资源,因此使用后一定要删除它们。处理方法与其他window对象类似。在栈中创建的对象,当此CGdiObject超出范围时会自动析构。在堆中用new创建的CGdiObject对象,可通过调用CGdiObject::DeleteObject显式删除(这会引起对GDI对象析构函数的调用)或用相应的delete运算符。如果是备用对象,则没必要专门删除,留给windows就可以了。
VC++有一种简单的方法用来确定是否成功的删除了GDI对象:只要在调试状态下运行应用程序的调试版本即可。在应用程序结束时,没有释放的资源会显示在调试窗口中。
一些知识点:
1.绘图模式:GDI将象素点输出到逻辑显示平面上时,并不只是简单的输出象素颜色,而是通过一系列布尔运算将输出象素点的颜色和输出目标位置上象素点的颜色进行合成再显示。所使用的逻辑运算关系就由DC的绘图模式确定。使用CDC:SetROP2()可更改绘图模式(ROP2,Raster Operation To)。默认绘图模式是R2_COPYPEN,直接将象素点输出到显示平面上。常用的还有R2_NOT,用于实现鼠标绘图时的Rubber技术。
2.映射模式:这是DC的属性之一,用于确定从逻辑坐标到设备坐标的转换(或者说mapping,映射)方式。设备坐标是指窗口中相应的象素点位置。单位只有象素一种。而逻辑坐标依映射模式不同而单位各异。如默认的MM_TEXT模式下,一个单位恰恰是一个象素点。MM_LOENGLISH模式下,一个单位则相当于百分之一英寸长。windows支持8种映射模式,其中的MM_ISOTROPIC和MM_ANISOTROPIC是可编程的。这意味着是用户而不是windows确定从逻辑坐标到设备坐标的转换方式。换句话说,就是用户来自行定制逻辑单位的实际大小。这两种模式最常用于根据窗口尺寸按比例自动调节画图输出大小的场合。两者之间的唯一区别在于:前者中X方向和Y方向具有同一缩放比例因子。设置映射模式的函数是CDC::SetMapMode()。
3.坐标系常用技术:
1)坐标变换:调用CDC::LPtoDP可将逻辑坐标值转换为设备坐标值。反之,调用CDC::DPtoLP可将设备坐标值转换为逻辑坐标值。在相应鼠标单击区域命中测试时,将使用这两个函数。因为鼠标单击得到的是设备坐标,而很多GDI函数使用逻辑坐标,必须转换为统一坐标,操作才有意义。
2)原点移动:默认方式下,DC的原点位于相应显示平面的左上角。MFC提供了两个CDC类成员函数,可改变原点位置。CDC::SetWindowOrg()移动窗口原点,CDC::SetViewportOrg()移动视图原点(详见MSDN)。一般只能使用两者之一。原点移动技术在边框滚动条设置时有所体现。
3)用户坐标与屏幕坐标:前者是设立在窗口客户区左上角的设备坐标值,后者是原点位于屏幕左上角的设备坐标值。两者的相互转换用函数是CWnd::ClientToScreen()和CWnd::ScreenToClient()。
4.GDI绘图函数
1)画线:包括MoveTo、LineTo、Polyline、PolylineTo、Arc、ArcTo、PolyBezier等。其中Polyline和PolylineTo都可以一次画多条线,两者的区别是PolylineTo使用DC的当前位置,而Polyline则不需要。ArcTo在win98下未实现。另外,所有GDI画线函数有一个共同特点,就是从不画最后一个象素点。
2)画图形:画封闭图形的GDI函数以外接矩形的坐标为参数。常用函数有Rectangle、Ellipse、RoundRect、Pie、Chord等。
5.画笔与画刷:在MFC中,画笔对象封装为CPen类,画刷对象封装为CBrush类。创建两者都有三种方法。最简单的就是构造一个CPen对象,直接给出所用参数进行初始化。第二种方法是调用一个未初始化的CPen对象,用CreatePen()初始化。还有一种方法是构造一个未初始化的CPen对象,通过向LOGPEN结构中填充描述画笔特性的参数,然后调用CPen::CreatePenIndirect()生成画笔。画刷基本相同。
1)CPen:windows用当前选入DC的画笔绘制线条,并给封闭图形镶制边框。画笔有三个特性,即样式、宽度和颜色。样式有PS_SOLID、PS_INSIDEFRAME、PS_NULL等。PS_NULL一般称其为“NULL画笔”,想画一个没有边框的图形,就用到它了。笔宽以逻辑单位给出,实际意义与当前映射模式相同。颜色是通过RGB宏把三个独立颜色成分的值合成为一个可传递给GDI的COLORREF值来确定的。
2)CBrush:画刷有三种基本类型,即单色、带阴影线和带图案。其中图案画刷允许用位图填充图形内部(这样,窗口背景也就没啥新鲜的了)。常用函数有
CDC::SetBkMode()、CDC::SetBkColor()等。
6.文本与字体:CDC有一打文本处理函数,重要的几个是DrawText、TextOut、GetTextMetrics、SetTextColor和SetTextAlign等。其中GetTextMetrics()传给TEXTMETRIC结构关于字体性质的相关信息。字体是一组具有特定尺寸(高度)和字样的字符;字样指示字体共有属性,如粗细等。字体封装在CFont类中,建立字体对象,要在CFont构造之后,再调用CFont的成员函数CreateFont或CreateFontIndirect等来建立GDI字体资源。针对字体,有一个LOGFONT结构,其中定义了字体的所有特性。也可通过填充它来创建字体。
后记:写这一章的笔记时有一种被作者牵着鼻子走的感觉。知识点太多,写出来有一种罗列或拼凑的嫌疑。我不希望这样,但想一想,技术也就是这样。所以我希望大家看的时候,能够思考着看,而不是记忆着看。思考着看只要求我们知道技术点、怎么用,用熟了,用多了,也就记住了。精华是浓缩的,至于如何把它化开,可要靠你自己了。
本文地址:http://com.8s8s.com/it/it2417.htm