我在C ++ / MFC应用程序中使用GDI +,而且我无法避免在窗口大小调整的情况下闪烁。
我已经尝试了这些步骤:
OnEraseBkGnd()
返回TRUE; OnCtlColor()
上返回NULL; 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的系统上)。 因此,最大限度地减少闪烁是尽可能快地绘制所有东西(通过增加在刷新周期内完成所有绘图的机会来减少撕裂),并避免透支(绘制屏幕的一部分,然后绘制其他东西顶部:风险呈现给用户一个部分绘制的屏幕)。
让我们来讨论一下这里介绍的技术:
无所事事OnEraseBkgnd() :通过防止窗口的无效区域被窗口的背景颜色填充来帮助避免过度绘制。 当你在WM_PAINT处理过程中再次绘制整个区域的时候很有用,就像在双缓冲绘图的情况下一样…但是请参阅注意避免在 WM_PAINT 方法 之后绘制的透支 。
为OnCtlColor()返回NULL :这实际上不应该做任何事情 …除非您的窗体上有子控件。 在这种情况下,请参阅注意避免透支,而不是在 WM_PAINT 方法 之后进行绘图 。
双缓冲绘图 :通过将实际的屏幕绘图减少到单个BitBLT来帮助避免撕裂(以及可能的透支)。 可能会损害绘图所需的时间:硬件加速无法使用(尽管使用GDI +,使用任何硬件辅助绘图的机会都很小),必须为每次重绘创建并填充屏幕外位图,并且整个窗口必须重新绘制每个重绘。 请参阅有关高效双缓冲的说明 。
对于BitBlt,使用GDI调用而不是GDI + :这通常是一个好主意 – Graphics::DrawImage()
可能非常慢。 我甚至发现正常的GDI BitBlt()
调用在某些系统上更快。 玩这个,但只有在尝试了一些其他的建议之后。
避免强制在每个调整大小( CS_VREDRAW , CS_HREDRAW )上完全重绘的窗口类样式 :这将有所帮助,但前提是在大小更改时不需要重绘整个窗口。
当一个窗口的全部或部分无效时,它将被删除并重新绘制。 如前所述,如果您打算重新绘制整个无效区域,则可以跳过擦除。 但是 ,如果您正在使用子窗口,则必须确保父窗口不会清除屏幕的区域。 WS_CLIPCHILDREN样式应该在所有父窗口上设置 – 这将防止子窗口(包括您的视图)占用的区域被绘制。
如果您的窗体上有托管的子控件,您将需要使用WS_CLIPCHILDREN样式来避免绘制它们(并且随后被其绘制)。请注意,这会影响BitBlt例程的速度。
现在,每当视图自行绘制时,您都会创建一个新的后台缓冲图像。 对于较大的窗口,这可能代表大量的内存被分配和释放, 并将导致显着的性能问题。 我建议在您的视图对象中保留一个动态分配的位图,根据需要重新分配以匹配视图的大小。
请注意,当窗口被调整大小时,这将导致与当前系统一样多的分配,因为每个新的大小将需要分配一个新的后台缓冲区位图来匹配它 – 你可以通过四舍五入到下一个最大倍数4,8,16等等,让你避免重新分配每一个微小的变化的大小。
请注意,如果窗口的大小自上次渲染到后台缓冲区以来没有改变,那么在窗口失效时不需要重新渲染它 – 只需将已渲染的图像放到屏幕。
此外,分配一个匹配屏幕位深度的位图。 你正在使用的Bitmap
的构造函数将默认为32bpp,ARGB-layout; 如果这不符合屏幕,那么它将不得不被转换。 考虑使用GDI方法CreateCompatibleBitmap()
来获取匹配的位图。
最后…我假设你的示例代码就是这样一个例子。 但是,如果实际上除了将现有的图像渲染到屏幕上之外什么都不做,那么您根本不需要维护一个后台缓冲区 – 直接从图像中直接进行Blt(并将图像的格式提前转换为匹配屏幕)。
你可能会尝试使用老式的GDI而不是GDI +写入DC,尤其是因为你已经在缓冲图像了。 使用Bitmap :: LockBits访问原始位图数据,创建一个BITMAPINFO结构,并使用SetDIBitsToDevice来显示位图。
确保窗口的窗口类不包含样式中的CS_VREDRAW和CS_HREDRAW标志。
请参阅http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx
通过使用Direct3D来“告诉”你什么时候出现vsync,你可能会得到一些牵引力,所以你可以在适当的时候进行BitBlt /更新。 请参阅GDI vsync以避免撕裂 (尽管将某些事情缩小到一个小BitBlt对于某些情况可能“足够好”)。
另外请注意,看起来GDI BitBlt与屏幕vsync不同步。 比BitBlt看到更快 。
另外请注意,使用CAPTUREBLT(允许您捕捉透明窗口)会导致鼠标闪烁(如果航空不在使用中)(如果使用的话)。
这个链接包含一些有用的信息: http : //www.catch22.net/tuts/flicker-free-drawing
(我知道这是线程的一个非常晚的补充,但是这是为了减少我的Win32应用程序闪烁时发现它的任何人(如我)…)
窗户上是否有儿童窗户? 窗口管理器通过发送一个WM_ERASEBKGND消息来获取父窗口来清除它的背景,然后发送一个wM_PAINT消息 – 大概这个映射到你的wx :: OnDraw方法。 然后它遍历每个子控件,并让它们自己绘制。
如果这是你的情况…使用Vistas新的航空看会解决你的问题,航空桌面窗口管理器自动窗口合成。 随着较旧的窗口管理器它皮塔饼。