为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

屏幕绘图

2011-09-30 50页 doc 811KB 21阅读

用户头像

is_031262

暂无简介

举报
屏幕绘图Visual C++和MFC的历史 第四章 屏幕绘图 4.1、MFC绘图的基本知识 4.1.1、设备描述表(device context)的概念 绝大多数的Windows应用程序都要向不同的输出设备(打印机、屏幕)上绘制文本和图形。由于Windows是一个具有与设备无关性的操作系统,所以任何向屏幕上进行输出的功能都要间接地通过一个叫做设备描述表DC(device context)的对象(简称为DC对象)来完成。用户向设备描述表对象提出输出的要求,然后由Windows自己来调用具体输出设备的驱动程序,及完成实际的输出工作。使用设...
屏幕绘图
Visual C++和MFC的历史 第四章 屏幕绘图 4.1、MFC绘图的基本知识 4.1.1、设备描述(device context)的概念 绝大多数的Windows应用程序都要向不同的输出设备(打印机、屏幕)上绘制文本和图形。由于Windows是一个具有与设备无关性的操作系统,所以任何向屏幕上进行输出的功能都要间接地通过一个叫做设备描述表DC(device context)的对象(简称为DC对象)来完成。用户向设备描述表对象提出输出的要求,然后由Windows自己来调用具体输出设备的驱动程序,及完成实际的输出工作。使用设备描述表带来的最大的好处就是输出格式的一致性,因为输出不再是直接针对具体的设备,而是通过统一格式的设备描述表间接地实现。设备描述表实际上是一种Windows的数据结构,它保存了设备(打印机、屏幕等)的绘图属性和绘图函数。设备描述表对象就是设备描述表数据结构的一个具体的实例,它提供了一个绘图的环境,所有的绘图工作都要通过它来实现。 MFC提供了不同类型的设备描述表类,每一个类都封装了代表Windows设备描述表的句柄(HDC)和函数。因此,使用MFC获取设备描述表的操作比使用Windows API方法容易的多。 围绕设备描述表,MFC还提供了一系列与其配合使用的绘图工具对象,这其中包括:画笔对象、刷子对象以及字体对象等等。它们的工作流程是这样的:首先对设备描述表进行设置,即获取设备描述表对象;然后选择进行输出所需要的绘图工具;最后用设备描述表对象的输出函数绘制图形。 4.1.2、窗口、客户区和非客户区的概念 窗口是Windows应用程序基本的操作单元,是其运行及与用户进行交互的基本平台。 屏幕输出的目标一般都是窗口内的客户区,它不包括窗口的边框、水平和垂直滚动条、状态条、工具条、菜单栏和标题栏。窗口内的客户区是一个万能的输出区域,可以接受无论是图形、文本、位图、还是其他类型的数据(例如OLE对象)。 窗口内除客户区之外的区域即为非客户区。图4-1显示了窗口内的客户区、非客户区及其相关部分的位置。 图4-1 窗口内的客户区与非客户区 4.1.3、绘图类 MFC提供了不同类型的设备描述表的类(绘图类):CDC、CPaintDC、CClientDC、CWindowDC和CmetafileDC。其中CDC类是MFC绘图类的根类,其它的绘图类都是CDC类的派生类。CDC类包含了绘图所需的所有成员函数。其它的派生类,除了CmetafileDC类之外,都仅仅是构造函数和析构函数的不同。 1.​ CDC类 CDC类是MFC绘图类的根类,它是一个功能非常完全的类,包含170多个成员函数和数据成员。使用它可以访问整个显示设备。 使用CDC类必须首先调用Win32 API的BeginPaint()函数为重绘工作做一些准备工作,在完成绘制之后还需用Win32 API的EndPaint()函数来结束绘制工作。所有的绘图操作都必须在这两个函数之间完成。 2.​ CPaintDC类 CPaintDC类是CDC类的派生类,其绘图控制区为窗口的客户区。它是OnPaint()函数使用的绘图类,亦即用来响应Windows消息WM_PAINT(窗口刷新)的成员函数的绘图类。因此,如果为了维护图形的完整性,而需要重新编写视窗的OnPaint()函数时,就必须使用CPaintDC类来定义一个绘图对象。 使用CPaintDC类绘图时,必须发出让包含要绘制图形的窗口刷新的指令,才能在窗口重画时,将图形绘制到客户区上,当然客户区内的其它图形同时也会重画。也就是说CPaintDC类不可以实时的将图形绘制到客户区上。 CPaintDC类封装了Win32 API的BeginPaint()函数和EndPaint()函数。 3.​ CClientDC类 CClientDC类也是CDC类的派生类,其绘图控制区为窗口的客户区,该类可以实时的将图形绘制到客户区上。它是为不响应Windows消息WM_PAINT的成员函数提供的绘图类,亦即在OnPaint()函数中不使用CClientDC类。 CClientDC类封装了Win32 API的GetDC()函数和ReleaseDC()函数。 4.​ CWindowDC类 CWindowDC类也是CDC类的派生类,其绘图控制区为整个窗口区域,既包括客户区,也包括非客户区,其它方面与CClientDC类似。 与CClientDC类相比,CWindowDC类更适用于框架窗口,亦即CWindowDC类一般在框架窗口类(CMainFrame)中引用。在视窗类(如CtestView类)中引用CWindowDC类时,由于视窗类只能管理客户区,所以并不能在非客户区绘图。 与CClientDC类一样,CWindowDC类也封装了Win32 API的GetDC()函数和ReleaseDC()函数。 5.​ CmetafileDC类 CmetafileDC类是用于创建Windows图元文件的设备描述表,它也是CDC类的派生类。Windows图元文件包含了一系列GDI(Graphics Device Interface)绘图命令。它不常用,如果使用它,必须自己调用OnPrepareDC函数。 4.1.4、绘图类的引用方法 一般情况下,应用程序的绘图工作都要在视图(View)类中进行,由AppWizard生成的程序中,有一个视图类的成员函数OnDraw,自动实现了在视图类中引用CDC类,这是由MFC程序内部的一个特殊机制实现的。当视图窗口绘制或重绘制(窗口刷新消息WM_PAINT)时,都要调用OnDraw函数。 在前面例子test中的testView.cpp文件中可以找到OnDraw函数,其自动生成的代码如下: // CTestView drawing void CTestView::OnDraw(CDC* pDC) { …… // TODO: add draw code for native data here // 用户在此添加代码。 } 其中,pDC就是一个设备描述表类CDC对象的指针,在此函数中,可以通过pDC指针调用CDC类的函数进行绘图。 如果需要在视图窗口以外的窗口(比如对话框)中绘制图形,就需要调用窗口类的WM_PAINT消息处理函数OnPaint来绘制或重画窗口,该函数中必须生成设备描述表类CPaintDC对象才能绘图,其代码示例如下: void CTestDialog::OnPaint() { // Device context for painting CPaintDC pdc(this); // 用户在此添加代码。 } 对于在OnDraw和OnPaint之外的函数中绘制图形,必须生成设备描述表类CClientDC对象才能绘图,其代码示例如下: void CTestView::FunctionName() { // Device context for painting CClientDC pdc(this); // 用户在此添加代码。 } CWindowDC类的引用方法与CClientDC类似,“CWindowDC pdc(this);”。只是其绘图控制区为整个窗口区域,既包括客户区,也包括非客户区。 4.1.5、几种常用的图形数据结构和类 绘图程序中常用到的几种Windows的结构类型:POINT、RECT、SIZE。以及在MFC中与之对应的类:CPoint、CRect、CSize。 ​ 点POINT数据结构: typedef struct tagPOINT { LONG x; LONG y; } POINT; 其中x、y表示一个点的坐标值。 ​ 矩形RECT数据结构: typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom; } RECT; 其中left、top表示矩形左上角的x、y坐标值,right、bottom表示矩形右下角的x、y坐标值。 ​ 表示矩形高宽的SIZE数据结构: typedef struct tagSIZE { int cx; int cy; } SIZE; 其中cx、cy表示矩形的宽度、高度值。 ​ CPoint类与POINT数据结构类似,它还包含了用来处理CPoint类和POINT结构类型数据的成员函数。CPoint类的对象可以用在任何使用POINT结构数据的地方。CPoint类与SIZE结构和CSize类可以互相使用,因为它们的数据结构是一样的。 CPoint类是由POINT结构派生出的类,因此POINT结构的成员变量x、y也是CRect类的成员变量。 CPoint类的构造函数的形式如下: CPoint( ); CPoint( int initX, int initY ); CPoint( POINT initPt ); CPoint( SIZE initSize ); CPoint( DWORD dwPoint ); // x坐标值放在dwPoint的低16位,y坐标值放在dwPoint的高16位 CPoint类重载的运算符有:==、!=、+=、-=、-、+等。 ​ CRect类与RECT数据结构类似,它还包含了用来处理CRect类和RECT结构类型数据的成员函数。CRect类的对象可以用在任何使用RECT结构、LPCRECT和LPRECT数据的地方。CRect类与SIZE结构和CSIZE类可以互相使用,因为它们的数据结构是一样的。 CRect类是由RECT结构派生出的类,因此RECT结构的成员变量left、top、right、bottom也是CRect类的成员变量。 CRect类的构造函数的形式如下: CRect( ); CRect( int left, int top, int right, int bottom ); CRect( const RECT& srcRect ); CRect( LPCRECT lpSrcRect ); CRect( POINT point, SIZE size ); // 给出矩形左上角点point和矩形的宽高值size) CRect( POINT topLeft, POINT bottomRight ); CRect类重载的运算符:=、==、!=、+=、-=、&=、|=、-、+、&、| 其中“&”求得两个CRect类对象的重叠部分。“|”求得两个CRect类对象的轮廓矩形。 CRect类的成员函数主要有:Width()、Height()、Size()、TopLeft()、BottomRight()、CenterPoint()等。 ​ CSize类是由SIZE结构派生出的类,故它与SIZE的数据结构类似,它常用作表示点的相对坐标或位置。CSize类的对象可以用在任何使用SIZE结构数据的地方。CSize类与SIZE结构的成员变量cx、cy是公有成员变量。 CSize类的构造函数的形式如下: CSize( ); CSize( int initCX, int initCY ); CSize( SIZE initSize ); CSize( POINT initPt ); CSize( DWORD dwSize ); // cx值放在dwPoint的低16位,cy值放在dwPoint的高16位)。 CSize类重载的运算符有:==、!=、+=、-=、-、+等。 下面举例说明这些数据类型的: CPoint p1(300,300), p2; //定义两个点p1、p2,并初始化p1点 CRect r1(CPoint(0,0),CPoint(100,100)), r2; //定义两个矩形r1、r2 CSize s(100,100); //定义一个CSize对象s,并对其初始化 p2 = p1-CSize(50,50); //p2为(250,250) r2 = r1+s; //r2为(100,100)-(200,200),是平移矩形r1的结果 r2 = r1+p1; //r2为(300,300)-(400,400),是平移矩形r1的结果 r2 |= r1; //r2为(0,0)-(400,400),是r1和r2的轮廓矩形 r2 = CRect(20,20,200,200); //r2为(20,20)-(200,200) r2 &= r1; //r2为(20,20)-(100,100),是r1和r2重叠的矩形 4.2、屏幕坐标系映射模式 在Windows环境中,所有的图形和文本输出的位置都对应于坐标系。图形和文本的坐标系是一致的。GDI支持两种坐标系,即设备坐标系和逻辑坐标系。 4.2.1、设备坐标系与逻辑坐标系 1.​ 设备坐标系 设备坐标系是指具体的物理设备的坐标系,它是以像素作为坐标单位,默认的坐标系原点在绘图区域的左上角,X轴向右为正,Y轴向下为正。 设备坐标系又有三种独立的坐标系:屏幕坐标系、窗口坐标系、客户区坐标系,如图4-2所示。关于窗口和客户区的定义见图4-1。大多数应用程序的绘图区域是客户区,经常使用的是客户区坐标系。以后如不特别声明,称设备坐标系时均指客户区坐标系。 图4-2 设备坐标系 2.​ 逻辑坐标系 逻辑坐标系是不考虑具体物理设备的一个统一的坐标系。图形的绘制是在以逻辑坐标系为基础的虚拟窗口(Window)中进行的,Windows能够通过映射模式将逻辑坐标系转化成设备坐标系,并将图形显示在视口(Viewport)中。Windows通过逻辑坐标系实现了与设备描述表的联系。 4.2.2、理解窗口与视口 要实现屏幕绘图,必须准确理解Windows的绘图机制。Windows在绘图时,并不是把图形直接绘制到屏幕上,而是以逻辑坐标系将图形绘制到虚拟的窗口(Window)中,而当前的设备(如屏幕上的窗口客户区、打印机等)显示出来的只是虚拟窗口的一部分,当前设备显示图形的区域被称作为视口(Viewport),如图4-3所示。 必须正确理解视口和窗口的概念,否则对不同映射模式下逻辑坐标系与设备坐标系的相互关系很难把握,并且也不会充分地实现Windows的绘图功能。绝大多数绘图函数都使用逻辑坐标系。 图4-3 窗口和视口 4.2.3、窗口和视口坐标系位置的对应设置 窗口和视口的位置对应关系是通过设置它们对应的基准点实现的,默认的基准点都是(0,0),即虚拟绘图窗口中逻辑坐标系的(0,0)点映射到显示视口中设备坐标系的(0,0)点,逻辑坐标系坐标轴的方向和单位由屏幕映射模式确定。 窗口基准点的设置可以通过调用CDC::SetWindowOrg函数实现。函数定义如下: CPoint SetWindowOrg( int x, int y ); CPoint SetWindowOrg( POINT point ); 参数(x,y)或point是以逻辑坐标值给定的窗口基准点。函数的返回值是一个CPoint对象,为原基准点(即设置前窗口的基准点),也是逻辑坐标值。 视口基准点的设置可以通过调用CDC::SetViewportOrg函数实现。函数定义如下: virtual CPoint SetViewportOrg( int x, int y ); virtual CPoint SetViewportOrg( POINT point ); 参数(x,y)或point是以设备坐标值给定的视口基准点,其值必须在设备坐标系的有效取值范围之内。函数的返回值是一个CPoint对象,为原基准点(即设置前视口的基准点),也是设备坐标值。 图4-4 窗口和视口的位置对应 例如,如果设定逻辑坐标系为笛卡儿直角坐标系(Y轴向上),且一个像素等于n个逻辑坐标单位。执行下面的基准点设置语句: SetWindowOrg( XL, YL ); SetViewportOrg( XD, YD ); 由图4-4可得到逻辑坐标(x,y)与设备坐标(X,Y)的转换关系为: X = x / n - ( XL / n - XD); Y = ( YL / n + YD ) – y / n; 4.2.4、坐标映射模式 逻辑坐标系映射模式的设置函数为CDC::SetMapMode,其格式为: virtual int SetMapMode( int nMapMode ); 参数nMapMode为映射模式的值。函数的返回值为前一个映射模式的值。 1.​ MM_TEXT映射模式 MM_TEXT为系统缺省的映射模式,它的坐标单位被映射到了像素,X轴向右为正,Y轴向下为正。 下面的代码把坐标映射模式设为MM_TEXT方式,并且把窗口的原点设在视口的(300,300)点处,亦即绘图的逻辑坐标系原点在视口的(300,300)点处。 DC.SetMapMode(MM_TEXT); DC.SetViewportOrg(CPoint(300,300)); 2.​ 逻辑坐标单位固定的映射模式 此类映射模式的逻辑坐标单位是固定的,X轴向右为正,Y轴向上为正。 此类映射模式有5种,列表如下: 映射模式 逻辑坐标单位 MM_LOENGLISH 0.01英寸 MM_HIENGLISH 0.001英寸 MM_LOMETRIC 0.1毫米 MM_HIMETRIC 0.01毫米 MM_TWIPS 1/1440英寸 MM_TWIPS映射模式常常用于打印机,一个“twip”单位相当于1/20磅(1磅等于1/72英寸)。 3.​ 逻辑坐标比例可变的映射模式 在这种映射模式下面,不但可以改变坐标单位的比例因子,还可以改变坐标轴的方向。借助于这样的映射方式,当用户改变窗口的尺寸的时候,绘制的图形的大小也可以根据比例发生相应的变化;当用户翻转某个轴方向的时候,绘制的图像也可以进行翻转;当用户对视窗进行滚动操作时,图像也可以进行滚动。 这样的映射方式有以下两种: 映射模式 逻辑坐标单位比例 MM_ISOTROPIC 可任意调节,但X和Y轴向的坐标单位相等 MM_ANISOTROPIC 可任意调节,且X和Y轴向的坐标单位可不相等 在MM_ISOTROPIC模式下,纵横的比例为1:1,换句话说,无论比例因子如何变化,画出的图形不会改变自己的形状。但是在MM_ANISOTROPIC模式下,X和Y轴向单位的比例因子可以独立地变化,亦即图形的形状可以发生变化。 这种映射模式下逻辑坐标单位比例和坐标轴方向的改变,是通过在窗口和视口中各设置一个矩形进行对应来实现的,两个矩形高宽幅度的比值确定了坐标系的换算关系。具体的操作是由两个函数CDC::SetWindowExt和CDC::SetViewportExt进行的,这两个函数只有在MM_ISOTROPIC和MM_ANISOTROPIC映射模式下才有效。 CDC::SetWindowExt函数定义如下: virtual CSize SetWindowExt( int cx, int cy ); virtual CSize SetWindowExt( SIZE size ); 参数(cx,cy)或size是以逻辑坐标值给定的窗口中一个矩形的宽高值(即x和y方向的幅度值)。 函数的返回值是一个CSize对象,为逻辑坐标值,是设定前窗口中一个矩形的高宽值。如果出错,返回的CSize对象的x和y值均为0。 CDC::SetViewportExt函数定义如下: virtual CSize SetViewportExt( int cx, int cy ); virtual CSize SetViewportExt( SIZE size ); 参数(cx,cy)或size是以设备坐标值给定的视口中一个矩形的宽高值(即x和y方向的幅度值)。 函数的返回值是一个CSize对象,为设备坐标值,是设定前视口中一个矩形的高宽值。如果出错,返回的CSize对象的x和y值均为0。 如果要设定逻辑坐标系的Y轴向上为正,可将其中一个矩形的高度取为负值即可。 在进行窗口和视口的矩形高宽幅度值的设定时,高宽值不表示窗口或视口的大小,其值的大小并不重要,重要的是两个矩形高宽幅度值的对应比例,因为它确定了逻辑坐标系与设备坐标系坐标值的换算比例。 注意:在MM_ISOTROPIC映射模式下,必须先设置窗口的幅度值(即先调用SetWindowExt函数),再设置视口的幅度值(即后调用SetViewportExt函数)。 下面这个程序: void CTestView::OnDraw(CDC* pDC) { // TODO: add draw code for native data here // 以下为第一段代码: CRect clientRect; GetClientRect(clientRect); pDC->SetMapMode(MM_ANISOTROPIC); pDC->SetWindowExt(1000,1000); pDC->SetViewportExt(clientRect.right, - clientRect.bottom); pDC->SetViewportOrg(clientRect.right/2, clientRect.bottom/2); pDC->Ellipse(CRect(-500,-500, 500,500)); //以下为第二段代码: pDC->SetMapMode(MM_ISOTROPIC); pDC->SetWindowExt(1000,1000); pDC->SetViewportExt(clientRect.right, - clientRect.bottom); pDC->SetViewportOrg(clientRect.right/2, clientRect.bottom/2); pDC->Ellipse(CRect(-500,-500, 500,500)); } 第一段代码的功能是这样的: 1)​ 由GetClientRect函数取得窗口客户区矩形的大小clientRect(设备坐标值),其左上角为(0,0),右下角为(clientRect.right, clientRect.bottom),即右下角的坐标值是窗口客户区矩形的宽高幅度值; 2)​ 设定映射模式为MM_ANISOTROPIC,即逻辑坐标单位可任意调节,且X和Y轴向的坐标单位可不相等; 3)​ 用SetWindowExt()函数设定窗口的宽高幅度为(1000,1000)逻辑坐标单位; 4)​ 用SetViewportExt()函数设定视口的宽高幅度为(clientRect.right, -clientRect.bottom)设备坐标单位,其中Y值取负的作用是设定逻辑坐标系Y轴向上为正,此时可得逻辑坐标与设备坐标的换算比例; 5)​ 由SetViewportOrg()函数设定视口的中心点与逻辑坐标系原点对应; 6)​ 执行绘图函数Ellipse,在(-500,-500, 500,500)的矩形内绘制出一个半径为500的椭圆(由于X、Y轴向比例不同,它看起来像椭圆),如图4-4所示。 在第二段代码中将映射模式改变为MM_ISOTROPIC,其它语句与第一段代码一样,结果将画出一个如图4-4中间所示的圆。在MM_ISOTROPIC映射方式下,逻辑坐标单位的比例以视口矩形宽(X轴向)、高(Y轴向)中的最小值来设定。 图4-4 坐标单位比例可变映射模式下的图形 由此可以得出在MM_ISOTROPIC和MM_ANISOTROPIC映射模式下,逻辑坐标到设备坐标的计算公式: 设定:SX为X轴向的比例因子,SY为Y轴向的比例因子。 视口的矩形范围为(W视口, H视口),窗口的矩形范围为(W窗口, H窗口) 逻辑坐标系原点(0,0)与设备坐标系中的(XO,YO)点对应。 则轴向比例因子的计算公式为: SX = W视口 / W窗口 SY = H视口 / H窗口 则逻辑坐标(x,y)到设备坐标(X,Y)的计算公式为: X = x * SX + XO Y = y * SY + YO 4.2.5、设备坐标系与逻辑坐标系之间的转换 当设定了设备描述表的映射模式之后,就可以直接使用逻辑坐标作为其参数了,但是从WM_MOUSEMOVE消息所获得的鼠标的坐标值是设备坐标。许多其他的MFC库函数,只接受设备坐标。所以有时要利用CDC的LPtoDP和DPtoLP函数在逻辑坐标和设备坐标之间进行转换的工作。 void LPtoDP( LPPOINT lpPoints); void DPtoLP( LPPOINT lpPoints); 例如:Cpoint p(100, 100); //定义一个点p,其值为设备坐标(100, 100) pDC->DPtoLP(&p); //将点实现从设备坐标转换成逻辑坐标 可以认为CDC类的绝大多数成员函数是以逻辑坐标作为参数的。 特别注意:应以逻辑坐标的形式保存图形数据,否则用户对视窗进行滚动操作的时候,这个数据就不再有效了。 4.3、绘图工具类 MFC中定义了一些Windows的图形设备界面(GDI)对象类,即绘图工具类,它们是作图的笔、给图形涂色的画刷,以及字体、位图、区域和调色板等影响绘图的工具。前面讲的设备描述表对象可以选入这些绘图工具来完成指定的图形操作。 绘图工具类主要有:CGdiObject、CPen、CBrush、CFont、CRgn和CBitmap类等。其中CGdiObject类是CObject类的派生类,它是绘图工具类的基类。CGdiObject类为它的派生类提供了大部分操作,但不能直接建立一个CGdiObject类的对象。 画笔CPen用于绘制直线、矩形、圆等几何图形,其属性主要有:线宽、线型和色彩等。画刷CBrush用于填充绘图区域,其属性有填充图案和色彩。 在生成设备描述表类对象时,如果没有指定笔和画刷,那么默认笔为线宽1个像素的黑色实线;默认的画刷为单一白色图案,即图形内部填充白色。 对绘图工具的使用包括:创建、选择和使用后的释放等过程。CDC类提供了两个成员函数SelectStockObject和SelectObject来选择GDI绘图工具。 选择自己创建的新绘图工具后,程序就将使用该工具绘图,直到选择其它的工具为止。当绘图完成后,应该恢复绘图以前的旧绘图工具,并及时释放当前绘图工具,以释放其占用的内存空间。程序中的代码(以画笔为例)通常如下所示: CPen *pPen,*pOldPen; //定义两个画笔指针变量 pPen=new CPen(PS_SOLID, 1, RGB(255,0,0)); //动态创建画笔(实线、线宽为1个单位、色彩为红色) pOldPen=(CPen *)pDC->SelectObject(pPen); //选入新画笔,保存旧画笔 …… //绘图工作 pDC->SelectObject(pOldPen); //恢复旧画笔 delete pPen; //删除动态创建的画笔 4.3.1、选择GDI库存的绘图工具 调用CDC类的成员函数SelectStockObject选择GDI库存的绘图工具。其格式为: virtual CGdiObject* SelectStockObject( int nIndex ); 其中参数nIndex的取值见下表: 参数值 库存绘图工具 画 刷 BLACK_BRUSH 黑刷(Black brush) DKGRAY_BRUSH 深灰刷(Dark gray brush) GRAY_BRUSH 灰刷(Gray brush) LTGRAY_BRUSH 淡灰刷(Light gray brush) WHITE_BRUSH 白刷(White brush)(系统默认值) HOLLOW_BRUSH 空刷(Hollow brush)(相当于NULL_BRUSH) NULL_BRUSH 空刷(Null brush)(无画刷填充) 画 笔 BLACK_PEN 黑笔(Black pen)(系统默认值) WHITE_PEN 白笔(White pen) NULL_PEN 空笔(Null pen)(即不画) 如果函数调用成功,其返回值为一指向被替换的CGdiObject绘图工具对象的指针(其实际所指的对象可能是CPen、CBrush或CFont)。如果函数调用失败,返回NULL。 下面的程序分别调用各种GDI库存绘图工具进行绘图,图形如图4-5所示。 void CTestView::OnDraw(CDC* pDC) { // TODO: add draw code for native data here CPoint p(180,120); pDC->SelectStockObject(LTGRAY_BRUSH); pDC->Ellipse(p.x-170, p.y-110, p.x+170, p.y+110); pDC->SelectStockObject(BLACK_BRUSH); pDC->Rectangle(p.x-120, p.y-70, p.x-20, p.y-20); pDC->SelectStockObject(DKGRAY_BRUSH); pDC->Rectangle(p.x+20, p.y-70, p.x+120, p.y-20); pDC->SelectStockObject(GRAY_BRUSH); pDC->Rectangle(p.x+20, p.y+20, p.x+120, p.y+70); pDC->SelectStockObject(WHITE_BRUSH); pDC->Rectangle(p.x-120, p.y+20, p.x-20, p.y+70); pDC->SelectStockObject(HOLLOW_BRUSH); pDC->SelectStockObject(WHITE_PEN); pDC->Rectangle(p.x-70, p.y-50, p.x+70, p.y-5); pDC->SelectStockObject(BLACK_PEN); pDC->Rectangle(p.x-70, p.y+5, p.x+70, p.y+50); } 图4-5 各种GDI库存绘图工具示例 4.3.2、自定义绘图工具的创建和使用 1.​ CPen绘图工具 CPen画笔主要用于绘制直线、矩形、圆等几何图形。CPen的构造函数如下: CPen( int nPenStyle, int nWidth, COLORREF crColor ); 其中参数nPenStyle为线型、nWidth为线宽、crColor为色彩。其创建画笔的成员函数CreatePen的参数与构造函数相同。 建立CPen画笔的方法有: 1)​ 定义一个CPen对象,并用其成员函数CreatePen构造画笔 CPen p; p.CreatePen(PS_SOLID, 1, RGB(255,0,0)); 创建了一个CPen画笔对象p,并把这个画笔设置成实线、线宽为1个坐标单位、色彩为红色。 2)​ 使用CPen构造函数建立一个画笔对象,并定义其参数 CPen p(PS_SOLID, 1, RGB(255,0,0)); 一次性的创建了一个同样的画笔对象p,其参数设置为红色实线、线宽1个单位。 3)​ 用CPen指针对象的形式动态创建一个画笔 CPen *pPen; pPen=new CPen(PS_SOLID, 1, RGB(255,0,0)); pPen指针指向一个由new建立的CPen画笔对象,并把这个画笔设置成实线、线宽1个单位、色彩为红色。 在一个函数中如果需要多次构造一个画笔时,应该采用这种方法。但应特别强调的是,使用完毕后必须释放给画笔分配的内存空间,即删除画笔: delete pPen; 画笔的线宽nWidth的取值以逻辑坐标为单位,如果nWidth=0,则忽略坐标映射模式,线宽为1个像素。 画笔的色彩参数crColor的数据类型为COLORREF。画笔的色彩只能是Windows中常用的16种纯色(见4.4.1中的内容),如果定义的色彩不是纯色,则在画线时会用最接近的纯色来代替。 画笔的线型nPenStyle的选择如下表: 线 型 值 线 型 PS_SOLID PS_DASH PS_DOT PS_DASHDOT PS_DASHDOTDOT PS_NULL 空 笔 PS_INSIDEFRAME PS_USERSTYLE 用户自定义笔 注意,线型PS_DASH、PS_DOT、PS_DASHDOT和PS_DASHDOTDOT只有在画笔宽度为1个像素时才能使用。如果线宽nWidth值大于1个像素,则画出给定宽度的实线(PS_SOLID)。 图4-6为选择不同线型绘制图形的示例,其OnDraw函数程序代码如下: void CTestView::OnDraw(CDC* pDC) { // TODO: add draw code for native data here CPen *pPen,*pOldPen; CPoint p1(15,15), p2(220,220); int ps[6]={ PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT,PS_DASHDOTDOT,PS_INSIDEFRAME }; int i; pDC->SetMapMode(MM_TEXT); pDC->SetViewportOrg(0,0); for(i=0;i<6;i++) { pPen=new CPen(ps[i], 0, RGB(0,0,0)); //线宽为0,表示线宽为1个像素 pOldPen=(CPen *)pDC->SelectObject(pPen); pDC->Rectangle(CRect(p1,p2)); p1.Offset(12,12); p2.Offset(-12,-12); pDC->SelectObject(pOldPen); delete pPen; } pDC->SetViewportOrg(280,0); p1=CPoint(15,15); p2=CPoint(220,220); for(i=0;i<6;i++) { pPen=new CPen(ps[i], 2, RGB(0,0,0)); //线宽为2个单位,将忽略线型 pOldPen=(CPen *)pDC->SelectObject(pPen); pDC->Rectangle(CRect(p1,p2)); p1.Offset(12,12); p2.Offset(-12,-12); pDC->SelectObject(pOldPen); delete pPen; } } 在该程序中,为了说明线宽对线型的影响,使用不同的线宽绘制同一个图形两次,第二次绘制时使用函数SetViewportOrg(280,0)将视口左移280个像素。 图4-6 选择不同线型绘制图形示例 如果需要自定义线型,可以使用CPen重载的另一个构造函数(重载的成员函数CreatePen的参数与它相同): CPen( int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL ); 其中参数:nPenStyle取值为PS_USERSTYLE。 nWidth取值为1个像素。 pLogBrush为指向LOGBRUSH结构类型数据的指针,该参数设置笔刷的样式、色彩和图案。 lpStyle只有在nPenStyle取值为PS_USERSTYLE才有意义,否则lpStyle必须为NULL。lpStyle为一个DWORD类型的数组,用来设置线型的具体参数,数组中第一个数据表示线段长(像素数),第二个数表示空隙长(像素数),以后各数以此类推。 nStyleCount也只有在nPenStyle取值为PS_USERSTYLE才有意义,否则,nStyleCount必须为0。nStyleCount表示数组lpStyle中数据的个数。 图4-7是用自定义线型(点划线和虚线)来创建画笔绘制的图形。该示例的OnDraw函数的代码如下: void CTestView::OnDraw(CDC* pDC) { // TODO: add draw code for native data here CPen *pPen,*pOldPen; CRect r(50, 50, 370, 370); pPen=new CPen(PS_SOLID, 3, RGB(0,0,0)); //创建线宽为3的实线画笔 pOldPen=(CPen *)pDC->SelectObject(pPen); //选入新画笔,保存旧画笔 pDC->Ellipse(r); //绘制粗实线圆 pDC->SelectObject(pOldPen); //恢复旧画笔 delete pPen; //删除动态创建的画笔 DWORD Style[4]; //定义存放线型长度的数组 LOGBRUSH b; b.lbStyle=BS_SOLID; //设置实心笔刷 b.lbColor=RGB(0,0,0); //设置笔刷色彩为黑色 b.lbHatch=0; //设置为实心笔刷时,图案值无效 Style[0]=5; Style[1]=1; //设置虚线的样式, 2个数为一单元, 长划为5、空隙为1 pPen=new CPen(PS_USERSTYLE, 1, &b, 2, Style); //动态创建画笔 pOldPen=(CPen *)pDC->SelectObject(pPen); //选入新画笔,保存旧画笔 r.DeflateRect(65,65); //将矩形r高宽各缩小65个单位 pDC->Rectangle(r); //绘制矩形r r.InflateRect(65,65); //将矩形r高宽各放大65个单位 pDC->SelectObject(pOldPen); //恢复旧画笔 delete pPen; //删除动态创建的画笔 Style[0]= 20; Style[1]= Style[2]= Style[3]= 2; //设置点划线的样式,4个数为一单元,长划20、空隙2、短划2、空隙2 pPen=new CPen(PS_USERSTYLE, 1, &b, 4, Style); //动态创建画笔 pOldPen=(CPen *)pDC->SelectObject(pPen); //选入新画笔,保存旧画笔 CPoint s1,s2; s1.x=s2.x=(r.left+r.right)/2; s1.y=r.bottom+20; s2.y=r.top-20; pDC->MoveTo(s1); pDC->LineTo(s2); //绘制竖直点划线 s1.x=r.left-20; s2.x=r.right+20; s2.y=s1.y=(r.top+r.bottom)/2; pDC->MoveTo(s1); pDC->LineTo(s2); //绘制水平点划线 pDC->SelectObject(pOldPen); //恢复旧画笔 delete pPen; //删除动态创建的画笔 } 图4-7 用自定义线型创建画笔的示例 创建画笔后,必须调用CDC:: SelectObject函数将其选入当前的设备描述表对象,才能在绘图中使用。调用该函数的同时必须保存以前的画笔对象。 SelectObject是一个重载函数,各种绘图工具均由它选入设备描述表对象中,函数的原型说明如下: CPen* SelectObject( CPen* pPen ); CBrush* SelectObject( CBrush* pBrush ); virtual CFont* SelectObject( CFont* pFont ); CBitmap* SelectObject( CBitmap* pBitmap ); 2.​ CBrush绘图工具 CBrush画刷主要用颜色和图案来填充指定的区域。画刷工具的使用与画笔工具类似。 CBrush( COLORREF crColor ); CBrush( int nIndex, COLORREF crColor ); CBrush( CBitmap* pBitmap ); 画刷的色彩参数crColor的取值与初始化画笔的色彩不同,不必是纯色,可以是任意的色彩值,如果不是纯色,Windows会自动产生配色。 A、建立实心画刷 这种画刷以定义的色彩实心填充区域。建立该种画刷的方法有: 1)​ 定义一个CBrush对象,并用其成员函数CreateSolidBrush构造画刷 CBrush b; b.CreateSolidBrush( RGB(255,0,0) ); 创建了一个红色的CBrush对象b。 2)​ 使用CBrush构造函数,一次性的构造一个红色画刷对象b CBrush b( RGB(255,0,0) ); 3)​ 用画刷指针动态创建一个红色画刷 CBrush *pBrush; pBrush=new CBrush(RGB(255,0,0) ); pBrush指针指向一个由new建立的CBrush对象。在一个函数中如果需要多次构造一个画刷时,应该采用这种方法。但应特别强调的是,使用完毕后必须释放给画刷分配的内存空间,即删除画刷: delete pBrush; B、建立图案画刷 这种画刷以定义的色彩和图案,在指定区域中填充阴影图案。构造方法与实心画刷相同: 1)​ 用成员函数CreateHatchBrush构造画刷 CBrush b; b.CreateHatchBrush( HS_FDIAGONAL, RGB(255,0,0) ); 创建了一个红色45°斜线图案的CBrush对象b。 图案样式nIndex的取值见下表: 图案值 图案的形式 图 案 HS_HORIZONTAL 水平阴影线 HS_VERTICAL 竖直阴影线 HS_CROSS 水平与竖直交叉阴影线 HS_BDIAGONAL 从左下到右上的45°斜线 HS_FDIAGONAL 从左上到右下的45°斜线 HS_DIAGCROSS 45°交叉线 2)​ 使用CBrush构造函数,一次性的构造一个画刷对象b CBrush b( HS_FDIAGONAL, RGB(255,0,0) ); 3)​ 用画刷指针动态创建一个画刷 CBrush *pBrush; pBrush=new CBrush( HS_FDIAGONAL, RGB(255,0,0) ); pBrush指针指向一个由new建立的CBrush对象。在一个函数中如果需要多次构造一个画刷时,应该采用这种方法。但应特别强调的是,使用完毕后必须释放画刷的内存空间,即删除画刷: delete pBrush; 在设备描述表对象中选入画刷的操作与选入画笔相同,也是调用CDC:: SelectObject重载函数进行选入。同样,调用该函数的同时必须保存以前的画刷对象。下面的程序段是同时对画笔和画刷进行创建、选择和使用后释放过程的示例。 …… CPen *pPen, *pOldPen; CBrush *pBrush, *pOldBrush; //定义两个画刷指针变量 pPen=new CPen(PS_SOLID, 1, RGB(0,0,0)); pOldPen=(CPen *)pDC->SelectObject(pPen); pBrush=new CBrush(HS_FDIAGONAL, RGB(0,0,0)); //动态创建画刷(从左上到右下的45°斜线、色彩为黑色) pOldBrush=(CBrush *)pDC->SelectObject(pBrush); //选入新画刷,保存旧画刷 …… //绘图工作 pDC->SelectObject(pOldPen); delete pPen; pDC->SelectObject(pOldBrush); //恢复旧画刷 delete pBrush; //删除动态创建的画刷 …… 图4-8是用不同的图案画刷绘制的柱状图。该示例的OnDraw函数的代码如下: void CTestView::OnDraw(CDC* pDC) { // TODO: add draw code for native data here CPen *pPen,*pOldPen; CBrush *pBrush,*pOldBrush; int bs[6]={ HS_CROSS, HS_HORIZONTAL, HS_BDIAGONAL, HS_FDIAGONAL, HS_VERTICAL, HS_DIAGCROSS }; CRect r(0,0,50,300); CPoint p1(40,20), p2(40,300); pPen=new CPen(PS_SOLID, 1, RGB(0,0,0)); //动态创建画笔 pOldPen=(CPen *)pDC->SelectObject(pPen); pDC->MoveTo(p1); pDC->LineTo(p2); //绘制直线 p2=CPoint(450,300); pDC->LineTo(p2); srand( (unsigned)time(NULL) ); //用系统的时间设置随机数发生器 int i; for(i=0;i<6;i++) { pBrush=new CBrush(bs[i], RGB(0,0,0)); //动态创建画刷 pOldBrush=(CBrush *)pDC->SelectObject(pBrush); r.top=(int)(250*rand()/(float)(RAND_MAX)); //随机产生矩形上边的Y坐标 r+=CPoint(52,0); pDC->SetBrushOrg( r.TopLeft() ); //设置画刷的原点 pDC->Rectangle(r); pDC->SelectObject(pOldBrush); //恢复旧画刷 delete pBrush; //删除动态创建的画刷 } pDC->SelectObject(pOldPen); delete pPen; //删除动态创建的画笔 } 图4-8 柱状图 C、位图填充的画刷 函数CBrush( CBitmap* pBitmap )和CreatePatternBrush( CBitmap* pBitmap )用来构造一个画刷,这个画刷用位图图像文件填充指定的区域。参数pBitmap是一个位图对象的指针。 4.4、设置绘图属性 4.4.1、颜色参数的设置 Windows的图形设备界面(GDI)采用的是“硬件独立”的颜色接口,即应用程序提供的是绝对颜色代码,GDI会产生合适的颜色映射到显示器硬件上。 颜色代码的数据类型是32-bit的COLORREF类型,这种类型的数据包含了三个8-bit的颜色值,分别对应红、绿、蓝三色,每一个色彩的取值范围是0-255。GDI绘图函数中的颜色参数一般为COLORREF类型。 定义颜色代码数值最简单的方法是通过使用Win32的RGB宏指定颜色,其格式如下: COLORREF RGB(BYTE bRed, BYTE bGreen, BYTE bBlue); 其中参数bRed、bGreen、bBlue为三个8-bit的BYTE类型数据,表示红绿蓝三色在0-255的取值。RGB宏的结果为COLORREF类型的颜色值。 Windows中通常用到的16种纯色为: 颜色 Red Green Blue 黑Black 0 0 0 蓝Blue 0 0 255 深蓝Dark blue 0 0 128 绿Green 0 255 0 深绿Dark green 0 128 0 淡青Cyan 0 255 255 深青Dark cyan 0 128 128 红Red 255 0 0 深红Dark red 128 0 0 淡洋红Magenta 255 0 255 深洋红Dark magenta 128 0 128 黄Yellow 255 255 0 深黄Dark yellow 128 128 0 深灰Dark gray 128 128 128 淡灰Ligh
/
本文档为【屏幕绘图】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索