使用 CPaintDC 时减少闪烁

Posted

技术标签:

【中文标题】使用 CPaintDC 时减少闪烁【英文标题】:Reduce flicker when using CPaintDC 【发布时间】:2013-07-04 07:01:38 【问题描述】:

我需要使用 MFC 制作交互式图表

它有点像一个均衡器控件,用户应该能够点击均衡器上的一个点,拖动它来改变它的 y 轴值

我也刚开始学习MFC

到目前为止,我已经在OnPaint() 函数中使用CPaintDC 在对话框中绘制图形。现在图表非常简单,矩形边框,填充白色,图表上有 4 个点。我使用OnMouseMove() 函数来了解光标是否在图形区域内,并使用OnLButtonDown() 函数来了解用户点击的位置。如果用户单击了一个位置,这意味着我想更改该位置图形点的 y 轴值,我使用Invalidate() 重新绘制图形并在OnLButtonDown() 内调用OnPaint()。但是,每次图表必须更新时,我都会看到闪烁。现在这不是问题,但我需要扩展此图,使其至少有 64 个可变点,并且能够通过拖动而不是单击我想要它去的位置来更改一个点的 y 轴值。闪烁问题会随着我增加点数和图形外观的复杂性而增加吗?稍后,该图表将需要具有轴、网格线、标签等。闪烁是我应该关心的吗?有什么办法可以预防吗?

----更新---- 这就是我根据我对 CodeDreamer 建议的理解更新我的 OnPaint() 函数的方式

void Cgraph_on_dlgboxDlg::OnPaint()

     CPaintDC dc_blt(this);
     CDC dc;
     CBitmap bmpDC;

     CRect rcClient;
     GetClientRect(rcClient);

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

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    
    else
    
        CDialogEx::OnPaint();
    

     dc.CreateCompatibleDC(&dc);
     bmpDC.CreateCompatibleBitmap(&dc, theGraph.width,theGraph.height );
     dc.SelectObject(&bmpDC);

    CPen pen;
    COLORREF pencolour = RGB(0, 0, 0);
    COLORREF brushcolour = RGB(0, 0, 255);
    COLORREF graphColour = RGB(0, 0, 150);

    // Draw boarder
    pen.CreatePen(PS_SOLID, 3, pencolour);
    // CBrush brush(HS_CROSS, brushcolour);
    dc.SetBkMode(TRANSPARENT);
    dc.SetMapMode(MM_TEXT);
    dc.SetViewportOrg(theGraph.x1, theGraph.y1);
     dc.SelectObject(&pen);

    // Draw graph boundary
    CPoint point1(0,0);
    point1.x = 0;
    point1.y = 0;
    CPoint point2(0,0);
    point2.x = point1.x + theGraph.width;
    point2.y = point1.y + theGraph.height;
    dc.Rectangle(CRect(point1, point2));
    pen.DeleteObject();

    // Draw Horizontal at 0
    pen.CreatePen(PS_SOLID, 1, pencolour);
    dc.SelectObject(&pen);
    dc.MoveTo(0, theGraph.height - ORG_DIST_FROM_BOTTOM);
    dc.LineTo(theGraph.width, theGraph.height - ORG_DIST_FROM_BOTTOM);
    pen.DeleteObject();

    dc.SetViewportOrg(theGraph.x1, theGraph.y1 + theGraph.height - ORG_DIST_FROM_BOTTOM); // dc.SetViewportOrg() always works relative to the clinet origin

    // Draw graph line
    pen.CreatePen(PS_SOLID, 2, graphColour);
    dc.SelectObject(&pen);
    for(int i = 0; i<NUM_OF_SECTIONS_IN_GRAPH; i++)
        dc.MoveTo(graphSamplePoints[i].x, graphSamplePoints[i].y);
        dc.LineTo(graphSamplePoints[i+1].x, graphSamplePoints[i+1].y);
    

    // draw circles at graph sample points
    for(int i = 0; i<NUM_OF_POINTS_IN_GRAPH; i++)
        CIRCLE(dc, graphSamplePoints[i].x, graphSamplePoints[i].y, GRP_SMP_RAD);        
    

    // dc_blt.BitBlt(0,0,rcClient.Width(), rcClient.Height(), &dc, 0, 0, SRCCOPY);
    dc_blt.BitBlt(theGraph.x1,theGraph.y1,theGraph.width, theGraph.height, &dc, 0, 0, SRCCOPY);


