GDI高效内存手动绘图

最近一直想要写几篇文章,但是由于懒癌的发作一直都没有写。今天趁着写代码写的闹心并且无力追番的闲心写一下。

最近在参加一个比赛,里面要求百万的图形能够在10s以内能够进行裁剪显示,这对于GDI绘图来说是基本不现实的,不过经过各种优化我在1秒以内完成这个任务(i7 四核 1.8GHz)。实现这个效果的最关键的就是手动绘图替代掉效率底下的GDI。

如果有DOS下图形编程经验的朋友可能都习惯用最基本的绘点函数来完成一些复杂的绘制,到了windows之后遇到GDI之后却发现绘点函数慢的恐怖。比如要SetPixel来绘制一个调色盘,这个效率基本是要让人疯掉的。

这里先分析一下GDI的效率为什么低下。众所周知,现在的Windows是运行在保护模式的,只有系统内核以及驱动程序才能接触到那些关键的显存,而普通的ring3应用程序必须通过操作系统的API才能调用硬件资源。于是每次绘图的时候程序都会调用gdi的dll,这个动态链接库会接受到调用之后在缓冲队列里面记录下操作,在特定的时候会触发CPU陷阱把缓冲队列的数据交给内核来执行。也就是说大量的GDI绘图指令会导致大量的内核切换的开销。

为了加速绘图微软公司也是有不少解决方案,在windows早期就提出过DirectX用来直接操作这些硬件资源。不过今天并不打算采用那种复杂到恐怖的东西。还是用类似于双缓冲的方式实现

GDI里面的重要的一个概念就是DC,而DC需要一个实际的数据区,这个数据区就是一个位图。如果我们能够直接对这个位图内部的内存进行读写那么就能够非常快的完成绘图操作。

关键的代码有下面这几个,如果有问题可以查询MSDN。我一开始也对这几个函数比较有疑惑,现在稍微记录一下吧。

  • CreateCompatibleDC能够创建一个空的兼容的DC,但是这个DC由于没有缓存区所以大小是1*1的。
  • CreateCompatibleBitmap能够创建一个大小一致的兼容的BMP,由于新创建的DC并没有大小,请注意这里要传入旧的DC
  • SelectObject用来把一个BMP绑定到DC上
  • BitBlt用来复制并且光栅操作
  • SetBitmapBits用来设置一个BMP的内容

还有一些其他的API可能也是有用的,不过由于调用并没有这里方便于是我就不介绍了

代码Demo可以看我当时写的代码:https://github.com/manageryzy/lineCircle/blob/master/lineCycle/draw/drawMemory.cpp