双缓冲解决VC++绘图时屏幕闪烁
通常来说程序根据需要调用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, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, 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, 0, 0, 100, 100, hdcImage, 0, 0, 100, 100, SRCCOPY); //这里才能正常画图,将hdcImage中的位图直接复制到内存缓冲区 StretchBlt(hDC, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, 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, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, SRCCOPY);//这里才能正常画图,将hdcImage中的位图直接复制到内存缓冲区 DeleteObject(hdcMEM); }
调用以上函数在内存中画第一张图:
DrawBmp(hdcTmp , hBitmap1);
画第二张图
如果要画多张图,就依次调用本函数绘制,记得一定要把所有的图全画到一个设备DC上,最后再一次性画到屏幕上,才不会出现闪烁现象。
等把所有图全画到hdcTmp中后,hdcTmp中已经有了完整的图形,再把完整的图形绘制到屏幕上:
至此,双缓冲画多幅图绘制完毕。
再给大家一个实例:
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, 0, rect.right - rect.left, rect.bottom - rect.top, &dcMem, 0, 0, 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); }
希望本文能够对大家熟练掌握双缓冲问题有所帮助。
上一篇:C++实现简单的图书管理系统
栏 目:C语言
下一篇:C++实现简单的职工信息管理系统
本文标题:双缓冲解决VC++绘图时屏幕闪烁
本文地址:https://www.xiuzhanwang.com/a1/Cyuyan/2910.html
您可能感兴趣的文章
- 01-10c语言求1+2+...+n的解决方法
- 01-10求子数组最大和的解决方法详解
- 01-10c语言 跳台阶问题的解决方法
- 01-10用贪心法求解背包问题的解决方法
- 01-10C语言 解决不用+、-、×、÷数字运算符做加法
- 01-10fatal error LNK1104: 无法打开文件“libc.lib”的解决方法
- 01-10Linux下semop等待信号时出现Interrupted System Call错误(EINTR)解决方法
- 01-10判断整数序列是否为二元查找树的后序遍历结果的解决方法
- 01-10c语言中用字符串数组显示菜单的解决方法
- 01-10ubuntu中打开终端的三种解决方法
阅读排行
本栏相关
- 04-02c语言函数调用后清空内存 c语言调用
- 04-02func函数+在C语言 func函数在c语言中
- 04-02c语言的正则匹配函数 c语言正则表达
- 04-02c语言用函数写分段 用c语言表示分段
- 04-02c语言中对数函数的表达式 c语言中对
- 04-02c语言编写函数冒泡排序 c语言冒泡排
- 04-02c语言没有round函数 round c语言
- 04-02c语言分段函数怎么求 用c语言求分段
- 04-02C语言中怎么打出三角函数 c语言中怎
- 04-02c语言调用函数求fibo C语言调用函数求
随机阅读
- 01-11ajax实现页面的局部加载
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-10C#中split用法实例总结
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 01-10delphi制作wav文件的方法
- 01-10SublimeText编译C开发环境设置
- 04-02jquery与jsp,用jquery
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 08-05dedecms(织梦)副栏目数量限制代码修改