关于GDI+的那些事(1)——效率、双缓冲

来源:互联网 发布:云端软件平台pc 编辑:程序博客网 时间:2024/06/05 16:19

前言:

一年来做了一些图像相关的工作,最开始也没什么基础,所以本着简单质朴的理念没有试用那些第三方图像处理库,而是选用了windows提供的GDI/GDI+,结果也能胜任需要。

这玩意嘛,上手也不难,用几天后,说实话,真感觉像网上一些人说的那样,“挺垃圾的”。


由于接下来很可能转行了,一些一直没想清楚也没机会求解答的问题就只能存疑了,比如:

1,到底是我对GDI/GDI+的了解不够深入,导致自认为它“垃圾”,还是它确实很差?

2,那些第三方图像库的实现机制是怎样的?它们在windows系统对底层的封闭态度下,如何做到类似控制显存等?


这个系列的文章,记录个人一些浅薄的经验心得,不算太透彻,甚至都不够准确,仅供参考罢了。



一、效率及“双缓冲”

只要用几天GDI+,我想几乎所有人都会遇到类似“画面闪烁”、“帧率低”等问题,然后上网一搜,就会搜到一堆“双缓冲”、“提高效率”的字眼。

这里就存在一个问题,因为你的“闪烁”和我的“闪烁”,其实是两种现象,而甲的“双缓冲”和乙的“双缓冲”,又完全是两码事。这里理一理:


A.第一种类型的“闪烁”及“双缓冲”:

比如你要在白的客户端背景上移动一幅图片。

很明显,需要擦除、重绘,如此循环实现。但是这样做就会产生一个问题,移动的过程中图片一闪一闪的,眼都花了。这是因为擦除完毕后白板状态也实时被呈现出来,你眼睛看到的是白板-图片-白板-图片,自然闪烁。

解决的方法,是在内存开辟一幅草稿图,每次擦除重绘,都在草稿上进行,而前台客户端呢,仅仅是周期的贴上草稿纸。这样你眼睛看到的是 草稿纸-新的草稿纸-新的草稿纸……这样就不闪了。


B.第二种类型的“闪烁”及“双缓冲”:

第二种闪烁发生在直接用Graphics对象的DrawImage方法绘制屏幕时,当然Graphics对象根据客户端窗口句柄生成的。

你会发现屏幕也在闪,但是不同于前一种,是那种感觉屏幕上的图像被分割成一条一条的“闪烁”。

我曾经以为这是因为DrawImage的绘制效率太低导致,然后改用GDI的BitBlt函数自己封了一个DrawScreen绘图函数,“成功”解决了此“闪烁”。

(在此插一句,我做过一个测试,先都预加载好图片,然后DrawImage和BitBlt均绘制几百次,对比其效率,发现相差有4倍左右)

昨天我重新对比测试了DrawScreen和DrawImage的效率,发现DrawScreen反而慢了近六倍。


实际上,真正的“闪烁”原因,这篇文章给出的解释比较合理:使用bitblt提高GDI+绘图的效率(转)

“GDI+画图,是将所有的图形先换入显卡的缓存中看,显卡会每隔一段时间将显存中的内容输出到显示器中,如果在这个显示周期内,无法将所有的图形都放到显存中,就会出现闪烁的现象”

它提出的解决方案,又是一种概念的“双缓冲”,这里我贴出我自己的DrawScreen,原理类似。

void DrawScreen(Image *image){bitmap = (Bitmap *)image;                    //image转为bitmapbitmap->GetHBITMAP(Color(0,0,0), &hBitmap);  //获取bitmap对应GDI位图句柄CDC *cdc = GetDC();                          //创建目标dcHDC srchdc = CreateCompatibleDC(cdc->m_hDC); //创建源dcSelectObject(srchdc, hBitmap);               //将源位图句柄选入源dcBitBlt(cdc->m_hDC, 0, 0, CLIENT_WIDTH, CLIENT_HEIGHT, srchdc, 0, 0, SRCCOPY);//绘图DeleteObject(hBitmap);DeleteDC(srchdc);ReleaseDC(cdc);}

所以实质上是牺牲了效率解决了闪烁问题,至于典型的利用BitBlt方法提高效率的例子,我还真给不出。


最后再贴篇我觉得比较专业的文章供参考:陈灯WGF工作内容及特点

这个叫陈灯的博主的blog中,还有其他论述此问题的文章,自己找吧。


0 0
原创粉丝点击