第8章 GDI编程1-绘图
GDI(Graphics Device Interface,图形设备接口)是Windows操作系统的传统图形子系统,负责与设备无关的图形绘制,Win32 API为应用程序提供了丰富的绘图函数和功能,MFC对它们进行了C++类封装,参见图8-1。
图8-1 GDI与Windows操作系统(其中彩色部分为操作系统)传统GDI是随Windows 1.0于1985年11月推出的,新式GDI+则是随Windows XP于2001年10月推出的GDI的改进版,增加了α混色、渐变画刷、样条曲线、矩阵变换、图像处理、持久路径等新功能。随Windows Vista及.NET框架3.0微软于2006年11月又推出了基于DirectX和.NET框架的全新图形子系统WPF(Windows Presentation Foundation,视窗显示/展现基础),它统一了桌面和浏览器等客户端应用程序的图形界面,采用XAML声明式编程,将用户界面的设计和编程彻底分离开来,是Windows的下一代GUI显示系统。
本书从第8章到第11章,将详细讨论如何使用GDI进行传统的Windows图形编程,包括绘图、文字、图像、动画、图标、图元文件和打印等内容。GDI+是建立在GDI之上的,计划在第14和15章中作简单介绍。WPF则是以.NET框架为基础的,准备在第19上和20章中再加以讨论。
在MFC应用程序中,绘图一般在视图类的(屏幕/打印机)绘图消息响应函数OnDraw 中进行,例如:
void CDrawView::OnDraw(CDC* /*pDC*/) {
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
}
每次需要重绘窗口时(如程序启动、窗口大小改变、全部或部分窗口重现、程序员调用函数RedrawWindow 或Invalidate/UpdateWindow ),应用程序框架都会调用此CWnd 消息响应成员函数(的覆盖)来绘制窗口客户区。
在Windows 中,绘图一般在框架窗口的客户区(对应于视图类C*V iew )进行,使用的是封装在MFC 的设备上下文(Device-Context ,DC )类CDC 中的各种绘图函数。
在绘图前,一般需先得到客户区大小和CDC 对象、设置绘图颜色,然后再根据文档数据或用户操作来绘制各种图形。
8.1 几何对象的结构和类
为了使用绘图函数,应该先了解绘图所用到的几种表示几何对象的结构和类,包括点、大小和矩形,其中常用的是点和矩形。这些结构和类被分别定义在头文件windef.h 和afxwin.h
中。MFC 中的几何对象类都是独立的类(不是CObject 的派生类),是对API 中对应结构的C++封装,参见图8-2。
8.1.1 点
点(point )在API 中的结构为POINT ,对应的MFC 类为CPoint 。
1.点结构POINT
API 中的点数据结构POINT 用来表示一点的x 、y 坐标:
typedef struct tagPOINT { LONG x; LONG y; } POINT;
其中,类型LONG (32位整数)的定义为:typedef long LONG;
2.点类CPoint
MFC 中的点类CPoint 为一个没有基类的独立类,封装了POINT 结构,有成员变量x 和y ,其构造函数有5种:
图8-2 GDI 几何对象
的结构与类
CPoint( ); // 默认
CPoint( int initX, int initY ); // 常用CPoint( POINT initPt ); CPoint( SIZE initSize );
CPoint( LPARAM dwPoint ); // 低字设
为x、高字设为y
CPoint类还定义了4个平移和设置的成员函数:
void Offset(int xOffset, int yOffset); void Offset(POINT point); void Offset(SIZE size); void SetPoint(int X, int Y);
另外,CPoint类还重载了+、-、+=、-=、==、!= 等运算符来支持CPoint对象和CPoint、POINT、SIZE对象之间的运算。
8.1.2 大小
大小(size,尺寸)在API中的结构为SIZE,在MFC中的类为CSize。
1.大小结构SIZE
大小结构SIZE用来表示矩形的宽cx和高cy:
typedef struct tagSIZE { LONG cx; LONG cy; } SIZE;
2.大小类CSize
MFC中的大小类CSize也为一个没有基类的独立类,封装了SIZE结构,有成员变量cx和cy,其构造函数也有5种:
CSize( );
CSize( int initCX, int initCY ); CSize( SIZE initSize ); CSize( POINT initPt );
CSize( DWORD dwSize ); // 低字设为
cx、高字设为cy
CSizet类也重载了+、-、+=、-=、==、!= 等运算符来支持CSize对象和CSize、POINT、SIZE、RECT对象之间的运算。
8.1.3 矩形
矩形(rectangle)在API中的结构为RECT,在MFC中的类为CRect。
1.矩形结构RECT
API中的矩形结构RECT定义了矩形的左上角与右下角的坐标:
typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom;} RECT;
2.矩形类CRect
MFC中的矩形类CRect也为一个没有基类的独立类,封装了RECT结构,有成员变量left、top、right和bottom,其构造函数有6种:
CRect( ); // 默认
CRect( int l, int t, int r, int b ); // 常用CRect( const RECT& srcRect ); CRect( LPCRECT lpSrcRect );
CRect( POINT point, SIZE size );
CRect( POINT topLeft, POINT bottomRight );
CRect类重载了=,+、-,+=、-=,==、!=,&、|,&=、|= 等运算符来支持CRect对象和CRect、POINT、SIZE、RECT对象之间的运算。还定义了转换符LPCRECT和LPRECT 来自动完成CRect对象到矩形结构和类指针LPCRECT和LPRECT的转换。
CRect类中常用的属性和成员函数有:
int Width( ) const;
int Height( ) const;
CSize Size( ) const; CPoint& TopLeft( ); CPoint& BottomRight( ); CPoint CenterPoint( ) const; void SwapLeftRight(); BOOL IsRectEmpty( ) const;
BOOL PtInRect( POINT point ) const; void SetRect( int x1, int y1, int x2, int y2 ); void SetRect(POINT topLeft, POINT
bottomRight);
void OffsetRect(int x, int y);
void MoveToXY(int x, int y);
3.判断点是否在矩形中
有时需要判断某点(如鼠标位置)是否在某一矩形区域(如控件)中,这可以调用CRect 类的PtInRect函数来做:
BOOL PtInRect( POINT point ) const;
该函数当点point在其矩形区域内时,返回真。注意,该矩形区域不包括矩形的右边界和底边界。例如:
CRect rect( 10, 10, 371, 267 );
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if ( rect.PtInRect( point ) ) ... ...
CView::OnLButtonUp(nFlags, point);
}
8.1.4 获得客户区大小
绘图一般都是在框架窗口的客户区(对应于视图类)进行,而客户区的大小在运行时可由用户动态改变,为了使绘制的图形能随窗口大小而自动改变,需先得到当前客户区大小的数据(宽和高)。
获取客户区大小的方法有如下两种:
1.OnSize
获取客户区大小的第一种方法是通过消息响应函数OnSize中获得。
可利用属性窗口的消息页,为视图类添加WM_SIZE消息的响应函数OnSize。该函数会在窗口第一次显示或窗口大小被改变时被系统调用。其输入参数中的cx和cy就是当前客户区的宽和高,可将它们赋值给类变量(如m_iW和m_iH)供绘图时使用。例如:void CDrawView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
m_iW = cx; m_iH = cy;
}
其中,nType的值为:
●SIZE_MAXIMIZED(窗口已被最大化)
●SIZE_MINIMIZED(窗口已被最小化)
●SIZE_RESTORED(窗口已被改变大小)
●SIZE_MAXHIDE(其他窗口被最大化)
●SIZE_MAXSHOW(其他窗口从最大化还原)
2.GetClientRect
获取客户区大小的第二种方法是调用窗口类的获取客户区矩形成员函数GetClientRect 来得到。具体做法是,在绘图前定义一个矩形结构变量rect,然后再调用CWnd类的成员函数GetClientRect来得到当前客户区矩形的数据,该函数的原型为:
void GetClientRect( LPRECT lpRect ) const;
其中,矩形结构的右(right)与底(bottom)就是客户区的宽与高(其左left与顶top都为0)。例如:
RECT rect;
GetClientRect(&rect);
int iW = rect.right, iH = rect.bottom;
8.2 绘图环境DC
在Windows中,绘图包括绘制图形、显示位图和输出文字,它们都需要使用绘图环境DC(Device-Context, 设备上下文)。MFC将DC结构和所有的绘图函数、绘图对象的访问函数、绘图模式与参数的设置函数都封装到了CDC类中。
8.2.1 DC结构
DC是Windows为设备无关的图形绘制而定义的一种数据结构,包含了一组图形对象及关联属性和若干影响输出的图形模式和绘图参数。所有的GDI绘图操作,都是采用DC中的当前图形对象作为绘图工具,并且都是在DC中的当前图形模式下使用当前的绘图参数进行的。
1.图形对象
Windows的图形对象(graphic object)包括画线状图的笔、绘制和填充面状图的刷、用于复制和滚动部分屏幕的位图、定义可用颜色的调色板、用于裁剪和其他操作的区域、用于绘制和画图操作的路径、及用于输出文字的字体,参见表8-1。图像对象所对应的MFC类将在8.3.6中介绍,其中笔和刷在8.3.3和8.3.4中介绍,字体在9.2中介绍。
表8-1 图形对象及DC的默认值
图形对象关联属性DC默认值
笔风格、宽度、颜色实心、单像素、黑色
刷风格、颜色、图案、原点实心、白色、单色、(0, 0) 位图
大小(字节)、尺寸(像素)、
颜色格式、压缩方式等
无
调色板颜色与大小(或颜色数)20种标准系统色
区域定位与尺寸窗口矩形
路径形状无
字体
字体名、宽、高、粗细、字
符集等system(宋体)、7、16、700(粗体)、GB2312_CHARSET
2.图形模式
图形模式(graphics mode)用于确定颜色如何混合、输出在何处出现、输出如何缩放等,参见表8-2。
表8-2 图形模式及DC的默认值
图形模式描述DC默认值介绍章节
映射定义图形输出如何从逻辑(或世界)
空间映射到窗口、屏幕或打印机纸,
即坐标体系
文本(MM_TEXT),即
设备坐标(单位为像素、
x向右、y向下)
8.4.1
绘图对笔、刷、位图和文本操作,定义
前景色如何与已存在的窗口或屏幕
颜色混合
覆盖(R2_COPYPEN)8.5.2
背景
对位图和文本操作,定义背景色如
何与已存在的窗口或屏幕颜色混合
不透明(OPAQUE)8.5.1
拉伸定义在位图被压缩(或缩小)时,
定义位图颜色如何与已存在的窗口
或屏幕颜色混合
存黑去白
(BLACKONWHITE)
9.3.3
多边形填充
定义刷图案如何用来填充复杂区域
的内部交替(ALTERNA TE),
即按奇偶规则填充
8.5.3
3.图形参数
除了图形对象和图形模式外,DC 中还保存有当前的笔位置(默认值为坐标原点)、背景色(默认为白色)、文本色(默认为黑色)、文本对齐(默认为左顶)、画弧方向(逆时针)等绘图参数。
虽然DC 是一种结构,但是Windows 不允许应有程序的代码直接访问它。程序员必须通过调用各种对应的Set*和Get*函数以及Select*来设置和获取其中的对象、模式和参数。
8.2.2 CDC 类
CDC 类是MFC 对DC 结构及其相关绘图和状态设置函数的C++类封装。CDC 是CObject 的直接派生类,CDC 类自己也有若干派生类,其中包括:窗口客户区DC 所对应的CClientDC 类、OnPaint 和OnDraw 消息响应函数的输入参数中使用的CPaintDC 类、图元文件对应的CMetaFileDC 类和整个窗口所对应的CWindowDC 类,参见图8-3。在普通的MFC 绘图中,一般直接使用CDC 类即可。
CDC 类中有许多成员函数,可以用来设置各种绘图环境、属性和参数,以及绘制各种图形和图像等,将在后面陆续加以介绍。
1.获得DC
在OnDraw 函数中应该使用输入参数pDC ,在其他函数中则可调用从CWnd 类继承的成员函数GetDC 来获得当前窗口(如客户区/视图类)DC 的指针,该函数的原型为:
CDC* GetDC( );
注意,每次从OnDraw 函数的输入参数或调用GetDC 所获得的DC ,都是一个全新的临时默认DC ,具有默认的绘图环境和设置。它不能用类变量来长期保存,而且原来选入的各种GDI 对象全都被作废、原来设置的各种状态也失效,一切都必须从头再来。
2.释放DC
因为Windows 限制可用DC 的数量,所以DC 属于稀缺的公用资源。因此,对每次获得的DC ,在使用完成后必须立即释放。
从OnDraw 函数的输入参数pDC 获得的DC ,在该函数运行结束后,系统会自动释放。
图8-3 DC 类
但由GetDC所获得的DC,则必须自己来手工释放,这可以通过调用从CWnd类继承的成员函数ReleaseDC来完成。该函数的原型为:
int ReleaseDC( CDC* pDC ); // 成功返回非0
例如:
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
CDC* pDC = GetDC(); // 获取DC
pDC->SelectObject(&pen); // 选入笔
pDC->Rectangle(&rect); // 绘制矩形
ReleaseDC(pDC); // 释放DC
CView::OnLButtonUp(nFlags, point);
}
3.安全DC句柄
可以用CDC类的成员函数GetSafeHdc来获取DC所对应窗口(如客户区)的安全DC 句柄,该函数的原型为:
HDC GetSafeHdc();
与用GetDC函数所得到的临时DC不同,该安全DC句柄在窗口的存在期间内一直是有效的。例如,可先定义类变量HDC m_hDC;,再在适当的地方给它赋值m_hDC = GetDC()->GetSafeHdc();,然后就可以放心地使用了。还可以使用CDC类的成员函数Attach 来将一空CDC对象与此安全DC句柄连接在一起,该函数的原型为:
BOOL Attach(HDC hDC); // 成功返回非0
4.屏幕DC
可以以NULL为输入参数,调用全局的API函数GetDC:
HDC GetDC(HWND hWnd);
来可获取屏幕DC的句柄。例如:
HDC hdc = ::GetDC(NULL); // 获取屏幕DC的句柄
Ellipse(hdc, 0, 0, 150, 100); // 用API函数在屏幕左上角画一椭圆
也可以利用CDC类的成员函数Attach,将屏幕DC的句柄选入自定义的CDC类中,来构造屏幕CDC类的对象。例如:
HDC hdc = ::GetDC(NULL); // 获取屏幕DC的句柄
CDC sDC; // 定义屏幕DC(空)对象
sDC.Attach(hdc); // 粘上屏幕DC句柄
sDC.Ellipse(0, 0, 150, 100); // 在屏幕左上角画一椭圆
8.3 绘图颜色与工具
在Windows API中用4个字节的双字来定义颜色类型COLORREF,有红(Red)绿(Green)蓝(Blue)三个字节分量,可用RGB宏来为颜色赋值。在GDI中有画线状图的笔(pen)、画填充图的刷(brush)、画文字的字体(font)等绘图工具。而GDI中使用的颜色种类则有4种之多,它们分别是像素所对应的点色、笔所对应的线色、刷所对应面色、文字所对应的文本色。其中的像素和文本色,可以通过调用对应的专门函数来设置和获取。而笔和刷的颜色,则必须先利用颜色参数创建笔和刷对象,然后调用通用的选择对象函数SelectObject将笔、刷等绘图工具选入DC,再利用DC来绘制各种颜色的图形。
8.3.1 颜色
Windows中的颜色一般用4个字节表示(0BGR(整数序)或RGB0(字节序),因为Intel CPU的低位字节在前),Win32 API中定义了一个专门表示颜色索引值的变量类型COLORREF(windef.h):
typedef DWORD COLORREF; // 0x00bbggrr
和一个由红绿蓝三原色构造颜色值的宏RGB(wingdi.h):
#define RGB(r,g,b)
((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16))) 其中,r、g、b为字节变量,分部对应于红、绿、蓝三原色,取值范围为0~255。RGB宏所对应的函数说明为:
COLORREF RGB( BYTE bRed, BYTE bGreen, BYTE bBlue );
例如:
COLORREF red, gray;
red = RGB(255, 0, 0);
gray = RGB(128, 128,128);
在API中还定义了由COLORREF变量获取各个颜色分量的宏GetXV alue(wingdi.h),它们对应的函数说明为(其中的DWORD rgb 等价于COLORREF col):
BYTE GetRV alue(DWORD rgb);
BYTE GetGV alue(DWORD rgb);
BYTE GetBV alue(DWORD rgb);
8.3.2 点色(像素)
在Windows中,像素(pixel)的颜色是直接由CDC类的成员函数SetPixel来设置的,该函数的原型为:
COLORREF SetPixel( int x, int y, COLORREF crColor );
COLORREF SetPixel( POINT point, COLORREF crColor );
其中,x与y分别为像素点的横坐标与纵坐标,crColor为像素的颜色值。例如:
pDC->SetPixel(10, 10, RGB(0, 255, 0));
另外,还可以用CDC的成员函数
COLORREF GetPixel( int x, int y ) const;
COLORREF GetPixel( POINT point ) const;
来获得指定点(x, y)或point的颜色。例如:
COLORREF col;
col = pDC->GetPixel(10, 10);
8.3.3 线色(笔)
在Windows中,线状图必须用笔(pen)来画,所以线的颜色就由笔色来确定。在MFC 中,笔的属性和功能由CPen类提供(CPen是CGDIObject的派生类)。
笔的创建与使用的步骤为:
1.创建笔对象
创建CPen类笔对象的方法有如下两种:
●使用非默认构造函数创建初始笔:
CPen( int nPenStyle, int nWidth, COLORREF crColor );
其中:
?nPenStyle为笔的风格,可取值见表8-3。
表8-3 笔风格nPenStyle值
符号常量数值风格名称线例
PS_SOLID 0 实心
PS_DASH 1 虚线
PS_DOT 2 点线
PS_DASHDOT 3 虚点线
PS_DASHDOTDOT 4 虚点点线
PS_NULL 5 空笔
PS_INSIDEFRAME 6 框内
注意:1~4号笔风格只是在笔宽=0或1时有效,笔宽>1时总为实心的。
?nWidth为笔宽,与映射模式有关,使用默认映射时为像素数,若nWidth = 0,
则不论什么映射模式,笔宽都为一个像素。
?crColor为笔的颜色值。
默认的笔为单像素宽的黑色实心笔。
例如:
CPen* pGrayPen = new CPen(PS_SOLID, 0, RGB(128, 128, 128));
CPen grayPen(PS_SOLID, 0, RGB(128, 128, 128));
●使用成员函数CreatePen(多次)创建笔:
BOOL CreatePen( int nPenStyle, int nWidth, COLORREF crColor );
可先用CPen类的默认构造函数创建一个空笔对象,然后再调用CreatePen创建具
有新内容的笔,例如:
CPen grayPen;
grayPen.CreatePen(PS_SOLID, 0, RGB(128, 128, 128));
不过在用CreatePen创建新笔之前,必须先调用DeleteObject函数删除笔对象的内容。该函数是CPen的基类CGDIObject的成员函数,下面是它的原型:
BOOL DeleteObject( );
2.选笔入DC
为了能使用我们自己所创建的笔对象,必须先将它选入DC,这可以调用设备上下文类CDC的成员函数SelectObject来完成:
CPen* SelectObject( CPen* pPen );
该函数的返回值为指向原来笔对象的指针(一般将其保存下来,供下次选出新笔时使用)。例如:
pOldPen = pDC->SelectObject(&pen);
另外,Windows中有一些预定义的笔对象(参见表8-4),可用CDC的另一成员函数SelectStockObject将其选入DC,其函数原型为:
virtual CGdiObject* SelectStockObject( int nIndex );
表8-4 预定义笔
符号常量数值笔
WHITE_PEN 6 白色笔
BLACK_PEN 7 黑色笔
NULL_PEN 8 空笔
DC_PEN 19 DC笔
其中,DC笔为单色实心笔,默认为黑色。是从Windows NT/2000开始新引进的,可用于直接设置笔色,参见本小节后面的第6部分。
例如:
pDC->SelectStockObject(BLACK_PEN);
3.绘制线状图
在将笔对象选入DC后,就可以使用CDC类中的绘图函数,利用DC中的笔对象,来画线状图及面状图的边线。如果不创建和选入笔对象,则使用的是DC中的默认笔,为单像素宽的实心黑色笔。
Windows中的线状图有直线、折线、矩形、(椭)圆(弧)等,详见8.4.3。
4.删除笔
不能直接删除已使用过的笔对象,必须先将它从DC中释放出来后才能删除。释放的方法是装入其他的笔对象(一般是重新装入原来的笔对象)。例如:
pDC->SelectObject(pOldPen);
删除笔对象的方法有如下几种:
●删除笔内容:调用笔类CDC的成员函数DeleteObject删除笔的当前内容(但是未
删除笔对象,以后可再用成员函数CreatePen在笔对象中继续创建新的笔内容)。
例如:pen.DeleteObject();
●删除笔对象:使用删除运算符delete将笔对象彻底删除,例如delete &pen;。
●自动删除:若笔对象为局部变量,则在离开其作用域时,笔对象会被系统自动删除。
5.例子
下面为一段较完整地创建与使用笔的例子代码:
CDC* pDC = GetDC(); // 获取DC
CPen pen, *pOldPen; // 定义笔对象和指针
// 创建10单位宽的绿色实心笔
pen.CreatePen(PS_SOLID, 10, RGB(0, 255, 0));
pOldPen = pDC->SelectObject(&pen); // 选入绿色笔
pDC->Rectangle(10, 10, 100, 100); // 画矩形
pDC->SelectObject(pOldPen); // 选出绿色笔
pen.DeleteObject(); // 删除绿色笔
// 创建单像素宽的红色虚线笔
pen.CreatePen(PS_DASH, 0, RGB(255, 0, 0));
pOldPen = pDC->SelectObject(&pen); // 选入红色笔
pDC->MoveTo(10, 10); pDC->LineTo(100, 100); // 画直线
ReleaseDC(pDC); // 释放DC
6.设置DC笔颜色
对Windows NT/2000及以上的版本,如果只需修改实心笔的颜色,而不需改变笔的粗细,则可以不用反复创建和选入新笔,而使用CDC类的成员函数SetDCPenColor,来直接设置DC笔的颜色:
COLORREF SetDCPenColor( COLORREF crColor ); // 返回原颜色值
对应的获取DC三笔颜色的成员函数为:
COLORREF GetDCPenColor( ) const;
不过这需要先调用CDC的成员函数SelectStockObject将DC笔选入DC后,才能使用SetDCPenColor函数来设置DC中笔的颜色。例如:
pDC->SelectStockObject(DC_PEN); // 选入DC笔
pDC->SetDCPenColor(RGB(255, 0, 0)); // 设置成红色
8.3.4 面色(刷)
在Windows中,面状图必须用刷(brush)来填充,所以面色是由刷色来确定的。MFC 中的刷类为CBrush(它也是CGDIObject的派生类),刷的创建与使用的步骤与笔的相似。
1.构造函数
CBrush类有4个构造函数,分别用于创建空刷、实心刷、条纹刷和图案刷:
●CBrush( ); // 创建一个刷的空对象
●CBrush( COLORREF crColor ); // 创建颜色为crColor的单色实心刷
●CBrush( int nIndex, COLORREF crColor ); // 创建风格由nIndex指定且颜色为
crColor的条纹(hatch,孵化/影线)刷,其中nIndex可取条纹风格(Hatch Styles)值见表8-5和图8-4。
表8-5 条纹风格nIndex值
符号常量数值风格
HS_HORIZONTAL 0 水平线
HS_VERTICAL 1 垂直线
HS_FDIAGONAL 2 正斜线
HS_BDIAGONAL 3 反斜线
HS_CROSS 4 十字线(正网格)
HS_DIAGCROSS 5 斜十字线(斜网格)
水平线垂直线正斜线反斜线十字线斜十字线
图8-4 条纹刷的种类
●CBrush(CBitmap* pBitmap); // 创建位图为pBitmap的图案(pattern)刷
默认的刷为白色实心刷。
2.创建函数
与构造函数相对应,CBrush类有多个创建不同类型刷的成员函数,如:
●BOOL CreateSolidBrush( COLORREF crColor ); // 创建(单色)实心刷
●BOOL CreateHatchBrush( int nIndex, COLORREF crColor ); // 创建条纹刷
●BOOL CreatePatternBrush( CBitmap* pBitmap ); // 创建图案刷
3.预定义刷
预定义的刷对象见表8-6有:
表8-6 预定义刷
符号常量数值刷
WHITE_BRUSH 0 白刷(默认)
LTGRAY_BRUSH 1 亮灰刷
GRAY_BRUSH 2 灰刷
DKGRAY_BRUSH 3 暗灰刷
BLACK_BRUSH 4 黑刷
HOLLOW_BRUSH 5 空刷
NULL_BRUSH 5 空刷
DC_BRUSH 18 DC刷
其中,DC刷为单色实心刷,默认为白色。是从Windows NT/2000开始新引进的,可用于直
接设置刷色,参见本小节后面的第5部分。
3.选入刷
与笔一样,可以用函数SelectObject或SelectStockObject将自定义刷或预定义刷选入DC 中,供绘面状图时使用。如果未选入任何刷,则系统会使用DC中的默认刷来绘图,默认刷为白色实心刷。
4.例子
下面是一个创建、选入并使用自定义实心刷的简单例子代码段:
CRect rect(10, 10, 210, 160);
CBrush brush(RGB(0, 255, 0));
CDC* pDC = GetDC();
pDC->SelectObject(&brush);
pDC->Rectangle(&rect);
ReleaseDC(pDC);
5.设置DC刷色
对Windows NT/2000及以上的版本,如果只需修改实心刷的颜色,而不改变刷的种类和风格,则可以不用反复创建和选入新刷,而使用CDC类的成员函数SetDCBrushColor,来直接设置DC刷的颜色:
COLORREF SetDCBrushColor( COLORREF crColor ); // 返回原颜色值对应的获取DC中刷颜色的成员函数为:
COLORREF GetDCBrushColor( ) const;
与设置DC笔类似,也需要先调用CDC的成员函数SelectStockObject将DC刷选入DC 后,才能使用SetDCBrushColor函数来设置DC中刷的颜色。例如:
pDC->SelectStockObject(DC_BRUSH); // 选入DC刷
pDC->SetDCBrushColor(RGB(255, 0, 0)); // 设置成红色
8.3.5 文本色
可使用CDC 类的成员函数SetTextColor/ GetTextColor 和SetBkColor/ GetBkColor 来分别设置/获取输出文本的前景色和背景色(默认的前景色为黑色,背景色为空):
COLORREF GetTextColor( ) const;
virtual COLORREF SetTextColor( COLORREF crColor ); COLORREF GetBkColor( ) const;
virtual COLORREF SetBkColor( COLORREF crColor );
例如:
pDC->TextOut(10, 10, L"Test text"); pDC->SetTextColor(RGB(0, 255, 0)); pDC->TextOut(10, 30, L"Test text"); pDC->SetBkColor(RGB(255, 0, 0));
pDC->TextOut(10, 50, L"Test text");
输出结果如图8-5所示。
8.3.6 绘图工具
Windows 的绘图工具为各种GDI 对象,被封装在MFC 对应的CGDIObject 派生类中。将GDI 对象用SelectObject 函数选入DC 后,就可利用对应的绘图工具进行图形绘制。
1.GDI 对象
GDI 对象(绘图工具)有笔、刷、位图、字体、调色板、区域等,对应的MFC 类为CPen 、CBrush 、CBitmap 、CFont 等。这些绘图工具对象类都是CGDIObject 的派生类,而CGDIObject 则是直接从CObject 类派生的抽象基类,参见图8-6。
其中的CPen 和CBrush 类上面已经讲过;CBitmap 和CFont 将在以后的位图和字体部分讲;调色板类CPalette 的使用非常复杂,本书不做介绍;CRgn 为区域(region )类,对应于窗口中的一个矩形、多边形或(椭)圆区域,可用于移动、拷贝、合并、判断和裁剪,在GDI 绘图部分也不讲解,但在GDI+中会做简单的介绍。
图8-5 文本色例
图8-6 GDI 对象的
类层次结构
2.选入函数
可用CDC类的多态成员函数SelectObject,将绘图工具对象选入DC,以供绘图时使用:CPen* SelectObject( CPen* pPen );
CBrush* SelectObject( CBrush* pBrush );
virtual CFont* SelectObject( CFont* pFont );
CBitmap* SelectObject( CBitmap* pBitmap );
int SelectObject( CRgn* pRgn );
CGdiObject* SelectObject( CGdiObject* pObject );
8.4 画图
在Windows中,绘图一般在视图窗口的客户区进行,使用的是设备上下文类CDC中各种绘图函数。为了达到理想的绘图效果,有时需要先利用CDC类设置绘图所用的坐标系(映射模式)和各种绘图属性。
8.4.1 映射模式与坐标系
在Windows中,绘图都是在当前的映射模式(map mode)下(坐标系中)进行的。而当前映射模式可以用CDC类的成员函数SetMapMode来设置。
1.默认映射模式
映射模式影响所有的图形和文本绘制函数,它定义(将逻辑单位转换为设备单位所使用的)度量单位和坐标方向,Windows总是用逻辑单位来绘图。
默认情况下,绘图的默认映射模式为MM_TEXT,其绘图单位为像素(只要不打印输出,屏幕绘图使用该模式就够了)。若窗口客户区的宽和高分别为w和h像素,则其x坐标是从左到右,范围为0 ~ w-1;y坐标是从上到下,范围为0 ~ h-1,参见图8-7。
图8-7 默认的MM_TEXT 图8-8 非自定义且非MM_TEXT映射映射模式下的窗口坐标系模式下的(初始)窗口坐标系
(注意y在当前窗口中的坐标值为负)2.设置映射模式
可使用CDC类的成员函数GetMapMode和SetMapMode来获得和设置当前映射模式:int GetMapMode( ) const; // 返回当前的映射模式
virtual int SetMapMode( int nMapMode ); // 返回先前的映射模式
映射模式nMapMode的取值见表8-7。
表8-7 映射模式的nMapMode取值与含义
符号常量数值x方向y方向逻辑单位
MM_TEXT 1 向右向下像素
MM_LOMETRIC 2 向右向上0.1 mm
MM_HIMETRIC 3 向右向上0.01 mm
MM_LOENGLISH 4 向右向上0.01 in
MM_HIENGLISH 5 向右向上0.001 in
MM_TWIPS 6 向右向上1/1440 in
MM_ISOTROPIC 7 自定义自定义自定义
MM_ANISOTROPIC 8 自定义自定义自定义
其中,in为英寸,1 in = 2.54 cm;twips(缇)是一种打印标准单位;isotropic(各向同性的),指x与y方向的单位相同;和anisotropic(各向异性的),则是指x与y方向的单位可不同。
可见,除了两种自定义映射模式外,x方向都是向右,y方向也只有MM_TEXT的向下,其余的都是向上,与数学上坐标系一致,参见图8-8。除了MM_ANISOTROPIC外,其他所