第10单元图形设备接口 第10单元图形设备接口 本单元教学目标 介绍 Windows的图形设备接口(GD1)和MFC的图形对象 学习要求 了解图形设备接口的概念,掌握画笔、画刷和字体的用法,以及逻辑坐标和设备坐标的 概念 授课内容 在 Windows程序中,应用程序的输出并不直接面向物理的设备,如显示屏或打印机, 而是针对一个称之为设备环境( Device Context)的逻辑设备进行操作,设备环境与实际设 备之间的信息传送由 Windows直接管理 在MFC应用程序中,所有的绘制调用均通过相应的设备环境对象实现,设备环境对象 封装了相应的 Windows apl功能,由一个32位的HDC类型句柄标识。在MFC类库中,用 CDC类封装设备文本对象。 CPaintDc类是从CDC类派生的设备环境类。 CPaintDC类的对象在 OnPaint()函数中 使用。另外, CClientDo类也是常用的CDC派生类,用于在 OnPaint()函数外访问设备环 境 在第9单元的程序举例中,我们已经看到了 CPaintDC类的应用。但是,那些举例都很 单调,没有颜色、线型和字体的变化。本单元首先介绍一些在设备环境下使用的绘图工具(图 形设备接口对象),使用它们可改善应用程序的外观,然后介绍图形设备接口(GD1)的坐 标系统 10.1画笔与画刷 画笔是用来画线的工具,是CPen类的对象。其使用方法为 /声明画笔对象,并创建宽度为3的红色实线画笔 CPen penNed nRed. CreatePen (PS solid, 3, RGB (255, 0, 0)) /使用新的画笔,保存原来的画笔以便恢复 CPen *pOldPen
第 10 单元 图形设备接口 - 195 - 第 10 单元 图形设备接口 本单元教学目标 介绍 Windows 的图形设备接口(GDI)和 MFC 的图形对象。 学习要求 了解图形设备接口的概念,掌握画笔、画刷和字体的用法,以及逻辑坐标和设备坐标的 概念。 授课内容 在 Windows 程序中,应用程序的输出并不直接面向物理的设备,如显示屏或打印机, 而是针对一个称之为设备环境(Device Context)的逻辑设备进行操作,设备环境与实际设 备之间的信息传送由 Windows 直接管理。 在 MFC 应用程序中,所有的绘制调用均通过相应的设备环境对象实现,设备环境对象 封装了相应的 Windows API 功能,由一个 32 位的 HDC 类型句柄标识。在 MFC 类库中,用 CDC 类封装设备文本对象。 CPaintDC 类是从 CDC 类派生的设备环境类。CPaintDC 类的对象在 OnPaint()函数中 使用。另外,CClientDC 类也是常用的 CDC 派生类,用于在 OnPaint()函数外访问设备环 境。 在第 9 单元的程序举例中,我们已经看到了 CPaintDC 类的应用。但是,那些举例都很 单调,没有颜色、线型和字体的变化。本单元首先介绍一些在设备环境下使用的绘图工具(图 形设备接口对象),使用它们可改善应用程序的外观,然后介绍图形设备接口(GDI)的坐 标系统。 10.1 画笔与画刷 画笔是用来画线的工具,是 CPen 类的对象。其使用方法为: // 声明画笔对象,并创建宽度为 3 的红色实线画笔 CPen penRed; penRed.CreatePen(PS_SOLID, 3, RGB(255, 0, 0)); // 使用新的画笔,保存原来的画笔以便恢复 CPen *pOldPen;
第10单元图形设备接口 196 pOldPen dc SelectObject(&pOldPen) //以下为作图代码,所画的线均使用新画笔 /恢复原来的画笔 dc. Select Object(pOldPen) 保存并恢复原来画笔的原因是,每个图形设备接口对象要占用一个HDC句柄,而可用的句 柄数量是有限的,在使用完后要及时释放。否则,每次执行 On Paint()函数时均要重新创 建图形接口对象,未被释放的非法句柄会留在设备上下文对象中,积累下去将导致严重的运 行错误 CPen类的成员函数 CreatePen()用于创建画笔,其原型为: BOOL CreatePen(int nPen Style, int n Width, COLORREF crColor); 第1个参数是画笔样式,可取 画笔样式 说明 IS SOLID 创建实线笔 PS DASH 创建由短线构成的虚线 PS DOT 创建由点构成的虚线 PS DASHDOT 创建由短线和点构成的虚线 PS DASH DOTDOT创建由短线、点、点构成的虚线 PS NULL 创建空(空白)画笔 各种虚线只有当线宽为1时有效。第2个参数为线宽,第3个参数为线的颜色,可使用RGB ()函数指定。RGB()函数有3个参数,分别代表选取颜色的红、绿、蓝分量,可取0 255之间的整数值。例如RGB(255,255,255)为白色,RGB(0,0,0)为黑色。 画刷是用来填充图形的工具,是 CBrush类的对象,使用方法与画笔类似,也要定义画 刷对象,创建画刷并保存原来的画刷,在绘图工作结束后恢复原来的画刷。创建画刷的成员 函数的原型为 BOOL Create Solid Brush( COLORREF crColor 参数 crColor指定了画刷的颜色。除此而外,还可以创建一个阴影风格的画刷: BOOL CreateHatch Brush( int nIndex, COLORREF crColor 其中参数 nIndex指定了阴影风格,可取值为: 阴影风格 说明 HS BDIAGONAL从左下角到右上角的45度斜线 HS CROSS 水平线与垂直线 HS DIAGCROSS 相互垂直的45度线 HS FDIAGONAL从左上角到右下角的45度斜线 HS HORIZONTAL水平线 HS VERTICAL 垂直线
第 10 单元 图形设备接口 - 196 - pOldPen = dc.SelectObject(&pOldPen); // 以下为作图代码,所画的线均使用新画笔 … … // 恢复原来的画笔 dc.SelectObject(pOldPen); 保存并恢复原来画笔的原因是,每个图形设备接口对象要占用一个 HDC 句柄,而可用的句 柄数量是有限的,在使用完后要及时释放。否则,每次执行 OnPaint()函数时均要重新创 建图形接口对象,未被释放的非法句柄会留在设备上下文对象中,积累下去将导致严重的运 行错误。 CPen 类的成员函数 CreatePen()用于创建画笔,其原型为: BOOL CreatePen (int nPenStyle, int nWidth, COLORREF crColor); 第 1 个参数是画笔样式,可取 画笔样式 说明 PS_SOLID 创建实线笔 PS_DASH 创建由短线构成的虚线 PS_DOT 创建由点构成的虚线 PS_DASHDOT 创建由短线和点构成的虚线 PS_DASH_DOTDOT 创建由短线、点、点构成的虚线 PS_NULL 创建空(空白)画笔 各种虚线只有当线宽为 1 时有效。第 2 个参数为线宽,第 3 个参数为线的颜色,可使用 RGB ()函数指定。RGB()函数有 3 个参数,分别代表选取颜色的红、绿、蓝分量,可取 0~ 255 之间的整数值。例如 RGB(255,255,255)为白色,RGB(0,0,0)为黑色。 画刷是用来填充图形的工具,是 CBrush 类的对象,使用方法与画笔类似,也要定义画 刷对象,创建画刷并保存原来的画刷,在绘图工作结束后恢复原来的画刷。创建画刷的成员 函数的原型为: BOOL CreateSolidBrush ( COLORREF crColor ); 参数 crColor 指定了画刷的颜色。除此而外,还可以创建一个阴影风格的画刷: BOOL CreateHatchBrush ( int nIndex, COLORREF crColor ); 其中参数 nIndex 指定了阴影风格,可取值为: 阴影风格 说明 HS_BDIAGONAL 从左下角到右上角的 45 度斜线 HS_CROSS 水平线与垂直线 HS_DIAGCROSS 相互垂直的 45 度线 HS_FDIAGONAL 从左上角到右下角的 45 度斜线 HS_HORIZONTAL 水平线 HS_VERTICAL 垂直线
第10单元图形设备接口 CDC类的 Selectobject()函数原型如下 CPen* SelectObject( CPen" pPen CBrush* SelectObject( CBrush* pBrush virtual CFont*SelectObject( CFont* pFont ) Selectobject()是重载的CDC类成员函数。 SelectObject()将一个GDl对象选入到设 备环境中,新选中的对象将替换原有的同类型对象,然后返回指向被替换的对象的指针 102绘画模式 在 Windows中,绘图的最终效果不但取决于画笔和画刷的设置,还可以通过设定绘图 模式来修正。屏幕绘图模式可通过CDC的成员函数 SetRoP2()设定,其原型为: int SetROP2( int nDrawMode 其中参数 n DrawMode为选定的绘图模式,常用模式有: 绘图模式 说明 R2 BLAC 无论画笔色如何,只用黑色绘图 WHITE 无论画笔色如何,只用白色绘图 R2 NOP 无论画笔色如何,用无色笔绘图 R2 NOT 用与背景色相反的颜色绘图 R2 NOTCOPYPEN用与画笔色相反的颜色绘图 R2 COPYPEN 用画笔色绘图 R2 XORPEN 对画笔色和背景色作异或(XOR)运算 其中R2NOT模式可保证所绘图形是可见的,即如果画笔色与背景色相同,则以与背景色 相反的颜色作图,避免了所画图形“淹没”在背景中;R2ⅹ ORPEN模式有一种特殊效果, 即对同一条线画两次会起到擦除作用 函数的返回值为原来的绘图模式 10.3GDI坐标系 GD支持两种类型的坐标系,即逻辑坐标系和设备坐标系。逻辑坐标系按坐标设置方式 (又称为映射模式)可分为8种,它们的坐标特性如下: 映射模式 逻辑单位 x递增方向 递增方向 MM TEXT 像素 向右 向下 MM LOMETRIC 0.Imm 向右 向上 MM HIMETRIC 0.01mm 向右 向上 MM LOENGLISH 0. linch 向右 向上 MM HIENGLISH 向右 向上 MM TWIPS 1/1440inch
第 10 单元 图形设备接口 - 197 - CDC 类的 SelectObject()函数原型如下: CPen* SelectObject( CPen* pPen ); CBrush* SelectObject( CBrush* pBrush ); virtual CFont* SelectObject( CFont* pFont ); 即 SelectObject()是重载的 CDC 类成员函数。SelectObject()将一个 GDI 对象选入到设 备环境中,新选中的对象将替换原有的同类型对象,然后返回指向被替换的对象的指针。 10.2 绘画模式 在 Windows 中,绘图的最终效果不但取决于画笔和画刷的设置,还可以通过设定绘图 模式来修正。屏幕绘图模式可通过 CDC 的成员函数 SetROP2()设定,其原型为: int SetROP2 ( int nDrawMode ); 其中参数 nDrawMode 为选定的绘图模式,常用模式有: 绘图模式 说明 R2_BLACK 无论画笔色如何,只用黑色绘图; R2_WHITE 无论画笔色如何,只用白色绘图; R2_NOP 无论画笔色如何,用无色笔绘图; R2_NOT 用与背景色相反的颜色绘图; R2_NOTCOPYPEN 用与画笔色相反的颜色绘图; R2_COPYPEN 用画笔色绘图; R2_XORPEN 对画笔色和背景色作异或(XOR)运算。 其中 R2_NOT 模式可保证所绘图形是可见的,即如果画笔色与背景色相同,则以与背景色 相反的颜色作图,避免了所画图形“淹没”在背景中;R2_XORPEN 模式有一种特殊效果, 即对同一条线画两次会起到擦除作用。 该函数的返回值为原来的绘图模式。 10.3 GDI 坐标系 GDI 支持两种类型的坐标系,即逻辑坐标系和设备坐标系。逻辑坐标系按坐标设置方式 (又称为映射模式)可分为 8 种,它们的坐标特性如下: 映射模式 逻辑单位 x 递增方向 y 递增方向 MM_TEXT 像素 向右 向下 MM_LOMETRIC 0.1mm 向右 向上 MM_HIMETRIC 0.01mm 向右 向上 MM_LOENGLISH 0.01inch 向右 向上 MM_HIENGLISH 0.001inch 向右 向上 MM_TWIPS 1/1440inch 向右 向上
第10单元图形设备接口 -198 MM ISOTROPIC 可调整(x=y)可选择 可选择 MM ANISOTROPIC可调整(xl=y)可选择 可选择 注意所有映射模式的坐标原点均在设备输出区域(如窗口客户区或打印纸上的打印 域)的左上角。因此,对于y坐标递增方向向下的映射模式(如 MM TEXT),y坐标值均 为正值,而对于y坐标递增方向向上的映射模式(如 MM LOMETRIC等),所有的y坐标 值均为负值,在编程时要特别注意。 最常用的映射模式是 MM TEXT,这也是缺省设置。在该模式下,坐标原点在客户区 左上角,ⅹ坐标值是向右递增,y坐标值是向下递增,单位值1代表一个像素,与屏幕坐标 系类似。采用除 MM TEXT外的其他映射模式的原因有二:一是欲使程序显示在不同的屏 幕分辨率(如640×480、800×600或1024×768等)下有相近的尺度;二是欲使程序的显 示和打印比例相近(参看13.2:“打印和打印预览”) 设置映射模式,可使用CDC类的 SetMap Mode()成员函数,其原型为 virtual int SetMap Mode( int nMapMode ) 其中参数nMaφ pMode为欲设置的映射模式,返回值为原来的映射模式。 MFC绘图函数均使用逻辑坐标作为位置参数。例如 String str("Hello, MFC!") dc TextOut(10, 10, str, str. GetLengthO) 这里的(10,10)是逻辑坐标而不是像素点数(只是在缺省映射模式 MM TEXT下,正好 与像素点相对应),在输出时GD函数会将逻辑坐标(10,10)依据当前映射模式转化为“设 备坐标”,然后将文字输出在屏幕上 设备坐标以像素点为单位,且ⅹ轴坐标值向右递增,y轴坐标值向下延伸,但原点(0, 0)位置却不限定在工作区左上角。依据设备坐标的原点和用途,可以将 Windows下使用的 设备坐标系统分为三种:客户区坐标系统,窗口坐标系统和屏幕坐标系统 1.客户区坐标系统:客户区坐标系统是最常见的坐标系统,以窗口客户区左上角为原 点(0,0),主要用于窗口客户区绘图输出以及处理窗口的一些消息。鼠标消息 WM LBUTTONDOWN、 WM MOUSEMOVE传给框架的消息参数以及CDC一些用于绘图 的成员都是使用客户区坐标 2.屏幕坐标系统:屏幕坐标系统是另一类常用的坐标系统,以屏幕左上角为原点(0, )。一些与窗口客户区不相关的函数均以屏幕坐标为单位,例如设置和取得光标位置的函数 SetCursor Pos()和 GetCursorPos():由于光标可以在任何一个窗口之间移动,它不属于任 何一个单一的窗口,因此使用屏幕坐标。弹出式菜单使用的也是屏幕坐标。另外, Create window()和 Move window()等函数用于设置窗口相对于屏幕的位置,使用的也是 屏幕坐标系统。 窗口坐标系统:窗口坐标系统以窗口左上角为坐标原点,它包含了窗口控制菜单 标题栏等内容。一般情况下很少在窗口标题栏上绘图,因此这种坐标系统很少使用 MFC提供 ClientToscreen()、 Screen toclient()两个函数用于完成客户区坐标和屏幕 坐标之间的转换工作 void Screen ToClient( LPPoiNT lpPoint )const void Screen To Client( LPRECT lpRect)const Client ToScreen( LPPOINT lpPoint)const; Client ToScreen( LPRECT lpRect )const
第 10 单元 图形设备接口 - 198 - MM_ISOTROPIC 可调整 (x = y) 可选择 可选择 MM_ANISOTROPIC 可调整(x != y) 可选择 可选择 注意所有映射模式的坐标原点均在设备输出区域(如窗口客户区或打印纸上的打印区 域)的左上角。因此,对于 y 坐标递增方向向下的映射模式(如 MM_TEXT),y 坐标值均 为正值,而对于 y 坐标递增方向向上的映射模式(如 MM_LOMETRIC 等),所有的 y 坐标 值均为负值,在编程时要特别注意。 最常用的映射模式是 MM_TEXT,这也是缺省设置。在该模式下,坐标原点在客户区 左上角,x 坐标值是向右递增,y 坐标值是向下递增,单位值 1 代表一个像素,与屏幕坐标 系类似。采用除 MM_TEXT 外的其他映射模式的原因有二:一是欲使程序显示在不同的屏 幕分辨率(如 640×480、800×600 或 1024×768 等)下有相近的尺度;二是欲使程序的显 示和打印比例相近(参看 13.2:“打印和打印预览”)。 设置映射模式,可使用 CDC 类的 SetMapMode()成员函数,其原型为 virtual int SetMapMode ( int nMapMode ); 其中参数 nMapMode 为欲设置的映射模式,返回值为原来的映射模式。 MFC 绘图函数均使用逻辑坐标作为位置参数。例如 CString str(“Hello, MFC!”); dc.TextOut(10, 10, str, str.GetLength()); 这里的(10,10)是逻辑坐标而不是像素点数(只是在缺省映射模式 MM_TEXT 下,正好 与像素点相对应),在输出时 GDI 函数会将逻辑坐标(10,10)依据当前映射模式转化为“设 备坐标”,然后将文字输出在屏幕上。 设备坐标以像素点为单位,且 x 轴坐标值向右递增,y 轴坐标值向下延伸,但原点(0, 0)位置却不限定在工作区左上角。依据设备坐标的原点和用途,可以将 Windows 下使用的 设备坐标系统分为三种:客户区坐标系统,窗口坐标系统和屏幕坐标系统。 1.客户区坐标系统:客户区坐标系统是最常见的坐标系统,以窗口客户区左上角为原 点(0,0),主要用于窗口客户区绘图输出以及处理窗口的一些消息。鼠标消息 WM_LBUTTONDOWN、WM_MOUSEMOVE 传给框架的消息参数以及 CDC 一些用于绘图 的成员都是使用客户区坐标。 2.屏幕坐标系统:屏幕坐标系统是另一类常用的坐标系统,以屏幕左上角为原点(0, 0)。一些与窗口客户区不相关的函数均以屏幕坐标为单位,例如设置和取得光标位置的函数 SetCursorPos()和 GetCursorPos();由于光标可以在任何一个窗口之间移动,它不属于任 何一个单一的窗口,因此使用屏幕坐标。弹出式菜单使用的也是屏幕坐标。另外, CreateWindow()和 MoveWindow()等函数用于设置窗口相对于屏幕的位置,使用的也是 屏幕坐标系统。 3.窗口坐标系统:窗口坐标系统以窗口左上角为坐标原点,它包含了窗口控制菜单、 标题栏等内容。一般情况下很少在窗口标题栏上绘图,因此这种坐标系统很少使用。 MFC 提供 ClientToScreen()、ScreenToClient()两个函数用于完成客户区坐标和屏幕 坐标之间的转换工作。 void ScreenToClient( LPPOINT lpPoint ) const; void ScreenToClient( LPRECT lpRect ) const; void ClientToScreen( LPPOINT lpPoint ) const; void ClientToScreen( LPRECT lpRect ) const;
第10单元图形设备接口 如果用户在窗口客户区移动鼠标或按下鼠标按键,就会得到鼠标位置的设备坐标。在使 用该数据绘图时,需要将其转化为逻辑坐标。CDC类提供了两个成员函数 LPtoDP()和 DPtoLP()完成逻辑坐标和设备坐标之间的转换工作,其中 LPtoDP用于将逻辑坐标转换为 设备坐标,而 DPtoLP()用于将设备坐标转换为逻辑坐标 void LPtoDP( LPPOiNT lpPoints, int n Count =1)const void LPtoDP( LPRECT lpRect )const yoid DPtoLP( LPPOINT lp Points, int n Count=1)const void DPtoLP( LPRECT lpRect )const 但如果采用 MM TEXT的映射模式,设备坐标和逻辑坐标一致,就无需转换了。第9单元 的例题程序均如此 例10-1在窗口中显示一个椭圆,并用鼠标切换该椭圆的图形参数。 说明:建立项目的方法见98:“用 Visual c++集成开发环境开发Wn32应用程序 程序: ∥/ Example10-1:用鼠标切换图形参数 #include <afxwin. h> /框架窗口类 class CMy Wnd: public CFrameWnd int m color CRect m rectEllipse: public CMy Wndo protected fx msg void OnPaint o afx msg void OnLButtonDown (UINT nFlags, CPoint point) DECLARE MESSAGE MAP O //消息映射 BEGIN MESSAGE MAP(CMyWnd, CFrameWnd ON WM PAINTO ON WM LBUTTONDOWN O END MESSAGE MAP O /框架窗口类的成员函数 CMyWnd: CMyWndO: m rectEllipse(100, 100, 300, 250) m cOlor o void CMyWnd: OnPaint o
第 10 单元 图形设备接口 - 199 - 如果用户在窗口客户区移动鼠标或按下鼠标按键,就会得到鼠标位置的设备坐标。在使 用该数据绘图时,需要将其转化为逻辑坐标。CDC 类提供了两个成员函数 LPtoDP()和 DPtoLP()完成逻辑坐标和设备坐标之间的转换工作,其中 LPtoDP 用于将逻辑坐标转换为 设备坐标,而 DPtoLP()用于将设备坐标转换为逻辑坐标: void LPtoDP ( LPPOINT lpPoints, int nCount = 1 ) const; void LPtoDP ( LPRECT lpRect ) const; void DPtoLP ( LPPOINT lpPoints, int nCount = 1 ) const; void DPtoLP ( LPRECT lpRect ) const; 但如果采用 MM_TEXT 的映射模式,设备坐标和逻辑坐标一致,就无需转换了。第 9 单元 的例题程序均如此。 [例 10-1] 在窗口中显示一个椭圆,并用鼠标切换该椭圆的图形参数。 说 明:建立项目的方法见 9.8:“用 Visual C++集成开发环境开发 Win32 应用程序”。 程 序: // Example 10-1: 用鼠标切换图形参数 #include <afxwin.h> // 框架窗口类 class CMyWnd: public CFrameWnd { int m_nColor; CRect m_rectEllipse; public: CMyWnd(); protected: afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP() }; // 消息映射 BEGIN_MESSAGE_MAP(CMyWnd,CFrameWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() // 框架窗口类的成员函数 CMyWnd::CMyWnd():m_rectEllipse(100,100,300,250) { m_nColor = 0; } void CMyWnd::OnPaint() {