在windows下绘图可以借助一些图形库用DrawLine和DrawCirccle这样的程序来画些基本图形元素,但是一旦出现新的标准就意味着要想办法支持最新的硬件,即使用升级版的图形库,将新代码加入自己的程序或给新视卡写驱动程序。对图形编程人员来说,其工作平台一直在变动,为了使程序与打印机和其他输出设备有适当的接口,还要做大量的工作。
通过给微机平台引入与硬件设备无冠的图形输出模式,windows改变了上述的被动局面,在windows中只要有相应的windows驱动程序,您所编写的代码就可以在任何图像适配器上运行,将输出发送到屏幕上的代码同样可用于打印机和其他硬拷贝设备上。也就是说,这种方法的优点就是不需要去为程序运行的具体硬件环境费心,而是去花时间编写应用程序代码上,也不需要第三方图形库。而windows中负责图形输出的是Graphics Device Interface(图形设备结构,简称GDI),能够实现一些简单的绘图操作。
windows程序在屏幕、打印机或其他输出设备上画图时,它并不是将像素直接输出到设备上,而是将图绘制到由设备描述表(DC)表示的逻辑意义上的“显示平面”上去。打个比方就是先在一块画布上画图,然后将画布贴到设备上。
画图流程是:1、画图之前windows程序从GDI获取设备描述表句柄;2、每次调用GDI输出函数时将句柄返回给GDI。若无有效的设备描述表句柄,则GDI不会画第一个像素点。
设备描述表类
MFC的CDC类将windows设备描述表和获取设备描述表句柄的GDI函数就近封装在一起,CDC的派生类则代表不同类型的设备描述表。
类名 | 描述 |
CPaintDC | 用于在窗口客户区画图(仅限于OnPaint处理程序) |
CClientDC | 用于在窗口客户区画图(除OnPaint处理程序外的任何处理程序) |
CWindowDC | 用于在窗口内任何地方画图,包括非客户区 |
CMetaFileDC | 用于向GDI元文件画图 |
CDC * pDC = GetDC();
// Do some drawing
ReleaseDC(pDC);
同样的程序代码出现在OnPaint程序中,则需用CWnd::BeginPaint和CWnd::EndPaint分别代替GetDC和ReleaseDC以保证合理地处理WM_PAINT消息
void CMFCApplicationDlg::OnPaint()
{
// 自动生成代码
PAINTSTRUCT ps;
CDC * pDC = BeginPaint(&ps);
// Do some drawing
EndPaint(&ps);
}
另外,CDC类可以直接进行实例化,构造函数和析构函数调用相应的函数捕获和释放设备描述表
在栈中实例化
CPaintDC dc(this);
// Do some drawing
在堆中实例化
CPaintDC * pDC = new CPaintDC(this)
// Do some drawing
delete pDC;
例如在接收到鼠标单击事件(WM_LBUTTONDOWN消息)时画一个X连接窗口客户区的四角代码如下
void CMFCApplication2Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CRect rect;
GetClientRect(&rect);
CClientDC dc(this);
dc.MoveTo(rect.left, rect.top);
dc.LineTo(rect.right, rect.bottom);
dc.MoveTo(rect.right, rect.top);
dc.LineTo(rect.left, rect.bottom);
}
如果需要使用非客户区(标题栏、窗口边框等),则可使用CWindowDC类。有时可以用CWindowDC类创造特殊效果,例如用户自己绘制标题栏和带圆角的窗口。一般情况下CWindowDC并不常用。如果想在非客户区作图,可以借助OnNcPaint处理程序捕获WM_NCPAINT消息确定非客户区需要绘制的时间。与OnPaint不同,OnNcPaint处理程序不需要也不应当调用BeginPaint和EndPaint。
更少见的场合是程序需要全屏幕的访问权,此时给构造函数传送NULL指针,例如
CClientDC dc(NULL);
dc.Ellipse(0, 0, 100, 100);
即在屏幕左上角画一个圆。截屏软件经常使用全屏DC访问整个屏幕。
设备描述表属性
当使用CDC输出函数在屏幕上画图时,输出的某些特性并没有在函数调用过程中规定,但可通过设备描述表自身获得。
Attribute | Default | Set with | Get with |
文本颜色 | Black | CDC::SetTextColor | CDC::GetTextColor |
背景颜色 | White | CDC::SetBkColor | CDC::GetBkColor |
背景模式 | OPAQUE | CDC::SetBkMode | CDC::GetBkMode |
映射模式 | MM_TEXT | CDC::SetMapMode | CDC::GetMapMode |
绘图模式 | R2_COPYPEN | CDC::SetROP2 | CDC::GetROP2 |
当前位置 | (0,0) | CDC::MoveTo | CDC::GetCurrentPosition |
当前画笔 | BLACK_PEN | CDC::SelectObject | CDC::SelectObject |
当前画刷 | WHITE_BRUSH | CDC::SelectObject | CDC::SelectObject |
当前字体 | SYSTEM_FONT | CDC::SelectObject | CDC::SelectObject |
画笔(Pen) 画刷(Brush) 字体(Font) 位图(Bitmap) 调色板(Palette) 区域(Region)
每当凑个windows中获取设备描述表时,设备描述表都被设置为默认值。如果不想反复对设备描述表进行初始化设定,那么可用CDC::SaveDC函数保存它的状态,并在下次使用时用CDC::RestoreDC将它恢复。另一种方法是注册一个自定义的WNDCLASS其中包含CS_OWNDC样式,CD_CLASSDC,它分配一个“半私有”设备描述表,该设备描述表可被同一WNDCLASS创建的所有窗口共享。当GDI对象被选入某个私有设备描述表后,如果没有被显示地替换,则它们依旧处于选择状态。
绘图模式
GDI将像素点输出到逻辑显示平面上时,它不只是简单地输出像素点颜色,而是通过一系列的布尔运算将输出像素点颜色和输出目标位置上像素点的颜色合成在一起。所使用的逻辑关系由设备描述表当前的绘图模式确定。默认绘图模式为R2_COPYPEN,它将像素点复制到显示平面上。
例如使用dc.SetROP2(R2_NOT);再用同样的颜色画同样的线就可以擦除之前画的线。
映射模式
映射模式是设备描述表的属性,用于确定从逻辑坐标值到设备坐标值的转换方式。传送给CDC输出函数的是逻辑坐标值。设备坐标值是指窗口中相应的像素点位置。
用dc.Rectangle(0, 0, 200, 100);调用Rectangle函数时不用告诉GDI画一个200个像素点宽,100个像素点高的矩形,而是告诉它画一个200个单位宽,100个单位高的矩形。在默认映射模式MM_TEXT下,一个像素点相当于一个单位。在其他映射模式中,逻辑单位被解释成不同的设备单位。