最近使用到了MFC的C++的双缓存绘图,这里简单做一下记录。


双缓存绘图的优点是可以降低图片显示过程中的闪动现象。

原因:

在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。

   因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。

我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。

引自:http://www.cppblog.com/wrhwww/archive/2011/03/01/140913.html


双缓存绘图思路:

1.创建内存DC。

2.创建Bitmap用来作为画图的画布。(如果用物理DC去创建可以画彩×××,如果用内存DC是黑白的)

3.把bitmap选入内存DC中。

4.进行画图。(可以把图片画在bitmap上,作为背景,也可以通过,moveto,lineto等进行画)

5.把内存DC上的内容复制到物理DC上。

6.关闭DC连接,清理创建的内存DC和bitmap。

注意:

1.内存DC如果和物理DC大小一致的话不用进行缩放,如果是不一致的话还要进行缩放处理。缩放与否使用bltblt和StretchBlt方法,一个是可以缩放的。这里不展开。

2.注意关闭DC连接,否则很容易造成资源泄露。尤其是GDI资源。


代码说明:

1.可以先关掉背景擦除事件,也可以不处理。

屏蔽背景刷新。背景刷新其实是在响应WM_ERASEBKGND消息。我们在视类中添加对这个消息的响应,可以看到缺省的代码如下:

BOOL CMYView::OnEraseBkgnd(CDC* pDC)
{
return CView::OnEraseBkgnd(pDC);
}

是调用父类的OnEraseBkgnd函数,我们屏蔽此调用,只须直接return TRUE;即可。


2.使用双缓存技术绘图

void CMonitorPolicyDlg::DrawFigure(vector m_FiguresVector)
{
    /*创建画笔样式*/
    CPen cDrawPen;
    cDrawPen.CreatePen(PS_SOLID,3,RGB(0,255,0));
    CDC memDC;
    CBitmap cMemBitmap;/*用来画图的bitmap*/
    CBitmap *pOldBitmap;/*用来替换旧的object*/
    CPen *pOldPen;
    CPoint cPoint1(1,1);
    CPoint cPoint2(10,10);
    /*获取显示控件的DC*/
    CDC * pDC = GetDlgItem(IDC_IMG_SHOW_WINDOW)->GetDC();
    /*先创建内存dc*/
    memDC.CreateCompatibleDC(NULL);
    /*创建bitmap,大小与所要显示控件大小一致,pDC使用物理dc生成彩×××像,用内存dc生成黑白图像*/
    cMemBitmap.CreateCompatibleBitmap(pDC,m_cImgRect.Width(),m_cImgRect.Height());/*m_cImgRect为显示控件的矩形大小*/
    /*把bitmap作为画图纸,绑定到memDC上*/
    pOldBitmap = memDC.SelectObject(&cMemBitmap);/*selectobject会返回原先的bitmap,装载新的bitmap*/
    /*初始化memDC上的背景颜色*/
    memDC.FillSolidRect(0,0,m_cImgRect.Width(),m_cImgRect.Height(),pDC->GetBkColor());
                                                                                                                                    
    /*这里如果需要加载图片的话,可以在这里加载,这样图片就变为了背景*/
    //例如CImage.draw();
    pOldPen = memDC.SelectObject(&cDrawPen);
    /*画了一条直线*/
    memDC.MoveTo(cPoint1);
    memDC.LineTo(cPoint2);
                                                                                                                                    
    /*把内存绘制到物理控件上*/
    pDC->BitBlt(0,0,m_cImgRect.Width(),m_cImgRect.Height(),&memDC,0,0,SRCCOPY);
                                                                                                                                    
    /*释放内存dc*/
    memDC.SelectObject(pOldBitmap);/*把创建的bitmap替换出来*/
    cMemBitmap.DeleteObject();/*对应cMemBitmap.CreateCompatibleBitmap,释放空间*/
    memDC.DeleteDC();/*对应memDC.CreateCompatibleDC,释放内存dc*/
    ReleaseDC(pDC);/*对应CDC * pDC = GetDlgItem(IDC_IMG_SHOW_WINDOW)->GetDC()*/
    return;
}