Presentation is loading. Please wait.

Presentation is loading. Please wait.

版权所有 复制必究 第 8 章 图形设备接口.

Similar presentations


Presentation on theme: "版权所有 复制必究 第 8 章 图形设备接口."— Presentation transcript:

1 版权所有 复制必究 第 8 章 图形设备接口

2 Windows是一个图形操作系统,其所有的图形可视效果都是通过绘制操作而完成的。图形显示的实质就是利用Windows提供的图形设备接口将图形绘制在显示器上。大多数应用程序都需要在客户区绘制一些图形,如绘制文本、几何图形、位图和光标等。 前面几章已经涉及到有关图形处理的内容,只是使用了Windows系统默认的图形设备接口和设备环境,绘制的图形没有颜色、线型和字体的变化。

3 本章主要学习内容: 图形处理的基本原理: 使用画笔和画刷绘制图形 文本与字体 位图、图标和光标 图形设备接口 设备环境 GDI坐标系
映射模式 使用画笔和画刷绘制图形 文本与字体 位图、图标和光标

4 8.1 图形设备接口 Windows提供了一个称为图形设备接口GDI(Graphics Device Interface)的抽象接口。GDI作为Windows的重要组成部分,它负责管理用户绘图操作时功能的转换。用户通过调用GDI函数与设备打交道,GDI通过不同设备提供的驱动程序将绘图语句转换为对应的绘图指令,避免了直接对硬件进行操作,从而实现所谓的设备无关性。 编程时采用MFC方法绘制图形也很方便,MFC对GDI函数和绘图对象进行了封装。

5 概述 图形设备接口GDI管理Windows应用程序图形的绘制,在应用程序中,通过调用GDI函数绘制不同尺寸、颜色、风格的几何图形、文本和位图。这些图形处理函数组成了图形设备接口GDI。 GDI是形成Windows核心的三种动态链接库之一,MFC将GDI函数封装在一个名为CDC的设备环境类中,因此我们可以通过调用CDC类的成员函数来完成绘图操作。 所谓设备无关性,是指操作系统屏蔽了硬件设备的差异,使用户编程时一般无需考虑设备的类型,如不同种类的显示器或打印机。

6 Windows绘图过程和设备无关性的实现:
GDI处于设备驱动程序的上一层,当程序调用绘图函数时,GDI将绘图命令传送给当前设备的驱动程序,以调用驱动程序提供的接口函数。驱动程序的接口函数将Windows绘图命令转化为设备能够执行的输出命令,实现图形的绘制。不同设备具有不同的驱动程序,设备驱动程序是设备相关的。

7 设备环境 为了实现设备无关性,应用程序的输出不直接面向显示器等物理设备,而是面向一个称之为设备环境DC(Device Context)的虚拟逻辑设备。 设备环境也称设备描述表或设备上下文,它是由Windows管理的一个数据结构,它保存了绘图操作中一些共同需要设置的信息,如当前的画笔、画刷、字体和位图等图形对象及其属性,以及颜色和背景等影响图形输出的绘图模式。 形象地说,一个设备环境提供了一张画布和一些绘画的工具,我们可以使用不同颜色的工具在上面绘制点、线、圆和文本。

8 设备环境中的“设备”是指任何类型的显示器或打印机等输出设备,绘图时用户不用关心所使用设备的编程原理和方法。所有的绘制操作必须通过设备环境进行间接的处理,Windows自动将设备环境所描述的结构映射到相应的物理设备上。 从根本上来说,设备环境DC是一个Windows数据结构,该结构存储着程序向设备输出时所需要的信息,应用程序利用它定义图形对象及其属性,并实现应用程序、设备驱动程序和输出设备之间绘图命令的转换。 在Windows中不使用DC无法进行输出,在使用任何GDI绘图函数之前,必须建立一个设备环境。

9 获取设备环境DC的方法: 在程序中不能直接存取DC数据结构,只能通过系统提供的一系列函数或使用设备环境的句柄HDC来间接地获取或设置设备环境结构中的各项属性,如显示器高度和宽度、支持的颜色数及分辨率等。 如果采用SDK方法编程,获取DC的方法有两种:在WM_PAINT消息处理函数中通过调用API函数BeginPaint()获取设备环境,在消息处理函数返回前调用API函数EndPaint()释放设备环境。在其他函数中通过调用API函数GetDC()获取设备环境,调用API函数ReleaseDC()释放设备环境。

10 如果采用MFC方法编程,MFC提供了不同类型的DC类,每一个类都封装了DC句柄,并且它们的构造函数自动调用获取DC的API函数,析构函数自动调用释放DC的API函数。因此,在程序中通过声明一个MFC设备环境类的对象就自动获取了一个DC,而当该对象被销毁时就自动释放了获取的DC。MFC AppWizard应用程序向导创建的OnDraw()函数自动支持所获取的DC。 MFC的DC类包括CDC、CPaintDC、CClientDC、CWindowDC和CMetaFileDC等,其中CDC类是MFC设备环境类的基类,其它的MFC设备环境类都是CDC的派生类。

11 MFC设备环境类: CDC类既作为其它MFC设备环境类的基类,又可以作为一个一般的设备环境类使用。利用它可以访问设备属性和设置绘图属性。CDC类对GDI的所有绘图函数进行了封装。 CPaintDC类是OnPaint()函数使用的设备环境类,它代表一个窗口的绘图画面。如果添加WM_PAINT消息处理函数OnPaint(),就需要使用CPaintDC类来定义一个设备环境对象。 CClientDC类代表了客户区设备环境。当在客户区实时绘图时,需要利用CClientDC类定义一个客户区设备环境。 CWindowDC类代表了整个程序窗口设备环境,可以在整个窗口区域绘图。

