GDI编程.docx
《GDI编程.docx》由会员分享,可在线阅读,更多相关《GDI编程.docx(24页珍藏版)》请在冰豆网上搜索。
![GDI编程.docx](https://file1.bdocx.com/fileroot1/2023-1/7/f1858741-d29b-423b-8e40-ecc6634efcde/f1858741-d29b-423b-8e40-ecc6634efcde1.gif)
GDI编程
GDI编程
一、GDI相关概念
1、GDI(Graphics DeviceInterfase):
图形设备接口,是一个应用程序与输出设备之间的中介。
它提供了一套函数库,这些函数在不同的输出设备上输出图形和文字。
一方面,GDI向应用程序提供一个与设备无关的编程环境,另一方面,它又以设备相关的格式和具体的设备打交道。
2、DC(DeviceContext):
设备描述表(设备上下文),是一种Windows数据结构,包括了如线的宽度和颜色、刷子的样式和颜色、字体、剪裁区域等信息。
所有的绘制操作通过一个设备描述表进行,绘制线条、形状和文本的WindowsAPI函数都与DC有关。
设备可以是显示器,打印机等。
Win32下获取DC的API函数有:
HDCBeginPaint(HWNDhwnd,LPPAINTSTRUCTlpPaint):
特定用于WM_PAINT消息
HDCGetDC(HWNDhWnd):
用于获得hWnd参数所指定窗口的客户区域的HDC。
HDCGetWindowDC(HWNDhWnd):
返回hWnd参数所指定的窗口的HDC,包括非客户区,如标题栏、菜单、滚动条,以及边框等。
hWnd为NULL时,获取整个屏幕的HDC。
3、GDI对象:
DC定义了一组GDI对象,包括画笔,画刷,字体,位图,调色板,剪裁区域,路径层(Path)。
他们有MFC和Win32两套实现版本,其对应关系如下:
要使用这些对象,可以使用Win32函数SelectObject来将其选入DC中:
HGDIOBJSelectObject(
HDChdc,//设备上下文句柄
HGDIOBJhgdiobj//GDI对象句柄
);
当然,使用之前,这些对象必须存在,可以通过如下Win32函数来创建这些对象:
或者,通过Win32函数HGDIOBJGetStockObject(intfnObject)来获取系统预先定义好的如下备用对象:
fnObject参数含义
二、设备描述表DC在MFC中的实现
MFC提供了CDC类作为设备描述表类的基类,它封装了Windows的HDC设备描述表对象和相关函数。
CDC类包含了各种类型的Windows设备描述表的全部功能,封装了所有的Win32GDI函数和设备描述表相关的SDK函数。
在MFC下,使用CDC的成员函数来完成所有的窗口绘制工作。
CDC类有两个成员变量:
m_hDC,m_hAttribDC,它们都是Windows设备描述表句柄。
CDC的成员函数作输出操作时,使用m_Hdc;要获取设备描述表的属性时,使用m_hAttribDC。
在创建一个CDC类实例时,缺省的m_hDC等于m_hAttribDC。
CDC在封装Win32函数SelectObject(HDChdc,HGDIOBJECThgdiobject)时,采用了重载技术,即它针对不同的GDI对象,提供了如下名同而参数不同的成员函数:
SelectObject(CPen*pen)//用于选入笔
SelectObject(CBitmap*pBitmap)//用于选入位图
SelectObject(CRgn*pRgn)//用于选入剪裁区域
SelectObject(CBrush*pBrush)//用于选入刷子
SelectObject(CFont*pFont)//用于选入字体
SelectPalette(CPalette*pPalette,BOOLbForceBackground)//选入调色板到DC
RealizePalletter()//实现逻辑调色板到物理调色板的映射
直接使用CDC的例子是内存设备上下文,例如:
CDCdcMem.CreateCompatibleDC(&dc);//创建设备描述表
CDCpbmOld=dcMem.SelectObject(&m_bmBall);//更改设备描述表属性
//作一些绘制操作
dcMem.SelectObject(pbmOld);//恢复设备描述表的属性
dcMem.DeleteDC();//可以不调用,而让析构函数去删除设备描述表
从CDC派生出四个功能更具体的DC类。
继承层次如下图所示:
下面分别讨论这四种设备描述表。
●CCientDC:
代表窗口客户区的设备描述表。
其构造函数CClientDC(CWnd*pWin)通过:
:
GetDC获取指定窗口的客户区的设备描述表HDC,并且使用成员函数Attach把它和CClientDC对象捆绑在一起;其析构函数使用成员函数Detach把设备描述表句柄HDC分离出来,并调用:
:
ReleaseDC释放设备描述表HDC。
●CPaintDC:
仅仅用于响应WM_PAINT消息时绘制窗口,因为它的构造函数调用了:
:
BeginPaint获取设备描述表HDC,并且使用成员函数Attach把它和CPaintDC对象捆绑在一起;析构函数使用成员函数Detach把设备描述表句柄HDC分离出来,并调用:
:
EndPaint释放设备描述表HDC,而:
:
BeginPaint和:
:
EndPaint仅仅在响应WM_PAINT时使用。
例如,MFC中CView对WM_PAINT消息的实现方法如下:
voidCView:
:
OnPaint()
{
CPaintDCdc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
●CMetaFileDC:
用于生成元文件。
●CWindowDC:
代表整个窗口区(包括非客户区)的设备描述表。
其构造函数CWindowDC(CWnd*pWin)通过:
:
GetWindowDC获取指定窗口的客户区的设备描述表HDC,并使用Attach把它和CWindowDC对象捆绑在一起;其析构函数使用Detach把设备描述表HDC分离出来,调用:
:
ReleaseDC释放设备描述表HDC。
三、Win32环境下绘图的基本过程
●获取或者创建设备描述表;
●必要的话,改变设备描述表的属性;
●使用设备描述表完成绘制操作;
●释放或删除设备描述表。
第一种绘图方式是对WM_PAINT消息的处理
voidCAaView:
:
OnPaint()
{
CPaintDCdc(this);//得到绘图DC
dc.TextOut(100,100,"HelloWorld");
}
或者
voidCAaView:
:
OnDraw(CDC*pDC)
{
pDC->TextOut(100,100,"HelloWorld");
}
上面的程序可以在窗口的100,100位置处,打印HelloWorld字符串。
那么什么时候会产生WM_PAINT消息呢?
由于Windows是一个多任务环境,某个应用程序的窗口上面可能被对话框或窗口覆盖,当撤消这些对话框或窗口时,这个应用程序窗口中就有一个"空洞",这个"空洞"就是一块无效的用户区域。
为重新显示无效用户区域,Windows发送WM_PAINT消息实现。
要求Windows发送WM_PAINT的情况有:
改变窗口大小,覆盖用户区的菜单或对话框关闭,使用UpdateWindow和ScrollWindow函数等。
Windows发送WM_PAINT消息时,把它放到应用程序队列的最后,使得其它的输入能够先于WM_PAINT消息被处理。
GetMessage函数也得到队列中WM_PAINT消息之后的其它消息,即只有有没有其它消息的情况下,才从队列中取出WM_PAINT消息进行处理。
这样做是为了让应用程序首先完成影响窗口显示结果的其它操作,不致因为频繁地执行输出操作而引起显示器的闪烁。
Windows把WM_PAINT消息放在队列最后就是这个原因。
Windows并非WM_PAINT消息的唯一来源,使用InvalidateRect或InvalidateRgn函数也可以产生绘图窗口的WM_PAINT消息。
这两个函数把用户区全部或部分标记成无效用户区而要求重新显示。
下面的函数调用是把整个用户区标记成无效:
InvalidateRect(hWnd,NULL,TRUE);
上面代码把hWnd句柄参数指定的窗口用户区标记成无效。
作为矩形结构的NULL参数指定整个用户区,TRUE参数表示擦除背景。
第二种绘图的方式是在非OnDraw/OnPaint中绘图
voidCAaView:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
CClientDCdc(this);
dc.Ellipse(point.x-50,point.y-50,point.x+50,point.y+50);
}
这段程序实现了:
以鼠标的当前位置为圆心,画一个半径为50的圆。
基本的画线函数有以下几种
CDC:
:
MoveTo(intx,inty);改变当前点的位置
CDC:
:
LineTo(intx,inty);画一条由当前点到参数指定点的线
CDC:
:
BOOLArc(LPCRECTlpRect,POINTptStart,POINTptEnd);画弧线
CDC:
:
BOOLPolyline(LPPOINTlpPoints,intnCount);将多条线依次序连接
基本的作图函数有以下几种:
CDC:
:
BOOLRectangle(LPCRECTlpRect);矩形
CDC:
:
RoundRect(LPCRECTlpRect,POINTpoint);圆角矩形
CDC:
:
Draw3dRect(intx,inty,intcx,intcy,
COLORREFclrTopLeft,COLORREFclrBottomRight);3D边框
CDC:
:
Chord(LPCRECTlpRect,POINTptStart,POINTptEnd);扇形
CDC:
:
Ellipse(LPCRECTlpRect);椭圆形
CDC:
:
Pie(LPCRECTlpRect,POINTptStart,POINTptEnd);
CDC:
:
Polygon(LPPOINTlpPoints,intnCount);多边形
对于矩形,圆形或类似的封闭曲线,系统会使用画笔绘制边缘,使用刷子填充内部。
如果你不希望填充或是画出边缘,你可以选入空(NULL_PEN)笔或空(NULL_BRUSH)刷子。
多边形和剪贴区域
CreateRectRgn由矩形创建一个多边形
CreateEllipticRgn由椭圆创建一个多边形
CreatePolygonRgn创建一个有多个点围成的多边形
PtInRegion某点是否在内部
CombineRgn两个多边形相并
EqualRgn两个多边形是否相等
基本的绘图函数
CDC类中提供各种各样的输出操作,从画线到写字应有尽有。
为了画线、矩形、圆、扇形和写字,可相应地调用一些函数。
这些函数使用已选择的画笔和画刷,来画出边框,并填写图形内部区域,以及使用已选择的字体写字。
●画点函数SetPixel
COLORREFCDC:
:
SetPixel(intx,inty,COLORREFcclrref);
该函数把x和y指定的点置为clrref指定的颜色。
●画线函数LineTo与移动函数MoveTo
LineTo函数用来画线,并且通常与MoveTo函数配合使用,如画一条从点(10,70)到点(250,100)的线:
MoveTo(10,70);
LineTo(250,100);
●画矩形函数Rectangle
Rectangle函数用来画矩形。
它使用已选择的画笔画出边框,使用已选择的刷子填满矩形内部。
下面的例子画一个左上角位于点(10,20),右下角位于点(40,100)的矩形:
Rectangle(10,20,40,100);
●画圆或椭圆函数Ellipse
Ellipse函数用来画圆或椭圆。
它使用已选择的笔画框,使用已选择的刷填满圆或椭圆的内部。
下面的例子画一个用点(10,20)和点(40,100)构成矩形框中的椭圆:
Ellipse(10,20,40,100);
●画圆弧函数Arc
Arc函数用来画一段弧,这段弧由包围它的矩形和弧的开始点和结束点共同定义。
下面的例子在点(10,90)和点(360,120)所指定的矩形中画一段弧,它的起点和终点分别是点(15,90)和点(360,90):
Arc(10,90,360,120,15,90,360,110);
弧的起点坐标和终点坐标精确地位于弧上。
●画扇形函数Pie
Pie函数用来画扇形。
扇形由一段弧和两条从弧焦点到弧端点的半径组成。
Pie函数使用已选择的笔画框,使已选择的刷填满扇形内部。
下面的例子画一个用点(310,30)和点(360,80)构成的矩形围成的扇形。
其起点和终点分别为点(360,30)和点(360,80):
Pie(310,30,360,80,310,30,360,80);
弧的起点和终点不必精确地位于弧线上。
四、GDI对象
前面的程序只能画基本的图形,我们不能改变线条的颜色,线条的大小,不能填充颜色,也不能改变字体,显示一张位图等。
要实现这些功能,我们就要使用GDI对象。
这里,需强调一下:
GDI对象要选入Windows设备描述表后才能使用;用毕,要恢复设备描述表的原GDI对象,并删除该GDI对象。
一般按如下步骤使用GDI对象:
a、先创建或得到一个GDI对象
b、然后把它选入设备描述表
c、绘图
d、恢复设备描述表原来的GDI对象并删除刚新创建的GDI对象。
MFCGDI对象
MFC用一些类封装了WindowsGDI对象和相关函数,层次结构如图所示:
(1)画笔
画笔决定了线条的颜色、宽度和线型(实线、点线或点划线等)。
Windows使用当前在设备描述表中已选择的画笔来画线。
程序中可以选择Windows的予定义画笔,也可以选择自定义的画笔。
预定义画笔有三种:
BLACK_PEN(黑色笔)、WHITE_PEN(白色笔)和NULL_PEN(空笔),这些都在windows.h中已经定义好了,程序员可使用GetStockObject函数来选择其中的一种,系统缺省的画笔为黑色笔。
Windows.h包含了HPEN的数据类型定义,使用该类型可以定义画笔句柄的变量。
仅靠系统提供的预定义画笔远远不能满足需求,应用程序可根据实际需要创建一种自定义的逻辑画笔。
其步骤一般为:
首先用CreatePen或CreatePenIndirect函数建立一支画笔,再调用SelectObject函数将其选入设备描述表,此后就可使用该画笔在选定的设备描述表中进行绘图操作。
任何时候某一设备描述表只能有一支画笔被选入作为当前画笔,当一支画笔被选入时,原先已选入的画笔便不再有效。
完成绘图操作后,可以通过调用DeleteObject来释放已建立的画笔。
*函数CreatePen()
语法:
HPENCreatePen(intfnPenStyle,intnWidth,COLORREFclrref);
说明:
该函数创建一个逻辑画笔。
其中
fnPenStyle参数指定画笔的线型,该参数可取由windows.h定义的七个标识符之一,其含义为:
PS_SOLID实线
PS_DASH虚线
PS_DOT点线
PS_DASHDOT夹一点虚线
PS_DASHDOTDOT夹二点虚线
PS_NULL无
PS_INSIDEFRAME线画在所有构件框架内
nWidth参数是用逻辑单位表示的画笔的宽度;
clrref参数是一个COLORREF类型的颜色值,指定画笔的颜色,可用宏指令RGB构造这个值,如:
clrref=RGB(byRed,byGreen,byBlue);
在使用CreatPen函数时,要检查其返回值,确保它是一个有效的句柄。
下面给出一段程序,说明建立、选择和释放画笔的一般方法,假定程序要用一支宽度为3的黑色作图,则程序如下:
CPen*p_Pen;
CDCdc;
p_Pen->CreatePen(PS_SOLD,3,RGB(0,0,0));
if(p_Pen)
{
dc.SelectObject(p_Pen);
//…//这里进行绘图操作
}
Deletep_Pen;//删除hPen画笔,释放空间
*函数CreatePenIndirect()
语法:
HPENCreatePenIndirect(LOGPENFAR*lpLogPen);
说明:
该函数用lpLogPen所指的LOGPEN结构中的信息创建一个逻辑画笔。
LOGPEN的结构如下:
typedefstructtagLOGPEN(
WORDlopnStyle;
POINTlopnWidth;
COLORREFlopnColor;
)LOGPEN;
其中lopnStyle指定画笔线型,该参数可取下列值之一:
PS_SOLID0
PS_DASH1
PS_DOT2
PS_DASHDOT3
PS_DASHDOTDOT4
PS_NULL5
PS_INSIDEFRAME6
nWidth参数是用逻辑单位表示的画笔的宽度
clrref参数是一个COLORREF类型的颜色值,指定画笔的颜色,可用宏指令RGB构造这个值。
(2)刷子
当我们在绘制一些区域图形时,其内部往往需要以某种图案进行填充,这就需要选定"刷子"作为绘图工具。
Windows系统不仅为用户提供了预定义刷子,而且还允许应用程序自定义刷子。
Windows系统中预定义的刷子有如下七种:
BLACK_BRUSH黑色刷子
DKGRAY_BRUSH深灰色刷子
GRAY_BRUSH灰色刷子
HOLLOW_BRUSH中空刷子,画边界而不填充
LTGRAY_BRUSH浅灰色刷子
NULL_BRUSH空刷子
WHITE_BRUSH白色刷子
应用程序可以调用GetStockObject函数选用其中一个,系统缺省的刷子是白色刷子。
Window.h包含了HBRUSH数据类型的定义,使用该类型就可定义刷子句柄的变量。
仅靠这七种刷子往往不能满足要求,应用程序通过调用如下几种函数创建逻辑刷子,这些函数返回值均为刷子句柄。
*函数CreateHatchBrush()
语法:
HBRUSHCreateHatchBrush(intfnStyle,COLORREFclrref);
说明:
该创建一个带阴影的逻辑刷子。
FnStyle指定的阴影格式如下:
HS_BDLAGONAL45度向上斜线组成的阴影图案(自左到右)
HS_CROSS水平和垂直交叉组成的阴影图案
HS_DIAGCROSS45度斜线交叉组成的阴影图案
HS_FDIAGONAL45度向下斜线组成的阴影图案(自左到右)
HS_HORZONA水平线组成的阴影图案
HS_VERTICAL垂直线组成的阴影图案
Clrref是具有COLORREF类型定义的刷子颜色值,可用宏指令RGB构造这个值。
*函数CreateSolidBrush()
语法:
HBRUSHCreateSolidBrush(COLORREFclrre);
说明:
该函数创建的是一种实心颜色的逻辑刷子。
clrre含义同上。
同样,使用创建刷子的函数时,要检查其返回,确保它是一个有效的句柄。
一旦创建了绘图工具之后,可以SelectObject函数把它选择到显示缓冲区里。
在使用显示缓冲区之前,并不一定非要创建和选择绘图工具,Windows为每个显示缓冲区提供默认的绘图工具。
例如:
黑色笔,白色刷子和系统字体。
DeleteObject函数用来删除不再需要的绘图工具,但不能删除一个已选进显示缓冲区的绘图工具,而是应该使用SelectObject函数恢复原有的绘图工具,然后再删除需要删除的工具。
(3)填充图形
绘制一些需要以某种图案进行填充的区域图形时,需要选定"刷子"作为绘图工具。
Windows系统中预定义的刷子有七种,应用程序可以调用GetStockObject函数选用其中一个,系统缺省的刷子是白色刷子。
当靠这七种刷子不能满足要求时,应用程序通过调用Windows函数创建逻辑刷子,这些函数返回值均为刷子句柄。
(4)文字与字体
Windows是使用定义好的与设备无关的字符集,Windows的"文本"字符也是图形,所以屏幕上所显示的用打印机或绘图仪等输出品的文本完全一样,做到"所见即所得"。
文本绘制函数有:
TextOut以当前的字体写一字符串
DrawText在一个特定矩形区中绘制某一格式的文本
ExtTextOut在一个特定矩形区中,以当前字体写一字符串
GrayString用灰色文本写一字符串
TabbedTextOut写一带扩展字符的字符串
要输出文本就离不开字体。
获取字体的相关信息可以使用函数:
BOOLGetTextMetrics(LPTEXTMETRIClpMetrics)
结构TEXTMETRIC的定义如下所示:
typedefstructtagTEXTMETRIC{//tm
LONGtmHeight;//字符高度
LONGtmAscent;//字符上部高度(基线以上)
LONGtmDescent;//字符下部高度(基线以下)
LONGtmInternalLeading;//由tmHeight定义的字符高度的顶部空间数目
LONGtmExternalLeading;//加在两行之间的空间数目
LONGtmAveCharWidth;//平均字符宽度
LONGtmMaxCharWidth;//最宽字符的宽度
LONGtmWeight;//字体的粗细轻重程度
LONGtmOverhang;//加入某些拼接字体上的附加高度
LONGtmDigitizedAspectX;//字体设计所针对的设备水平方向
LONGtmDigitizedAspectY;//字体设计所针对的设备垂直方向
BCHARtmFirstChar;//为字体定义的第一个字符
BCHARtmLastChar;//为字体定义的最后一个字符
BCHARtmDefaultChar;//字体中所没有字符的替代字符
BCHARtmBreakChar;//用于拆字的字符
BYTEtmItalic;//字体为斜体时非零
BYTEtmUnderlined;//字体为下划线时非零
BYTEtmStruckOut;//字体被删去时非零
BYTEtmPitchAndFamily;//字体间距(低4位)和族(高4位)
BYTEtmCharSet;//字体的字符集
}TEXTMETRIC;
GDI字体族和字样
GDI字体族和字样表如下表所示:
字体族字体族常量字样说明
DontcareFF_DONTCARESystem当不能提供字体信息或字体并不
重要时使用
DecorativeFF_DECORATIVESymbol