我在C++/MFC应用程序中使用GDI +,每当窗口调整大小时,我似乎无法避免闪烁.
我已经尝试过这些步骤:
返回TRUE OnEraseBkGnd()
;
返回NULL OnCtlColor()
;
根据此代码使用双缓冲:
void vwView::OnDraw(CDC* pDC) { CRect rcClient; GetClientRect(rcClient); Bitmap bmp(rcClient.Width(), rcClient.Height()); Graphics graphics(&bmp); graphics.DrawImage(m_image, rcClient.left, rcClient.top); Graphics grph(pDC->m_hDC); grph.DrawImage(&bmp, 0, 0); }
难道我做错了什么?或者有另一种方法来实现这一目标吗?
要完全避免闪烁,您需要在屏幕更新之间的间隔内完成所有绘图.Windows不提供任何简单的方法来实现普通窗口绘制(Vista通过DWM提供复合绘图,但即使在运行Vista的系统上也不能依赖它).因此,尽量减少闪烁的最佳方法是尽可能快地绘制所有内容(通过增加在刷新周期内完成所有绘图的机会来减少撕裂),并避免过度绘制(绘制部分屏幕然后绘制其他内容) top:冒险向用户展示部分绘制的屏幕).
让我们讨论到目前为止在这里介绍的技术:
Do-nothing OnEraseBkgnd():通过防止窗口的无效区域被窗口的背景颜色填充来帮助避免过度绘制.有用的,当你将在其中进行绘制整个区域再次WM_PAINT处理反正,在双缓冲绘图的情况下,...但看到上防止您的图纸后透支避免注 WM_PAINT 方法.
为OnCtlColor()返回NULL:这实际上不应该做任何事情 ......除非你的表单上有子控件.在这种情况下,请参阅WM_PAINT 方法后通过阻止绘制来避免过度绘制的注意事项.
双缓冲绘图:通过将实际的屏幕绘制减少到单个BitBLT,有助于避免撕裂(并可能过度绘制).可能会损坏绘图所需的时间:不能使用硬件加速(尽管使用GDI +,使用任何硬件辅助绘图的可能性非常小),必须为每次重绘创建并填充屏幕外位图,并且必须为每次重绘重新绘制整个窗口.请参阅有关高效双缓冲的说明.
使用GDI调用而不是GDI +用于BitBlt:这通常是一个好主意 - Graphics::DrawImage()
可能非常慢.我甚至发现正常的GDI BitBlt()
调用在某些系统上更快.玩这个,但只有在先尝试了一些其他建议之后.
避免强制在每次调整大小时完全重绘的窗口类样式(CS_VREDRAW,CS_HREDRAW):这将有所帮助,但前提是当大小更改时不需要重绘整个窗口.
当窗口的全部或部分无效时,它将被擦除并重新绘制.如前所述,如果您计划重新绘制整个无效区域,则可以跳过删除.但是,如果您正在使用子窗口,则必须确保父窗口不会擦除您的屏幕区域.在WS_CLIPCHILDREN风格应该在所有父窗口中设置-这将防止被绘制在子窗口(包括您的视图)占领的地区.
如果您有任何托管窗体上子控件,你将要使用WS_CLIPCHILDREN风格,以避免对他们绘制(随后被过度他们绘制的.要知道,这将在一定程度影响的BitBlt常规的速度.
现在,每次视图绘制自己时,您都会创建一个新的后缓冲区图像.对于较大的窗口,这可能表示分配和释放大量内存,并将导致严重的性能问题.我建议在视图对象中保留动态分配的位图,根据需要重新分配它以匹配视图的大小.
请注意,在调整窗口大小时,这将导致与当前系统一样多的分配,因为每个新大小都需要分配新的后缓冲位图以匹配它 - 您可以通过舍入维度来减轻痛苦到4,8,16等的下一个最大倍数,允许你避免重新分配每个微小的变化.
请注意,如果自上次渲染到后台缓冲区后窗口的大小没有改变,则在窗口失效时无需重新渲染窗口 - 只需将已渲染的图像Blt到屏幕.
另外,分配一个与屏幕位深相匹配的位图.Bitmap
您当前使用的构造函数将默认为32bpp,ARGB-layout; 如果这与屏幕不匹配,则必须进行转换.考虑使用GDI方法CreateCompatibleBitmap()
来获取匹配的位图.
最后......我假设你的示例代码只是一个说明性的代码片段.但是,如果你实际上除了将现有图像渲染到屏幕上之外什么都不做,那么你根本不需要维护一个后台缓冲区 - 只需直接从图像中Blt(并提前将图像格式转换为匹配屏幕).