12 GDI坐标系和映射模式 Windows坐标系分为逻辑坐标系和设备坐标系两种,GDI支持这两种坐标系。一般而言,GDI的文本和图形输出函数使用逻辑坐标,而在客户区移动或按下鼠标的鼠标位置是采用设备坐标。 逻辑坐标系是面向DC的坐标系,这种坐标不考虑具体的设备类型,在绘图时,Windows会根据当前设置的映射模式将逻辑坐标转换为设备坐标。 设备坐标系是面向物理设备的坐标系,这种坐标以像素或设备所能表示的最小长度单位为单位,X轴方向向右,Y轴方向向下。设备坐标系的原点位置(0, 0)不限定在设备显示区域的左上角。

13 屏幕坐标系、窗口坐标系和客户区坐标系 设备坐标系分为屏幕坐标系、窗口坐标系和客户区坐标系三种相互独立的坐标系。 屏幕坐标系以屏幕左上角为原点,一些与整个屏幕有关的函数均采用屏幕坐标,如GetCursorPos()、SetCursorPos()、CreateWindow()、MoveWindow()。弹出式菜单使用的也是屏幕坐标。 窗口坐标系以窗口左上角为坐标原点,它包括窗口标题栏、菜单栏和工具栏等范围。 客户区坐标系以窗口客户区左上角为原点,主要用于客户区的绘图输出和窗口消息的处理。鼠标消息的坐标参数使用客户区坐标,CDC类绘图成员函数使用与客户区坐标对应的逻辑坐标。

14 坐标之间的相互转换 编程时,有时需要根据当前的具体情况进行三种设备坐标之间或与逻辑坐标的相互转换。 MFC提供了两个函数CWnd::ScreenToClient()和CWnd::ClientToScreen()用于屏幕坐标与客户区坐标的相互转换。 MFC提供了两个函数CDC::DPtoLP()和CDC:: LPtoDP()用于设备坐标与逻辑坐标之间的相互转换。

15 例 修改例5-13中的程序MyDraw,采用将设备坐标转换为逻辑坐标的方法实现滚动视图的功能。
Windows鼠标位置使用设备坐标系,以客户区窗口原点作为基准,而在OnDraw()函数中使用逻辑坐标。因此,为了在滚动视图中重绘图形,必须在存储线段起点和终点之前将其坐标转换为逻辑坐标。实质上,OnDraw()函数由OnPaint()函数调用,在调用OnDraw()函数前,OnPaint()函数已经调用了函数OnPrepareDC()对设备环境进行了调整。 打开应用程序项目MyDraw,修改单击鼠标和鼠标移动的消息处理函数。

