在 WM_PAINT 中绘制(太慢)会导致闪烁?

Jic*_*hao 1 winapi gdi+ gdi

我想使用以下代码在 WM_PAINT 消息处理程序中绘制很多行。

//DrawLine with double buffering
LRESULT CALLBACK CMyDoc::OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    std::vector<Gdiplus::Point> points;
    std::vector<Gdiplus::Point>::iterator iter1, iter2;
    HDC hdc, hdcMem;
    HBITMAP hbmScreen, hbmOldBitmap;
    PAINTSTRUCT ps;
    RECT    rect;

    hdc = BeginPaint(hWnd, &ps);

    //Create memory dc
    hdcMem = CreateCompatibleDC(hdc);
    GetClientRect(hWnd, &rect);
    hbmScreen = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
    hbmOldBitmap = (HBITMAP)SelectObject(hdcMem, hbmScreen);

    //Fill the rect with white
    FillRect(hdcMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));

    //Draw the lines
    Gdiplus::Graphics graphics(hdcMem);
    Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0));

    points = m_pPolyLine->GetPoints();
    for (iter1 = points.begin(); iter1 != points.end(); iter1++) {
        for (iter2 = iter1 + 1; iter2 != points.end(); iter2++)
            graphics.DrawLine(&blackPen, *iter1, *iter2);
    }

    //Copy the bitmap from memory dc to the real dc
    BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);

    //Clean up
    SelectObject(hdcMem, hbmOldBitmap);
    DeleteObject(hbmScreen);
    DeleteDC(hdcMem);

    EndPaint(hWnd, &ps);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果点的大小超过 20,则客户端 rect 只是闪烁。我认为原因是 Gdiplus::DrawLines 太慢了。

有没有解决闪烁问题的方法?谢谢。

mit*_*ity 6

闪烁可能是由缓慢绘制以及其他原因引起的。一般来说,请尝试/确保以下几点:

  • 尽量不要依赖WM_ERASEBKGND消息,即返回非零值,或NULLWNDCLASS::hbrBackground可能的情况下指定in 。通常paint方法将脏区域的所有背景都绘制出来,然后就不需要进行擦除了。

  • 如果您需要擦除,通常可以对其进行优化,使其WM_ERASEBKGND返回非零值,然后绘制方法通过绘制常规绘制内容未覆盖的区域(如果PAINTSTRUCT::fErase设置)来确保“擦除” 。

  • 如果合理可能,请编写paint 方法,以便它不会在一次调用中重新绘制相同的像素。例如,用红色边框制作蓝色矩形,不要FillRect(red),然后用FillRect(blue). 尝试尽可能合理地为每个像素绘制一次。

  • 对于复杂的控件/窗口,通常可以优化绘制方法,PAINTSTRUCT::rcPaint通过适当组织控件数据轻松跳过脏矩形 ( )之外的大量绘制。

  • 更改控件状态时,仅使控件的最小所需区域无效。

  • 如果它不是顶级窗口,请考虑使用CS_PARENTDC. 如果您的绘制方法不依赖于系统将裁剪矩形设置为控件的客户端矩形,则此类样式会带来更好的性能。

  • 如果您看到控件/窗口调整大小闪烁,请考虑不使用CS_HREDRAWCS_VREDRAW。而是WM_SIZE手动使控件的相关部分无效。这通常允许仅使控件的较小部分无效。

  • 如果您看到控件滚动闪烁,请不要使整个客户端无效,而是ScrollWindow()仅使用暴露新(滚动)内容的小区域并使之无效。

  • 如果以上一切都失败了,那么使用双缓冲。