绘图时防止闪烁

Posted

技术标签:

【中文标题】绘图时防止闪烁【英文标题】:Prevent Flickering When Drawing 【发布时间】:2015-02-27 01:23:27 【问题描述】:

所以我有这段代码可以在我的屏幕上绘制一个矩形:

LOGBRUSH m_LogBrush;
HBRUSH m_hBrush;
HPEN m_hPen;

HDC m_hDC;

void DrawBox(int x, int y, int r, int g, int b, int size, int thickness)

    // Brush style to hollow
    m_LogBrush.lbStyle = BS_NULL;

    // Create a logical brush and select into the context
    m_hBrush = CreateBrushIndirect(&m_LogBrush);
    SelectObject(m_hDC, m_hBrush);

    // Create a logical pen and select into the context
    m_hPen = CreatePen(PS_SOLID, thickness, RGB(r, g, b));
    SelectObject(m_hDC, m_hPen);

    // Draw the rectangle
    Rectangle(m_hDC, (x - size / 2), (y - size / 2), (x + size / 2), (y + size / 2));

    // Remove the object
    DeleteObject(m_hBrush);
    DeleteObject(m_hPen);

但是,当在循环内重复调用时,它会在屏幕上闪烁。我想知道是否有办法防止这种闪烁?

任何帮助将不胜感激。

谢谢

【问题讨论】:

我该怎么做? 你不应该需要双缓冲。您没有显示相关代码。显示更多。显示闪烁的完整程序。 更准确地说,你是如何、何时、在哪里绘制的?你的目标是什么? 很久以前,当我做Win32编程时,所有的绘图都必须在响应OnPaint消息时完成,否则会闪烁 @sp2:这完全是假的。没有OnPaint 消息。 MFC 或 WTL 具有由框架调用以响应 WM_PAINT 消息的 OnPaint 方法。闪烁是在绘制周期中多次更改像素的结果,无论是否在WM_PAINT 消息处理程序之外。为了防止闪烁,每个像素在一个绘制周期中最多只能更改一次。双缓冲是实现这一目标的一种方式。 【参考方案1】:

这不应该是一个答案,但我不能在 cmets 中发布代码:

您的代码中有许多 GDI 泄漏。

复制/粘贴以下代码并在闪烁时报告我们减少

void DrawBox(int x, int y, int r, int g, int b, int size, int thickness)

    // Brush style to hollow
    m_LogBrush.lbStyle = BS_NULL;

    // Create a logical brush and select into the context
    m_hBrush = CreateBrushIndirect(&m_LogBrush);
    HBRUSH hbrOldBrush = SelectObject(m_hDC, m_hBrush);

    // Create a logical pen and select into the context
    m_hPen = CreatePen(PS_SOLID, thickness, RGB(r, g, b));
    HPEN hpOldPen = SelectObject(m_hDC, m_hPen);

    // Draw the rectangle
    Rectangle(m_hDC, (x - size / 2), (y - size / 2), (x + size / 2), (y + size / 2));

    // Remove the object
    SelectObject(m_hDC, hbrOldBrush);  // first you must restore DC to original state
    SelectObject(m_hDC, hpOldPen);     // same here
    DeleteObject(m_hBrush);
    DeleteObject(m_hPen);

在 MSDN 上阅读有关 GDI 泄漏的信息。

这应该会减少闪烁,但要完全消除闪烁,您应该执行以下操作:

从您的窗口类定义中删除CS_VREDRAW | CS_HREDRAW; 在您的窗口过程中返回1L(或在您的对话框过程中返回TRUE)以响应WM_ERASEBKGND; 在内存位图上绘制所有内容,然后将BitBlt 绘制到您的m_hDC -> 这称为双缓冲(您可以在网上找到很多示例);

【讨论】:

很少需要双缓冲。如果一切都做对了,就不需要了 @DavidHeffernan:在这种情况下,也许确实不是。让我们假设他有蓝色背景并在那里打电话给他的DrawBox。当调整窗口大小时,WM_ERASEBKGND 将用蓝色填充背景,然后才绘制红色矩形。用户将按照我描述的相同顺序感知这一点。他可以加倍缓冲以躲避这一点,或者他可以以某种方式剪裁红色矩形。比他不需要双重缓冲,但我不相信他能做到这一点->如果我弄错了,请纠正我。最好的问候。【参考方案2】:
    /*Hi You May Change Your Code To This*/ 

    LOGBRUSH m_LogBrush;
    //HBRUSH m_hBrush;
    //HPEN m_hPen;
    //HDC m_hDC;
    HWND m_hWND; //Your WindowHandle instead of your DC
    //
    void DrawBox(int x, int y, int r, int g, int b, int size, int thickness)
    

    // Lock & Get Forground DC From m_hWND
    HDC m_hDC = GetDC(m_hWND);
    if (m_hDC != 0) //Make Sure It's ok
    

    // Double Buffering Begins Here

    // Create Background DC From m_hDC
    HDC mem_m_hDC = CreateCompatibleDC(m_hDC);

    // Calculate Window Bounds
    RECT ClientRect =  0 ;
    GetClientRect(m_hWND, &ClientRect);

    // Create Background Buffer Frame
    BITMAPINFO bmi =  0 ;
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = ClientRect.right - ClientRect.left;
    bmi.bmiHeader.biHeight = ClientRect.bottom - ClientRect.top;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biPlanes = 1;
    HBITMAP memBMP = CreateDIBSection(mem_m_hDC, &bmi, DIB_RGB_COLORS, 0, 0, 0);

    // Select Background Buffer Frame
    SelectObject(mem_m_hDC, memBMP);

    // Brush style to hollow
    m_LogBrush.lbStyle = BS_NULL;

    // Create a logical brush and select into the context
    HBRUSH m_hBrush = CreateBrushIndirect(&m_LogBrush);
    HGDIOBJ oldHGDIOBJ1 = SelectObject(m_hDC, m_hBrush); //Save Old Seleteed GDI Object To oldHGDIOBJ1

    // Create a logical pen and select into the context
    HPEN m_hPen = CreatePen(PS_SOLID, thickness, RGB(r, g, b));
    HGDIOBJ oldHGDIOBJ2 = SelectObject(m_hDC, m_hPen); //Save Old Seleteed GDI Object To oldHGDIOBJ2

    // Draw the rectangle in Background Memory DC
    Rectangle(mem_m_hDC, (x - size / 2), (y - size / 2), (x + size / 2), (y + size / 2));

    // Copy Background DC To Forground DC
    BitBlt(m_hDC, 0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, mem_m_hDC, 0, 0, SRCCOPY);

    // Delete Background Buffer Frame
    DeleteObject(memBMP);

    // Delete Background DC
    DeleteDC(mem_m_hDC);

    // Double Buffering Ends Here

    // Unlock Forground DC
    ReleaseDC(m_hWND, m_hDC);

    // Remove the objects
    DeleteObject(m_hBrush);
    DeleteObject(m_hPen);

    
    

【讨论】:

以上是关于绘图时防止闪烁的主要内容,如果未能解决你的问题,请参考以下文章

更新标题时如何防止UIButton闪烁

调整大小时防止 TPaintBox 闪烁

如何防止背景图像在更改时闪烁

更新单个 ListViewItem 的文本时如何防止 ListView 闪烁?

如何防止自定义 UITableViewCells 在取消选择时闪烁白色?

防止圆在 KineticJS 中闪烁