Chinaunix首页 | 论坛 | 博客
  • 博客访问: 157765
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 360
  • 用 户 组: 普通用户
  • 注册时间: 2017-02-28 08:37
个人简介

没有绝活,怎能风骚.....

文章分类

全部博文(31)

文章存档

2017年(31)

我的朋友

分类: C/C++

2017-04-27 17:48:11

一、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项目的应用程序


②在全局变量中添加位图显示的句柄,因为背景的生命周期与应用程序的生命周期一致
  1. // 全局变量:
  2. HINSTANCE hInst;                                // 当前实例
  3. TCHAR szTitle[MAX_LOADSTRING];                    // 标题栏文本
  4. TCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
  5. HBITMAP hbmpBack = NULL; //客户区背景位图显示句柄
HBITMAP 是GDI对象中的位图对象,GDI对象有:
    HPEN 画笔、HBRUSH 画刷、HFONT 字体、HRGN 区域、HBITMAP 位图

③在创建主窗口的InitInstance函数中通过LoadBitmap或LoadImage函数加载指定的位图资源
  1. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
  2. {
  3.    HWND hWnd;
  4.    hInst = hInstance; // 将实例句柄存储在全局变量中
  5.    hbmpBack = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP1));
  6.    //hbmpBack = (HBITMAP)LoadImage(NULL,L"D:/37.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
  7. }
这里简单介绍一下LoadBitmap和LoadImage函数的区别
    LoadBitmap只能加载资源管理器中的位图资源,这里的MAKEINTRESOURCE是一个资源名转换的宏,这个宏是把一个数字类型转换成指针类型的宏,它不存在释放的问题;凡涉及"资源"的API或者MFC类,在参数类型为LPCTSTR时,就应该使用 MAKEINTRESOURCE。
    而LoadImage可以加载资源管理器和文件路径的图标、光标或位图,它功能比LoadBitmap强大,所以带的参数也比较多,而且最后返回值是HANDLE,最后需要强制转换为HBITMAP;HANDLE是一个通用句柄表示,用来表示对象的。
    加载了位图句柄hbmpBack,就得释放,因为一张位图的数据占用的空间相当大,我们使用DeleteObject函数释放位图数据,具体代码加在_tWinMain函数中的消息循环下方:

点击(此处)折叠或打开

  1. int APIENTRY _tWinMain(HINSTANCE hInstance,
  2.                      HINSTANCE hPrevInstance,
  3.                      LPTSTR lpCmdLine,
  4.                      int nCmdShow)
  5. {
  6.     // 主消息循环:
  7.     while (GetMessage(&msg, NULL, 0, 0))
  8.     {
  9.         if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
  10.         {
  11.             TranslateMessage(&msg);
  12.             DispatchMessage(&msg);
  13.         }
  14.     }
  15.     
  16.     DeleteObject(hbmpBack);
  17.     return (int) msg.wParam;
  18. }

④在处理主窗口消息的WndProc函数中,在绘制主窗口WM_PAINT消息添加自己绘制图像的代码
  1. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3.     int wmId, wmEvent;
  4.     PAINTSTRUCT ps;
  5.     HDC hdc;

  6.     switch (message)
  7.     {
  8.     case WM_PAINT:
  9.         {
  10.             hdc = BeginPaint(hWnd, &ps);
  11.             // TODO: 在此添加任意绘图代码...
  12.             HDC hDCMem = CreateCompatibleDC(hdc);//创建兼容DC
  13.             HBITMAP hOldBmp = (HBITMAP)SelectObject(hDCMem,hbmpBack);//把hbmBack的位图选择到兼容DC HDCMem,之后这个兼容DC就拥有和hbmpBack同样大小的绘图区域
  14.             BITMAP bmp;
  15.             GetObject(hbmpBack,sizeof(BITMAP),&bmp);//获取位图的大小信息,事实上也是兼容DC绘图输出的范围
  16. #if 0
  17.             BitBlt(hdc,0,0,bmp.bmWidth,bmp.bmHeight,hDCMem,0,0,SRCCOPY);//原样拷贝,不支持拉伸
  18. #else
  19.             RECT rcClient;
  20.             GetClientRect(hWnd,&rcClient);//获得客户区的大小
  21.             int nWidth = rcClient.right - rcClient.left;//客户区的宽度
  22.             int nHeight = rcClient.bottom - rcClient.top;//客户区的高度
  23.             StretchBlt(hdc,0,0,nWidth,nHeight,hDCMem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);//拉伸拷贝
  24. #endif
  25.             SelectObject(hDCMem,hOldBmp);//复原兼容DC数据
  26.             DeleteDC(hDCMem);//删除兼容DC,避免内存泄漏
  27.             EndPaint(hWnd, &ps);
  28.             break;
  29.         }
  30.     }
  31. }
使用BitBlt显示,不能填满客户区,如下图:    

使用StretchBlt显示,可以填满客户区,如下图:


四、总结
下面总结一下绘图的步骤:
    ①获得目标DC的句柄
    ②创建与目标DC兼容的临时内存DC
    ③将位图句柄选入到临时内存DC中
    ④使用BitBlt或StretchBlt函数将临时内存DC中的位图拷贝到目标DC上来
    ⑤将位图句柄选出临时内存DC
    ⑥销毁临时的内存DC

再总结一下DC与GDI之间的关系:
    其实DC像是白板,描述绘图的状态和方式
    而GDI则像是各种画笔、画刷等,具体为采用什么样的绘图工具



阅读(5266) | 评论(0) | 转发(0) |
0

上一篇:vs2008中使用gdi+

下一篇:智能家居项目

给主人留下些什么吧!~~