双缓冲方法解决屏幕刷新闪烁的问题
http://dev.21tx.com/2005/05/06/11850.html
下载本文所附源代码
关键字 双缓冲
原作者姓名 戚高
介绍
在论坛中经常见到关于刷新时界面闪烁的帖子,如何控制在进行高效绘图时不出现界面闪烁的感觉呢,下文就双缓冲方法进行讲解.
正文
图形为什么会闪烁的原因是:我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来绘制的图形进行清除,而又叠加上了新的图形。有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。
如何实现双缓冲:在OnDraw(CDC *pDC)中:
CDC MemDC; //首先定义一个显示设备对象
CBitmap MemBitmap;//定义一个位图对象
//随后建立与屏幕显示兼容的内存显示设备
MemDC.CreateCompatibleDC(NULL);
//这时还不能绘图,因为没有地方画 ^_^
//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小
MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);
//将位图选入到内存显示设备中
//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);
//先用背景色将位图清除干净,这里我用的是白色作为背景
//你也可以用自己应该用的颜色
MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));
//绘图
MemDC.MoveTo(……);
MemDC.LineTo(……);
//将内存中的图拷贝到屏幕上进行显示
pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);
//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();
以论坛的一个帖子例子为例来说明一些具体如何解决问题.
帖子那容是:
我想让一个区域动起来,
如何解决窗口刷新时区域的闪烁。
void CJhkljklView::OnDraw(CDC* pDC)
{
CJhkljklDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
int i;
int x[20],y[20];
CPen hPen;
POINT w[5];
x[0]=a/100+10;
x[1]=a/100+30;
x[2]=a/100+80;
x[3]=a/100+30;
x[4]=a/100+10;
y[0]=10;
y[1]=10;
y[2]=25;
y[3]=40;
y[4]=40;
for (i=0;i<5;i++)
{ w[i].x=x[i];
w[i].y=y[i];
}
//CClientDC dc(this);
//hPen=CreatePen(PS_SOLID,1,RGB(255,0,0));
CRgn argn,Brgn;
CBrush abrush(RGB(40,30,20));
argn.CreatePolygonRgn(w, 5, 1);// point为CPoint数组,
pDC->FillRgn(&argn, &abrush);
abrush.DeleteObject();
}
void CJhkljklView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
InvalidateRect(NULL,true);
UpdateWindow();
a+=100;
CView::OnTimer(nIDEvent);
}
int CJhkljklView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
SetTimer(1,10,NULL);
return 0;
}
利用定时器直接进行10毫秒的屏幕刷新,这样效果会出现不停的闪烁的情况.
解决方法利用双缓冲,首先触发WM_ERASEBKGND,然后修改返回TRUE;
定义变量:
CBitmap *m_PBitmapOldBackground ;
CBitmap m_bitmapBackground ;
CDC m_dcBackground;
//绘制背景
if(m_dcBackground.GetSafeHdc()== NULL|| (m_bitmapBackground.m_hObject == NULL))
{
m_dcBackground.CreateCompatibleDC(&dc);
m_bitmapBackground.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height()) ;
m_pBitmapOldBackground = m_dcBackground.SelectObject(&m_bitmapBackground) ;
//DrawMeterBackground(&m_dcBackground, rect);
CBrush brushFill, *pBrushOld;
// 背景色黑色
brushFill.DeleteObject();
brushFill.CreateSolidBrush(RGB(255, 255, 255));
pBrushOld = m_dcBackground.SelectObject(&brushFill);
m_dcBackground.Rectangle(rect);
m_dcBackground.SelectObject(pBrushOld);
}
memDC.BitBlt(0, 0, rect.Width(), rect.Height(),
&m_dcBackground, 0, 0, SRCCOPY) ;
//绘制图形
int i;
int x[20],y[20];
CPen hPen;
POINT w[5];
x[0]=a/100+10;
x[1]=a/100+30;
x[2]=a/100+80;
x[3]=a/100+30;
x[4]=a/100+10;
y[0]=10;
y[1]=10;
y[2]=25;
y[3]=40;
y[4]=40;
for (i=0;i<5;i++)
{ w[i].x=x[i];
w[i].y=y[i];
}
//CClientDC dc(this);
//hPen=CreatePen(PS_SOLID,1,RGB(255,0,0));
CRgn argn,Brgn;
CBrush abrush(RGB(40,30,20));
argn.CreatePolygonRgn(w, 5, 1);// point为CPoint数组,
memDC.FillRgn(&argn, &abrush);
abrush.DeleteObject();
}
这样编译运行程序就会出现屏幕不闪烁的情况了.