您的位置:首页 > 编程语言

windows基础应用程序编程(三):GDI简介

2014-08-11 21:09 274 查看
GDI即Graphics Device Interface图形设备接口,计算机的输出设备是由很大的不同的。即使仅对于显示器而言也存在着不同大小的尺寸等等区别。那么为了屏蔽这些区别所造成的程序编写内容的不同。于是,这个东西就诞生了,我们可以利用这个统一的接口,利用GDI为我们提供的绘图函数,来输出我们程序所要求的输出结果。

设备描述表

GDI中有一个很重要的概念,我们称之为设备描述表,简称为DC。实际上它是GDI内部所保存的一个数据结构,DC与特定的输出设备有关系。我们可以把它理解成沟通我们程序与具体的显示设备之间的桥梁。当我们需要在一个输出设备上输出图形时,我们首先必须要获得一个设备描述表的句柄。(还记得句柄就是一个数值,简答的用于标识windows中的某些东西吗?)当这个句柄返回给程序时,Windows函数就给了我们使用输出设备的权限。
那么我们该如何去获得这个设备描述表的句柄呢,一般来说,常见的获得设备描述表的方法有两种。(当然实际不止两种,我们这里仅介绍最常用的。)

第一种方法

通过我们上一篇文章的学习,我们了解到了WM_PAINT消息是“需要回复以前窗口时”即重画窗口时,所产生的消息。那么既然要重画窗口,根据之前所讲的,如果要在窗口上绘制,那么首先我们必须要获得一个设备描述表句柄,那么在处理WM_PAINT消息中,我们在哪里看到获取到设备描述表句柄了呢?还记得上一篇文章中在介绍WM_PAINT消息一般的处理过程时的内容吗?可能大家已经猜到了,是的,我们通过BeginPaint函数就可以或得一个设备描述表句柄。我们首先来看看这个函数的原型。
HDC BeginPaint( HWND hwnd, LPPAINTSTRUCT lpPaint );


第一个参数是要重画窗口的句柄,第二个参数是一个指向PAINTSTRUCT结构体的一个指针。返回值为HDC类型,即DC句柄。
我们不妨在看一下PAINTSTRUCT结构的定义:

typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
}PAINTSTRUCT;


第一个参数是DC的句柄,第二个参数表示背景是否需要被擦除。如果为TRUE则背景需要擦除,否则不擦出背景。第三个参数为一个RECT结构体的变量,RECT结构体是一个矩形的结构体定义,存储了一个矩形的左上角点的坐标和右下角点的坐标。这里它表示了需要重绘的矩形。(windows一般不需要对整个客户区进行重绘,如果客户区只是部分被遮挡,那么只需要重绘被遮挡的部分。)至于后三个参数是系统保留的,在系统内部被使用,这里不再解释。
大家需要注意的是,lpPaint是一个输出参数,也就是说我们不需要自己去定义这个结构体的各个值,它是由程序自动来填入的。
在获得一个设备描述表句柄之后,我们就可以调用GDI为我们提供的大量绘图函数来对输入设备进行绘制了。一般来讲,这个获得的设备描述表句柄被当做GDI绘图函数的第一个参数。
在绘制完成之后,我们需要释放这个句柄。在函数,WM_PAINT消息的处理中,我们必须成对的使用BeginPaint函数和EndPaint,EndPaint函数即用来释放句柄,它的参数类型和BeginPaint函数一致。
在这里需要说明的是如果我们需要去处理WM_PAINT消息(一般来讲,我们都需要处理这个消息),那么我们就必须调用BeginPaint函数和EndPaint函数。即使我们在这两个函数之间不调用任何GDI绘图函数。另外,在WM_PAINT消息中,我们只能用BeginPaint消息来获得DC句柄。而不能用下面所讲的方法来获得DC句柄。同样,在处理其他消息时,如果我们需要获得设备描述表句柄,我们也不会用BeginPaint函数来获取,而是采用下面所讲述的方法。

第二种方法

另外一种得到窗口客户区的设备描述表句柄的方法是GetDC函数。这个函数只有一个参数,即窗口的句柄。这个函数和ReleaseDC来成对使用,即如下所示:

hdc = GetDC(hwnd);
// 调用GDI绘图函数
ReleaseDC(hwnd,hdc);


我们知道使用BeginPaint获取句柄,将传入一个rcPaint(PAINTSTRUCT结构体里的一个变量)来标识无效矩形的大小。与GetDC函数来获取DC句柄所不同的是,GetDC函数所返回的设备描述表里具有一个剪取矩形,它等于整个客户区,也就是说我们可以在整个客户区而不是仅仅在无效矩形内绘图。

上面说到只有获得一个设备描述表之后,我们才能够进行绘制图形,那么这个设备描述表中到底是什么?在解决这个问题之前,我们不妨来看一个实例。首先来认识一个函数。
BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString);


