双缓冲解决VC++绘图时屏幕闪烁

双缓冲解决VC++绘图时屏幕闪烁,第1张

概述通常来说程序根据需要调用Invalidate(FALSE)使窗口客户区无效引起重绘,然后在窗口OnPaint函数(基于文档视图的程序则是OnDraw)中进行稳定绘图就行了。但是,我们在OnPaint中进行多重绘制(画背景、棋盘、棋子等),

通常来说程序根据需要调用InvalIDate(FALSE)使窗口客户区无效引起重绘,然后在窗口OnPaint函数(基于文档视图的程序则是OnDraw)中进行稳定绘图就行了。但是,我们在OnPaint中进行多重绘制(画背景、棋盘、棋子等),前后绘制的反差造成了闪烁现象。以前知道Java中解决屏幕闪烁问题是用双缓冲的方法,现在发现在vc++中也是可以这么做的。简单来说,双缓冲就是先把需要绘制的东西全部一口气画在内存中,最后把内存中的数据搬到屏幕上显示。

最近做中国象棋,绘制界面时遇到些问题,绘图过程中屏幕闪烁,估计都会想到利用双缓冲来解决问题,但查了下网上双缓冲的资料,发现基本是MFC的,转化为VC++后,大概代码如下:

voID DrawBmp(HDC hDC,HBITMAP hBitmap){  HDC hdcmeM; //用于缓冲作图的内存DC  HBITMAP bmp; //内存中承载临时图象的位图  HANDLE hold;  hdcmeM = CreateCompatibleDC(hDC);//依附窗口DC创建兼容内存DC  bmp = CreateCompatibleBitmap(hDC,100,100); //创建与hDC环境相关的设备兼容的位图  SelectObject(hdcmeM,bmp);  hold = SelectObject(hdcImage,hBitmap);  StretchBlt(hDC,hdcmeM,SRCcopY);  SelectObject(hdcImage,hold);  DeleteObject(hold);}

但以上代码似乎没有用到hBitmap,当然屏幕上也不会有任何输出,但网上的资料基本一样。查了一番资料,才明白如果hDC中已经有位图数据,BitBlt的时候,就会直接把hDC中的数据画到内存缓冲区里。所以,还需要建一DC,名为hdcImage,把要画的位图选入内存hdcImage中,然后再在内存缓冲区上绘图。

整理代码如下:

voID DrawBmp(HDC hDC,HBITMAP hBitmap){  HDC hdcImage;  HDC hdcmeM; //注意此处,创建了两个HDC  hdcmeM = CreateCompatibleDC(hDC);  hdcImage = CreateCompatibleDC(hDC);  HBITMAP bmp = ::CreateCompatibleBitmap(hDC,nWIDth,nHeight);//创建与hDC环境相关的设备兼容的位图  SelectObject(hdcmeM,bmp);  SelectObject(hdcImage,hBitmap);//注意此处,将要画的位图选入hdcImage  StretchBlt(hdcmeM,hdcImage,SRCcopY); //这里才能正常画图,将hdcImage中的位图直接复制到内存缓冲区  StretchBlt(hDC,SRCcopY); //再将内存缓冲区中的数据绘制到屏幕上.  DeleteObject(hdcImage);}

当然,要注意的一点就是,如果要绘制多张图片,比如两张,如果大家这样调用:

DrawBmp(hDC,hBitmap1);DrawBmp(hDC,hBitmap2);

依然会发生闪烁,下面解释原因:

举个例子,屏幕绘图就像现场作画,如果两次调用绘图函数,就相当于在观众面前作画,第一次画第一张(例如中国象棋的背景)。第二次画第二张(如棋盘)。这样,在画背景和棋盘时,由于颜色有反差,必然在贴第二张图时会发现闪烁,这样利用双缓冲相当于没用,还浪费了内存空间。

双缓冲的原理是:在内存中先把第一张图画好,此时不要转画到屏幕上,然后继续在原来的内存中画第二张,等把所有的图全画好后,再一次性贴到屏幕上。那样内存中存在的就是完整的图形,观众看不到绘图的过程,只能看到绘图的结果,而最后是一次性复制到屏幕上的,当然不会发生闪烁现象。

为了更好解释双缓冲的原理,附图片如下:

PS:以上照片来自网络,只为能更好理解,本人无意侵权。

在以上代码的基础上作如下更改:

voID DrawBmp(HDC hDC,HBITMAP hBitmap) //此处返回类型改为HDC{  HDC hdcmeM;  hdcmeM = CreateCompatibleDC(hDC);  SelectObject(hdcmeM,hBitmap); //将位图选择进内存DC  StretchBlt(hDC,SRCcopY);//这里才能正常画图,将hdcImage中的位图直接复制到内存缓冲区  DeleteObject(hdcmeM);}

调用以上函数在内存中画第一张图:

DrawBmp(hdctmp,hBitmap1); 

画第二张图
复制代码 代码如下:DrawBmp(hdctmp,hBitmap2); //此时传的为hdctmp,其中hdctmp中已经有第一张图片的数据,此次调用后就会把第二张图片绘到原来的基础上。

如果要画多张图,就依次调用本函数绘制,记得一定要把所有的图全画到一个设备DC上,最后再一次性画到屏幕上,才不会出现闪烁现象。
等把所有图全画到hdctmp中后,hdctmp中已经有了完整的图形,再把完整的图形绘制到屏幕上:
复制代码 代码如下:BitBlt(hDC,hdctmp,SRCcopY); //此处第一个参数才为hDC,即窗口句柄

至此,双缓冲画多幅图绘制完毕。

再给大家一个实例:

voID C****Dlg::OnPaint() {  if (IsIconic())  {    //......  }  else  {    //CDialog::OnPaint(); //不要调用这个    CPaintDC dc(this);//对话框的dc//通常CPaintDC用来响应WM_PAINT消息。    //CPaintDC是从CDC派生出来的:在构造时自动调用CWnd::BeginPaint,析构时调用CWnd::EndPaint。        RECT rect;// 客户区矩形    GetClIEntRect(&rect);        // 使用双缓冲避免屏幕刷新时闪烁    CDC dcmem;// 内存dc    CBitmap bmpMem; // 位图    dcmem.CreateCompatibleDC(NulL);// 创建兼容dc    bmpMem.CreateCompatibleBitmap(&dc,rect.right-rect.left,rect.bottom-rect.top);//创建跟客户区域大小一样的(空)位图    // 把位图选到设备上下文环境中    CBitmap *pold = dcmem.SelectObject(&bmpMem);    //  dcmem.FillSolIDRect(&rect,RGB(255,255,255));      // 在此处将绘制内容全画到dcmem内存中,(即把之前使用CPaintDC绘制的dc换成dcmem即可)      Drawtable(dcmem);//画棋盘    DrawChesses(dcmem); // 画棋子    //......    // 至此,内存中绘图完毕        // 从内存拷贝到设备dc    dc.BitBlt(0,rect.right - rect.left,rect.bottom - rect.top,&dcmem,SRCcopY);        dc.SelectObject(pold);    // 释放资源    bmpMem.DeleteObject();    dcmem.DeleteDC();       }    }

解决方法:

1)添加BOol类型的成员变量bgroundChanged,初始化为FALSE;
2)在切换背景图片前调用ModifyStyle(WS_CliPCHILDREN,0)去掉WS_CliPCHILDREN属性,并把bgroundChanged设置为TRUE;
3)在OnPaint中最后增加

    if (TRUE == bgroundChanged)    {      bgroundChg = FALSE;      ModifyStyle(0,WS_CliPCHILDREN);    }

希望本文能够对大家熟练掌握双缓冲问题有所帮助。

总结

以上是内存溢出为你收集整理的双缓冲解决VC++绘图时屏幕闪烁全部内容,希望文章能够帮你解决双缓冲解决VC++绘图时屏幕闪烁所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1254508.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存