使用GDI +和C ++减少闪烁 [英] Reduce flicker with GDI+ and C++

查看:144
本文介绍了使用GDI +和C ++减少闪烁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C ++ / MFC应用程序中使用GDI +,我只是看起来不能避免闪烁每当窗口调整大小。



我已经尝试过步骤:




  • OnEraseBkGnd(); $ b
  • 根据此代码在 OnCtlColor();

  • 上使用双缓冲返回NULL



  void vwView :: OnDraw )
{
CRect rcClient;
GetClientRect(rcClient);

位图bmp(rcClient.Width(),rcClient.Height());
图形图形(& bmp);

graphics.DrawImage(m_image,rcClient.left,rcClient.top);

图形格式(pDC-> m_hDC);
grph.DrawImage(& bmp,0,0);
}

我做错了什么?

解决方案

要完全避免闪烁,您需要完成全部

em>绘制在屏幕更新之间的间隔。 Windows不提供任何简单的方法来完成这个正常的窗口绘画(Vista提供复合图纸通过 DWM ,但这不能甚至在运行Vista的系统上依赖)。因此,您可以最大限度地减少闪烁的方法是尽快绘制所有内容(通过增加刷新周期内完成所有绘图的机会,减少撕裂),并避免过度绘制(绘制部分屏幕,然后在顶部绘制其他东西:冒着向用户呈现部分绘制屏幕的风险)。



让我们讨论到目前为止的技术:




  • strong> Do-nothing OnEraseBkgnd() :通过防止窗口的无效区域被窗口的背景颜色填充,有助于避免过度绘制。在 处理时再次绘制整个区域时非常有用,如在双缓冲绘图的情况下,但请参阅


  • OnCtlColor()返回NULL

  • / i> :除非您的表单上有子控件,否则这应该不会实际执行任何操作。在这种情况下,请参阅


  • 双缓冲绘图:通过将实际屏幕上的绘图缩减为一个 BitBLT 。可能伤害绘制所需的时间虽然:硬件加速不能使用(虽然使用GDI +,任何硬件辅助绘图使用的机会是非常苗条),必须为每个重绘创建和填充一个离屏位图,并且整个窗口必须重绘每个重画。


  • 使用GDI调用而不是GDI +调用BitBlt 这通常是个好主意 - Graphics :: DrawImage()可能很慢。我甚至发现正常的GDI BitBlt() 调用在一些系统上更快。


  • 避免在每次调整大小时强制重绘的窗口类样式i> CS_VREDRAW CS_HREDRAW :这将有所帮助,但前提是您不需要在大小更改时重绘整个窗口。




通过防止在 WM_PAINT 方法之前绘制而避免覆盖的注意事项



当窗口的全部或部分无效时,它将被擦除和重绘。如上所述,如果计划重绘整个无效区域,则可以跳过清除。 ,如果您使用子窗口,则必须确保父窗口不会同时擦除屏幕的区域。应该在所有父窗口上设置 WS_CLIPCHILDREN 样式 - 这将防止绘制子窗口(包括您的视图)占用的区域。



< h3> > > 你需要使用 WS_CLIPCHILDREN 风格,以避免绘制它们(并且随后被他们绘制)。注意,这会影响BitBlt程序的速度。



有关高效双缓冲的注意事项



现在,您每次视图绘制时都创建一个新的后缓冲映像对于较大的窗口,这可能表示大量的内存被分配和释放,并且将导致显着的性能问题。我建议在您的视图对象中保留动态分配的位图,重新分配



请注意,当窗口正在调整大小时,这将导致与当前系统一样多的分配,因为每个新的大小将需要一个新的后台缓冲区位图被分配以匹配它 - 你可以通过舍入尺寸到下一个最大的倍数4,8,16等等来缓解疼痛,允许你避免重新分配在每个微小的尺寸变化。



请注意,如果窗口的大小自上次渲染到后台缓冲区后没有改变,则无需重新渲染窗口无效 - 只是将已经渲染的图像放大到屏幕上。



此外,分配一个与屏幕的位深度匹配的位图。 Bitmap 的构造函数默认使用32bpp,ARGB-layout;如果这不匹配屏幕,那么它将必须转换。考虑使用 CreateCompatibleBitmap() 以获得匹配的位图。



最后...我假设你的示例代码就是一个说明性的代码片段。但是,如果除了将现有图像渲染到屏幕上之外,实际上什么都不做,那么你根本不需要维护一个后台缓冲区 - 只需直接从图像Blt(并提前转换图像的格式匹配屏幕)。


