一、GDI(图形设备接口(Graphics Device Interface))的介绍
GDI其实就是图形设备接口,其主要任务就是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出
。GDI的出现使得程序员无需要关心硬件设备及硬件设备驱动,就可以将应用程序的输出转换为硬件设备上的输出,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。
所以在Windows操作系统下,绝大多数具备图形界面的应用程序都离不开GDI,我们利用GDI所提供的众多函数就可以方便地在屏幕、打印机及其他设备输出设备上输出图形、文本等操作。
简单来说,GDI就是应用程序和计算机硬件设备的桥梁。
二、GDI环境介绍
在GDI环境下,还有一个要说明的问题就是坐标系。GDI的坐标系中,水平方向为X轴,垂直方向为Y轴。当前显示环境(可能是整个屏幕,也可能是窗口,或窗口的客户区)的左上角是坐标原点,X轴正方向是向右,Y轴正方向是向下。
设备环境的概念:
设备环境本质上是系统的一个内部对象,程序向系统申请得到设备环境,然后在绘图函数中使用它。可以说设备环境句柄是GDI函数的一个“通行证”,有了这个句柄,程序就可以自如的在显示区域上绘图,表达任何想表达的内容。
从程序设计的角度看,设备环境就像是一个GDI内部使用的一个数据结构,它包含很多的值。这些值和具体的显示设备有关,记录了各种和图形有关的属性。比如字体的颜色、窗口显示区域的大小等。
程序在绘图时,一般的操作步骤就是先得到一个设备环境。也就是先得到一个设备环境句柄(HDC)。在这个设备环境中保存着相关的属性,可以通过GDI函数得到这些属性或者修改这些属性的值。然后调用GDI函数进行绘图,在绘图结束后程序必须释放这个句柄。这样做是为了保证程序的安全。
在这里要说明一点是绘图的时机,一般来说绘图操作是在处理WM_PAINT时进行的。
程序中产生WM_PAINT消息的情况有以下5种:
①窗口最初创建时
②窗口移动或者大小发生改变
③窗口隐藏后重新显示或者被其他窗口遮掩的部门重新可见
④调用InvalidateRect、InvalidateRgn等刷新函数
⑤调用ScrollWindow或者ScrollDC函数滚动客户区
得到设备环境句柄的两种方法:
①在处理WM_PAINT消息时,它涉及BegainPaint和EndPaint两个函数,这两个函数需要窗口句柄(作为参数传给窗口消息处理程序)和PAINTSTRUCT结构变量(在WINUSER.h头文件中定义)的地址为参数。
②在非WM_PAINT消息时,在处理非WM_PAINT消息处理期间绘制显示区域的某个部分也是非常有用的。或者需要将设备内容句柄用于其他目的,例如取得设备内容的信息。要得到窗口显示区域的设备内容句柄,可以通过GetDC来取得句柄,在使用完后记得需要ReleaseDC进行释放。
三、GDI显示图像例程
本例程主要实现利用GDI函数在客户区显示图像与使图像充满整个客户区。
①首先创建一个win32项目的应用程序
②在全局变量中添加位图显示的句柄,因为背景的生命周期与应用程序的生命周期一致
-
// 全局变量:
-
HINSTANCE hInst; // 当前实例
-
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
-
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
-
HBITMAP hbmpBack = NULL; //客户区背景位图显示句柄
HBITMAP 是GDI对象中的位图对象,GDI对象有:
HPEN 画笔、HBRUSH 画刷、HFONT 字体、HRGN 区域、HBITMAP 位图
③在创建主窗口的InitInstance函数中通过LoadBitmap或LoadImage函数加载指定的位图资源
-
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
-
{
-
HWND hWnd;
-
hInst = hInstance; // 将实例句柄存储在全局变量中
-
hbmpBack = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP1));
-
//hbmpBack = (HBITMAP)LoadImage(NULL,L"D:/37.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
-
}
这里简单介绍一下
LoadBitmap和LoadImage函数的区别:
LoadBitmap只能加载资源管理器中的位图资源,这里的MAKEINTRESOURCE是一个资源名转换的宏,这个宏是把一个数字类型转换成指针类型的宏,它不存在释放的问题;凡涉及"资源"的API或者MFC类,在参数类型为LPCTSTR时,就应该使用 MAKEINTRESOURCE。
而LoadImage可以加载资源管理器和文件路径的图标、光标或位图,它功能比LoadBitmap强大,所以带的参数也比较多,而且最后返回值是HANDLE,最后需要强制转换为HBITMAP;HANDLE是一个通用句柄表示,用来表示对象的。
加载了位图句柄hbmpBack,就得释放,因为一张位图的数据占用的空间相当大,我们使用DeleteObject函数释放位图数据,具体代码加在_tWinMain函数中的消息循环下方:
-
int APIENTRY _tWinMain(HINSTANCE hInstance,
-
HINSTANCE hPrevInstance,
-
LPTSTR lpCmdLine,
-
int nCmdShow)
-
{
-
// 主消息循环:
-
while (GetMessage(&msg, NULL, 0, 0))
-
{
-
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
-
{
-
TranslateMessage(&msg);
-
DispatchMessage(&msg);
-
}
-
}
-
-
DeleteObject(hbmpBack);
-
return (int) msg.wParam;
-
}
④在处理主窗口消息的WndProc函数中,在绘制主窗口WM_PAINT消息添加自己绘制图像的代码
-
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
-
{
-
int wmId, wmEvent;
-
PAINTSTRUCT ps;
-
HDC hdc;
-
-
switch (message)
-
{
-
case WM_PAINT:
-
{
-
hdc = BeginPaint(hWnd, &ps);
-
// TODO: 在此添加任意绘图代码...
-
HDC hDCMem = CreateCompatibleDC(hdc);//创建兼容DC
-
HBITMAP hOldBmp = (HBITMAP)SelectObject(hDCMem,hbmpBack);//把hbmBack的位图选择到兼容DC HDCMem,之后这个兼容DC就拥有和hbmpBack同样大小的绘图区域
-
BITMAP bmp;
-
GetObject(hbmpBack,sizeof(BITMAP),&bmp);//获取位图的大小信息,事实上也是兼容DC绘图输出的范围
-
#if 0
-
BitBlt(hdc,0,0,bmp.bmWidth,bmp.bmHeight,hDCMem,0,0,SRCCOPY);//原样拷贝,不支持拉伸
-
#else
-
RECT rcClient;
-
GetClientRect(hWnd,&rcClient);//获得客户区的大小
-
int nWidth = rcClient.right - rcClient.left;//客户区的宽度
-
int nHeight = rcClient.bottom - rcClient.top;//客户区的高度
-
StretchBlt(hdc,0,0,nWidth,nHeight,hDCMem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);//拉伸拷贝
-
#endif
-
SelectObject(hDCMem,hOldBmp);//复原兼容DC数据
-
DeleteDC(hDCMem);//删除兼容DC,避免内存泄漏
-
EndPaint(hWnd, &ps);
-
break;
-
}
-
}
-
}
使用BitBlt显示,不能填满客户区,如下图:
使用StretchBlt显示,可以填满客户区,如下图:
四、总结
下面总结一下绘图的步骤:
①获得目标DC的句柄
②创建与目标DC兼容的临时内存DC
③将位图句柄选入到临时内存DC中
④使用BitBlt或StretchBlt函数将临时内存DC中的位图拷贝到目标DC上来
⑤将位图句柄选出临时内存DC
⑥销毁临时的内存DC
再总结一下DC与GDI之间的关系:
其实DC像是白板,描述绘图的状态和方式
而GDI则像是各种画笔、画刷等,具体为采用什么样的绘图工具
阅读(5244) | 评论(0) | 转发(0) |