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上的代码片段,希望对大家有所帮助。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->/*   
@ 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上

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值