【问题标题】:Flashing in OnPaint在 OnPaint 中闪烁
【发布时间】:2012-02-27 18:45:06
【问题描述】:

我有一个 CWind 类的孩子,我正在订阅 OnPaint 事件:

BEGIN_MESSAGE_MAP(MyListBox, CWnd)
    ON_WM_PAINT()
END_MESSAGE_MAP()

如果在 OnPaint 处理程序中我在窗口中绘制一些简单的东西,比如边框,一切都很好。但是如果我添加Sleep(50);(通过这样做我模拟了一些硬绘图操作)我的窗口将闪烁。我不明白为什么会发生这种情况......问题可能是 OnPaint 函数经常被调用,大约每秒 2-3 次。

更新:我正在使用双分流:首先我在 PaintDeviceContent 中绘制窗口内容,然后将此 DC 复制到窗口的 DC。

Update2:这里是代码:

void CDirectionsListBox::OnPaint() 
{ 
    CRect rectClient;
    GetClientRect(rectClient);

    CPaintDC dc(this); // device context for painting

    CDC DCMem;
    DCMem.CreateCompatibleDC( &dc );

    // Draw window here, workign with DCMem

    dc.BitBlt(0, 0, rectClient.Width(), rectClient.Height(), &DCMem, 0, 0, SRCCOPY);
}

【问题讨论】:

  • 请显示您复制设备上下文的代码。

标签: winapi visual-c++ mfc gdi


【解决方案1】:

默认情况下,背景是通过使用背景画笔清除客户区来绘制的。你想把它关掉。处理 WM_ERASEBKGND 并且什么都不做,因为无论如何你都会在任何现有图像的顶部进行 blit。

另见How to avoid flicker while handling WM_ERASEBKGND in Windows dialog

【讨论】:

  • 问题是闪烁不是每次OnPaint 发生时都会发生。但如果我增加OnPaint 执行时间 - 每次都会发生闪烁。所以我想这在某种程度上与函数执行时间有关,但为什么 - 这就是我无法理解的.. 我已经尝试处理 WM_ERASEBKGND 但仍然闪烁,我返回 TRUE 或 FALSE 都没关系..
  • 这是正确的答案。如果它对您没有帮助,那么您的实现还有其他一些不相关的问题。 MFC 中的双缓冲涉及三件事:将所有内容绘制到位图中(例如 CMemDC),避免调用基类(或确保发送 WM_PAINT 消息与缓冲的 HDC 一起发送),从WM_ERASEBKGND.
  • @Seekeer:当然,在 blit 影响闪烁之前,您在OnPaint 中花费的时间,因为这决定了擦除背景在屏幕上的时间。如果您在刷新周期中同时擦除和 blit,则擦除的背景永远不会超过视频卡(不会进入物理显示器)。如果擦除和 blit 之间存在延迟,则被擦除的屏幕将在显示器上显示几帧,并被用户注意到。
【解决方案2】:

您的窗口闪烁的原因是它试图显示尚未完成绘制的内容。

使用 MFC 绘图时,如果绘图操作不重要,则需要手动双缓冲要绘制的窗口。本质上,您要做的是在内存中创建一个绘图上下文并改为绘制到该上下文。绘制完成后,您将在内存上下文中找到的内容复制到窗口上下文中。

【讨论】:

  • 我相信问题出在你睡在 OnPaint 中以模拟复杂的绘图。如果您使用 OnPaint 进行绘制,您希望它快速返回以将控制权交还给 Windows 消息泵。你真的应该在其他地方对你的后台缓冲区进行绘图,并且在 OnPaint 方法中只将后台缓冲区内容blit到窗口。
  • 你建议将“昂贵”的绘图放在辅助方法中并称之为异步?
  • 这确实取决于您的应用程序需求,但可能是的。
  • 没有理由让它异步。只是不要在擦除和 blit 之间进行复杂的渲染。避免这种情况的最好方法是完全消除擦除,但在 OnPaint 开头对旧图像进行 blit 也会产生预期的效果(以浪费 GPU 带宽为代价,用于现有数据的擦除和重新 blit)跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多