16 void CMyDrawView::OnLButtonDown(
UINT nFlags, CPoint point) { // TODO: Add your message handler code here CClientDC dc(this); OnPrepareDC(&dc); // 调整设备环境的属性 dc.DPtoLP(&point); // 将设备坐标转换为逻辑坐标 SetCapture(); // 捕捉鼠标 ::SetCursor(m_hCross); // 设置十字光标 m_ptOrigin=point; m_bDragging=TRUE; // 设置拖拽标记 // CScrollView::OnLButtonDown(nFlags, point); }

17 void CMyDrawView::OnMouseMove(UINT nFlags, CPoint point)
{ if(m_bDragging) CMyDrawDoc *pDoc=GetDocument(); ASSERT_VALID(pDoc); CClientDC dc(this); OnPrepareDC(&dc); dc.DPtoLP(&point); pDoc->AddLine(m_ptOrigin, point); dc.MoveTo(m_ptOrigin); dc.LineTo(point); m_ptOrigin=point; } // CScrollView::OnMouseMove(nFlags, point);

18 映射模式 映射模式确定了在绘制图形时所依据的坐标系,它定义了逻辑单位的实际大小、坐标增长方向,所有映射模式的坐标原点均在设备输出区域(如客户区或打印区)的左上角。此外,对于某些映射模式,用户还可以自定义窗口的长度和宽度,设置视图区的物理范围。 Windows定义了8种映射模式:MM_TEXT、MM_LOMETRIC、MM_HIMETRIC、MM_LOE- NGLISH、MM_HIENGLISH、MM_TWIPS、MM _ISOTROPIC、MM_ANISOTROPIC 映射模式使得程序员可不必考虑输出设备的具体设备坐标系,而在一个统一的逻辑坐标系中进行图形的绘制。

19 Windows映射模式 映射模式 逻辑单位 坐标系设定 MM_TEXT 一个像素 X轴正方向朝右,Y轴正方向朝下 MM_LOMETRIC
0.1毫米 X轴正方向朝右,Y轴正方向朝上 MM_HIMETRIC 0.01毫米 MM_LOENGLISH 0.01英寸 MM_HIENGLISH 0.001英寸 MM_TWIPS 1/1440英寸 MM_ISOTROPIC 系统确定 X、Y轴可任意调节,X、Y轴比例为1:1 MM_ANISOTROPIC X、Y轴可任意调节,X、Y轴比例任意

20 设置原点的坐标: 通过调用函数CDC::SetWindowOrg()设置设备环境的窗口原点的坐标,调用CDC::SetViewportOrg()重新设置设备的视口原点的坐标。这里,窗口是对应于逻辑坐标系(设备环境)由用户设定的一个区域,而视口是对应于实际输出设备由用户设定的一个区域。 窗口原点是指逻辑窗口坐标系的原点在视口(设备)坐标系中的位置,视口原点是指设备实际输出区域的原点。 除了映射模式,窗口和视口也是决定一个点的逻辑坐标如何转换为设备坐标的一个因素。一个点的逻辑坐标按照如下式子转换为设备坐标: 设备(视口)坐标 = 逻辑坐标 – 窗口原点坐标 视口原点坐标

21 例 分别在OnDraw()函数中添加如下代码,设置不同的窗口原点和视口原点,结果有什么不同。
(1) pDC->SetMapMode(MM_TEXT); pDC->Rectangle(CRect(50, 50, 100, 100)); (2) pDC->SetMapMode(MM_TEXT); pDC->SetWindowOrg(50, 50); (3) pDC->SetMapMode(MM_TEXT); pDC->SetViewportOrg(50,50); (4) pDC->SetMapMode(MM_TEXT);

22

23 颜色的设置 Windows用COLORREF类型的数据存放颜色,它是一个32位整数。任何一种颜色都是由红、绿、蓝三种基本颜色组成,COLORREF类型数据的低位字节存放红色强度值,第2个字节存放绿色强度值,第3个字节存放蓝色强度值,高位字节为0,每一种颜色分量的取值范围为0到255。 直接设置COLORREF数据不太方便,Windows提供了RGB宏用于设置颜色,将其中的红、绿、蓝分量值转换为COLORREF类型的颜色数据: RGB(byRed, byGreen, byBlue) 其中参数byRed、byGreen和byBlue分别表示红、绿、蓝分量值(范围0到255)。

24 RGB宏的使用: 很多涉及到颜色的GDI函数都需要使用COLORREF类型的参数,如设置背景色的成员函数CDC::SetBkColor()、设置文本颜色的成员函数CDC::SetTextColor()。例如: COLORREF rgbBkClr=RGB(192,192,192); // 定义灰色 pDC->SetBkCorlor(rgbBkClr); // 背景色为灰色 pDC->SetTextColor(RGB(0,0,255)); // 文本颜色为兰色

25 标准彩色的RGB值 颜色 RGB分量值 浅红 255,0,0 深红 128,0,0 浅绿 0,255,0 深绿 0,128,0 浅蓝
0,0,255 深蓝 0,0,128 浅黄 255,255,0 深黄 128,128,0 浅青 0,255,255 深青 0,128,128 紫色 255,0,255 灰色 192,192,192 白色 255,255,255 黑色 0,0,0

26 8.2 画笔和画刷 在默认状态下,当用户创建一个设备环境并在其中绘图时,系统使用设备环境缺省的绘图工具及其属性。如果要使用不同风格和颜色的绘图工具进行绘图,用户必须重新为设备环境设置自定义的画笔和画刷等绘图工具。 画笔和画刷是Windows中两种最重要的绘图工具,画笔用于绘制点、线、矩形和椭圆等几何图形,画刷用指定的颜色和图案来填充绘图区域,这些绘图工具又统称为GDI对象。

27 GDI对象 Windows GDI提供了一些绘图对象,程序通过这些GDI对象设置绘图的工具和风格,这里的对象是指Windows数据结构,而不是C++类的对象。 GDI对象是Windows图形设备接口的抽象绘图工具。除了画笔和画刷,其它GDI对象还包括字体、位图和调色板。 MFC对GDI对象进行了很好的封装,提供了封装GDI对象的类,如CPen、CBrush、CFont、CBitmap和CPalette等,这些类都是GDI对象类CGdiObject的派生类。

28 选择一个GDI对象: CDC类提供了成员函数SelectObject()选择用户自己创建的GDI对象,该函数有多种重载形式,可以选择用户已定制好的画笔、画刷、字体和位图等不同类型的GDI对象。 CPen* SelectObject(CPen* pPen); CBrush* SelectObject(CBrush* pBrush); virtual CFont* SelectObject(CFont* pFont); CBitmap* SelectObject(CBitmap* pBitmap); 函数参数是一个指向用户已定制好的GDI对象的指针,选择操作成功函数将返回以前GDI对象的指针,否则返回NULL。

29 使用画笔 当用户创建一个用于绘图的设备环境时,该设备环境自动提供了一个宽度为一个像素单位、风格为实黑线(BLACK_PEN)的缺省画笔。如果要在设备环境使用自己的画笔绘图,首先需要创建一个指定风格的画笔,然后将创建的画笔选入设备环境,最后,在使用该画笔绘图结束后需要释放该画笔。 1. 创建画笔 创建画笔最简单的方法是调用CPen类的一个带参数的构造函数来构造一个CPen类画笔对象,以下代码创建了一个红色虚线画笔: CPen PenNew (PS_DASH, 1, RGB(255, 0, 0));

30 创建画笔的第二种方法是首先构造一个没有初始化的CPen类画笔对象,然后调用成员函数CPen::CreatePen()创建定制的画笔工具: CPen PenNew; PenNew.CreatePen(PS_DASH, 1, RGB(255,0,0)); 函数CreatePen()的参数类型与带参数的CPen类构造函数完全一样。当画笔对象的声明与创建不在同一个地方时(如需要多次改变画笔)只有采用这种方法。 样 式 说 明 PS_SOLID 实线 PS_DASHDOTDOT 双点划线 PS_DOT 点线 PS_NULL 空的边框 PS_DASH 虚线 PS_INSIDEFRAME 边框实线 PS_DASHDOT 点划线

31 2. 选择创建的画笔 创建画笔后必须调用成员函数CDC:: SelectObject()将创建的画笔选入当前设备环境。如果选择成功,函数SelectObject()将返回以前画笔对象的指针。选择新的画笔时应该保存以前的画笔对象,如下代码所示: CPen* pPenOld pPenOld =pDC->SelectObject(&PenNew);

32 3. 还原画笔 创建和选择画笔工具后,应用程序就可以使用该画笔绘图。当绘图完成后,应该通过调用成员函数CDC::SelectObject()恢复设备环境以前的画笔工具,并通过调用成员函数CGdiObject::DeleteObject()释放GDI对象所占的内存资源,如下代码所示: pDC->SelectObject(pPenOld); // 恢复设备环境DC中原来的画笔 PenNew.DeleteObject(); // 删除底层的GDI对象

33 例 编写一个SDI应用程序,绘制不同风格、宽度和颜色的直线。
在OnDraw()函数中添加如下所示的代码: CPen *pPenOld, PenNew; int nPenStyle[]= { PS_SOLID, // 实线 PS_DOT, // 点线 PS_DASH, // 虚线 PS_DASHDOT, // 点划线 PS_DASHDOTDOT, // 双点划线 PS_NULL, // 空的边框 PS_INSIDEFRAME, // 边框实线 }; char *strStyle[]={"Solid","Dot","Dash","DashDot", "DashDotDot","Null","InsideFrame"}; pDC->TextOut(60,10,"用不同样式的画笔绘图");

34 for(int i=0; i<7; i++) // 用不同样式的画笔绘图 { if(PenNew
for(int i=0; i<7; i++) // 用不同样式的画笔绘图 { if(PenNew.CreatePen(nPenStyle[i],1,RGB(0,0,0))) //创建画笔 pPenOld=pDC->SelectObject(&PenNew); // 选择画笔 pDC->TextOut(10,30+20*i,strStyle[i]); pDC->MoveTo(100,40+20*i); pDC->LineTo(200,40+20*i); pDC->SelectObject(pPenOld); // 恢复原来的画笔 PenNew.DeleteObject(); // 删除底层的GDI对象 } else MessageBox("不能创建画笔!");

35 char *strWidth[]={"1","2","3","4","5","6","7"}; pDC->TextOut(260,10,"用不同宽度的画笔绘图"); for(i=0; i<7; i++) // 用不同宽度的画笔绘图 { if(PenNew.CreatePen(PS_SOLID,i+1,RGB(0,0,0))) // 创建画笔 pPenOld=pDC->SelectObject(&PenNew); // 选择画笔 pDC->TextOut(260,30+20*i,strWidth[i]); pDC->MoveTo(300,40+20*i); pDC->LineTo(400,40+20*i); pDC->SelectObject(pPenOld); // 恢复原来的画笔 PenNew.DeleteObject(); // 删除底层的GDI对象 } else MessageBox("不能创建画笔!");

36 char *strColor[]={"红","绿","蓝","黄","紫","青","灰"}; COLORREF rgbPenClr[]={RGB(255,0,0),RGB(0,255,0), RGB(0,0,255),RGB(255,255,0),RGB(255,0,255), RGB(0,255,255),RGB(192,192,192)}; pDC->TextOut(460,10,"用不同颜色的画笔绘图"); for(i=0; i<7; i++) // 用不同颜色的画笔绘图 { CPen *pPenNew=new CPen(PS_SOLID,2,rgbPenClr[i]); // 创建画笔的另一种方法 pPenOld=pDC->SelectObject(pPenNew); // 选择创建的画笔 pDC->TextOut(460,30+20*i, strColor[i]); pDC->MoveTo(500,40+20*i); pDC->LineTo(600,40+20*i); pDC->SelectObject(pPenOld); // 恢复原来的画笔 delete pPenNew; // 自动删除底层的GDI对象 }

37

38 使用画刷 当创建一个设备环境时,该设备环境自动提供了一个填充色为白色(WHITE_BRUSH)的缺省画刷。与画笔一样,也可以利用MFC画刷类CBrush创建自己的画刷,用于填充图形的绘制。 画刷有三种基本类型:纯色画刷、阴影画刷和图案画刷,CBrush类提供了多个不同重载形式的构造函数。以下创建三种不同类型的画刷: CBrush brush1(RGB(255,0,0)); // 创建纯色画刷 CBrush brush2(HS_DIAGCROSS, RGB(0,255,0)); // 创建阴影画刷 CBrush brush3(&bmp); // 创建图案画刷

39 如下代码创建了一个填充色为红色、图案为垂直相交阴影线的画刷:
创建画刷也可先构造一个没有初始化的CBrush类画刷对象,然后调用CBrush类的初始化成员函数创建定制的画刷工具。CBrush类提供的常用创建函数有:CreateSolidBrush()用指定的颜色创建一个纯色画刷;CreateHatchBrush()用指定的阴影样式和颜色创建一个阴影画刷;CreatePatternBrush()用位图创建一个图案画刷;CreateSysColorBrush()用系统默认颜色创建一个指定阴影样式的画刷。 如下代码创建了一个填充色为红色、图案为垂直相交阴影线的画刷: CBrush BrushNew; BrushNew.CreateHatchBrush(HS_CROSS, RGB(255, 0, 0));

40 例 编写一个对话框应用程序,并重新设置对话框的背景色。
例 编写一个对话框应用程序,并重新设置对话框的背景色。 1.建立一个基于对话框的应用程序UseBrush,为对话框类CUseBrushDlg添加一个CBrush类型的成员变量m_BrushBkClr。在对话框初始化成员函数OnInitDialog()中创建一个自定义颜色的画刷。 BOOL CUseBrushDlg::OnInitDialog() { // TODO: Add extra initialization here m_BrushBkClr.CreateSolidBrush(RGB(0,0,255)); // 创建一个蓝色画刷 return TRUE; // return TRUE unless you set the focus . . . }

41 2.利用ClassWzard为对话框类CUseBrushDlg添加WM_CTLCOLOR的消息处理函数,返回用户自己创建的画刷m_BrushBkClr。
HBRUSH CUseBrushDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { //HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // 不使用缺省的画刷 // TODO: Return a different brush if the default return m_BrushBkClr; }

42

43 使用GDI堆对象 Windows预定义了一些简单风格的GDI对象,用户使用这些GDI对象时,无需自己创建它们,可以直接将它们选入当前的设备环境,这些GDI对象称作为堆(Stock)对象。堆对象包括堆画笔、堆画刷和堆字体等。 通过调用成员函数CDC::SelectStockObject()可以选择一个堆对象绘图工具,以下代码将堆画笔和堆画刷作为当前的绘图工具: pPenOld=(CPen*) pDC->SelectStockObject(NULL_PEN); // 使用堆画笔对象 pBrhOld=(CBrush*) pDC->SelectStockObject(LTGRAY _BRUSH); // 使用堆画刷对象

44 堆画笔、画刷的样式及说明 样 式 说 明 BLACK_PEN 黑色画笔 WHITE_PEN 白色画笔 NULL_PEN 空画笔
样 式 说 明 BLACK_PEN 黑色画笔 WHITE_PEN 白色画笔 NULL_PEN 空画笔 BLACK_BRUSH 黑色画刷 WHITE_BRUSH 白色画刷 NULL_BRUSH 空画刷 GRAY_BRUSH 灰色画刷 DKGRAY_BRUSH 深灰色画刷 LTGRAY_BRUSH 浅灰色画刷 HOLLOW_BRUSH 虚画刷

45 CBrush *pBrhOld, BrhNew;
也可以利用CGdiObject::CreateStockObject()将GDI对象设置成指定的堆对象,这时需要首先声明一个GDI对象,最后还需要调用函数SelectObject()将与堆对象关联的GDI对象选入当前的设备环境,如下代码所示: CBrush *pBrhOld, BrhNew; BrhNew.CreateStockObject(LTGRAY_BRUSH); // 设置堆画刷对象 pBrhOld= pDC->SelectObject(&BrhNew);

46 例 编写一个SDI应用程序,使用堆画笔和堆画刷绘制图形。
利用MFC AppWizard向导创建一个SDI应用程序UseStock,利用ClassWizard为类CUseStockView添加WM_PAINT消息处理函数OnPaint(): void CUseStockView::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here CPen *pPenOld, PenNew; CBrush *pBrhOld, BrhNew; pPenOld=(CPen*)dc.SelectStockObject(BLACK_PEN); // 使用堆画笔对象

47 pBrhOld=(CBrush. )dc. SelectStockObject(GRAY_BRUSH); // 使用堆画刷对象 dc
pBrhOld=(CBrush*)dc.SelectStockObject(GRAY_BRUSH); // 使用堆画刷对象 dc.Rectangle(100,100,300,300); PenNew.CreateStockObject(NULL_PEN); // NULL_PEN用于绘制无边界的填充图形 dc.SelectObject(&PenNew); BrhNew.CreateStockObject(LTGRAY_BRUSH); dc.SelectObject(&BrhNew); dc.Ellipse(400,100,600,200); dc.SelectObject(pPenOld); // 恢复系统默认的GDI对象 dc.SelectObject(pBrhOld); dc.Ellipse(400,210,600,310); // Do not call CView::OnPaint() for painting messages }

48

49 绘制基本图形 生成设备环境、设置绘图属性和选择绘图工具后,就可以开始绘制不同形状的几何图形,Windows中可以绘制的基本几何图形包括点、直线、曲线、矩形、椭圆、弧、扇形、弦形和多边形等。 GDI为提供了绘制基本图形的成员函数,这些函数封装在MFC的CDC类中。 绘图函数使用的坐标都是逻辑坐标。

50 函 数 功 能 SetPixel 用指定的颜色在指定的坐标画一个点 MoveTo 移动当前位置到指定的坐标,函数返回以前位置的坐标。 LineTo 从当前位置到指定位置画一条直线 Polyline 从当前位置开始,根据函数参数绘制多条折线。 PolyBezier 根据两个端点和两个控制点绘制贝济埃(Bezier)曲线。 Rectangle 根据指定的左上角和右下角坐标绘制一个矩形 RoundRect 绘制一个圆角矩形。 Ellipse 根据指定的矩形绘制一个内切椭圆 Arc 根据指定的矩形绘制内切椭圆上的一段弧边 ArcTo 该函数功能与Arc函数相同,不同之处在于画弧成功后 Pie 绘制扇形 Chord 绘制弦形,弦形是一条椭圆弧和其对应的弦所组成的封闭图形。 Polygon 根据两个或两个以上顶点绘制一个多边形 DrawIcon 在指定位置画一个图标,如果成功函数返回非0,否则返回0。

51 例 编写一个绘图程序,利用表8-6中的成员函数绘制几种常见的几何图形。
例 编写一个绘图程序,利用表8-6中的成员函数绘制几种常见的几何图形。 利用MFC AppWizard建立一个SDI应用程序,在OnDraw()函数中添加如下程序代码: void CMyGraphView::OnDraw(CDC* pDC) { CMyGraphDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here for(int xPos=20;xPos<100;xPos+=10) pDC->SetPixel(xPos,30,RGB(0,0,0)); // 绘制像素点 POINT polylpt[5]={{10,100},{50,60},{120,80}, {80,150},{30,130}}; pDC->Polyline(polylpt,5); // 绘制五条折线

52 POINT polybpt[4]={{150,160},{220,60},{300,180}, {330,20}}; pDC->PolyBezier(polybpt,4); // 绘制贝济埃曲线 CBrush *pBrhOld; pBrhOld=(CBrush*)pDC->SelectStockObject( LTGRAY_BRUSH); // 使用浅灰色堆画刷 pDC->RoundRect(400,30,550,100,20,20); // 绘制圆角矩形 pDC->Arc(20,200,200,300,200,250,20,200); // 绘制椭圆弧 pDC->Pie(220,200,400,380,380,270,240,220); // 绘制扇形 pDC->Chord(420,120,540,240,520,160,420,180); // 绘制弦形 POINT polygpt[5]={{450,200},{530,220},{560,300}, {480,320},{430,280}}; pDC->Polygon(polygpt,5); // 绘制五边形 pDC->SelectObject(pBrhOld); // 恢复系统默认的画刷 }

53

54 8.3 文本与字体 很多Windows应用程序都需要显示文本,并且,文本还是一些应用程序的主要处理对象,如MS Word字处理软件。
8.3 文本与字体 很多Windows应用程序都需要显示文本,并且,文本还是一些应用程序的主要处理对象,如MS Word字处理软件。 文本与字体密切相关,输出文本时选择不同类型的字体在很大程度上影响程序的界面风格,合适的字体可以增强程序的感染力。因此,对软件用户来说,文本输出也是很重要的。 Windows为文本的显示提供了多种物理字体支持,而在程序中我们可以创建不同风格的逻辑字体来输出文本。

55 绘制文本 以图形方式进行文本的输出是Windows操作系统一个特性,文本输出实际上是按照指定的字体样式将文本中的每个字符绘制出来。 Windows图形设备接口GDI提供了很多有关文本输出的函数,MFC的CDC类对这些GDI文本输出函数进行了封装。 编程时最常用的文本输出函数是TextOut()函数,该函数只能输出单行文本。要绘制多行文本可以调用DrawText()函数,另一个函数ExtTextOut()可以用一个矩形框对输出文本串进行裁剪。

56 在缺省情况下绘制文本时,字体颜色是黑色,背景颜色是白色,背景模式为不透明模式。可以通过调用CDC类成员函数重新设置字体颜色、背景颜色和文本对齐方式等文本属性。
SetTextColor() 设置显示文本的颜色 GetTextColor 获得当前文本的颜色 SetBkColor() 设置显示文本的背景颜色 GetBkColor() 获得当前文本的背景颜色 SetBkMode() 设置文本的背景模式 GetBkMode() 获得当前文本的背景模式 SetTextAlign() 设置显示文本的对齐方式 GetTextAlign() 获得当前文本的对齐方式

57 8.3.2 字体概述 文本与字体密切相关,输出文本的大小和外观是由字体描述的。字体是指采用某种字样的一套字符和符号,每一种字体都有字符集。
字体概述 文本与字体密切相关,输出文本的大小和外观是由字体描述的。字体是指采用某种字样的一套字符和符号,每一种字体都有字符集。 决定字体的三个要素是字样、风格和大小。字样是字母的样式和文本的视觉外观,字体的风格是字体的粗细和倾斜度。 Windows支持光栅字体、矢量字体和TrueType三种字体。光栅字体即点阵字体,这种字体需要为每一种大小的字体创建独立的字体文件。矢量字体以一系列线段存储字符。TrueType字体是与设备无关的字体,字符以轮廓的形式存储,包括线段和曲线。

58 TrueType字体正成为真正的主流,这种字体能够以一种非常出色的字体技术绘制文本。TrueType字体能够缩放为任何大小的字体,而不会降低图形的质量。Windows中提供的TrueType字体主要有Arial、Courier、Symbol、Time New Roman等,可以通过Windows“控制面板|字体”浏览系统已安装的字体。 输出文本时,默认情况下使用系统提供的缺省字体,如果需要可以改变显示文本的字体。与画笔和画刷一样,字体也是一种GDI对象,MFC类CFont对GDI字体对象进行了封装,我们一般利用CFont类创建自己的字体(GDI对象),然后把创建的字体选入设备环境,以用于在设备环境中绘制文本。

59 选择堆字体: 除了选择任意尺寸TrueType字体,也可以选择固定尺寸的系统字体(堆字体)。当选择堆字体作为文本输出的字体时,无需创建字体对象,只需简单地调用成员函数CDC::SelectStockObject()将堆字体对象选入设备环境。 Windows提供了以下六种堆字体对象:ANSI_ FIXED_FONT、ANSI_VAR_FONT、SYSTEM_ FONT、SYSTEM_FIXED_FONT、DEVICE _DEFAULT_ FONT、OEM_FIXED_FONT。 例如: pDC->SelectStockObject( ANSI_FIXED_FONT);

60 TEXTMETRIC结构: 输出文本时,Windows使用一个矩形框以位图的方式绘制出每一个字符的形状。文本的显示是以像素为单位,有时需要精确地知道文本的详细属性,如高度、宽度等。 编程时我们可以通过访问TEXTMETRIC结构来获取显示器关于文本字符的属性信息,因为每一种物理字体的信息由数据结构TEXTMETRIC描述。调用函数CDC::GetTextMetrics()可得到当前字体的TEXTMETRIC结构。

61 创建字体 Windows本身提供了丰富的字体,直接选用其中的字体就能满足一般需要。也可以根据Windows提供的字体创建自己的字体,但利用CFont类创建自定义字体并不是创建一种新的物理字体,而是创建一种逻辑字体。 逻辑字体是一种抽象的字体描述,是用与设备无关的方式来描述一个字体。逻辑字体只定义了字体的一般特征,如高度、宽度、旋转角度、黑体、斜体及下划线等宏观特性,它并没有描述字体详细的微观特性,也没有生成对应的字库文件。

62 值得注意的是,有时不知道机器上是否安装了需要的字体,因此,程序运行时显示文本的字体可能并不是你想要的字体。实际上,在程序中创建一种字体并不是真正创建一种完全满足程序要求的字体,而是仅寻找匹配的Windows字体并与之相关联。 当利用CFont类创建逻辑字体并利用成员函数CDC::SelectObject()将它选入设备环境时,GDI字体映射器根据逻辑字体给出的特性,从现有的物理字体中选择与之最匹配的物理字体,这就是所谓的字体实现(Font realization)。

63 创建字体的方法: 1. 使用成员函数CFont::CreatPointFont() 2. 使用成员函数CFont::CreateFontIndirect() 3. 使用成员函数CFont::CreateFont() 例: CClientDC dc(this); CFont fntZdy, *pfntOld; VERIFY(fntZdy.CreatePointFont(200, "Arial", &dc)); pfntOld=dc.SelectObject(&fntZdy); // 选入设备环境 dc.TextOut(100, 100, "Hello! This is 20 Pt Arial Font."); dc.SelectObject(pfntOld); // 恢复原来字体 fntZdy.DeleteObject(); // 删除自定义字体

64 例:使用CreateFontIndirect()函数和LOGFONT结构。
CFont font; LOGFONT LogFnt; memset(&LogFnt, 0, sizeof(LOGFONT)); // 清零结构LogFont LogFnt.lfHeight = 22; // 字体高度为22像素 strcpy(LogFnt.lfFaceName, "Courier"); // 匹配字体为Courier VERIFY(font.CreateFontIndirect(&LogFnt)); // 创建字体 CClientDC dc(this); CFont* def_font = dc.SelectObject(&font); // 选入设备环境 dc.TextOut(100, 130, "Hello! This is 22-pixel-height Courier Font."); dc.SelectObject(def_font); font.DeleteObject();

65 例 编写一个文本输出程序UseFont,采用不同方法创建字体,并根据创建的字体输出不同的文本串。

66 公用字体对话框 Windows还提供了一个公用字体对话框,很多程序都利用它来选择不同的字体,并可以设置字体的大小和颜色。公用字体对话框对应的MFC类是类CFontDialog,编程时可以通过访问CFontDialog类的有关成员变量或调用成员函数获得用户所选择的字体及其属性,程序员无须具体定义这种字体就可以通过调用函数CreateFontIndirect()创建字体。

67 例 编写一个单文档应用程序FontDlg,当执行菜单命令“字体”时,使用公用字体对话框动态设置字体。

68 8.4 位图、图标和光标 使用图象形式的标志可以使用户很快地找到某个程序或了解一个程序的大致功能,因此在Windows环境中大量使用各种图形图像标志。Windows应用程序中主要使用位图、图标和光标等几种图形资源。 利用Visual C++集成开发环境中的资源编辑器可以创建或编辑这几种图形资源,在程序中需要时可以通过编写源代码使用创建的图形资源。

69 8.4.1 位图 位图是一个由位构成的图象,它是由一系列数据排列而成的点阵结构,这些数据分别表示各点的颜色信息。Windows支持两种不同形式的位图:设备相关位图DDB(Device Dependent Bitmap)和设备无关位图DIB(Device Independent Bitmap)。 DDB又称GDI位图,它是某种显示设备的内部表示。DDB是针对某个设备创建的位图,显示它依赖具体硬件的调色板。 DIB是不依赖硬件的位图,它包含了创建DIB位图时所在设备的颜色格式、分辨率和调色板等信息。DIB位图通常以BMP文件形式保存在磁盘中,或者以资源形式存在于EXE或DLL执行文件中。

70 BOOL LoadBitmap( LPCTSTR lpszResourceName );
MFC处理位图的方法: MFC只提供了处理DDB位图的类CBitmap,要显示DIB位图,可以先将一个DIB位图转换为DDB位图。类CBitmap提供了一个成员函数,用于从程序的资源中装载位图,并可以将基于资源的DIB位图转换成GDI位图,该函数声明如下: BOOL LoadBitmap( LPCTSTR lpszResourceName ); BOOL LoadBitmap( UINT nIDResource ); 其中参数lpszResourceName或nIDResource分别为资源名称或资源标识,载入成功返回值为真,否则返回值为假。

71 显示位图的编程方法: 位图在显示之前必须先装入内存,当驻留在内存的位图数据送到视频内存时,位图就在显示器上显示。显示一个DDB位图步骤: (1) 调用CDC::CreateCompatibleDC()创建一个兼容的内存设备环境; (2) 调用CBitmap::LoadBitmap()装入位图资源或调用CBitmap::CreateCompatibleBitmap()创建一个与内存设备环境兼容的位图; (3) 调用CDC::SelectObject()将位图选入设备环境; (4) 调用CDC::BitBlt()或CDC::StretchBlt()将位图从内存设备环境中复制到指定设备如显示器。

72 例 编写一个应用程序MyBMP,程序运行后在客户区显示一幅位图。
利用向导创建一个SDI应用程序MyBMP。执行菜单命令Insert|Resource插入一个Bitmap资源。利用资源编辑器对位图进行编辑,并将其ID改为IDB_MYBITMAP。在函数OnDraw()中添加代码: void CMyBMPView::OnDraw(CDC* pDC) { CMyBMPDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here CDC MemDC; MemDC.CreateCompatibleDC(pDC); // 创建内存设备环境

73 CBitmap Bitmap; Bitmap. LoadBitmap(IDB_MYBITMAP); // 装入位图资源 CBitmap
CBitmap Bitmap; Bitmap.LoadBitmap(IDB_MYBITMAP); // 装入位图资源 CBitmap *pOldBitmap=MemDC.SelectObject(&Bitmap); // 将位图对象选入设备环境 BITMAP bm; Bitmap.GetObject(sizeof(BITMAP), &bm); // 读取位图信息 // 将内存中的位图复制到屏幕上 pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &MemDC, 0, 0, SRCCOPY); MemDC.SelectObject(pOldBitmap); // 恢复原来位图对象 }

74

75 图标 在Windows中,每个文件都有一个图标(Icon)。应用程序图标通常会出现在程序标题栏的左上角、Windows底部的任务栏、资源管理器窗口和Windows桌面上。 图标实质上是特殊形式的位图,但图标与位图有两个不同之处。首先,图标大小尺寸只能有三种,一种是用于标题栏和最小化时的16×16图标,另外两种是用于桌面、资源管理器的32×32和48×48图标。其次,设计图标时可以指定像素的颜色为屏幕颜色或屏幕反转色,如图所示。这样,Windows在显示图标时,采用屏幕颜色的像素位置颜色不变,该位置图标部分看起来是透明的,而屏幕反转色部分在任何彩色背景下都能显示。

76

77 对于图标,MFC没有提供对应的类,编程时只有采用句柄的方式使用一个图标。
添加自己的图标: 一般应用程序使用MFC提供的缺省图标,也可以添加自己的图标。通过Insert|Resource菜单命令插入Icon图标资源,利用图形资源编辑器编辑图标。图标创建后通过调用函数CWinApp::LoadIcon()加载图标并获得其句柄,该函数原型为: HICON LoadIcon(LPCTSTR lpszResourceName) const; HICON LoadIcon(UINT nIDResource) const; 对于图标,MFC没有提供对应的类,编程时只有采用句柄的方式使用一个图标。 用户也可以通过调用CWinApp::LoadStandardIcon()加载Windows系统提供的预定义图标。

78 图标被加载后,为了在窗口显示图标,可以调用成员函数CDC::DrawIcon(),该函数原型为:
显示图标: 图标被加载后,为了在窗口显示图标,可以调用成员函数CDC::DrawIcon(),该函数原型为: BOOL DrawIcon(int x, int y, HICON hIcon); BOOL DrawIcon(POINT point, HICON hIcon); 其中,参数x、y或point指定图标显示的左上角坐标,hIcon为图标句柄。 在初始化函数InitInstance()中可以通过调用成员函数CWnd::SetIcon()安装图标,此时应该同时安装16×16和32×32大小标准的图标,该函数原型为: HICON SetIcon( HICON hIcon, BOOL bBigIcon ); 其中,参数hIcon为要安装的图标句柄,bBigIcon确定安装何种大小的图标。

79 2.为了在标题栏显示创建的图标IDI_ICON1,在初始化函数InitInstance()中添加如下代码:
例 编写一个应用程序MyIcon,程序运行后在标题栏删除程序原来默认的图标,显示自己创建的图标,并在客户区显示该图标和Windows预定义图标。 1.利用MFC AppWizard[exe]创建一个SDI应用程序MyIcon,然后执行Insert|Resource菜单命令,要创建的资源选为Icon,单击New按钮。也可通过Import命令导入一个图标文件。打开图形编辑器后就可以开始创建图标,新创建图标的ID设为IDI_ICON1。 2.为了在标题栏显示创建的图标IDI_ICON1,在初始化函数InitInstance()中添加如下代码: HICON hIcon=AfxGetApp()->LoadIcon(IDI_ICON1); m_pMainWnd->SetIcon(hIcon,TRUE); // 设置32×32图标 m_pMainWnd->SetIcon(hIcon,FALSE); // 设置16×16图标

80 3.在函数OnDraw()中使用图标,需要编写代码加载和显示图标。在OnDraw()函数中添加如下代码:
HICON hIcon=AfxGetApp()->LoadIcon(IDI_ICON1); // 加载自定义图标 pDC->DrawIcon(0, 0, hIcon); // 显示图标 DestroyIcon(hIcon); // 释放图标资源 hIcon=AfxGetApp()->LoadStandardIcon( IDI_EXCLAMATION); // 加载系统预定义图标 pDC->DrawIcon(50, 0, hIcon); DestroyIcon(hIcon); 也可以通过发送消息WM_SETICON设置应用程序的图标,这时需要调用消息发送函数SendMessage()。

81 MyIcon 运行结果

82 8.4.3 光标 光标(Cursor)是一种特殊的、可移动的32×32点阵图形,它是用来作为鼠标指针的图形标志。
光标 光标(Cursor)是一种特殊的、可移动的32×32点阵图形,它是用来作为鼠标指针的图形标志。 光标与图标最大的区别是光标有一个热点,用于确定光标当前的像素位置。利用光标资源编辑器可以重新设置光标的热点,如图所示,光标资源编辑器的右上部有一个热点设置按钮“*”,单击这个按钮,在光标编辑区会出现一个十字光标,将十字光标的中心放在要设定热点的位置,单击鼠标就可以设置光标的热点。

83 设置光标的热点

84 改变光标的形状: 在Windows环境下,光标形状的变化代表了程序当前状态的改变。不需要用户专门处理,Windows就支持三种最普通的光标:箭头、沙漏和I形光标。一般情况下,光标为箭头形状;如果应用程序忙,光标呈沙漏状;如果光标超出程序窗口或进行文本输入,光标为I形状。 MFC类CCmdTarget提供成员函数BeginWait- Cursor()、EndWaitCursor()和RestoreWaitCursor()用于改变光标的状态。 光标与图标一样,MFC也没有提供对应的类,一般采用句柄的方式使用光标。

85 例 编写一个应用程序WaitCur,程序运行后单击鼠标光标处于沙漏状,2秒钟光标恢复正常状态。
1.利用MFC AppWizard创建SDI应用程序WaitCur,利用ClassWizard添加消息WM_LBUTTONDOWN的处理函数,并添加如下代码: void CWaitCurView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here SetCapture(); BeginWaitCursor(); // 显示沙漏状光标 SetTimer(1,2000,NULL);// 启动一个2秒的计时器 CView::OnLButtonDown(nFlags, point); }

86 2.利用ClassWizard添加消息WM_TIMER的处理函数,在函数中添加如下代码:
void CWaitCurView::OnTimer(UINT nIDEvent) // 2秒后调用该消息处理函数 { // TODO: Add your message handler code here ReleaseCapture(); EndWaitCursor(); // 2秒后恢复光标原来的形状 KillTimer(1); // 删除计时器 CView::OnTimer(nIDEvent); }

87 WaitCur运行结果

88 添加光标: 首先利用Insert|Resource菜单命令添加和编辑Cursor光标资源,然后通过调用CWinApp:: LoadCursor()加载光标。加载成功后,调用函数SetCursor()设置光标。 一般在WM_SETCURSOR的消息处理函数OnSet- Cursor()中重新设置光标。 用户也可以直接使用Windows系统提供的光标,首先需要调用CWinApp::LoadStandardCursor()来加载,通过不同的参数加载不同的系统光标。 如:IDC_ARROW、IDC_IBEAM、IDC_WAIT、IDC_CROSS、IDC_UPARROW。

89 例 编写应用程序MyCursor,程序运行后,当光标移到客户区时变为自定义的形状。执行菜单命令“查看|系统光标”打开一个对话框,当光标移到对话框时,光标变为Windows预定义的四个箭头光标。
1.利用MFC AppWizard创建应用程序MyCursor,执行菜单命令Insert|Resource插入一个Cursor资源。利用光标资源编辑器绘制一个手形光标,设置光标热点,并将ID改为IDC_HAND。利用ClassWizard添加消息WM_SETCURSOR的处理函数: HCURSOR hcursor; hcursor=AfxGetApp()->LoadCursor(IDC_HAND); SetCursor(hcursor); // 设置光标 return TRUE;

90 2.添加一个ID为IDD_MYDLG、标题为“使用系统光标”的对话框资源和对话框类CMyDlg。利用ClassWzard添加消息WM_SETCURSOR的处理函数:
BOOL CMyDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { // TODO: Add your message handler code here HCURSOR hcursor; hcursor=AfxGetApp()->LoadStandardCursor(IDC_SIZEALL); // 加载系统光标 SetCursor(hcursor); return TRUE; // return CDialog::OnSetCursor . . . }

91 3.在菜单资源中增加菜单项“查看|系统光标”,其ID为ID_VIEW_SYSCUR。利用ClassWizard类向导在CMyCursorView类中添加该菜单项的命令处理函数:
void CMyCursorView::OnViewSyscur() { // TODO: Add your command handler code here CMyDlg dlg; dlg.DoModal(); }

92 MyCursor运行结果

93 本章重点作业题 8-31,8-32,8-34,8-35,8-36,8-38,8-40,8-42,8-43,8-46,8-49,8-53,8-54


Download ppt "版权所有 复制必究 第 8 章 图形设备接口."

Similar presentations


Ads by Google