MFC双缓冲画图
- 格式:doc
- 大小:30.00 KB
- 文档页数:4
首页 > MFC编程 > 双缓冲区绘图操作的实现双缓冲区绘图操作的实现2010年12月31日代码疯子发表评论阅读评论在图形图象处理编程过程中,双缓冲是一种基本的技术。
我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。
解决这一问题的有效方法就是双缓冲技术。
因为窗体在刷新时,总要有一个擦除原来图像的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图像颜色的反差。
当WM_PAINT的响应很频繁的时候,这种反差也就越发明显,于是我们就看到了闪烁现象。
我们会很自然的想到,避免背景色的填充是最直接的办法。
但是那样的话,窗体上会变的一团糟。
因为每次绘制图像的时候都没有将原来的图像清除,造成了图像的残留,于是窗体重绘时,画面往往会变的乱七八糟。
所以单纯的禁止背景重绘是不够的。
我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。
它可以支持图形块的复制,速度很快。
我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。
以上也就是双缓冲绘图的基本的思路。
下面来看一个简单的例子,在CProjectNameView的OnDraw函数进行稍微复杂的绘图操作(复杂一点闪烁效果明显)1 2 3 4 5 6 7 8 9 10 11 12//普通的画图操作C P o i n t p t C e n t e r;C R e c t r e c t,e l l i p s e R e c t;G e t C l i e n t R e c t(&r e c t);p t C e n t e r=r e c t.C e n t e r P o i n t();f o r(i n t i=60;i>0;--i){e l l i p s e R e c t.S e t R e c t(p t C e n t e r,p t C e n t e r);e l l i p s e R e c t.I nf l a t e R e c t(i*5,i*5);p D C->E l l i p s e(e l l i p s e R e c t);}下面是双缓冲区的绘图代码:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25//双缓冲区画图C P o i n t p t C e n t e r;C R e c t r e c t,e l l i p s e R e c t;G e t C l i e n t R e c t(&r e c t);p t C e n t e r=r e c t.C e n t e r P o i n t();C D C d c M e m;//用于缓冲作图的内存C DC B i t m a p b m p;//内存中存在临时图像的位图d c Me m.C r e a t e C o m p a t i b l e D C(p D C);//依附窗口D C创建兼容D C//创建兼容位图b m p.C r e a t e C o m p a t i b l e B i t m a p(&dc M e m,r e c t.W id t h(),re c t.H e i g h t());d c Me m.S e l e c t O b j e c t(&b m p);//将位图选入内存D Cd c Me m.F i l l S o l i d R e c t(r e c t,p D C->G e t B k C o l o r());//按照原有背景色填充客户区//绘图操作f o r(i n t i=60;i>0;--i){e l l i p s e R e c t.S e t R e c t(p t C e n t e r,p t C e n t e r);e l l i p s e R e c t.I nf l a t e R e c t(i*5,i*5);d c Me m.E l l i p s e(e l l i p s e R e c t);//在内存D C上绘图}//将内存D C上的东西复制到p D Cp D C->B i t B l t(0,0,r e c t.W i d t h(),r e c t.H e i g h t(),&d c M e m,0,0,S R C C O P Y);d c Me m.D e l e t e D C();//删除D Cb m p.D e l e t e O b j ec t();//删除位图更多可以看到,当拖动窗口大小时,使用普通绘图操作会不停的闪烁,而双缓冲区的几乎看不到闪烁。
C# WinForm利用GDI+的双缓冲技术来进步绘图效率前言进入.NET时代,Windows的绘图技术也从GDI晋级到了GDI+,从名字就能知道GDI+是对以前传统GDI 绘图技术的一次晋级,不过在微软几乎把所有的新技术都冠之.NET的情况下,GDI+竟然不叫做,还真让我感到有点意外了。
:〕GDI+在一种与设备无关的环境下提供了一套统一的绘图编程模型,极大的进步了Windows绘图编程的方便性,我们再也不用创立什么各种各样复杂的设备环境了,说实话,我如今想起来都头疼。
题归正传,关于如何进展GDI+的根本编程,我不能过多的加以描绘,假如有对此概念还不太清楚的朋友,建议先去理解一下相关的资料,我们在这里主要讨论的是一种进步绘图效率〔主要是动画效率〕的双缓冲技术在GDI+中的应用和实现。
实现目的为了能清楚的比照应用双缓冲技术前后的效果,我编写了一段程序来进展测试。
首先,我创立了一个普通的Windows Application,在主Form中,我放置了一个定时器:timer1,然后将它的Interval属性设置为10,然后在Form上放置两个按纽,分别用来控制定时器的开启和关闭,最后,我还放置了一个label控件,用来显示绘图的帧数。
测试程序在timer1的timer1_Tick事件中,我写下了如下的代码(其中flag是一个bool型标志变量):DateTime t1 = DateTime.Now;Graphics g = this.CreateGraphics();if(flag){brush = new LinearGradientBrush(new PointF(0.0f, 0.0f),new PointF(700.0f, 300.0f), Color.Red, Color.Blue);flag = false;}else{brush = new LinearGradientBrush(new PointF(0.0f, 0.0f),new PointF(700.0f, 300.0f), Color.Blue, Color.Red);flag = true;}for(int j = 0; j < 60; j ++){for(int i = 0; i < 60; i++){g.FillEllipse(brush, i * 10, j * 10, 10, 10);}}DateTime t2 = DateTime.Now;TimeSpan sp = t2 - t1;float per = 1000 / liseconds;运行后,我点击“开场〞按纽,效果如下列图所示:应用双缓冲以前的效果图〔帧数:5帧/秒〕正如大家所看到的,我在程序中使用循环画了几百个圆形,然后在每次的定时器脉冲事件中使用不同方向的线性渐变来对它们进展填充,形成了一个动画效果。
1创建画笔,画刷void CGraphView::OnDraw(CDC* pDC){CGraphDoc* pDoc = GetDocument( );ASSERT_V ALID(pDoc);// TODO: add draw code for native data here// 使用画笔第一步:创建一支新画笔CPen pen(PS_SOLID, 1, RGB(255,0,0));CPen pen(PS_DASHDOT, 1, RGB(255,0,0));//第二步:选择新画笔到设备环境中,同时保存旧画笔CPen * pOldPen = pDC->SelectObject(&pen);// 使用画刷第一步:创建一支新画刷CBrush brush(RGB(255,0,0)); //创建实体画刷CBrush brush(HS_CROSS, RGB(255,0,0)); //创建阴影模式画刷//第二步:选择新画刷到设备环境中,同时保存旧画刷CBrush * pOldBrush = pDC->SelectObject(&brush);// 常用的绘图函数////绘制一个彩色点pDC->TextOut(20, 20, "点");pDC->SetPixel(100, 40, RGB(255,0,0));//绘制直线pDC->TextOut(320, 20,"线段");pDC->MoveTo(400, 40);pDC->LineTo(500, 40);//绘制折线pDC->TextOut(20, 170, "折线");POINT polyline[4]={{240,240},{80,120},{240,120},{80,240}}; pDC->Polyline(polyline,4);//绘制矩形pDC->TextOut(320, 170, "矩形");pDC->Rectangle(390, 110, 600, 230);//绘制椭圆pDC->TextOut(20, 320, "椭圆");pDC->Ellipse(80, 260, 280, 380);//绘制多边形pDC->TextOut(320, 320, "多边形");POINT polygon[3]={{380,330},{530,260},{500,360}};pDC->Polygon(polygon,3);//第三步:恢复设备环境的旧画笔,以便释放新画笔// pDC->SelectObject(pOldPen);//第三步:恢复设备环境的旧画刷,以便释放新画刷// pDC->SelectObject(pOldBrush);}2创建字体的两种方法第一种创建字体的方法:使用CreateFont函数,如下定义:CFont::CreateFontBOOL CreateFont( int nHeight, //字体的高度int nWidth, //字体的宽度int nEscapement, //字体显示的角度int nOrientation, //字体的角度int nWeight, //字体的磅数BYTE bItalic, //斜体字体BYTE bUnderline, //带下划线的字体BYTE cStrikeOut, //带删除线的字体BYTE nCharSet, //所需的字符集BYTE nOutPrecision, //输出的精度BYTE nClipPrecision, //裁减的精度BYTE nQuality, //逻辑字体与输出设备的实际//字体之间的精度BYTE nPitchAndFamily, //字体间距和字体集LPCTSTR lpszFacename //字体名称);例子:font.CreateFont(12, // nHeight0, // nWidth0, // nEscapement0, // nOrientationFW_NORMAL, // nWeightFALSE, // bItalicFALSE, // bUnderline0, // cStrikeOutANSI_CHARSET, // nCharSetOUT_DEFAULT_PRECIS, // nOutPrecisionCLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQualityDEFAULT_PITCH | FF_SWISS, // nPitchAndFamily "Arial"); // lpszFacename一般只修改几项。
MFC双缓冲双缓冲说⽩就是贴图,将数据全部绘制在缓冲兼容DC上,再将兼容DC的数据⼀次全部绘制在屏幕上。
相较直接在DC上绘图,双缓冲是将绘制数据全部输出,⽽⾮分步绘制,并且,可以避免Windows刷新背景⾊避免闪烁问题。
⽰例如下:双缓冲://在缓冲区作图,最后将缓冲区数据⼀次全部拷贝⾄⽬标DCvoid CDoubleBufferView::DrawEx(CDC* pDC){CRect rect;GetClientRect(rect);//创建兼容当前DC的缓冲DCCDC cacheDC;cacheDC.CreateCompatibleDC(pDC);//创建兼容当前DC的位图CBitmap bitmap;bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());//将位图选⼊到缓冲DCCBitmap* pOldBitmap = cacheDC.SelectObject(&bitmap);//填充缓冲DC的背景(默认是⿊⾊)cacheDC.FillSolidRect(0, 0, rect.Width(), rect.Height(), RGB(255, 255, 255));//在缓冲DC中画图int x = 0;int y = 0;int step = 2;int idx = 0;while (idx++ < 10000){cacheDC.MoveTo(x, y);cacheDC.LineTo(rect.Width() - x, y);cacheDC.LineTo(rect.Width() - x, rect.Height() - y);x += step;cacheDC.LineTo(x, rect.Height() - y);y += step;}//将缓冲DC输出到当前DCpDC->BitBlt(0, 0, rect.Width(), rect.Height(), &cacheDC, 0, 0, SRCCOPY);//选回旧的bitmapcacheDC.SelectObject(pOldBitmap);//释放资源bitmap.DeleteObject();cacheDC.DeleteDC();}不⽤双缓冲://直接在DC上作图void CDoubleBufferView::Draw(CDC* pDC){CRect rect;GetClientRect(rect);//在缓冲DC中画图int x = 0;int y = 0;int step = 2;int idx = 0;while (idx++ < 10000){pDC->MoveTo(x, y);pDC->LineTo(rect.Width() - x, y);pDC->LineTo(rect.Width() - x, rect.Height() - y);x += step;pDC->LineTo(x, rect.Height() - y);y += step;}}。
双缓冲的意思就是先将图画在内存中,然后再一次拷贝显示到屏幕上画图一般是在OnPaint中画:MFC双缓冲绘图2008-03-25 13:46如何避免闪烁在知道图形显示闪烁的原因之后,对症下药就好办了。
首先当然是去掉MFC提供的背景绘制过程了。
实现的方法很多,* 可以在窗口形成时给窗口的注册类的背景刷付NULL* 也可以在形成以后修改背景static CBrush brush(RGB(255,0,0));SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);* 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变得一团乱。
怎么办?这就要用到双缓存的方法了。
双缓冲就是除了在屏幕上有图形进行显示以外,在内存中也有图形在绘制。
我们可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常规整的内存拷贝)。
这样在内存中绘图时,随便用什么反差大的背景色进行清除都不会闪,因为看不见。
当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。
如何实现双缓冲首先给出实现的程序,然后再解释,同样是在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();BOOL CDataStructureView::OnEraseBkgnd(CDC* pDC){CRect rc;CDC dcMem;GetClientRect(&rc);CBitmap bmp; //内存中承载临时图象的位图dcMem.CreateCompatibleDC(pDC); //依附窗口DC创建兼容内存DC//创建兼容位图(必须用pDC创建,否则画出的图形变成黑色)bmp.CreateCompatibleBitmap(pDC,rc.Width(),rc.Height());CBitmap *pOldBit=dcMem.SelectObject(&bmp);//按原来背景填充客户区,不然会是黑色dcMen.FillSolidRect(rc,RGB(255,255,255))//画图,添加你要画图的代码,不过用dcMem画,而不是pDC;......pDC->BitBlt(0,0,rc.Width(),rc.Height(),&dcMem,0,0,SRCCOPY);//将内存DC上的图象拷贝到前台//绘图完成后的清理dcMem.DeleteDC(); //删除DCbmp.DeleteObject(); //删除位图return true;//这里一定要用return true,如果用自动生成的,会调用基类,把画出来的覆盖,就什么结果也没有了view sourceprint?}。
《1》普通绘图就是直接在我们看得到的黑板上绘图《2》双缓冲就是先在一个虚拟的黑板上画完,等用到的时候在把虚拟黑板上的图画复制到我们看得到的黑板上去;利用双缓冲的优点就是能够使画面流畅,可以想象把画好的图直接粘贴到黑板上一定比在黑板上重新画要快的多。
——————————————————————开始第一步:新建一个对话框工程第二步:添加两个按钮:一个命名为双缓冲绘图;一个命名为普通绘图;第三步:声明变量:在CMyDlg类上右击添加变量如下:CDC MyDC;CBitmap bmp;CBitmap *oldbmp;首先声明一个与窗口DC兼容的内存DC(MyDC)和两个与内存相兼容的位图(bmp,*oldbmp)第四步:在OnInitDialog()函数中添加以下代码://窗口DCCDC *dc=GetDC();//创建与窗口DC兼容的内存DC(MyDC)及位图(bmp,*oldbmp )MyDC.CreateCompatibleDC(dc);bmp.CreateCompatibleBitmap(dc,200,200);//把内存位图选进内存DC中用来保存在内存DC中绘制的图形oldbmp=MyDC.SelectObject(&bmp);//在内存DC中绘制一些小的圆形,数量要多(体现双缓存的优点)for(int i=0;i<200;i+=6)for(int j=0;j<200;j+=6)MyDC.Ellipse(i-3,j-3,i+3,j+3);第五步:右击CMyDlg类添加windows消息响应函数WM_CLOSE,添加以下代码:MyDC.SelectObject(oldbmp);bmp.DeleteObject();MyDC.DeleteDC();//选进原来的位图,删除内存位图对象和内存DC第六步:双击”双缓冲“按钮添加以下代码:GetDC()->StretchBlt(0,0,200,200,&MyDC,0,0,200,200,SRCCOPY);//把内存DC中的图形粘贴到窗口中;第七步:双击“普通绘图”按钮添加以下代码:for(int i=0;i<200;i+=6)for(int j=0;j<200;j+=6)GetDC()->Ellipse(i-3,j-3,i+3,j+3);//按普通方式在窗口中绘制和在内存DC中一样数量和大小的位图;第八步:运行程序............................先单击普通绘图按钮,大家可以看到绘图的速度有点慢再单击双缓冲绘图按钮,图像马上就显示出来了,这就是双缓冲和普通绘图的区别了______________________________________________________________________完成双缓冲技术说起来也没有那么神秘,举个形象一点的例子吧,有两张纸A和B,纸A代表屏幕,纸B代表后台缓冲,我们将所有的绘图操作都显示在纸B上,然后将纸B覆盖在纸A上,这样体现在纸A上的操作就是绘制了整张图,体现在纸B上的就是纷繁复杂的绘图操作。
在实现绘图的过程中,显示的图形总是会闪烁,笔者曾经被这个问题折磨了好久,通过向高手请教,搜索资料,问题基本解决,现将文档整理出来以供大家参考.1.显示的图形为什么会闪烁我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏幕显示时是由OnPaint进行调用的。
当窗口由于任何原因需要重绘时,总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来在闪。
如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。
当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来绘制的图形进行清除,而又叠加上了新的图形。
有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。
例如在OnDraw(CDC *pDC)中这样写:pDC->MoveTo(0,0);pDC->LineTo(100,100);这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见闪烁。
其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。
比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪烁5次;如果清楚屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画只会闪烁一次。
这个也可以试验,在OnDraw(CDC *pDC)中这样写:for(int i=0;i<100000;i++){pDC->MoveTo(0,i);pDC->LineTo(1000,i);}程序有点极端,但是能说明问题。
说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要闪得厉害一些,但是闪烁频率要低。
那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。
因为动画的连续两个帧之间的差异很小所以看起来不闪。
如果不信,可以在动画的每一帧中间加一张纯白的帧,不闪才怪呢。
2、如何避免闪烁在知道图形显示闪烁的原因之后,对症下药就好办了。
首先当然是去掉MFC提供的背景绘制过程了。
实现的方法很多:* 可以在窗口形成时给窗口的注册类的背景刷付NULL* 也可以在形成以后修改背景static CBrush brush(RGB(255,0,0));SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);* 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE(屏蔽掉系统的自动刷新)这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变得一团乱。
怎么办?这就要用到双缓存的方法了。
双缓冲就是除了在屏幕上有图形进行显示以外,在内存中也有图形在绘制。
我们可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常规整的内存拷贝)。
这样在内存中绘图时,随便用什么反差大的背景色进行清除都不会闪,因为看不见。
当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。
3、如何实现双缓冲首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:(前提是要屏蔽掉OnEraseBkgnd()的刷新,返回TRUE;)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();请参阅注释。
4、如何提高绘图的效率实际上,在OnDraw (CDC *pDC)中绘制的图并不是所有都显示了的,例如:你在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。
裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。
因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。
因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。
但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。
可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。
如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。
具体程序大家可看以下源代码:5、无闪烁背景图的绘制代码实现/////////////// 无闪烁背景图绘制 ////////////// 程序设计: icemen (温冰) 树爱兵//////////////////////////////////////////////BOOL CStrucView::OnEraseBkgnd(CDC* pDC){ int nWidth;int nHeight;//CView::OnEraseBkgnd(pDC);CStrucDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);CRect rect;GetWindowRect(&rect);nWidth = rect.Width();nHeight= rect.Height();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));/////////////////////////////////////////////////////////// 以上为画背景色 ////////////////// 以下为画背景图 /////////////////////////////////////////////////////////////////////GetClientRect(rect);BITMAP bm;CDC dcMem;VERIFY(m_bmp.GetObject(sizeof(bm),(LPVOID)&bm));dcMem.CreateCompatibleDC(pDC);CBitmap *pOldBMP =( CBitmap *)dcMem.SelectObject(&m_bmp);MemDC.BitBlt( (rect.right - bm.bmWidth)/2,(rect.bottom - bm.bmHeight)/2,bm.bmWidth,bm.bmHeight,&dcMem,0,0,SRCCOPY);dcMem.SelectObject(pOldBMP);/////////////////////////////////////////////////////////// 以上为画背景图 /////////////////////////////////////////////////////////////////////pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);MemBitmap.DeleteObject();MemDC.DeleteDC();return TRUE;}/////////////// 无闪烁背景图绘制 ///////////////////////////////////////////////////////注:程序中m_bmp 为相应的 StrucView.h中定义,为位图资源类protected:CBitmap m_bmp;并应加上下面段,当然,你可在任何时候加入loadBitmap 子例程,也可加入文件资源,那由得你了!CStrucView::CStrucView(){VERIFY(m_bmp.LoadBitmap(IDB_BITMAP3));。