这周在用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