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(
0 0 , 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,  0 0 , m_width, m_height, hdcMen,  0 0 , 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(
0 0 , 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,  0 0 , m_width, m_height, hdcMen,  0 0 , 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

 

你可能感兴趣的:(优化)