为什么选择GDI?
我并不觉得写游戏用GDI是件好事,然而如果你打算写点小打小闹的程序,选择GDI作图是个好主意,但我觉得更妙的是GDI的开销小,不必象DX那么大动干戈,最让我动心的是它和Windows的控件可以紧密结合比如在C++Builder里面可以通过控制每个控件的HDC来达到自己给控件画图的目的,而且用GDI编写一个地图编辑器是相当方便的事情,配合VCL或者MFC在小规模的图形编程方面DirectX完全是不能比的。
使用的原则
我现在做图形化程序用的是VC写界面程序用的是C++Builder,而研究算法程序用的是BC++3.1。而最近又养成一个坏习惯:写2D图形程序用DDraw而写开始写3D时又用OpenGL。所以写于平台无关的代码对我来说有着迫切的需要,说这个的目的是我打算用WinAPI而不是CBitmap或者TBitmap来完成,代码只可以在VC或者BCB底下运行并不是好主意,因此用API写关键部分然后再配合VCL或者MFC,因此抽象出一个Bitmap类,提供BLIT, LoadFromStream, LoadFromFile, Lock, Unlock等操作,是一个好主意,DDraw的表面Dx8的纹理,或者OpenGL里面的纹理还有GDI位图都通过继承它来实现某些共同特性的功能,实现统一接口的调用,代码做到不偏不倚才能灵活的运用于各个平台。
速度是关键
实现的GDI位图必须是可以自己渲染的及Lock/Unlock的实现。其次就是BLIT的实现,可能我见识有限,我看过的关于GDI文章或者代码对于BLIT功能都是以一种相当慢或者不稳定的方式处理的:
|
1. 根据DIB缓冲区调用CreateBitmap创建一个hBitmap |
2. 用CreateCompatibleDC创建一个hDC |
3. 用SelectObject把hBitmap选进hDC |
4. 然后BitBlt在实现两个hDC间的BLIT功能 | |
|
直接用SetDIBitsToDevice把位图传递到HDC上面,直接送DIB数据到HDC |
我们看看第一种方法Windows到底做了些什么:1.首先创建一个大小和DIB一样的hBitmap然后把数据复制过去 2.简单创建一个hDC 3.把hBitmap设置成为hDC的作图对象 4.做BitBlt。这样可以明显看出,速度在1/2两步被拖的地方太多了。于是我发现了第二种方法,速度真实快,但是这种方法最不好的就是基于字节的传送得不到硬件加速。且如果我直接把DIB数据传送到桌面HDC上时就要出错,只有传到窗口的HDC上才稳定。
两种方法对我来说都没有真正发挥GDI的好处,于是下面我介绍神秘的第三种方法。不知各位可曾注意,大名鼎鼎的《图形程序员指南》曾有涉及GDI作图的片段代码,那就是一个步常被WinAPI提及的一个Win32 API:CreateDIBSection。请看说明:
The CreateDIBSection function creates a device-independent bitmap (DIB) that applications can write to directly. The function gives you a pointer to the location of the bitmap's bit values. You can supply a handle to a file mapping object that the function will use to create the bitmap, or you can let the operating system allocate the memory for the bitmap.
|
HBITMAP CreateDIBSection( |
|
|
HDC hdc, |
// handle to device context |
|
CONST BITMAPINFO *pbmi, |
// pointer to structure containing bitmap size, format, and color data |
|
UINT iUsage, |
// color data type indicator: RGB values or palette indices |
|
VOID *ppvBits, |
// pointer to variable to receive a pointer to the bitmap's bit values |
|
HANDLE hSection, |
// optional handle to a file mapping object |
|
DWORD dwOffset) |
// offset to the bitmap bit values within the file mapping object |
于是作图步奏不同了,首先用CreateDIBSection从DIB创建hBitmap此时Windows不再复制DIB数据到hBitmap而是直接使用DIB数据为基础每次跟新DIB的点阵数据时hBitmap也就发生了变化,而不象CreateBitmap那样需要再次创建,于是这下第一种方法的一到三步的工作就可以提前到创建DIB的时候,实验证明,这样的做法比通常的做法要快两到三倍!!这是制关重要的提高,大家不信可以自己试试,下面是一个老外写的经典代码,使用了CreateDIBSection的,代码虽然用Delphi写成,不过WinAPI是不变的:-)