偶贴的上一个帖子名字是“GDI+的绘图效率问题,大家讨论一下吧!”。在其中解决的效率问题远远不够。现在工作中的模块做的差不多了,总结了一下最近得到的经验结论,贴出来大家进一步讨论下。。。也算是在CSDN上得到了诸多帮助的一个小小的回报:) 在交流中共同进步吧!
不知道为什么MS把GDI+中的 DrawImage 这个函数效率做的这么低,(当然了,它的优点是使用方便、支持透明PNG格式等等,还有什么优点有经验的朋友不妨贴出来共享下:) 那么到底有多低呢?我也不知道,所以刚才做了个试验。让DrawImage和::BitBlt速度做了个比较,结果让了吓一跃!
先说下我的机器配置:2*3.0G Intel CPU,1G的内存,Intel945的显卡。在一个窗口中用DrawImage画一个590X480大小的PNG图片,画了100次,用了N.............长时间;用BitBlt画同样大小的bmp图片,画了1000次,用了...n长时间,多少时间我不管了,帧速我是计算出来了大约是 DrawImage 16.5帧/秒,BitBlt 2169.2帧/秒 。没错!确实是这个数字。我数学学的不好,就用计算器算了一下BitBlt的绘图速度是DrawImage的131.5倍。。。
算了,代码也贴出来吧,写的比较粗糙:
void CCmpBitBltToDrawImageDlg::OnBnClickedButton1()
{
Bitmap* ppng = NULL;
CDC* pDC = GetDC ();
Graphics* pGrp = Graphics::FromHDC (pDC-> GetSafeHdc ());
ppng = Bitmap::FromFile (L"res\\tray.png");
clock_t start = clock ();
for (int i = 0; i < 100; i++)
{
pGrp-> DrawImage (ppng, 0, 0);
}
delete pGrp;
ReleaseDC (pDC);
delete ppng;
clock_t time = clock () - start;
float ftime = time / 1000.0f;
CString str;
str.Format ("%.1f frame per second\n", 100 / ftime);
AfxMessageBox (str);
}
void CCmpBitBltToDrawImageDlg::OnBnClickedButton2()
{
CDC memDC;
CBitmap bmp;
if (0 == bmp.LoadBitmap (IDB_BITMAP1))
{
AfxMessageBox ("载入图片失败");
return;
}
CDC* pDC = GetDC ();
memDC.CreateCompatibleDC (pDC);
memDC.SelectObject (&bmp);
clock_t start = clock ();
for (int i = 0; i < 2000; i++)
{
::BitBlt (pDC-> GetSafeHdc (), 0, 0, 590, 480, memDC.GetSafeHdc (),
0, 0, SRCCOPY);
}
ReleaseDC (pDC);
clock_t time = clock () - start;
float ftime = time / 1000.0f;
CString str;
str.Format ("%.1f frame per second\n", 2000 / ftime);
AfxMessageBox (str);
}
对了,我没有在OnPaint用CPaintDC,是因为用CPaintDC DrawImage不出东西。不知道为什么?哪位高手知道烦请告诉我喔!
前面罗嗦了半天,下面进入正题。
我做的界面模块之前用DrawImage方法来绘图,以我的机器配置有70多个帧吧,人眼是察觉不出来了。觉得速度可以了,但这个模块是跑在虚拟机是的,内存的使用是受限制的,限制到128M,另外可能也有模拟硬件的原因,速度由70多帧降到了9帧。这怎么行!大家都知道只有屏幕的刷新速度达到24帧/秒以上,人眼才会感觉画面流畅。
所以,我得想办法解决问题呵呵呵呵。
因为利用了双缓冲,我在内存中建立了一个Graphics绘图平面m_pMemGraphics,先把零碎元素画到这个内存平面上,再一次性将它绘制到屏幕上。这个Graphics对象对象是利用FromImage方法创建的(也就是说,在这个绘图平面上绘图是把所有的东西都画到了这个Graphics对象所依赖的图片上,然后需要绘制到屏幕上的时候,只能用DrawImage的办法将这个图片画到屏幕上。偶也想到了用BitBlt的方法以提高效率,但用FromImage方法创建的Graphcis对象的DC是一片漆黑呀!找了半天利用它的DC的方法也不得要领,在codeproject上找到一个例子是VB.NET的,分析了半天还是没有办法。。。这又是一个疑问,希望知道的高手告诉俺怎么做呀,在这里先谢了!),我就只好用DrawImage了。也想了其它的几个办法,包括GDI和GDI+混合使用,GDI+使用了GDI创建的DC。但反过来,就像上面我描述的"一片漆黑",行不通嗯,我还是先说一下暂时没有使用BitBlt怎样改进的绘图效果吧:
1. 使用SetClip限定你的绘制区域。
2. 仅仅是限定的绘制区域也是不行的,还要把你所要绘制的图片剪切的尽量小,和SetClip配合使用。
3. 多浪费点儿内存使用 CachedBitmap 吧,绘图速度会好很多,DrawCachedBitmap 要比 DrawImage 快一些哟!
使用了以上几种方法我的程序绘制速度由70多帧提高到了200多帧。。。还可以哦,仍然是DrawImage和DrawCachedBitmap而没有使用BitBlt。嘿嘿好了,今天想到的就这么多,先写这些吧。俺滴结论是:绘图尽量使用BitBlt,离DrawImage远点儿!
贴段示例代码吧,也因为功能性的东西写的比较分散了,就贴一个函数好了,道理是相同的。。。
void CDesktopDlg::DrawRing ()
{
// 设置剪切区域
m_pGraphics-> SetClip (m_pClipRgn);
// 内存中绘制背景
m_pMemTrayGrp-> DrawCachedBitmap (m_pCachedTrayBmp, 0, 0); // <------注意m_pCachedTrayBmp和 // m_pTrayBmp是相同的图片,只是一份复 // 制,用来提高绘图速度。
if (m_lstShowBtnRing.size () > 0)
{
// 内存中绘制按钮
for (list <CEbankButtonAttr*> ::iterator itor = m_lstShowBtnRing.begin ();
itor != m_lstShowBtnRing.end (); itor ++)
{
m_pMemTrayGrp-> DrawCachedBitmap ((*itor)-> m_pCachedBmp, (*itor)-> x,
(*itor)-> y));
}
}
// 绘制到屏幕
m_pGraphics-> DrawImage (m_pTrayBmp, *m_pTrayRect);
}
原文链接:
http://blog.csdn.net/kaka_iseeyou/archive/2008/01/10/2033690.aspx