MFC主要实现了两种绘图类的封装,图形设备接口(GDI, Graphic Device Interface)类CGdiObject,主要用来创建基本的绘图对象,和设备环境(DC, Device Context)类CDC,用于提供映像函数,绘图工具函数,显示区域函数等,结合绘图对象完成绘图操作。设备环境是设备与数据之间的纽带。
1. 绘图原理
Windows操作系统不允许绘图应用程序直接访问设备。程序在屏幕,打印机或其他输出设备上绘图时,将图像绘制到由设备环境表示的“逻辑显示平面”上,这个“逻辑显示平面”也被称为“虚拟窗口”。最后,再由设备环境将“虚拟窗口”中的图形映射到具体设备之上。
Windows应用程序绘制图形的具体步骤:
A. 取得指定窗口的当前显示设备环境(比如,CClientDC dc(this), this是CWnd *);
B. 选择用户坐标系及映射方式(如上所述,Windows是将图像绘制在“逻辑显示平面”,这样就存在一个“逻辑显示平面坐标系”和设备实际坐标系, 映射模式就是将逻辑坐标系转换到设备坐标系的方法)
C. 设定用户坐标系中的观察者窗口和设备坐标系的显示视区(比如,CDC::SetWindowExt设定观察者窗口,CDC::SetViewportExt设置显示视区,把图像绘制到一个观察者窗口上,然后将观察者窗口显示到显示视区)
D. 输出图形(比如,CDC::Ellipse输出(显示)一个圆或者椭圆)
E. 释放所使用的显示设备环境
在MFC绘图程序中,首先创建针对此设备的DC对象,而每个DC对象又必须有GDI绘图对象的配合,才能使用CDC类的绘图函数实现绘图(比如,在调用CDC::LineTo绘制一条直线前,首先要设置一个画笔对象CDC::SelectObject(CPen*),来设置线的颜色,宽度,虚实线等)
2. GDI对象
GDI用来创建绘图对象,即用来绘制图形的对象(里面包含了图形的特性和风格),然后被设备环境使用绘制图形(比如,在调用CDC::LineTo绘制一条直线之前,首先要创建一个CPen,然后将一个画笔对象设置给设备环境CDC::SelectObject(CPen*),设置线的颜色,宽度,虚实线等)。这些绘图对象和输出设备的特性无关。
常用的GDI对象,包括画笔,画刷,字体,位图,区域,调色板等。
Windows的绘图对象基类CGdiObject,继承关系
CObject
|---CGdiObject, 绘图对象基类
|---类CPen,封装画笔对象
|---类CBrush,封装画刷对象
|---类CFont,封装字体对象
|---类CRgn,封装区域对象
|---类CBitbmp,封装位图对象
|---类CPalette,封装调色板
1)画笔,画刷,字体和区域
(1)画笔(CPen)
画笔是用来绘制直线,弧线或者边框的绘图对象,可以指定它的颜色,宽度,虚线或者实线等。
(2)画刷(CBrush)
画刷定义了一种位图形式的像素,可以被用于填充封闭图形的内部区域,默认的画刷是将封闭图形的内部填充为白色。画刷的特性:影线的样式,画刷的颜色和位图画刷(比如,myBrush.CreateHatchBrush(HS_FDIAGONAL, RGB(255, 0, 0))创建了一个影线样式为“左上向右下的45度斜线”,画刷颜色为RGB(255, 0, 0)的画刷)。
(3)字体(CFont)
字体是一种具有某种风格和尺寸的所有字符的集合。
(4)区域(CRgn)
区域是指多边形,椭圆或者两者组合而成的图形范围,它可以被填充,裁剪,反转及鼠标击中测试。
2)调色板(CPalette)
调色板是一种颜色映射接口,它的存在允许应用程序充分利用设备的颜色描绘能力完成绘图操作。
(1)颜色
一张图的颜色可定义为单色(monochrome),16色(EGA),256色(VGA),65536色(Hi color)或16777216色(True color)。颜色的种类是由每种颜色具有的位数决定的。
显示器显示的图像是由像素组成的,每一个像素都有自己的颜色属性。像素的颜色基于RGB模型:颜色由红(R),绿(G),蓝(B)三原色组合而成。每种原色用8位表示。
(2)颜色的显示原理
从上图可以看出,图像要通过显卡才能显示,显卡的内存大小限制图像的颜色分辨率。比如,一个只能容许256色分辨率的显卡,要完成多于256色分辨率的图像显示,就必须要把图像的色彩映射到256色,并创建一个256色的颜色表,及系统调色板。
3)位图(CBitmap)
位图(Bitmap)是一种功能非常强大的图形对象,运用它可以建立和操作图像,并且可以把图像作为文件存储在磁盘上
(1)位图概述
位图一种数字化的图像表示形式,实际上是一个像素值阵列。
(2)位图类型
位图有两种类型:设备有关位图(DDB, Device-dependent Bitmap)和设备无关位图(DIB, Device-independent Bitmap)
DDB位图
A. DDB的颜色模式必须与输出设备一致
B. 在256色以下的位图中存储的像素值是系统调色板的索引,其颜色依赖于系统调色板。
BITMAP结构,用于存储CBitmap加载的位图图像信息
typedef struct tagBITMAP{
int bmType; //位图种类
int bmWidth; //图像宽度(像素)
int bmHeight;//图像高度(像素)
int bmWidthBytes; //图像宽度字节数
BYTE bmPlanes; //色平面数量
BYTE bmBitsPixel;// 色平面的颜色位数
LPVOID bmBits; // 指向存数像素阵列的数组
}BITMAP;
DIB位图
A. DIB的颜色模式与设备无关
B. 256色以上(包括256色)的DIB拥有自己的颜色表,像素颜色独立于系统调色板
(3)位图格式
位图文件由3个包含图像信息的结构和一组图像数组组成。
BITMAPFILEHEADER结构与文件自身信息有关
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 文件格式标识
DWORD bfSize; // 位图文件大小
WORD bfReserved1; // 预留,为0
WORD bfReserved2; // 预留,为0
DWORD bfOffBits; // 图像位置
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
WORD bfType; // 文件格式标识
DWORD bfSize; // 位图文件大小
WORD bfReserved1; // 预留,为0
WORD bfReserved2; // 预留,为0
DWORD bfOffBits; // 图像位置
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
BITMAPINFOHEADER结构定义位图参数
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 结构大小
LONG biWidth; // 图像宽度(像素)
LONG biHeight; // 图像高度(像素)
WORD biPlanes; // 色平面数量,为1
WORD biBitCount; // 像素的位数
DWORD biCompression; // 压缩方式
DWORD biSizeImage; // 图像数据大小
LONG biXPelsPerMeter; // 以目标设备每米的像素数来说明位图的水平分辨率
LONG biYPelsPerMeter; // 以目标设备每米的像素数来说明位图的垂直分辨率
DWORD biClrUsed; // 颜色表中的颜色数量
DWORD biClrImportant; // 重要颜色数目
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
DWORD biSize; // 结构大小
LONG biWidth; // 图像宽度(像素)
LONG biHeight; // 图像高度(像素)
WORD biPlanes; // 色平面数量,为1
WORD biBitCount; // 像素的位数
DWORD biCompression; // 压缩方式
DWORD biSizeImage; // 图像数据大小
LONG biXPelsPerMeter; // 以目标设备每米的像素数来说明位图的水平分辨率
LONG biYPelsPerMeter; // 以目标设备每米的像素数来说明位图的垂直分辨率
DWORD biClrUsed; // 颜色表中的颜色数量
DWORD biClrImportant; // 重要颜色数目
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
RGBQUAD结构定义了位图的颜色表
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 蓝色强度
BYTE rgbGreen; // 绿色强度
BYTE rgbRed; // 红色强度
BYTE rgbReserved; // 保留字节,为0
} RGBQUAD;
BYTE rgbBlue; // 蓝色强度
BYTE rgbGreen; // 绿色强度
BYTE rgbRed; // 红色强度
BYTE rgbReserved; // 保留字节,为0
} RGBQUAD;
3. 设备环境(DC, Device Context)
设备环境(Device Context)是设备和数据之间的媒介,通过设备环境,Windows应用程序把图形反应在屏幕,打印机或者其他输出设备上。
每个DC都要由GDI对象配合,即绘图之前必须选择绘图工具,比如画笔--画线,封闭区域--画刷,字符--字体等等。只有选定了绘图工具DC对象才能使用DC绘图函数。
1)DC属性
设备环境,可以看成一个用来确认任何设备的GDI输出的位置和形象的属性集合;也可以看成一个图形的输出模板,依靠这个模板,当用户调用CD函数输出图形或者文字时,不必关心背景颜色,字体等问题。
2)DC类
设备环境类CDC,用于提供映像函数,绘图工具函数,区域函数等,结合绘图对象完成绘图操作
CObject
|--CDC
|-----类CPaintDC,构造和析构窗口设备环境,用于窗口客户区绘图,仅限于OnPaint函数
|-----类CClientDC,窗口客户区设备环境操作,用于窗口客户区绘图,除了OnPaint函数外
|-----类CWindowsDC,整个窗口设备环境操作,用于窗口内任何地方绘图
|-----类CMetaFileDC,Windows元文件设备环境操作,用于向GDI元文件绘图
(1)类CClientDC
用于窗口客户区绘图,除了OnPaint函数外。构造此DC时会调用函数CWnd::GetDC以获取一个代表窗口客户区设备环境的指针。
(2)类CMetaFileDC
用于向GDI元文件绘图。构造函数是一个空函数,必须在调用初始化方法的Create函数,才能实现此DC的创建,函数Close用来结束此DC的使用。
(3)类CPaintDC
用于窗口客户区绘图,仅限于OnPaint函数。与类CClientDC不同,在构造此DC的时候会调用函数CWnd::BeginPaint准备绘图,同时建立一个PAINTSTRUCT结构以存储窗口信息:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
当Windows发送消息WM_PAINT时候,窗口调用消息响应函数OnPaint,建立此DC对象实现绘图。在此DC析构的时候,应用程序调用CWnd::EndPaint函数结束绘图
(4)类CWindowDC
用于窗口内任何地方绘图,除了窗口客户区外,还包含诸如菜单区,状态区,边框等等。
与CClientDC不同,此构造此DC的时候会调用函数CWnd::GetWindowDC以获取整个窗口的设备环境指针。绘图完成后,调用函数CWnd::ReleaseDC结束窗口设备环境的使用,完成DC的析构。
3)CDC基本绘图函数
(1)点,直线与曲线
绘制点的函数
COLORREF SetPixel(int x, int y, COLORREF crColor);
COLORREF SetPixel(POINT point, COLORREF crColor);
COLORREF SetPixel(POINT point, COLORREF crColor);
常用绘制直线的函数
CPoint MoveTo(int x, int y);
CPoint MoveTo(POINT point);
BOOL LineTo(int x, int y);
BOOL LineTo(POINT point);
BOOL Polyline(const POINT* lpPoints, int nCount);
CPoint MoveTo(POINT point);
BOOL LineTo(int x, int y);
BOOL LineTo(POINT point);
BOOL Polyline(const POINT* lpPoints, int nCount);
BOOL PolylineTo(const POINT* lpPoints, int nCount);
常用绘制曲线的函数
BOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
BOOL Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd);
BOOL Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd);
BOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
BOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd);
BOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd);
BOOL PolyBezier(const POINT* lpPoints, int nCount);
BOOL PolyBezierTo(const POINT* lpPoints, int nCount);
BOOL PolyBezierTo(const POINT* lpPoints, int nCount);
(2)圆,矩形,多边形与其他图形与效果
常用绘制圆或者椭圆的函数
BOOL Ellipse(int x1, int y1, int x2, int y2);
BOOL Ellipse(LPCRECT lpRect);
BOOL Ellipse(LPCRECT lpRect);
常用绘制矩形的函数
直角矩形
BOOL Rectangle(int x1, int y1, int x2, int y2);
BOOL Rectangle(LPCRECT lpRect);
BOOL Rectangle(LPCRECT lpRect);
圆角矩形
BOOL RoundRect(int x1, int y1, int x2, int y2, int x3, int y3);
BOOL RoundRect(LPCRECT lpRect, POINT point);
BOOL RoundRect(int x1, int y1, int x2, int y2, int x3, int y3);
BOOL RoundRect(LPCRECT lpRect, POINT point);
常用绘制多边形的函数
BOOL Polygon(const POINT* lpPoints, int nCount);
函数Chord用于绘制椭圆和直线相交后围成的封闭图形
函数Pie用于绘制饼状的楔形物
3)文本
virtual BOOL TextOut(int x, int y, LPCTSTR lpszString, int nCount);
BOOL TextOut(int x, int y, const CString& str);
virtual BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect,
LPCTSTR lpszString, UINT nCount, LPINT lpDxWidths);
BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect,
const CString& str, LPINT lpDxWidths);
virtual CSize TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount,
int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin);
CSize TabbedTextOut(int x, int y, const CString& str,
int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin);
BOOL TextOut(int x, int y, const CString& str);
virtual BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect,
LPCTSTR lpszString, UINT nCount, LPINT lpDxWidths);
BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect,
const CString& str, LPINT lpDxWidths);
virtual CSize TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount,
int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin);
CSize TabbedTextOut(int x, int y, const CString& str,
int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin);
4. 映射模式
映射模式(mapping mode)是设备环境的属性,用于确定从逻辑坐标值到设备坐标值的转换方式,用户可不必考虑输出设备的坐标情况,而在统一的逻辑坐标系中进行图形的绘制和操作。
应用程序支持两类坐标系统:设备坐标系统和逻辑坐标系统。
设备坐标系统是物理设备的坐标系统,比如显示器坐标系统,包含了三种相互独立的坐标系统:屏幕坐标系统,窗口坐标系统和客户区坐标系统。设备坐标系统以像素为度量单位,x轴的正方向为右,y轴的正方向为下。
除了设备坐标系统,其他的就是逻辑坐标系统。映射模式指定了从逻辑坐标转换到设备坐标系的方法。
映射模式定义了将逻辑单位转化为设备单位,以及将逻辑x/y轴方向转化为设备的x/y轴方向。
映射模式中比较特别的是MM_ANISOTROPIC和MM_IOSTROPIC,这两种模式通过将图形从用户自定义的逻辑坐标窗口映射到设备的视口以实现坐标转换,且允许自定义x和y方向。比如
void CExArcView::OnDraw(CDC* pDC)
{
{
CExArcDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CClientDC dc(this);
CRect rc;
GetClientRect(&rc);
CRect rc;
GetClientRect(&rc);
// 设置映射模式
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetMapMode(MM_ANISOTROPIC);
// 设置用户自定义的逻辑坐标窗口
dc.SetWindowExt(500, 500);
dc.SetWindowExt(500, 500);
// 设置设备的视口
dc.SetViewportExt(rc.Width(), rc.Height());
dc.SetViewportExt(rc.Width(), rc.Height());
// 图形输出
dc.Ellipse(0, 0, 500, 500);
dc.Ellipse(0, 0, 500, 500);
}
5. 绘图模式
利用类CDC绘图函数在指定设备上进行图形绘制时,像素并不是简单的输出到逻辑显示平面上,而是通过一系列的布尔运算将输出像素点的颜色和输出目标位置上像素点的颜色合成在一起,所使用的逻辑关系由设备环境当前的绘图模式确定。绘图模式表示两个变量的可能布尔组合,主要使用二进制运算符(AND, OR, XOR和NOT)。
默认的绘图模式是R2_COPYPEN,将像素点复制到显示平面上。