偶贴的上一个帖子名字是“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);
}  

解决方案 »

  1.   

    DrawImage和BitBlt当然没有可比性
    DrawImage是将图像绘制出来,注意:此时的图像还是用处于图像格式状态,是各种格式的图像数据.要对图像进行解码,呈现.
    BitBlt直接执行复制操作,最简单的时候,只需要进行简单的内存复制就可以了.
    DrawImage就像打印机,BitBlt就像复印机,让他们比速度???都不在同一起跑线上嘛.
      

  2.   

    不知道对带半透明的png贴图,CachedBitmap是否仍然实用?