C++ 如何使用GDI避免屏幕过度闪烁

C++ 如何使用GDI避免屏幕过度闪烁,c++,winapi,gdi,paint,C++,Winapi,Gdi,Paint,我对用GDI渲染图形有点陌生 我做了一个画图程序,效果很好,只是它导致了很多恼人的屏幕闪烁。我承认我的绘画代码并没有真正优化(缺少时间),但它也不应该是超低效的,所以我感到困惑 我基本上是在init上创建一个兼容的DC,然后创建一个兼容的位图。然后我将其选择到兼容DC中,并绘制到兼容DC中。然后我使用BitBlit()将其复制到窗口hDC 有谁能告诉我这次屏幕撕裂的可能原因吗? 编辑:顺便说一句,屏幕闪烁仅在绘制路径期间发生(在将路径绘制到hMemDC之前,它将绘制到窗口的hDC) 代码示例:

我对用GDI渲染图形有点陌生

我做了一个画图程序,效果很好,只是它导致了很多恼人的屏幕闪烁。我承认我的绘画代码并没有真正优化(缺少时间),但它也不应该是超低效的,所以我感到困惑

我基本上是在init上创建一个兼容的DC,然后创建一个兼容的位图。然后我将其选择到兼容DC中,并绘制到兼容DC中。然后我使用BitBlit()将其复制到窗口hDC

有谁能告诉我这次屏幕撕裂的可能原因吗? 编辑:顺便说一句,屏幕闪烁仅在绘制路径期间发生(在将路径绘制到hMemDC之前,它将绘制到窗口的hDC)

代码示例: (编辑:如果您需要查看更多您认为相关的代码,请发表评论,我将进行编辑)


路径::DrawTo(HDC)
MyApp::Paint()
任何帮助都将不胜感激。

BeginPaint为您提供您应该绘制的HDC。您正在忽略它。

如果您正在绘制整个客户端区域,请覆盖
WM_ERASEBKGND
,然后简单地返回
TRUE
。这将减少闪烁

正如其他人指出的那样;使用
WM_PAINT
提供的
HDC
,因为它可能包含剪辑区域和其他可能优化屏幕更新的内容

编辑
如果您没有绘制整个客户端区域,则可以在您知道的
WM_PAINT
处理程序不会绘制的区域中执行背景绘制

您运行的是什么操作系统?如果是Vista或Windows7,您是否在某种“兼容模式”下运行,禁用桌面合成

据推测,Vista引入的桌面窗口管理器(DWM)的优点之一是():

在Windows XP中,当操作系统运行时,应用程序直接更新其Windows 请他们。这些请求可以与异步执行 关于监视器的刷新率或可能发生的任何更新 当前正在运行。这些请求的效果是用户 看到窗口撕裂并重新绘制错误或缓慢。DWM 窗口显示样式消除了撕裂瑕疵, 提供高质量的桌面体验。好处到底是什么 用户希望系统的响应速度更快,并且 经验更干净

ie使用DWM,桌面合成将与绘图同步,以避免看到部分绘制的“撕裂”的东西


如果不是这样,你确定你没有把“撕裂”和背景擦除闪烁之类的东西混淆吗?

我想你看到的是闪烁,而不是撕裂。为了最大限度地减少闪烁,您的
WM_PAINT
应该只向窗口DC写入一次。通常,这一操作是一个
BitBlt

HDC hdc = BeginPaint(m_hwnd, &m_PaintStruct);
... paint to bitmap ...
BitBlt(hdc, ...); // blt from bitmap to screen
EndPaint(m_hwnd, &m_PaintStruct);
如果在多个步骤中绘制窗口DC,则打开窗口进行闪烁


您对问题的描述与代码不匹配。在您的描述中,您说您正在从兼容的DC到Windows hDC进行快速切换。但是在您的代码中,
BitBlt
后面跟着一个
m_pPath->DrawTo(m_hDC)
。如果在
DrawTo
期间发生刷新,则屏幕将以部分绘制的视图刷新,导致闪烁。

