Jiang's C++ Space

创作,也是一种学习的过程。

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
位图这个概念对于计算机图形学来说是个至关重要的概念,我们在屏幕上看到的任何东西,对计算机来说,其实都是位图,简单地说,无论你是想显示文字,还是线条,抑或bmp,png,jpg和gif等图像文件,最终都是要直接或间接转变为计算机显示设备所认识的位图,才能显示在屏幕上。

我最近接手了一个项目,是Windows Mobile平台的,主要做UI美化,贴图是其中的一大块,我遇到的最大的问题就是贴图的效率问题,如何将一张内存里的图片高效绘制出来,实现平滑流畅的UI动画效果。我尝试了许多办法,甚至DirectDraw,但我发觉在硬件不支持的情况下,DirectDraw除了让代码变得更复杂之外,没有任何优点。好,接下去我们来分析一下如何尽量发挥GDI的威力。

跟贴图相关的函数有几个:
BitBlt:最基本的块传输函数。
StretchBlt:比同BitBlt,它支持图像的拉伸和压缩,当不需要拉伸和压缩时候,它的效果和BitBlt并无二致。
TransparentBlt:比同StretchBlt,它多了个“抠色”(Color Keying)功能,能把某种颜色或者某个范围的颜色抠去而不作块传输处理,以此来绘制不规则图像。
AlphaBlend:比同StretchBlt,它支持Alpha混合,即“半透明”效果,效果比简单的“抠色”更好。

这几个函数是一个比一个强,但也意味着效率一个比一个低,总体上看差不多是这样的,运算量越大,当然就越慢,但测试下来发觉这其实并不绝对,后面会提到。

了解了这几个函数之后,我们开始加载一张图片来观察效果,我准备的是一张320*320的png图片,利用Windows Mobile 6.0提供的IImage接口来加载它,并把它转变为位图,获得HBITMAP。加载png的代码可以通过搜索引擎搜索“IImage用法”等关键词来获取,此处略过。

加载好图片后,创建一个和设备显示设备兼容的DC(Device Context),选入上面加载的位图,用BitBlt绘制,代码如下(为简洁起见,只贴出关键代码,并且不考虑资源释放):
HDC hWndDC = GetDC(hWnd);
HDC hMemDC 
= CreateCompatibleDC(hWndDC);
SelectObject(hMemDC, hBitmap); 
//hBitmap是前面加载的图片
BitBlt(hWndDC, 00, iWidth, iHeight, hMemDC, 00, SRCCOPY);

我们通过添加一些debug代码来观察BitBlt的执行时间,在我的模拟器上大约是50 - 60ms,我发觉这个速度并不快,按道理说,BitBlt应该可以在极短的时间之内完成的(1 - 2ms),也只有这样才能实现“流畅”的UI动画效果,否则图一旦多起来,岂不是更慢。


我尝试修改BitBlt的最后一个参数,我发觉换成NOTSRCCOPY,速度更慢,变成了70多ms,说明运算量更大了,这不是简单的内存拷贝;而当我把最后一个参数换成BLACKNESS或者WHITENESS的时候,速度则很快,1ms-2ms即可完成,很显然,对BitBlt来说,把目标全部置为黑色或者白色,运算量远少于像素传送。在实验的时候把BitBlt替换为另外的几个函数,效果和预期的相差不大,如果图像需要拉伸,则执行得更慢一些,但如果图像不是拉伸,而是压缩,即缩小显示,执行速度居然比较快,有些意外,这是因为压缩后图像变小,需要传输的像素变少的缘故。

我考虑如何提高绘图效率,经过很多次尝试,终于有所突破,我最后发现:如果先把位图存放在一个和DC兼容的位图中,再用这个位图对目标设备进行像素传输,速度十分理想。代码:
HDC hWndDC = GetDC(hWnd);
HDC hMemDC 
= CreateCompatibleDC(hWndDC);
HDC hMemDCToLoad 
= CreateCompatibleDC(hWndDC);
HBITMAP hMemBmp 
= CreateCompatibleBitmap(hWndDC, iWidth, iHeight); // The compatible bitmap
HGDIOBJ hOldBmp = SelectObject(hMemDC, hMemBmp);
SelectObject(hMemDCToLoad, hBitmap);
BitBlt(hMemDC, 
00, iWidth, iHeight, hMemDCToLoad, 00, SRCCOPY); //Do this in initialization
BitBlt(hWndDC, 00, iWidth, iHeight, hMemDC, 00, SRCCOPY); //This BitBlt's speed is very fast

