有时,在有大量图片或者复杂的界面下,会出现界面闪烁。这是由于windows默认在绘制界面前会用背景色清空,然后重绘。

因为默认背景色一般是白色的,当重绘速度慢时,看起来界面就在闪烁了。

那有没有在不提高界面绘制速度的情况下,防止闪烁呢,常用的办法就是启用双缓冲机制。

双缓冲绘制,就是在内存预先绘制好图形,再拷贝到界面上。中途不再清白背景。

delphi的wincontrol组件提供了双缓冲机制,所以从该组件继承的比如窗体,各类windows控件都有双缓冲绘制功能。

但默认情况下双缓冲绘制是关闭的,在手工设置 DoubleBuffered 属性为true后双缓冲绘制机制开启。无需添加其他代码。

下面就delphi的双缓冲原理做一简单的注释。(为简单起见,用屏幕上的和内存中的来表示双缓冲的两个部分)

procedure TWinControl.WMPaint(var Message: TWMPaint);
var
DC, MemDC: HDC;
MemBitmap, OldBitmap: HBITMAP;
PS: TPaintStruct;
begin
if not FDoubleBuffered or (Message. DC <> 0) then   //查看DoubleBuffered属性和指定消息值,如果双缓冲关闭或者dc值有效,那么直接绘制图像 注★
begin
if not (csCustomPaint in ControlState) and (ControlCount = 0) then
inherited
else
PaintHandler(Message); //调用具体过程来重画到Message指定的设备上(内存中的或者屏幕上的)
end
else
begin   //进入双缓冲处理
DC := GetDC(0);
MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom); //创建一个设备兼容位图(内存中)
ReleaseDC(0, DC);
MemDC := CreateCompatibleDC(0);   //创建一个兼容上下文绘图设备(内存中的)
OldBitmap := SelectObject(MemDC, MemBitmap);    //将位图选中为绘图设备的当前对象,返回值为老的对象,给予保存
    try
DC := BeginPaint(Handle, PS);   //申明开始绘制,该函数会返回当前控件的上下文绘图设备(屏幕上的)
      Perform(WM_ERASEBKGND, MemDC, MemDC); //发送背景清空消息(内存中的)
Message.DC := MemDC; (注★ 内存中的设备,该值不为零,会被上面的代码处理)
      WMPaint(Message);         (递归调用本过程,让前面部分流程代码绘制图像到内存中)
Message.DC := 0;
BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY); (将内存中的图像拷贝到屏幕上)
EndPaint(Handle, PS); (结束绘制,并使得当前屏幕的无效区域变为有效)

    finally
SelectObject(MemDC, OldBitmap); (重新将老的对象选择回去)
DeleteDC(MemDC); (删除内存中的上下文设备)
DeleteObject(MemBitmap); (删除内存中的对象)
end;
end;
end;

另外 BeginPaint和getdc虽然返回值相同,但他们有明显区别。

BeginPaint和EndPaint常用于wm paint消息,且仅绘制无效区域,并使其有效。

就是说 getdc属于主动型,而 BeginPaint 属于被动型的需在存在无效区域时才工作。

更多细节部分,可参看msdn

posted on 2009-08-28 16:04  on_road  阅读(2228)  评论(0编辑  收藏  举报