前言:

 闪烁问题,之前的经验是使用双缓冲,借此机会,把双缓冲的研究心得总结下。

双缓冲的含义:

           缓冲这个词,相信大家都不陌生,Cache。主要是为了解决上下游(或者模块、或者系统)等性能不匹配问题。如果把上游看成“生产者”,下游看成“消费者”,当“生产者”与“消费者”的处理速度不同时,为了避免干等,中间会加一些缓冲区。

                     Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

                                                                      无缓冲,双方都容易阻塞。

                    Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

                                    最常用的生产者-消费者模型,增加弹性缓冲,缺点是每生产+每消费都需要加锁

             Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

                            双缓冲,等消费者队列为空时加锁,执行exchange(如swap方法),大大减少了加锁次数。

     

双缓冲在界面的使用:

                      Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

        在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。
         双缓冲我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造 成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用 BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪 烁。以上也就是双缓冲绘图的基本的思路。
 

GDI双缓冲:

       WM_ERASEBKGND : 返回1,禁止背景重绘

         

HDC hDc = ::GetDC( m_hWnd) ;
RECT rcDest;
HDC hMemDc = CreateCompatibleDC(hDc);
HBITMAP hMemBitmap = CreateCompatibleBitmap(hDc);  //内存画布,放到内存DC中就可以在内存中画图了
HBITMAP hOldBitmap = ::SelectObject (hDC, hBitmap);
//....hMemDc作画
::BitBlt (m_hDestDC, rc.left, rc.top, rc.Width(), rc.Height(), hDC, rc.left, rc.top, SRCCOPY); //拷贝到显示器缓冲

//资源释放与还原
::SelectObject (hDC, hOldBitmap);
DeleteObject(hMemBitmap);
DeleteDC(hMemDc);
ReleaseDC(m_hWnd,hDc);

  

WTL双缓冲:

CDoubleBufferImpl 在AtlFrame.h中。

1.首先继承自CDoubleBufferImpl

class TCtrl:
        public CWindowImpl< TCtrl>,
        public WTL::CDoubleBufferImpl<TCtrl>  // 继承双缓冲类

2.由于双缓冲类中已经处理了WM_ERASEBKGND 和WM_PAINT消息,所以需要从你的代码中删除对这些消息的处理。然后加上双缓冲的消息处理即可。

    BEGIN_MSG_MAP(TCtrl)
//        MESSAGE_HANDLER(WM_PAINT,        OnPaint)
        CHAIN_MSG_MAP( WTL::CDoubleBufferImpl<TCtrl>)
    END_MSG_MAP()

3.增加一个DoPaint函数,函数声明如下:

void DoPaint(CDCHandle dc);

4.将原来OnPaint函数中的代码移到DoPaint中,注意原来的CPaintDC需要改用参数中的CDCHandler

void TCtrl::DoPaint( CDCHandle dc )
{
    //CPaintDC dc(m_hWnd);

dc.MoveTo( xx… )
}

Duilib双缓冲:

    Duilib使用的GDI+引擎,也已经支持双缓冲,这里简单介绍下它的实现过程

    1、Duilib也默认处理了WM_REASEBKGND 并返回1;

        Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

    2、IRenderContext  类负责渲染。

          目前由RenderContext_GdiPlus来实现。

          1)构造函数中,创建内存DC

          Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

        2) 获取绘图区域后,创建内存Bitmap

        

     Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

    Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

         然后作画,细节暂时略,后续源码剖析会补充,敬请期待。

        Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

          最后,拷贝到显示器缓冲。

        Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现-LMLPHP

06-30 06:01