我需要多次更改视口的来源,我的猜测是这可能是错误的原因之一。欢迎提出任何建议。 这就是我的输出在没有双缓冲的情况下的样子 这就是我尝试双缓冲时的样子

【问题讨论】:

Flicker does not reduce after double buffering in MFC CPaintDC 的可能重复项 【参考方案1】:

在这种情况下,一般的解决方案是“双缓冲”。 原理是预先创建一个兼容的内存dc用于绘制,绘制结束后输出到屏幕dc。

代码示例如下。

//in OnPaint() function
CPaintDC dc(this);    
CDC dcMem;
CBitmap bmpDC;

CRect rcClient;
GetClientRect(&rcClient);

dcMem.CreateCompatibleDC(pDC);
bmpDC.CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height());
dcMem.SelectObject(&bmpDC);

CRect rect(0, 0, 100, 200);
dcMem.Rectangle(rect);

dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY);

以下是一些参考资料。

introduction

another reference

希望对你有所帮助。

【讨论】:

感谢您的回复。这个样本中的 pDC 是什么? MSDN 说应该是 CDC* 。它是全局变量还是应该在 OnPaint 中声明? 如果我理解正确,无论我现在使用CPaintDC 对象在屏幕上绘制什么,我都应该使用CDC 对象做同样的事情,而不是在屏幕上绘制,我应该使用CDC 对象绘制到CBitmap 上,然后使用CPaintDC 对象将这个CBitmap 复制回屏幕上。这是正确的还是我在这里遗漏了什么? 1) pDC:这是我的错误。您只需使用当前的直流电。这里是 dc,所以 'dcMem.CreateCompatibleDC(&dc);'是准确的代码。对不起。 2)您完全了解双缓冲。我的英语不好..谢谢。 所以我正在绘制的实际图形是 (0,0,100,200) 处的矩形,但是 BitBlt 会重新绘制整个客户区吗?或者我应该只包括我的图形区域作为 BitBlt 的第一个四个参数?还有什么是倒数第三个和第二个参数(两个 0)?逻辑坐标是否与客户区坐标相同?【参考方案2】:

试试CMemDC MFC 类。

在您的 OnPaintFunction 中,您将拥有:

  CPaintDC   DC(this);
  CMemDC mDC(&DC, this);
  // now use mDC instead of DC

还可以查看here 以获得更多示例和解释。

【讨论】:

在这种情况下 pDC 是什么?也是 CDC *pDC 吗? 您的意思是对我一直使用的所有 CPaintDC 变量都使用 mDC 吗?如果我将 CPaintDC 对象替换为 CMemDC 对象,则其他基于 CDC 的函数(例如 Rectangle())将不再工作 对不起,我刚刚编辑了答案,没有更多的 pDC。也请看这里:codeproject.com/Articles/33/Flicker-Free-Drawing-In-MFC @user13267,这种方法没有解决您的问题吗?它给你带来了一些改进吗?

以上是关于使用 CPaintDC 时减少闪烁的主要内容,如果未能解决你的问题,请参考以下文章

CDC,CPaintDC,CClientDC,CWindowDC区别

MFC PrintWindow(CPaintDC) 有效,但 PrintWindow(CDC) 无效

OnPaint的使用,让窗口立即重绘的方法,CPaintDC的使用及各种DC的使用

WindowsDC

绘图的相关类

vc++折线图示例