[转]GDI+效率问题与优化

这周在用GDI+实现图片的动画效果,起初每画一帧都在30-40ms左右,动画效果自然不能令人满意。

困惑了良久,后面在高人的指点下,效率有了显著地提高,最主要是清楚了时间都花在了那里,优化起来也就方便很多。

我现在知道的影响GDI+效率的主要有:
1、DrawImage,比起BitBlt实在差的不是一点点,解决的方法就是用BitBlt替换DrawImage。
2、PixelFormat ,原来没想到这个也会影响效率,但事实证明确实是这样。

Bitmap在new出来的时候是和图片本身的格式有关的,但在最终画到DC上的可不一定是这个格式,具体的格式请查阅MSDN:Image Pixel Format Constants

如果格式不对,DrawImage时会自己做一次图片格式转换,这个也会浪费一定的时间。

一个做法就是Bitmap在new出来后用clone到一个新的 Bitmap,这时是可以转换成指定的PixelFormat的:)
统一成一种格式,特别是大图的时候,效果比较明显。
3、尽量用CachedBitmap替代直接的Bitmap使用也能优化一些效率。

4、每次都全部重画是一种浪费,能不重画的就不重画,刷新的区域也是优化的一个有效方式。

5、对象的构造比较费时,比如用于双缓冲的内存Bitmap和Graphics最好设成成员变量,而不是在OnPaint中每次新建

6、减小图片大小 :)

……

关于GDI和GDI+

GDI的效率更高,而GDI+无疑更加易用,鱼与熊掌不可兼得。

我现在的做法是用GDI+画到内存Bitmap上,最后用GDI BitBlt画到DC上。

小结:

最开始很郁闷,怎么优化都不见效果,感觉乱成一团,于是不断地msdn,google,找人……

终于,在理清大部分的细节后发现:答案其实也很简单。

事情还没结束,今天也太晚了,就先写个大概,备忘下,下次有空时再整理个例子出来。希望大家批评指正。

感谢老师给我的帮助:)

2009-07-05 0:31 --THE END--

// 添加于2009/7/31

把gdi+转成gdi来画到dc上的代码片段,希望对大家有所帮助。

 

/*   
@ Function  : CreateDib   
@ brief     : 创建内存位图   
@ parameter : w   
@ parameter : h   
@ parameter : [out] hBmpSection OnPaint中会把这个位图直接往dc上贴   
@ parameter : [out] bmpData 位图的实际数据,gdiplus的会把东西都话到上面   
@ return    :    
@ remark    :    
@
*/     
BOOL CreateDib(
int w, int h, OUT HBITMAP& hBmpSection, OUT BYTE** bmpData)     
{     
    BITMAPINFO info 
= {0};     
    info.bmiHeader.biSize 
= sizeof(info.bmiHeader);     
    info.bmiHeader.biWidth 
= w;     
    info.bmiHeader.biHeight 
= -h;     
    info.bmiHeader.biPlanes 
= 1;     
    info.bmiHeader.biBitCount 
= 32;     
    info.bmiHeader.biCompression 
= BI_RGB;     
    info.bmiHeader.biSizeImage 
= w * h * 32 / 8;     
    HDC hdc 
= ::GetDC(NULL);     
    hBmpSection 
= ::CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void**)bmpData, NULL, 0);     
    ::ReleaseDC(NULL, hdc);     
    
