为了完全避免闪烁,您需要在屏幕更新之间的时间间隔内完成所有绘制。Windows没有提供任何简单的方法来实现这一点(Vista通过
DWM提供复合绘图,但即使在运行Vista的系统上也不能依赖它)。因此,为了最大程度地减少闪烁,您可以尽快绘制所有内容(通过增加在刷新周期内完成所有绘制的机会来
减少撕裂),并避免过度绘制(绘制屏幕的一部分,然后在其上绘制其他东西:可能会呈现给用户部分绘制的屏幕)。
让我们讨论到目前为止介绍的技术:
Do-nothing OnEraseBkgnd(): 避免在窗口的无效区域填充窗口背景颜色,从而帮助避免过度绘制。当您将在WM_PAINT处理中重新绘制整个区域时很有用,例如双缓冲绘制的情况...但请参见有关通过防止在WM_PAINT方法之后进行绘制以避免过度绘制的注释。
对于 OnCtlColor() 返回NULL: 除非您的窗体上有子控件,否则实际上不会做任何事情...在这种情况下,请参见有关通过防止在WM_PAINT方法之后进行绘制以避免过度绘制的注释。
双缓冲绘图: 通过将实际屏幕绘制减少到单个BitBLT来帮助避免撕裂(以及潜在的过度绘制)。但可能会损害绘制所需的时间:无法使用硬件加速(尽管使用GDI+,任何硬件辅助绘制的机会非常渺茫),必须为每次重绘创建并填充一个离屏位图,并且必须为每次重绘重新绘制整个窗口。请参见有关有效双缓冲的注释。
使用GDI调用而不是GDI+进行BitBlt: 这通常是个好主意 - Graphics::DrawImage()
可能非常慢。在某些系统上,我甚至发现普通的GDI BitBlt()
调用更快。尝试其他建议后再试验一下。
避免窗口类样式强制在每次调整大小时进行完全重绘(CS_VREDRAW,CS_HREDRAW): 这将有所帮助,但仅当您不需要在大小更改时重新绘制整个窗口时才有效。
避免在WM_PAINT方法之前绘制以避免过度绘制的注意事项
当窗口的所有或部分区域无效时,它将被擦除和重绘。如前所述,如果您计划重新绘制整个无效区域,则可以跳过擦除。 然而,如果您正在使用子窗口,则必须确保父窗口未擦除屏幕上的您的区域。应在所有父窗口上设置WS_CLIPCHILDREN样式-这将防止子窗口(包括您的视图)占用的区域被绘制。
避免在WM_PAINT方法之后绘制以避免过度绘制的注意事项
如果您在表单上托管了任何子控件,则希望使用WS_CLIPCHILDREN样式以避免覆盖它们(并随后被它们覆盖)。请注意,这会在某种程度上影响BitBlt例程的速度。
有效双缓冲的注意事项
现在,每次视图绘制自身时,您都会创建一个新的后备缓冲图像。对于较大的窗口,这可能代表分配和释放大量内存,并且将导致显著的性能问题。我建议在您的视图对象中保留一个动态分配的位图,根据需要重新分配它以匹配您的视图的大小。
请注意,当窗口被调整大小时,这将导致与当前系统一样多的分配,因为每个新大小都需要分配一个新的后备缓冲位图来匹配它 - 您可以通过将维度舍入到下一个最大的4、8、16等倍数来减轻痛苦,从而避免在每个微小的尺寸变化时重新分配。
请注意,如果窗口的大小自上次渲染到后备缓冲区以来没有改变,那么在窗口失效时不需要重新渲染它 - 只需将已经渲染好的图像 Blt 到屏幕上即可。
此外,请分配一个与屏幕位深匹配的位图。您目前使用的 Bitmap 构造函数将默认为32bpp,ARGB布局;如果这与屏幕不匹配,则必须进行转换。考虑使用 GDI 方法
CreateCompatibleBitmap() 获取匹配的位图。
最后...我假设你的示例代码只是一个说明性的片段。但是,如果您实际上什么都不做,只是将现有图像呈现到屏幕上,那么您根本不需要维护后备缓冲区 - 只需直接从图像 Blt(并将图像格式提前转换以匹配屏幕)。