第一讲:GDI编程的基本概念
图形设备接口:是一个抽象的接口,是一个关于图形显示的函数库。通过这个接口可以实现对图形的颜色、线条的粗细等属性的控制。通过GDI,可以进行设备无关性操作。所有的GDI函数中必须带有设备环境这个参数。设备环境DC(Device Context 又称为设备上下文,也称为设备描述表),它是一个关于如何绘制突袭的方法的集合,它既可以绘制各种图形,也可以确定在应用窗口中绘制图形的方式,就是绘图模式和映射模式。在Win32编程中,设备环境是用HDC来标识的。H的意思就是Handle句柄的意思。
如何在DC上绘制呢?就是用GDI函数来对DC进行操作。而GDI函数的声明是包含在WinGDI.h文件中的。DC好比一块画布,所有东西都是画在画布上的。所以在绘制图形之前就是要先获得画布DC,那么有这么几种方法可以来获得相应的HDC。
第一种:通过GetDC函数来获得相应的窗口的HDC,GetDC函数适合在需要窗口在现有图形的基础上进行绘制时,用来获取HDC。
代码段:
{
HDC hDC = ::GetDC(hWnd);
//其它的绘图操作代码
……
::ReleaseDC(hWnd,hDC);
}
注:GetDC中传入的参数是hWnd,即窗口的句柄,它返回和这个窗口相关的DC。
GetDC(hWnd)用完之后,一定要记得用ReleaseDC(hWnd,hDC)来释放,否则会造成内存泄露,过多的内存泄露可能会使GetDC申请不到DC而导致窗口黑屏。
第二种方法:通过BeginPaint函数来获取所在窗口的HDC
BeginPaint函数是将窗口上所有内容清除干净,然后在上面绘制。
代码段:
{
PAINTSTRUCT ps;
HDC hDC = ::BeginPaint(hWnd,&ps);
//其它绘制操作代码
::EndPaint(hWnd,&ps);
}
注意:在调用BeginPaint(hWnd,&ps)来获取hDC用完之后,一定要记得调用EndPaint(hWnd,&ps)来释放这个hDC,同时也提交绘图,最终显示在显示器上面.(hWnd是窗口句柄)。
PAINTSTRUCT结构:
typedef struct tagPAINTSTRUCT{
HDC hdc; //显示器的设备环境句柄
BOOL fErase; // 是否擦除背景
RECT rcPaint; //指定了需要绘图的窗口区域
BOOL fRestore; // 是否需要恢复
BOOL fIncUpdate; //更新
BYTE rgbReserved[32]
}PAINTSTRUCT;
调用EndPaint函数可以只更新工作区域中的部分区域,这个比重绘整个屏幕要快。因此BeginPaint和EndPaint总是被用来响应WM_PAINT消息。
第三种方法:通过CreateCompatibleDC函数来创建兼容内存DC
通过调用CreateCompatibleDC函数来创建与目标DC兼容的内存DC,然后可以在内存DC上进行各种各样的操作,需要注意的是这里的绘制不会直接显示到显示器上去,那要等到什么时候才会显示上去呢?只有等到BitBlt把内存DC上的内容拷贝到目标DC上来以后,才会被显示到显示器上,当然这里的目标DC,假定就是窗口DC,最后不要忘了调用DeleteDC来释放hMemDC。其中这个hDest就是窗口的目标DC。其它的绘制操作代码都是基于内存DC,hMemDC的,那么要把在内存中绘制的结果,呈现在目标DC上的话,需要调用BitBlt将内存DC中的图像拷贝到目标DC上来。一般用于解决界面闪烁问题。
代码段:
{
HDC hMemDC = ::CreateCompatibleDC(hDest);
//其它绘制操作代码
……
::DeleteDC(hMemDC);
}
常用的GDI对象:
画笔Pen:
在GDI中,用HPEN来表示画笔,H是代表句柄的意思。
在GDI中,画线,画边框(如矩形边框,圆边框,饼边框)都是采用画笔来操作的。
GDI默认的画笔是黑色,宽度为1,样式为实线的画笔。
画刷Brush:
在GDI中,填充矩形,填充圆,填充饼等以及刷窗口背景都是通过画刷来操作的。
在GDI中,默认的画刷是白色的画刷。
字体Font:
在GDI中,用HFONT来表示字体。
在GDI中,所有文本绘制,都是要基于具体的字体对象来绘制的。GDI中默认的字体,是系统等宽字体,比如对话框标题一般就采用这个字体。
区域Region:
在GDI中,用HRGN来表示区域。在窗口中,一个区域可以是椭圆形,多边形,或是矩形等。
位图Bitmap:
在GDI中,用HBITMAP来表示位图。
GDI的绘制,从大体上,分为图形绘制和图像绘制。对于线,矩形,圆,饼等的绘制是属于图形绘制。而对于位图的绘制则是属于图像绘制。
如何来使用这些对象?使用SelectObject函数将这些对象选入到DC中,就可以用了。
SelectObject原型:
HGDIOBJ WINAPI SelectObject(HDC hdc, HGDIOBJ h);
第一个参数是设备DC
第二个参数是HGDIOBJ,其实就是一个void*。这个函数传入一个新的GDI对象到第一个参数的DC中,并返回该DC的老的相同类型的GDI对象。
返回值为一个HGDIOBJ,是所有GDI对象的共同基类的一个类型。
WINAPI表示是系统API函数。
下面是一个完整的使用GDI对象的例子代码:
{
LOGFONT if={0};
lf.lfWidth = 16;
lf.lfHeight = 40;
lf.lfCharSet = GB2312_CHARSET;
HFONT hOldFont = ::SelectObject(hDC,hFont);
DrawText(hDC,_T(“abc”),3,&rect,DT_LEFT);
//其它绘制操作代码
……
::SelectObject(hDC,hOldFont);
::DeleteObject(hFont);
}
LOGFONT这个其实就是字体的逻辑结构,把它初始化为0.然后把lf地址传入CreateFontIndirect(),CreateFontIndirect()就根据lf初始化的字体的逻辑结构信息来创建字体,并返回一个HFONT句柄。创建完之后,用SelectObejct来将其选入到HDC中来,但是这里我们把之前的老的hOldFont先保存起来,待用完之后,再将这个老的选入到HDC中来,最后调用DeleteOject(hFont);来删除这个用完的hFont对象。
创建GDI对象,把新的对象选入HDC,把老的保存起来,然后进行绘制,最后把老的对象再选入,最后删除这个用完的GDI对象。
如果要用的对象是windows能够默认提供的,也就是所谓的库存的对象,那么我们就不需要动态的去创建和销毁这些对象了,只需要直接使用就可以了。这样就可以提高程序运行的效率。
库存GDI对象,例如:
画刷
#define WHITE_BRUSH 0 //白色画刷
#define LTGRAY_BRUSH 1 //亮灰色画刷
#define GRAY_BRUSH 2 //灰色画刷
#define DKGRAY_BRUSH 3 //暗灰色画刷
#define BLACK_BRUSH 4 //黑色画刷
#define NULL_BRUSH 5 //空画刷
#define HOLLOW_BRUSH NULL_BRUSH
画笔
#define WHITE_PEN 6 //白色画笔
#define BLACK_PEN 7 //黑色画笔
#define NULL_PEN 8 //空画笔
字体
#define OEM_FIXED_FONT 10
#define ANSI_FIXED_FONT 11
#define ANSI_VAR_FONT 12
#define SYSTEM_FONT 13
#define DEVICE_DEFAULT_FONT 14
#define DEFAULT_PALETTE 15
#define SYSTEM_FIXED_FONT 16
#define DEFAULT_GUI_FONT 17
用的最多的是DEVICE_DEFAULT_FONT字体
可以通过调用对象的ID号来获得库存的GDI对象的函数如下:
HGDIOBJ GetStockObject(int i);
Stock即库存的意思
返回对应的GDI句柄,库存的对象是不需要你创建,也不需要你来销毁
除了对GDI进行选入之外,有时候还需要获得GDI的一些信息,那么获得对象的一些信息可以通过调用GetObject函数来实现。
int WINAPI GetObject(HANDLE h , int c , LPVOID pv);
第一个参数填入GDI对象的句柄
第二个参数填入结构的大小
第三个参数填入结构的指针,这里的结构是对应相应的GDI对象的。
比如:位图HBITMAP对象的结构是BITMAP,画刷HBRUSH的对象结构是LOGBRUSH。现在以获得位图对象的信息为例:得到位图的宽度和高度。
BITMAP bm;
::GetObject(hBitmap,sizeof(BITMAP),&bm);
GDI对象的信息获取
typedef struct tagBITMAP
{
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
LONG bmPlanes;
LONG bmBitsPixel;
LPVOID bmBits;
}BITMAP,*PBITMAP,NEAR *NPBITMAP,FAR *LPBITMAP;
DC和GDI设备之间的关系,DC主要描述绘图的状态和方式,GDI设备主要描述了采用什么样的绘图工具来绘制。前者就像是一块白板,后者就好像是各种的画笔和画刷。
图片和颜色的关系
RGB格式中,R(0~255)红色分量,G(0~255)绿色分量,B(0~255)蓝色分量。没有透明信息。通常RGB三个分量各占一个字节,所以RGB实际占用的是三个字节。
ARGB格式中,A(0~255)代表Alpha,代表透明度信息。后面的RGB就是红、绿、蓝分量。通常ARGB四个分量各占一个字节,所以ARGB实际占用的是四个字节。