I'm using GDI+ in a C++/MFC application and I just can't seem to avoid flickering whenever the window is resized.

I have already tried these steps:

  • returned TRUE on OnEraseBkGnd();
  • returned NULL on OnCtlColor();
  • used double buffering according to this code:

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);
}

Am I doing something wrong? Or is there another way to achieve this?

解决方案

To completely avoid flicker, you would need to complete all drawing in the interval between screen updates. Windows does not provide any easy means of accomplishing this for normal window painting (Vista provides composite drawing via the DWM, but this cannot be relied on even on systems running Vista). Therefore, the best you can do to minimize flicker is to draw everything as quickly as possible (reduce tearing by increasing your chances of completing all drawing within a refresh cycle), and avoid overdraw (drawing part of the screen and then drawing something else over the top: risks presenting user with a partially-drawn screen).

Let's discuss the techniques presented here so far:

  • Do-nothing OnEraseBkgnd(): helps to avoid over-draw by preventing the invalidated area of the window from being filled with the window's background color. Useful when you will be drawing the entire area again during WM_PAINT handling anyway, as in the case of double-buffered drawing... but see Notes on avoiding overdraw by preventing drawing after your WM_PAINT method.

  • Returning NULL for OnCtlColor(): this shouldn't actually do anything... unless you have child controls on your form. In that case, see Notes on avoiding overdraw by preventing drawing after your WM_PAINT method instead.

  • Double buffered drawing: helps to avoid tearing (and potentially overdraw as well), by reducing the actual on-screen drawing to a single BitBLT. May hurt the time needed for drawing though: hardware acceleration cannot be used (although with GDI+, the chances of any hardware-assisted drawing being used are quite slim), an off-screen bitmap must be created and filled for each redraw, and the entire window must be repainted for each redraw. See Notes on efficient double-buffering.

  • Using GDI calls rather than GDI+ for the BitBlt: This is often a good idea - Graphics::DrawImage() can be very slow. I've even found the normal GDI BitBlt() call to be faster on some systems. Play around with this, but only after trying a few other suggestions first.

  • Avoiding window class styles that force a full redraw on each resize (CS_VREDRAW, CS_HREDRAW): This will help, but only if you don't need to redraw the entire window when size changes.

Notes on avoiding overdraw by preventing drawing prior to your WM_PAINT method

When all or a portion of a window is invalidated, it will be erased and repainted. As already noted, you can skip erasing if you plan to repaint the entire invalid area. However, if you are working with a child window, then you must ensure that parent window(s) are not also erasing your area of the screen. The WS_CLIPCHILDREN style should be set on all parent windows - this will prevent the areas occupied by child windows (including your view) from being drawn on.

Notes on avoiding overdraw by preventing drawing after your WM_PAINT method

If you have any child controls hosted on your form, you will want to use the WS_CLIPCHILDREN style to avoid drawing over them (and subsequently being over drawn by them. Be aware, this will impact the speed of the BitBlt routine somewhat.

Notes on efficient double-buffering

Right now, you're creating a new back-buffer image each time the view draws itself. For larger windows, this can represent a significant amount of memory being allocated and released, and will result in significant performance problems. I recommend keeping a dynamically-allocated bitmap in your view object, re-allocating it as needed to match the size of your view.

Note that while the window is being resized, this will result in just as many allocations as the present system, since each new size will require a new back buffer bitmap to be allocated to match it - you can ease the pain somewhat by rounding dimensions up to the next largest multiple of 4, 8, 16, etc., allowing you to avoid re-allocated on each tiny change in size.

Note that, if the size of the window hasn't changed since the last time you rendered into the back buffer, you don't need to re-render it when the window is invalidated - just Blt out the already-rendered image onto the screen.

Also, allocate a bitmap that matches the bit depth of the screen. The constructor for Bitmap you're currently using will default to 32bpp, ARGB-layout; if this doesn't match the screen, then it will have to be converted. Consider using the GDI method CreateCompatibleBitmap() to get a matching bitmap.

Finally... I assume your example code is just that, an illustrative snippet. But, if you are actually doing nothing beyond rendering an existing image onto the screen, then you don't really need to maintain a back buffer at all - just Blt directly from the image (and convert the format of the image ahead of time to match the screen).

这篇关于使用GDI +和C ++减少闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