GDI绘图方法的初步分析

GDI绘图方法的初步分析

此文仅对用GDI绘图进行探讨,不包括其它如DirectDraw、OpenGL等方法。

一、利用设备文本(DC)进行贴图操作针对的图形对象是DDB位图,即HBITMAP句柄

二、贴图操作是只能在DC间进行而不是在图形对象间,为此需要建立图形对象和DC的关联
GDI主要贴图函数有BitBlt和TransparentBlt两个,另外COM对象IImagingFactory不在此说明。

三、各种DC的使用
1、设备DC:与硬件相关的DC
(1)屏幕DC:它进行操作可将图像直接显示到屏幕(所见即所得)
举例:
HDC hdcDevice = ::GetDC(m_hWnd);
说明:m_hWnd为主窗口句柄,此函数获得的hdcDevice就是设备DC的句柄。因为它是内核对象,在不用的时候需要调用ReleaseDC释放
(2)打印机DC:可以将图形输出到打印机
(3)其它DC:略

2、内存DC:创建在内存的DC,因为是动态分配的内存,在不用的时候需要释放
(1)图像DC:用来关联需要显示的图像,如背景图、图标等。对于使用频率高的图像,其对应DC应该是全局的,提高显示效率。
举例:
//STEP1. 获得屏幕DC
HDC hdcDevice = GetDC(m_hWnd);
//STEP2.建立与屏幕DC兼容的图像DC
HDC hdcBmp = CreateCompatibleDC(hdcDevice);
//STEP3. 加载位图资源
HANDLE hBmp= LoadImage(m_hInst,MAKEINTRESOURCE(IDB_MAINWND),IMAGE_BITMAP,0,0,0);
//STEP4. 建立图形对象与图像DC的关联,并保存旧图像对象与图像DC
HGDIOBJ hOldBmpSel = SelectObject(hdcBmp,hBmp);
//STEP5. 将图像DC贴到设备DC(图像马上显示)
BitBlt(hdcDevice,0,0,IMG_WIDTH,IMG_WIDTH,IMG_WIDTH,IMG_HEIGHT,hdcBmp,0,0,SRCCOPY);
//STEP6. 恢复老的图像对象与图像DC的关联
SelectObject(hdcBmp,hOldBmpSel);
//STEP7. 释放图像对象
DeleteObject(hBitmap);
//STEP8. 释放图像DC
DeleteDC(hdcBmp);
//STEP9. 释放设备DC
ReleaseDC(m_hWnd,hdcDevice);
补充说明:在创始创建兼容的DC里面是没有Bitmap的,此时STEP6或者DeleteObject(hOldBmpSel)是没有意义的;如果兼容DC有关联Bitmap,STEP6才有意义

补充说明:在图像DC与位图资源建立联系后,位图资源就不能再与其它图像建立联系了。如果想建立新的联系则要与之前的DC断开联系

(2)画布DC:贴到此DC的图像是生成在内存中的,在生成完后再贴到设备DC上就可以显示出来。此方法即双缓冲技术,是为了避免界面显示的屏幕闪烁
同样的,我们也需要创建一个DC,也需要关联一个图像对象,不用的时候也要释放。但是因为画布是用来绘图而不是保存图像的,这个地方和上例是有点差异的。
//创建一个和屏幕大小一样的图像资源用于绘图
HBITMAP hBitmap = CreateCompatibleBitmap(hdcDevice,IMG_MAINWND_WIDTH,IMG_MAINWND_HEIGHT);
//创建和设备DC兼容的画布DC
HDC hdcWrap = CreateCompatibleDC(hdcDevice);
//建立图形对象与画布DC的关联
HGDIOBJ hOldSel = SelectObject(hdcWrap,hBitmap);
完成了这一步,在进行STEP5贴图工作的时候,不是直接将位图贴到设备DC而是贴到画布DC
BitBlt(hdcWrap,0,0,IMG_WIDTH,IMG_HEIGHT,hdcBmp,0,0,SRCCOPY);
如果有多张图像需要贴,则重复调用BitBlt在画布DC上生成需要的界面图形,在生成完毕后就可以将画布DC贴到设备DC上显示出来
BitBlt(hdcDevice,0,0,IMG_WIDTH,IMG_HEIGHT,hdcWrap,0,0,SRCCOPY);
此DC除了是为了避免闪烁,还有一个目的就是:对于那种有很多个界面切换的,我们可以在一个画布上保留使用率高的界面,这样在进入的时候就不需要重新绘制。

(3)临时DC:主要也是用来保存图像进行绘图,但是因为使用频率低所以用完就销毁

注:关于图像DC或画布DC名称,是本人根据功能划分自命名的。

四、一些绘图技巧
1、双缓冲
见上面关于画布DC的说明。另外如果界面上窗口很多,而且改变大小时很多窗口都要移动和改变大小,如果 使用MoveWindow或者SetWindowPos两个API来改变窗口的大小和位置,由于他们是等待窗口重画完成后才返回,所以过程很慢,这样视觉 效果就可能会闪烁。可调用BeginDeferWindowPos, DeferWindowPos,EndDeferWindowPos。见参考一

2、Flip
如果硬件支持多窗口(即所谓的overlay分层),则不需要画布DC而将图像直接画到不同窗口,然后通过硬件操作来flip界面。关于此可以见DirectX中的代码

3、WM_ERASEBKGND/WM_PAINT
OnEraseBkgnd一定要返回TRUE,全部都在WM_PAINT消息中画。这样只有一个地方画,逻辑更好处理,效率更高。见参考二

4、局部刷新
InvalidateRect/ExcludeClipRect
见参考三

5、GDI绘图的三种方法
这是从gameres上看到的(见参考五),我所说的是第一种,第二种是SetDIBitsToDevice,第三种是CreateDIBSection

你可能感兴趣的:(GDI绘图方法的初步分析)