c++ - 使用GDI +和C++减少闪烁

标签 c++ windows gdi+

我在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):这会有所帮助,但前提是在大小更改时不需要重绘整个窗口。

  • 关于通过防止在WM_PAINT方法之前进行绘制来避免 overdraw 的注意事项
    当窗口的全部或一部分无效时,它将被擦除并重新绘制。如前所述,如果您打算重新粉刷整个无效区域,则可以跳过擦除。 但是,如果您正在使用子窗口,则必须确保父窗口不会同时擦除屏幕区域。应该在所有父窗口上设置WS_CLIPCHILDREN样式-这将防止在子窗口(包括您的 View )上绘制区域。
    关于通过在WM_PAINT方法之后防止绘制来避免 overdraw 的注意事项
    如果您的窗体上托管有任何子控件,则您将希望使用WS_CLIPCHILDREN样式来避免对其进行绘制(并随后被它们过度绘制。请注意,这会在一定程度上影响BitBlt例程的速度。
    关于高效双缓冲的注意事项
    现在,每次 View 绘制时都会创建一个新的后缓冲图像。对于较大的窗口,这可能表示分配和释放了大量内存,并且将导致严重的性能问题。我建议在 View 对象中保留一个动态分配的位图,并根据需要重新分配它以匹配 View 的大小。
    请注意,在调整窗口大小时,这将导致与当前系统一样多的分配,因为每个新大小都需要分配一个新的后缓冲区位图来匹配它-您可以通过向上舍入尺寸来减轻痛苦扩展到4、8、16等的下一个最大倍数,这样您就可以避免在每次微小更改时重新分配。
    请注意,如果自上次渲染到后缓冲区以来窗口的大小没有改变,则在窗口无效时无需重新渲染它-只需将已经渲染的图像弹出到屏幕。
    另外,分配与屏幕的位深度匹配的位图。您当前使用的Bitmap的构造函数将默认为32bpp,ARGB布局;如果与屏幕不匹配,则必须进行转换。考虑使用GDI方法 CreateCompatibleBitmap() 获得匹配的位图。
    最后...我假设您的示例代码就是这样,是一个说明性代码段。但是,如果您实际上除了在屏幕上渲染现有图像外什么也不做,那么您根本不需要维护后台缓冲区-只需直接从图像中进行Blt(并将图像格式提前转换为匹配屏幕)。

    关于c++ - 使用GDI +和C++减少闪烁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/197948/

    相关文章:

    c++ - 没有自动完成 Visual Studio 2010

    windows - 如何使用 docker 在 Windows Server 2019 中使用 nginx 设置反向代理?

    c# - 将操作图形绘制到另一个图形中

    ASP.NET 创建缩略图服务器端

    c++ - 确定元素是否包含在多重映射中的最快方法?

    c++ - C++ 有没有像 getdelim 这样的函数?

    python - 保持打开一个cmd窗口

    c# - 在给定矩形的情况下在 GDI+ 中绘制三角形

    c++ - 在字符串上使用调整大小

    c++ - 如何在我的 vc++ 代码中包含 CTime