return hBmpSection != NULL;     
}     
/*   
@ Function  : FlushToDib   
@ brief     : 将Gdiplus的Bitamp转存到gdi的内存位图上   
@ parameter : [in] pMemBitmap   
@ parameter : [in, out] bmpData 就是上面的函数创建处理的内存位图的数据区   
@ return    :    
@ remark    :    
@
*/     
void FlushToDib(IN Bitmap* pMemBitmap, IN OUT BYTE** bmpData)     
{     
    BitmapData data;     
    data.Height 
= pMemBitmap->GetHeight();     
    data.PixelFormat 
= pMemBitmap->GetPixelFormat();     
    data.Scan0 
= *bmpData;     
    data.Stride 
= (pMemBitmap->GetWidth() * 32 / 8);     
    data.Width 
= pMemBitmap->GetHeight();     
    Rect rct(
00, pMemBitmap->GetWidth(), pMemBitmap->GetHeight());     
    pMemBitmap
->LockBits(&rct, ImageLockModeRead | ImageLockModeUserInputBuf, pMemBitmap->GetPixelFormat(), &data);     
    pMemBitmap
->UnlockBits(&data);     
}     
// m_hBmpSection是InitDialog时用CreateDib创建出来的     
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)     
{     
    CPaintDC dc(m_hWnd);     
    HDC hdcMen 
= ::CreateCompatibleDC(dc.m_hDC);     
    HBITMAP hOldBmp 
= (HBITMAP)::SelectObject(hdcMen, m_hBmpSection);     
    
// 宽度和高度自己取下     
    ::BitBlt(dc.m_hDC, 00, m_width, m_height, hdcMen, 00, SRCCOPY);     
    ::SelectObject(hdcMen, hOldBmp);     
    ::DeleteDC(hdcMen);     
    
return TRUE;     
}    
/* 
@ Function  : CreateDib 
@ brief     : 创建内存位图 
@ parameter : w 
@ parameter : h 
@ parameter : [out] hBmpSection OnPaint中会把这个位图直接往dc上贴 
@ parameter : [out] bmpData 位图的实际数据,gdiplus的会把东西都话到上面 
@ return    :  
@ remark    :  
@
*/  
BOOL CreateDib(
int w, int h, OUT HBITMAP& hBmpSection, OUT BYTE** bmpData)  
{  
    BITMAPINFO info 
= {0};  
    info.bmiHeader.biSize 
= sizeof(info.bmiHeader);  
    info.bmiHeader.biWidth 
= w;  
    info.bmiHeader.biHeight 
= -h;  
    info.bmiHeader.biPlanes 
= 1;  
    info.bmiHeader.biBitCount 
= 32;  
    info.bmiHeader.biCompression 
= BI_RGB;  
    info.bmiHeader.biSizeImage 
= w * h * 32 / 8;  
    HDC hdc 
= ::GetDC(NULL);  
    hBmpSection 
= ::CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void**)bmpData, NULL, 0);  
    ::ReleaseDC(NULL, hdc);  
    
return hBmpSection != NULL;  
}  
/* 
@ Function  : FlushToDib 
@ brief     : 将Gdiplus的Bitamp转存到gdi的内存位图上 
@ parameter : [in] pMemBitmap 
@ parameter : [in, out] bmpData 就是上面的函数创建处理的内存位图的数据区 
@ return    :  
@ remark    :  
@
*/  
void FlushToDib(IN Bitmap* pMemBitmap, IN OUT BYTE** bmpData)  
{  
    BitmapData data;  
    data.Height 
= pMemBitmap->GetHeight();  
    data.PixelFormat 
= pMemBitmap->GetPixelFormat();  
    data.Scan0 
= *bmpData;  
    data.Stride 
= (pMemBitmap->GetWidth() * 32 / 8);  
    data.Width 
= pMemBitmap->GetHeight();  
    Rect rct(
00, pMemBitmap->GetWidth(), pMemBitmap->GetHeight());  
    pMemBitmap
->LockBits(&rct, ImageLockModeRead | ImageLockModeUserInputBuf, pMemBitmap->GetPixelFormat(), &data);  
    pMemBitmap
->UnlockBits(&data);  
}  
// m_hBmpSection是InitDialog时用CreateDib创建出来的  
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)  
{  
    CPaintDC dc(m_hWnd);  
    HDC hdcMen 
= ::CreateCompatibleDC(dc.m_hDC);  
    HBITMAP hOldBmp 
= (HBITMAP)::SelectObject(hdcMen, m_hBmpSection);  
    
// 宽度和高度自己取下  
    ::BitBlt(dc.m_hDC, 00, m_width, m_height, hdcMen, 00, SRCCOPY);  
    ::SelectObject(hdcMen, hOldBmp);  
    ::DeleteDC(hdcMen);  
    
return TRUE;  
}   

 

 

以上代码是从我自己的工程中抽取出来的,没有定义的变量请自行处理。

最近太忙没空也懒得去抽成一个简单的例子。但最重要的部分已经都在上面的代码中呈现。

再啰嗦下我的的使用流程:

1、InitDialog中用CreateDib创建出hBmpSection,并得到他的bmpData

2、程序的否个地方把你的东西画到gdi+的Bitmap上

3、调用FlushToDib,把你的Bitmap写到第一步的bmpData中

4、OnPaint中间hBmpSection用BitBlt到dc上

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/debehe/archive/2009/07/05/4322356.aspx

 

posted @ 2010-10-20 15:32  逆时针  阅读(2985)  评论(0编辑  收藏  举报