GDI基础(1):绘制线条和图形
1、
绘制一个像素点:SetPixel()。
绘制直线:MoveTo(),LineTo()。
绘制多个首尾相连的线:Polyline()。
绘制矩形:FrameRect(),Rectangle(),FillRect() ,FillSolidRect()。
绘制一个四个角是弧形的矩形:RoundRect()。
绘制圆形或椭圆:Ellipse()。
绘制弧线:Arc(),ArcTo()。
绘制三角形或多边形:Polygon()。
绘制饼形图:Pie()。
对矩形或指定区域的像素颜色进行反转:InvertRect()、InvertRgn()。
以上函数如果没有参数用来指定的话则使用的是当前默认画笔和画刷,可以使用CDC::SelectObject()来改变当前画笔和画刷,而如果只是想改变画笔或画刷的颜色则使用SetDCPenColor()、SetDCBrushColor()即可。
Polyline(const POINT* lpPoints, int nCount)中的lpPoints是要连结的那些点的数组,nCount为点的个数。如果nCount为2的话其相当于MoveTo()和LineTo()绘制一条直线。
FrameRect()用来绘制一个矩形边框,边框颜色由其参数指定,矩形内部是透明的,相当于使用了透明画刷;
Rectangle()用来绘制带边框的矩形,边框颜色和类型使用当前DC默认画笔类型,矩形内部使用当前DC默认画刷,如果想要改变边框或画刷的颜色、类型,可以使用SelectObject()将指定画笔或画刷选入当前DC,也可以直接使用SetDCPenColor()、SetDCBrushColor()。
FillRect()用来绘制一块无边框的矩形,矩形内部使用指定画刷颜色或类型。
FillSolidRect()与FillRect类似,FillSolidRect只能使用固体色(COLORREF类型),但FillRect使用画刷,因此可以为矩形填充固体色、抖动色、阴影、位图或使用调色板,而且FillSolidRect通常比FillRect快。
以下为使用FrameRect、Rectangle、FillRect的对比:
Arc()与ArcTo()的区别在于,Arc函数只是绘出给定的弧线,不会对画笔位置产生影响;而ArcTo函数在工作时将会从画笔原来所在点(默认起始为0,0)开始绘制一条直线到弧线的开始点,绘画弧线完成后还会将画笔移动到弧线的终点,从而对画笔位置造成影响。下面是网上找的两个示例图:
2、
画笔CPen、画刷CBrush、字体CFont、位图CBitmap、调色板CPalette等都是GDI对象,通常在函数没有参数指定的情况下我们使用的是DC当前默认GDI对象,如在使用MoveTo/LineTo画线的时候线条的颜色是黑色实线,即当前DC默认画笔为黑色实线,使用Rectangle()画矩形的时候使用的是默认的白色画刷。我们可以使用CDC::SelectObject()来改变DC的默认GDI对象,该函数返回DC中被替换的GDI对象,我们应该保存这个旧的GDI对象,当我们不需要使用新的GDI对象的时候再使用CDC::SelectObject()将旧的GDI对象恢复到DC中。
还可以通过HGDIOBJ GetStockObject(int i)函数来获得系统标准的GDI对象,其参数可以为:NULL_BRUSH透明(空)画刷、SYSTEM_FONT系统字体、DEFAULT_PALETTE缺省调色板等。GetStockObject返回的类型为HGDIOBJ,所以在使用的时候还应该将其返回值强制转换为我们所使用的GDI句柄类型,如HBRUSH、HFONT、HPALETTE等。还可以使用各GDI类型的静态成员函数FromHandle()由这个GDI句柄获得其对象指针。比如CBrush::FromHandle()可以由画刷句柄获得画刷对象的指针,它是一个静态成员函数。同理,对于CPen、CFont、CBitmap、CPalette等GDI对象,甚至CDC和CWnd等也包含静态成员函数FromHandle()。实际上,MFC对各种包含内核对象的封装类都有FromHandle(HANDLE h)方法。
CDC类的成员函数GetSafeHdc(void)可以获得CDC对象的句柄。
下面是一个使用例子,它先将当前DC画刷设置为了系统透明画刷再使用Rectangle()来画矩形:
HBRUSH hNull_Brush = (HBRUSH)GetStockObject(NULL_BRUSH); CBrush *pBrush = CBrush::FromHandle(hNull_Brush); CBrush* pOldBrush = dc.SelectObject(pBrush); CRect rect(0, 0, 100, 100); dc.Rectangle(&rect); dc.SelectObject(pOldBrush);
pBrush->DeleteObject();
与前面的例子对比就可以看出效果:
3、
CPen是画笔类,用来在DC上完成绘制线条的任务,常用的构造函数:
CPen(Int style,int width ,COLORREF color);
style:画笔样式,可以为以下样式:
PS_SOLID 实线
PS_DASH 虚线,该值只有当画笔宽度等于1个设备单位或更小时才有效
PS_DOT 点线,该值只有当画笔宽度等于1个设备单位或更小时才有效
PS_DASHDOT 点和虚线交替,该值只有当画笔宽度等于1个设备单位或更小时才有效
PS_DASHDOTDOT 双点线和虚线交替,该值只有当画笔宽度等于1个设备单位或更小时才有效
PS_NULL 空画笔
PS_GEOMETRIC 几何画笔
.......
width:画笔宽度。
color:画笔颜色。
如果CPen在定义的时候没有被初始化,那么在使用之前应该调用其以下的初始化函数来进行初始化。
CreatePen()用指定的样式、宽度、颜色初始化画笔。
CreatePenIndirect()用结构LOGPEN 中指定的风格初始化画笔。
绘制连续曲线的例子:
void CMFC_testDlg::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if(nFlags == MK_LBUTTON)//鼠标左键按下标志 { CClientDC dc(this); CPen pen(PS_DOT, 1, RGB(255,0,0));//创建一个虚线线条,宽度为1,红色的画笔对象 CPen* pOldPen = dc.SelectObject(&pen);//将画笔对象选入到设备描述表中 dc.MoveTo(m_ptOrigin); dc.LineTo(point); m_ptOrigin = point;//修改线段的起点 dc.SelectObject(pOldPen);//恢复设备描述表
pen.DeleteObject(); } CDialogEx::OnMouseMove(nFlags, point); }
CDC::SetROP2(int nDrawMode)用来设置画笔绘画的模式,其参数可以为R2_NOTXORPEN、R2_NOT等绘图模式。R2_NOTXORPEN绘图模式就是先把画笔颜色与屏幕颜色异或,异或之后再取反最后得到一个颜色值显示在屏幕上,而这种做法就会产生一个效果:比如用画笔画了一条线,然后再用画笔在相同的位置画这条线就会擦除原来画的线。R2_NOT绘画模式同样有在同一个地方画两次相当于什么都没画的功能,不过R2_NOT绘画模式第一次画的时候显示颜色并不是你选定的画笔颜色,而是系统默认画笔颜色。
所以SetROP2()可以用来当做橡皮筋来使用。
4、
CBrush为画刷类,画刷通常用来填充一块区域,可以为普通固体色画刷,位图画刷,阴影线画刷等。常用的构造函数:
CBrush( COLORREF crColor ); //普通画刷
CBrush( int nIndex, COLORREF crColor ); //阴影线画刷
CBrush( CBitmap* pBitmap ); //位图画刷
crColor:画刷或阴影线的颜色
nIndex:阴影线的风格,有以下风格可选:
HS_HORIZONTAL 水平的阴影线
HS_VERTICAL 垂直的阴影线
HS_CROSS 水平和垂直方向以网格线作出阴影
HS_BDIAGONAL 45度的向下影线(从左到右)
HS_FDIAGONAL 45度的向上阴影线(从左到右)
HS_DIAGCROSS 45度的网格线阴影
同CPen一样,如果CBrush在定义的时候没有被初始化,那么在使用之前应该调用其以下的初始化函数来进行初始化:
CreateSolidBrush() 用指定的颜色初始化画刷。
CreateHatchBrush() 用指定的阴影线初始化画刷。
CreateBrushIndirect() 用结构LOGBRUSH中指定的风格、颜色和模式初始化画刷。
CreatePatternBrush() 用位图指定的模式初始化画刷。
CreateDIBPatternBrush() 用独立于设备的位图(DIB)初始化画刷。
CreateSysColorBrush() 创建一个使用系统缺省颜色的画刷。
使用阴影线画刷:
CPaintDC dc(this); CRect rect(10, 10, 100, 100); CBrush brush(HS_HORIZONTAL, RGB(255,0,0)); dc.FillRect(&rect, &brush); CRect rect2(120, 10, 210, 100); CBrush brush2(HS_CROSS, RGB(255,0,0)); dc.FillRect(&rect2, &brush2); CRect rect3(230, 10, 320, 100); CBrush brush3(HS_BDIAGONAL, RGB(255,0,0)); dc.FillRect(&rect3, &brush3);
brush.DeleteObject();
使用位图画刷:
//使用位图画刷来填充一块矩形区域 CClientDC dc(this); CBitmap bitmap; bitmap.LoadBitmapW(IDB_BITMAP1);//初始化位图对象 CBrush brush(&bitmap);//构造位图画刷 CRect rect(10, 10, 100, 100); dc.FillRect(&rect, &brush);
bimap.DeleteObject();
如果在绘图过程中要经常改变DC中的画刷或画笔得颜色,可以使用SetDCBrushColor()、SetDCPenColor()函数来改变DC的画刷或画笔的颜色,在使用这两个函数之前要先将系统画刷DC_BRUSH或画笔DC_PEN选到DC中去:
CPaintDC dc(this); dc.SelectObject(GetStockObject(DC_BRUSH)); dc.SelectObject(GetStockObject(DC_PEN)); //画圆,边框设为蓝色,内部为红色 dc.SetDCPenColor(RGB(0,0,255)); dc.SetDCBrushColor(RGB(255,0,0)); dc.Pie(35,320,300,600,56,470,60,360); //画饼,边框为红色,内部为蓝色 dc.SetDCPenColor(RGB(255,0,0)); dc.SetDCBrushColor(RGB(0,0,255)); dc.Ellipse(35, 100, 235, 300);
5、
CPen、CBrush、CBitmap、CFont等GID对象在使用结束后应该调用DeleteObject()函数用来释放相关资源,而且只有这样才能重新初始化使用。我觉得MFC中的GDI对象会在析构的时候自动调用DelectObject(),win32的话必须显示调用DelectObject()防止内存泄露,所以用完之后再delete是一个好习惯。关于CFont和CBitmap这两个GDI对象会在下面的文章中详解。
6、
Windows中显示是基于设备环境(DC)的,在使用GDI函数之前必须先获得窗口的DC。在Win32 SDK中绘图的话需要调用GetDC()(WM_PAINT消息响应代码块中为BeginPaint)获得指定窗口的DC,绘制结束后还要调用ReleaseDC()(EndPaint())来释放DC资源。MFC的设备环境类CDC封装了窗口的DC对象和绘图所需要的所有函数。CClientDC、CWindowDC、CPaintDC都是从CDC类派生而来,只要在初始化的时候传入窗口的指针那么就可以通过这些对象的方法来方便的对窗口进行绘制等操作,而且DC资源释放会在对象的析构函数中自动执行。还有一个CMetaFileDC类,它对图像的保存比像素更精确,因而往往在要求较高的场合下使用,例如AutoCAD的图形保存等。
CClientDC获得的是窗口客户区的DC,所以只能在客户区画图,其原点坐标为客户区左上角;
CWindowDC获得的是整个窗口的DC,包括标题栏、边框等,其原点坐标也是整个窗口的左上角;
CPaintDC只用在窗口重绘消息响应函数中;
GetDesktopWindow()函数可以获得桌面窗口的指针,所以想在当前屏幕上进行绘图操作可以类似这样:
CWindowDC dc(GetDesktopWindow());//获得与当前桌面窗口相关联的CWindowDC对象 dc.MoveTo(100, 100); dc.LineTo(100, 800);
7、
可以调用GradientFill()函数来绘制一块渐变色的矩形区域或三角形区域,eg:
void CMFCApplication9Dlg::OnPaint() { CPaintDC dc(this); TRIVERTEX vertex[2]; vertex[0].x = 0; vertex[0].y = 0; vertex[0].Red = 0x0000; vertex[0].Green = 0x8000; vertex[0].Blue = 0x8000; vertex[0].Alpha = 0x0000; vertex[1].x = 320; vertex[1].y = 80; vertex[1].Red = 0x0000; vertex[1].Green = 0xd000; vertex[1].Blue = 0xd000; vertex[1].Alpha = 0x0000; GRADIENT_RECT gRect; gRect.UpperLeft = 0; gRect.LowerRight = 1; dc.GradientFill(vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H); }
以上绘制的是水平渐变方式的矩形,也可以修改GradientFill()的最后一个参数为GRADIENT_FILL_RECT_V来实现垂直渐变的效果,如下图:
下面为绘制一个渐变三角形的示例:
void CMFCApplication9Dlg::OnPaint() { CPaintDC dc(this); TRIVERTEX vertex[3]; vertex[0].x = 150; vertex[0].y = 0; vertex[0].Red = 0xff00; vertex[0].Green = 0x8000; vertex[0].Blue = 0x0000; vertex[0].Alpha = 0x0000; vertex[1].x = 0; vertex[1].y = 150; vertex[1].Red = 0x9000; vertex[1].Green = 0x0000; vertex[1].Blue = 0x9000; vertex[1].Alpha = 0x0000; vertex[2].x = 300; vertex[2].y = 150; vertex[2].Red = 0x9000; vertex[2].Green = 0x0000; vertex[2].Blue = 0x9000; vertex[2].Alpha = 0x0000; GRADIENT_TRIANGLE gTriangle; gTriangle.Vertex1 = 0; gTriangle.Vertex2 = 1; gTriangle.Vertex3 = 2; dc.GradientFill(vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE); }