第一次调用BitBlt,可以看作是初始化,我们计算第二个BitBlt的耗时,只有1 - 2ms,非常不错,经过分析,我认为原因应该是这样(不一定全对,如有不妥请读者指出):

只有在位图格式完全一致的情况下,BitBlt才能执行真正的内存拷贝,否则是要经过转换的,转换是需要消耗CPU时间的,所以慢。

那如何知道位图的格式呢?用GetObject可以看出来:

如上图所示,bmp是从png文件加载进来的位图的信息,而bmp2是用CreateCompatibleBitmap创建的位图的信息,从这我们能看到,前者是24bit位图,即一个像素用3个字节表示,而后者是16bit位图,一个像素用两个字节来表示,这个BitBlt执行过程中,就需要转换了,因此耗时。而实际上位图的差别可能比这个还要复杂些,如果再讨论设备无关位图,那就说不完了……

总而言之,为了提高效率,我们要想方设法把加载进来的位图转变为设备兼容位图,绘制的时候直接BitBlt这些设备兼容位图,来实现位图的高效绘制。

前面讨论的主要是BitBlt,那对于别的几个Blt函数呢?我都尝试过了,除了AlphaBlend之外,兼容位图到设备的Blt速度上都有显著的提高,而AlphaBlend则无法正常工作,因为兼容位图不带Alpha通道,而AlphaBlend貌似需要32bit的ARGB格式的位图方可正常工作,这个问题我思考了好久都无解,如果哪位读者对提高AlphaBlend的工作效率有心得,不妨跟我联系下,我正急需这方面的技术资料。

因此,我给出这样的结论,阶段性结论:从文件(或资源)加载位图后,把位图转为设备兼容位图,这样使得BitBlt在执行SRCCOPY的时候直接使用内存拷贝,速度很快,即便需要拉伸压缩或者抠色等运算,使用兼容位图的速度也是相当不错的,而使用AlphaBlend的时候,如果需要较高的效率,就应从设计上避免绘制大幅位图,改用小幅位图,在不必要对每一帧都执行Alpha混合的时候,就避免执行,以免影响画面的流畅性。
posted on 2010-06-20 20:04 Jiang Guogang 阅读(3241) 评论(6)  编辑 收藏 引用 所属分类: Windows Embedded Programming

评论

# re: Windows Mobile的高效贴图 2010-06-20 22:52 Benjamin
这应该就是双缓冲  回复  更多评论
  

# re: Windows Mobile的高效贴图 2010-06-21 13:01 Matthew
唉,没有弄过  回复  更多评论
  

# re: Windows Mobile的高效贴图 2010-06-22 09:03 凡客诚品
如果需要较高的效率,就应从设计上避免绘制大幅位图,改用小幅位图,在不必要对每一帧都执行Alpha混合的时候,就避免执行,以免影响画面的流畅性  回复  更多评论
  

# re: Windows Mobile的高效贴图 2010-06-22 17:23 Condor
这不就是双缓冲吗?
和windows上的一样。  回复  更多评论
  

# re: Windows Mobile的高效贴图 2010-06-22 17:35 f.f.
作者很能折腾,受教受教!!
文章的本意跟双缓冲没关系,只是从bitblt的性能差异猜想其实现。  回复  更多评论
  

# re: Windows Mobile的高效贴图 2011-03-17 20:37 Jiang Guogang
事隔那么久,今天又有所突破,在一篇文章里看到的,可能我之前的理解稍微有些出入,但结论是一样的,那就是需要用CreateCompatibleBitmap创建出来的位图来参与BitBlt等操作,速度最佳。

位图有许多不同的种类,可以通过呼叫函式CreateDIBSection、CreateBitmap或CreateCompatibleBitmap来建立位图,从而得到相对应的位图句柄。前两个函式是分配在系统内存中的。而CreateCompatibleBitmap根据装置驱动程序和硬设备的不同由装置驱动程序分配内存,同时装置驱动程序可以分配影像内存。影像内存是一个很有价值的资源,这是因为在它之中的小区域操作速度会大大增加。如果十分在意速度并希望能够快速地对位图进行操作,那么呼叫函式CreateCompatibleBitmap或许是最佳的选择。
在某些系统平台上,只有当信息源和目标文件同时存在于影像内存中时硬件加速卡才能起作用。因此将信息源和目标文件同时放在影像内存中会大大提高系统的影像性能。  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理