这个函数用于在窗口的指定位置处显示一串字符。第一个函数即DC句柄,nXStart和nYStart是要显示字符串的起始位置,lpString是一个字符串的指针,指向我们要显示的字符串。最后一个参数是要显示字符的个数。
了解这个函数之后,如果我们想要在窗口上显示一行字符串,那么我们可以这么去做。在第一篇文章中所讲的框架的基础上,我们在窗口处理函数上添加如下代码:

[align=left][/align]
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TCHAR  *szBuffer = _T( "Hello Win32!" );
switch (message)
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
TextOut( hdc, 10, 20, szBuffer, lstrlen(szBuffer) );
EndPaint( hWnd, &ps );
break ;
case WM_DESTROY:
PostQuitMessage(0);
break ;
default :
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}


[align=left][/align]

运行程序,可以看到窗口中显示了“Hello Win32!”,如下所示:



[align=left] 我们在调用TextOut函数时,可以看到我们只像这个函数中去传入了要显示的字符串,以及字符个数,和要显示的位置。但是,我们并没有看到传入字体的大小,字体的样式,颜色等等信息,那么windows是如何知道显示出这样的样子呢?[/align]
这就是在设备描述表中为我们定义的,也就是说,设备描述表中的某些值是图形的“属性”。我们可以通过一些函数从设备描述表中来设置和获取这些值。
介绍到这里,其实我们只是仅仅的介绍了GDI的九牛一毛的东西。GDI是个相当庞大的东西,但是我们了解了这些之后,就可以很容易的利用GDI提供的一些绘图函数来绘制一些简单的图形了,至于绘图中其他的内容,我们将在以后穿插进行讲解,比如绘制文字体等等。

常用函数

下面简单的介绍几个GDI绘图函数。具体参数含义以及更复杂的图像函数,在需要时,可以通过查找MSDN上来获取。

点的绘制

COLORREF SetPixel(HDC hdc, int X, int Y, COLORREF crColor);


X,Y为要绘制点的坐标,crColor为COLORREF类型值,COLORREF为一个32位的数表示要绘制的颜色,高八位为0,后面以此为蓝色分量,绿色分量和红色分量。我们可以通过RGB宏来进行设置。

直线的绘制

BOOL MoveToEx(HDC hdc, int X, int Y, LPOINT lpPoint);


设置直线绘制的起点,lpPoint为先前的位置,如果不需要这个值,一般设置为NULL
BOOL LineTo( HDC hdc, int nXEnd, int nYEnd );


要绘制直线的重点坐标。

矩形的绘制

BOOL Rectangle( HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect );


参数分别代表矩形左上角和右下角点的坐标。

椭圆的绘制

BOOL Ellipse( HDC hdc, int nLeftRect, int nTopRect, nRightRect, int nBottomRect );


参数和矩形绘制参数一直,该椭圆为这个矩形的内切圆。

圆角矩形

BOOL RoundRect( HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int xCornerEllipse, int yCornerEllipse );


借用windows程序设计一书中的配图来说明参数含义:



曲线的绘制

BOOL Arc( HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nXStartArc, int nYStartArc, int nXEndArc, int nYEndArc );




点(xStart,yStart)和(xEnd,yEnd)之所以不定义在矩形的边上,是为了保持更大的灵活性。
其他绘图函数,再次不再一一介绍,大家可以查看MSDN来获得。

实例

现在我们仍然使用Windows程序设计一书中的例子来简单的用一下这个函数,在使用之前,我们来简单说明一下WM_SIZE消息。
当我们改变窗口大小时,Windows将会给窗口过程发送一个WM_SIZE消息。并且在传给窗口过程中的lParam(还记得消息是一个MSG结构体,在结构体里存在着lParam的变量,作为附属信息吗?)参数的低位字中包含客户区的宽度,高位字包含客户区的高度。所以我们可以通过处理这个参数来获得客户区的大小。
仍然使用之前的框架,在窗口过程函数中这些更改代码:

[align=left][/align]
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxClient, cyClient;
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_SIZE:
cxClient = LOWORD( lParam );
cyClient = HIWORD( lParam );
break ;
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );

Rectangle( hdc, cxClient/8, cyClient/8,
7*cxClient/8, 7*cyClient/8 );

MoveToEx( hdc, 0, 0, NULL );
LineTo( hdc, cxClient, cyClient );

MoveToEx( hdc, 0, cyClient, NULL );
LineTo( hdc, cxClient, 0 );

Ellipse( hdc, cxClient/8, cyClient/8,
7*cxClient/8, 7*cyClient/8);

RoundRect( hdc, cxClient/4,  cyClient/4,
3*cxClient/4, 3*cyClient/4, cxClient/4, cyClient/4 );
EndPaint( hWnd, &ps );
break ;
case WM_DESTROY:
PostQuitMessage(0);
break ;
default :
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}


[align=left][/align]

运行结果

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: