前言:
闪烁问题,之前的经验是使用双缓冲,借此机会,把双缓冲的研究心得总结下。
双缓冲的含义:
缓冲这个词,相信大家都不陌生,Cache。主要是为了解决上下游(或者模块、或者系统)等性能不匹配问题。如果把上游看成“生产者”,下游看成“消费者”,当“生产者”与“消费者”的处理速度不同时,为了避免干等,中间会加一些缓冲区。
无缓冲,双方都容易阻塞。
最常用的生产者-消费者模型,增加弹性缓冲,缺点是每生产+每消费都需要加锁
双缓冲,等消费者队列为空时加锁,执行exchange(如swap方法),大大减少了加锁次数。
双缓冲在界面的使用:
在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应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;
2、IRenderContext 类负责渲染。
目前由RenderContext_GdiPlus来实现。
1)构造函数中,创建内存DC
2) 获取绘图区域后,创建内存Bitmap
然后作画,细节暂时略,后续源码剖析会补充,敬请期待。
最后,拷贝到显示器缓冲。