似乎无法解决我的问题。。。我试过了,还是一样的撕屏。m_hDC是我要绘制的窗口的hDC。BeginPaint()应该收到与DC相同的句柄(我想)@xcrypt为什么要这样做?这不是文档所说的。您应该在BeginPaint提供的DC中绘制,因为该DC被剪裁到无效区域,并且它是由PAINTSTRUCT描述的区域。在绘制程序中不会出现撕裂,它是由移动对象引起的伪影。我必须猜测闪烁。基本规则很简单,在
WM_ERASEBKGND
中什么都不做,只需在
WM_PAINT
中使用快速
BitBlt
将内存DC复制到屏幕上。重要提示:
WM_PAINT
消息仅在处理其他消息时发送。您可以通过对窗口执行
invalidate竖立(hWnd,NULL,FALSE)
PostMessage(hWnd,WM_PAINT,0,0)
来加速屏幕刷新,例如您对
WM_MOUSEMOVE
的响应。另一个小贴士:如果还没有,请调用
SetCapture
ReleaseCapture
,这样你可以更快地接收
WM_MOUSEMOVE
(在速度较慢的CPU上)。而且你真的不应该使用
SetPixel
,因为它太慢了。使用
CreateDIBSection
创建内存DC,并直接修改像素(
RGBQUAD
s)。我问我的老师。我的代码描述是正确的,但不完整。我在bitblt()之后绘制窗口DC,这就是问题所在。今天我问我的老师,基本上他告诉我必须使用双缓冲来避免闪烁。似乎GDI可以决定在任何绘图调用(
BitBlt
等)后刷新屏幕,并且它不会等待
EndPaint
,对吗?只是出于好奇,这是在计时器上发生的还是什么?我猜
BitBlt
会写入某个像素缓冲区,如果上次刷新后经过足够的时间,Windows偶尔会在绘图调用后将该缓冲区复制到视频内存中?Classic GDI会提供一个DC,其备份存储是视频卡本身。如果你将一个像素写入该DC,它将直接进入视频卡并显示在屏幕上。(DWM会更改此模型,但通常应使用经典模型。)
LRESULT MyApp::WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    if(iMsg == WM_CREATE)
    {
        CREATESTRUCT *pCS = (CREATESTRUCT*)lParam;
        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)pCS->lpCreateParams);

    }
    else
    {
        //retrieve the stored "this" pointer
        MyApp* pApp = (MyApp*)GetWindowLongPtr(hWnd, GWLP_USERDATA);

        switch (iMsg)
        {
            case WM_PAINT:
                {
                pApp->Paint();
                return 0;
                }

            case WM_COMMAND:
            {
                int wmId    = LOWORD(wParam);
                int wmEvent = HIWORD(wParam);

                // Parse the menu selections:
                switch (wmId)
                {
                case IDM_NEW:
                    {
                        ////
                        return 0;
                    }
                    return 0;
                case IDM_LOAD:
                    {
                        //////
                        return 0;
                    }
                case IDM_SAVE:
                    {
                    //////
                    return 0;
                    }
                case IDM_SAVEAS:
                    {
                        //////
                        return 0;
                    }
                case IDM_COLOURMAIN:
                    {
                        COLORREF col;
                        if(MyWin32Funcs::OnColorPick(col)) {
                            pApp->m_pPath->SetColor1(col);
                        }
                    return 0;
                    }
                case IDM_COLOURSECONDARY:
                    {
                    COLORREF col;
                        if(MyWin32Funcs::OnColorPick(col)) {
                            pApp->m_pPath->SetColor2(col);
                        }
                    return 0;
                    }
                case IDM_PEN:
                    {
                        pApp->m_pPath->SetTool(Tool_Pen);
                        return 0;
                    }
                case IDM_LINE:
                    {
                        pApp->m_pPath->SetTool(Tool_Line);
                        return 0;
                    }
                case IDM_ELLIPSE:
                    {
                        pApp->m_pPath->SetTool(Tool_Ellipse);
                        return 0;
                    }
                case IDM_RECTANGLE:
                    {
                        pApp->m_pPath->SetTool(Tool_Rectangle);
                        return 0;
                    }
                case IDM_LINETRACK:
                    {
                        pApp->m_pPath->SetTool(Tool_LineTrack);
                        return 0;
                    }
                default:
                    {
                    //////
                    return 0;
                    }
                }
            }

            case WM_LBUTTONUP:
                {
                    //////
                    Point2D p;
                    p.x = LOWORD(lParam); 
                    p.y = HIWORD(lParam);

                    switch(pApp->m_pPath->GetTool()) {
                        case Tool_Pen:
                            {
                                pApp->m_bPaintToBitmap = true;
                                InvalidateRect(pApp->m_hWnd,NULL,true);
                                break;
                            }
                        case Tool_Ellipse:
                            {
                                pApp->m_bPaintToBitmap = true;
                                InvalidateRect(pApp->m_hWnd,NULL,true);
                                break;
                            }
                        case Tool_Rectangle:
                            {
                                pApp->m_bPaintToBitmap = true;
                                InvalidateRect(pApp->m_hWnd,NULL,true);
                                break;
                            }
                        case Tool_Line:
                            {
                                pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                                InvalidateRect(pApp->m_hWnd,NULL,false);
                                break;
                            }
                        case Tool_LineTrack:
                            {
                                pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                                InvalidateRect(pApp->m_hWnd,NULL,false);
                                break;
                            }
                    }

                    return 0;
                }

            case WM_RBUTTONUP:
                {
                    //////
                    int x = LOWORD(lParam);
                    int y = HIWORD(lParam);

                    switch(pApp->m_pPath->GetTool()) {
                        case Tool_Line:
                            {
                                pApp->m_bPaintToBitmap = true;
                                InvalidateRect(pApp->m_hWnd,NULL,true);
                                break;
                            }
                        case Tool_LineTrack:
                            {
                                pApp->m_bPaintToBitmap = true;
                                InvalidateRect(pApp->m_hWnd,NULL,true);
                                break;
                            }
                    }

                    return 0;
                }
            case WM_LBUTTONDOWN:
                {
                    Point2D p;
                    p.x = LOWORD(lParam);
                    p.y = HIWORD(lParam);
                    switch(pApp->m_pPath->GetTool()) {
                    case Tool_Pen:
                        pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                        InvalidateRect(pApp->m_hWnd,NULL,false);
                        break;
                    }
                }
            case WM_MOUSEMOVE:
                {
                    Point2D p;
                    p.x = LOWORD(lParam);
                    p.y = HIWORD(lParam);
                    if (wParam & MK_LBUTTON) {
                        switch(pApp->m_pPath->GetTool()) {
                        case Tool_Pen:
                            {
                            pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                            InvalidateRect(pApp->m_hWnd,NULL,false);
                            break;
                            }
                        case Tool_Ellipse:
                            {
                            if( pApp->m_pPath->GetLen() >= 1) {
                                pApp->m_pPath->SetPointAt(1,p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                            }
                            else {
                                pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                            }
                            InvalidateRect(pApp->m_hWnd,NULL,false);
                            break;
                            }
                        case Tool_Rectangle:
                            {
                            if( pApp->m_pPath->GetLen() >= 1) {
                                pApp->m_pPath->SetPointAt(1,p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                            }
                            else {
                                pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
                            }
                            InvalidateRect(pApp->m_hWnd,NULL,false);
                            break;
                            }
                        }
                    }

                    return 0;
                }

            case WM_CLOSE:
                {
                    //////
                                return 0;
                            }
                        }
                    PostQuitMessage(0);
                    return 0;
                }
        }
    }
    return DefWindowProc (hWnd, iMsg, wParam, lParam) ;
}
void MyApp::Paint()
{
    BeginPaint(m_hWnd,&m_PaintStruct);
    if (m_bPaintToBitmap) {
        Point2D p;
        p.x = BMPXOFFSET;
        p.y = BMPYOFFSET;
        m_pPath->Offset(p);
        m_pPath->DrawTo(m_pBitmapPainter->GetMemDC());
        m_pPath->ClrPath();
        m_pBitmapPainter->Paint(); //this is where BitBlt() occurs
        m_bPaintToBitmap = false;
        if(m_pBitmapPainter->IsAdjusted() == false) {
            m_pBitmapPainter->SetbAdjusted(true);
        }
    }
    else {
        m_pBitmapPainter->Paint(); //this is where BitBlt() occurs
        m_pPath->DrawTo(m_hDC);
    }
    EndPaint(m_hWnd,&m_PaintStruct);
}
HDC hdc = BeginPaint(m_hwnd, &m_PaintStruct);
... paint to bitmap ...
BitBlt(hdc, ...); // blt from bitmap to screen
EndPaint(m_hwnd, &m_PaintStruct);