解决MFC绘图过程中的闪烁
- 格式:pdf
- 大小:149.09 KB
- 文档页数:17
VC无闪烁刷屏技术的实现---经验总结之防止窗口闪烁的方法也许我们都碰到过这种情况,当你想重画某个窗口的时候,或你需要每隔一段时间要进行重画窗口,窗口会不停的闪烁。
那么如何消除闪烁呢?借鉴了别人的经验,自己也总结一下,现将总结的几种方法介绍一下,供大家参考。
1、将Invalidate()替换为InvalidateRect()。
因为Invalidate()会导致整个窗口的图象重画,需要的时间比较长,而InvalidateRect()仅仅重画Rect区域内的内容,所需时间会少一些。
所以替换之后在很大程度上会减少闪烁。
如果你确实需要改善闪烁的情况,计算一个Rect所用的时间比起重画那些不需要重画的内容所需要的时间要少得多。
2、不要让系统擦除你的窗口。
系统在需要重画窗口的时候会帮你用指定的背景色来擦除窗口。
可是,也许需要重画的区域也许非常小。
或者,在你重画这些东西之间还要经过大量的计算才能开始。
这个时候你可以禁止系统擦掉原来的图象。
直到你已经计算好了所有的数据,自己把那些需要擦掉的部分用背景色覆盖掉(如:dc.FillRect(rect,&brush);rect是需要擦除的区域,brush是带背景色的刷子),再画上新的图形。
要禁止系统擦除你的窗口,可以重载OnEraseBkgnd()函数,让其直接返回TRUE就可以了。
如BOOL CMyWin::OnEraseBkgnd(CDC* pDC){return TRUE;//return CWnd::OnEraseBkgnd(pDC);//把系统原来的这条语句注释掉。
}3、有效的进行擦除。
擦除背景的时候,不要该擦不该擦的地方都擦。
比如,你在一个窗口上放了一个很大的Edit框,几乎占了整个窗口,那么你频繁的擦除整个窗口背景将导致Edit不停重画形成剧烈的闪烁。
事实上你可以CRgn创建一个需要擦除的区域,只擦除这一部分。
如GetClientRect(rectClient);rgn1.CreateRectRgnIndirect(rectClient);rgn2.CreateRectRgnIndirect(m_rectEdit);if(bineRgn(&rgn1,&rgn2,RGN_XOR) == ERROR)//处理后的rgn1只包括了Edit框之外的客户区域,这样,Edit将不会被我的背景覆盖而导致重画。
[C#开发⼩技巧]解决WinForm控件TabControl闪烁问题在⽤C#开发WinForm程序时,常发现TabControl出现严重的闪烁问题,这主要是由于TabControl控件在实现时会绘制默认的窗⼝背景。
其实以下⼀段简单的代码可以有效的缓解该问题的发⽣。
这就是技巧的作⽤,不需要理解太多的知识,但需要多多积累,就能做到事半功倍的效果。
1using System;2using System.Collections.Generic;3using System.Text;4using System.Windows.Forms;5namespace WfGUI.Forms6{7///<summary>8///不会闪烁的TabContriol9///</summary>10public class NoFlashTabControl : TabControl11 {12///<summary>13///构造函数,设置控件风格14///</summary>15public NewTabControl()16 {17 SetStyle18 ( ControlStyles.AllPaintingInWmPaint //全部在窗⼝绘制消息中绘图19 | ControlStyles.OptimizedDoubleBuffer //使⽤双缓冲20 , true);21 }22///<summary>23///设置控件窗⼝创建参数的扩展风格24///</summary>25protected override CreateParams CreateParams26 {27get28 {29 CreateParams cp = base.CreateParams;30 cp.ExStyle |= 0 x02000000;31return cp;32 }33 }34 }35 }标签: C#, TabControl, WinForm, 控件, 程序开发。
一般的windows 复杂的界面需要使用多层窗口而且要用贴图来美化,所以不可避免在窗口移动或者改变大小的时候出现闪烁。
先来谈谈闪烁产生的原因原因一:如果熟悉显卡原理的话,调用GDI函数向屏幕输出的时候并不是立刻就显示在屏幕上只是写到了显存里,而显卡每隔一段时间把显存的内容输出到屏幕上,这就是刷新周期。
一般显卡的刷新周期是1/80秒左右,具体数字可以自己设置的。
这样问题就来了,一般画图都是先画背景色,然后再把内容画上去,如果这两次操作不在同一个刷新周期内完成,那么给人的视觉感受就是,先看到只有背景色的图像,然后看到画上内容的图像,这样就会感觉闪烁了。
解决方法:尽量快的输出图像,使输出在一个刷新周期内完成,如果输出内容很多比较慢,那么采用内存缓冲的方法,先把要输出的内容在内存准备好,然后一次输出到显存。
要知道一次API 调用一般可以在一个刷新周期内完成。
对于GDI,用创建内存DC的方法就可以了原因二:复杂的界面有多层窗口组成,当windows在窗口改变大小的时候是先重画父窗口,然后重画子窗口,子父窗口重画的过程一般无法在一个刷新周期内完成,所以会呈现闪烁。
我们知道父窗口上被子窗口挡住的部分其实没必要重画的解决方法:给窗口加个风格WS_CLIPCHILDREN ,这样父窗口上被子窗口挡住的部分就不会重画了。
如果同级窗口之间有重叠,那么需要再加上WS_CLIPSIBLINGS 风格原因三:有时候需要在窗口上使用一些控件,比如IE,当你的窗口改变大小的时候IE会闪烁,即使你有了WS_CLIPCHILDREN也没用。
原因在于窗口的类风格有CS_HREDRAW 或者CS_VREDRAW,这两个风格表示窗口在宽度或者高度变化的时候重画,但是这样就会引起IE闪烁解决方法:注册窗口类的时候不要使用这两个风格,如果窗口需要在改变大小的时候重画,那么可以在WM_SIZE的时候调用RedrawWindow。
原因四:界面上窗口很多,而且改变大小时很多窗口都要移动和改变大小,如果使用MoveWindow 或者SetWindowPos两个API来改变窗口的大小和位置,由于他们是等待窗口重画完成后才返回,所以过程很慢,这样视觉效果就可能会闪烁。
一种基于自定义剪切区集合的MFC程序GDI绘图优化方法罗幸明,徐伟强浙江中控技术股份有限公司,浙江杭州,310053摘要:传统MFC程序GDI绘图仅考虑屏幕刷新闪烁的优化,而对于绘图内容本身的优化考虑有限。
常规应用下,GDI绘图本身确实不会成为程序的性能瓶颈,但在高分辨率、复杂画面的绘制显示时,这个问题就不得不考虑了。
通常我们会使用系统剪切区进行优化控制,但是实际应用发现当分辨率或者画面复杂度大于一定程度后,该优化的性能提升有限。
鉴于此,本文提出了一种简单可行的基于自定义剪切区集合判断的MFC程序GDI绘图优化方法,可以有效解决高分辨率、复杂画面的GDI绘图性能问题。
关键词:自定义剪切区、MFC、GDI、绘图One Optimizing Design Based On Custom Clipping Region Sets Of MFC Programs Drawing With GDILuo Xingming,Xu WeiqiangZhejiang SUPCON Co.,Ltd, Hangzhou, Zhejiang, 310053Abstract:When drawing with GDI of traditional MFC programs,the most considering things is the screen flashing problems but not drawing itself.Normally drawing performance will not be the choke point,but it really will be when screen resolution is high enough or graphics is complex enough.It is usual to resolve this problem by using system clipping region,but after some industial practice we found that the benefits from this optimizing is limited.Based on this situation,this paper presents a simple effective and practical optimizing design based on custom clipping region sets which can effectivly improve drawing performance of complex graphics in industrial field. Keyword:Custom Clipping Region Sets, MFC, GDI, Drawing基金项目“石化、轨道交通行业分布式综合监控系统(SCADA)研发及应用示范”资助1.GDI概述GDI(Graphics Device Interface)是Windows操作系统的传统图形子系统,负责与设备无关的图形绘制,Win32 API为应用程序提供了丰富的绘图函数和功能,而MFC又对他们进行了C++类的封装。
::首页>> 文档中心>> 在线杂志>> 位图与调色板[ 在线杂志第32期][ 原创文档本文适合中级读者已阅读9601次]VC实现波形不闪烁动态绘图作者:重庆大学通信工程学院吴华下载源代码一、程序运行时波形效果二、在信号处理中,通常需要把采集信号的实时波形显示出来。
如果直接在屏幕上动态绘图的话,会出现闪烁现象,为了克服这个问题,本文采用的就是先在内存绘图,然后再拷贝到屏幕,从而实现动态绘图而不闪烁。
详细介绍如下:2.1首先在头文件中定义以下私有变量,并在对话框资源中放置一个picture控件private:CDC *pDC; //屏幕绘图设备CDC memDC; //内存绘图设备int m_High; //绘图起点int m_Low; //绘图终点int m_lCount[1024]; //数据存储数组int m_now; //记录波形当前点2.2在实现文件中初始化变量,并设置定时器BOOL CDrawTest::OnInitDialog()?{CDialog::OnInitDialog();// TODO: Add extra initialization herem_Low = 0;m_High = 1024;m_now =0;SetTimer(1,100,NULL);return TRUE; // return TRUE unless you set the focus to a control// EXCEPTION: OCX Property Pages should return FALSE}2.3在定时器里创建内存绘图设备,并调用绘图函数在内存设备中绘图,绘图完毕后把内存设备中图拷贝到屏幕void CDrawTest::OnTimer(UINT nIDEvent){// TODO: Add your message handler code here and/or call defaultCRect rect;// 获取绘制坐标的文本框CWnd* pWnd = GetDlgItem(IDC_COORD);//获得对话框上的picture的窗口句柄pWnd->GetClientRect(&rect);// 指针pDC = pWnd->GetDC();pWnd->Invalidate();pWnd->UpdateWindow();//内存绘图CBitmap memBitmap;CBitmap* pOldBmp = NULL;//创建内存绘图设备memDC.CreateCompatibleDC(pDC);memBitmap.CreateCompatibleBitmap(pDC,rect.right,rect.bottom);pOldBmp = memDC.SelectObject(&memBitmap);memDC.BitBlt(rect.left,rect.top,rect.right,rect.bottom,pDC,0,0,SRCCOPY);//自定义绘图函数,详细见源程序DrawWave(&memDC);//把内存绘图拷贝到屏幕pDC->BitBlt(rect.left,rect.top,rect.right,rect.bottom,&memDC,0,0,SRCCOPY);memDC.SelectObject(pOldBmp);memDC.DeleteDC();memBitmap.DeleteObject();CDialog::OnTimer(nIDEvent);}最新评论[发表评论][文章投稿]查看所有评论推荐给好友打印我是作者,感谢大家的宝贵意见,尤其感谢successq!我现在一边攻读硕士,一边在公司兼职做课题,比较忙,但我会尽快抽时间添加打印功能和实现任意地方绘制!封装成一个MFC扩展类! ( superlamster 发表于2005-2-7 18:48:00)文章写的很好,很实用。
如何解决绘图过程中的闪烁在VC中进行绘图过程处理时,如果图形刷新很快,经常出现图形闪烁的现象。
利用先在内存绘制,然后拷贝到屏幕的办法可以消除屏幕闪烁,具体的方法是先在内存中创建一个与设备兼容的内存设备上下文,也就是开辟一快内存区来作为显示区域,然后在这个内存区进行绘制图形。
在绘制完成后利用BitBlt函数把内存的图形直接拷贝到屏幕上即可。
具体的代码实现为:(1)创建内存区域CDC* pMem=new CDC;CBitmap* pBmp=new CBitmap;CBitmap* pOldBmp;CDC* pDC=GetDC();CRect rectTemp;为绘图区域pMem->CreateCompatibleDC(pDC);pBmp->CreateCompatibleBitmap(pDC, rectTemp.Width(), rectTemp.Height());pOldBmp=pMem->SelectObject(pBmp);(2)进行图形绘制pMem->LineTo(...); 进行绘图处理(3)拷贝到屏幕pDC->BitBlt(rectTemp.left,rectTemp.top,rectTemp.Width(),rectTemp.Height(),pMem,0,0,SRCCO PY);pMem->SelectObject(pOldBmp);pBmp->DeleteObject() ;pMem->DeleteDC();====================================================================== ==========================双缓存机制解决VC++绘图时的闪烁问题显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。
而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。
MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。
我想就我长期(呵呵当然也只有2年多)使用MFC绘图的经验谈谈我的一些观点。
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)中: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、如何提高绘图的效率我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。
如果真要在一次重绘过程重画这么多元件,可想而知这个过程是非常漫长的。
如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。
怎么办?只有再研究研究MFC 的绘图过程了。
实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。
裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。
因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC 中的裁剪区了。
因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。
但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。
可以先用 pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。
如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。
====================================================================== ================================================================================================= =================================================VC++大数据量绘图时无闪烁刷屏技术实现引言当我们需要在用户区显示一些图形时,先把图形在客户区画上,虽然已经画好但此时我们还无法看到,还要通过程序主动地刷新用户区,强制Windows发送一条WM_PAINT消息,这将引发视类OnDraw函数简单地将所有的图形对象重画,这样才完成了图形的显示工作,但在刷新的同时会引起较明显的闪烁尤其是当画面面积较大、图像元素过多时尤为明显甚至达到无法正常工作的地步。
因此,我们需要做相应的处理。
本文介绍了采用先在内存中绘制图形,然后再把绘好的图形以位图方式从内存拷贝到窗口客户的消除刷屏闪烁的一种方法。
WM_PAINT消息和无效区·在用户移动窗口或显示窗口时,窗口中先前被隐藏的区域重新可见。
·用户改变窗口的大小。
·滚动窗口用户区。
·程序调用InvalidateRect或InvalidateRgn函数显式地发送一条WM_PAINT消息。
当上面情况之一发生时,就要求应用程序一定刷新其用户区的一部分或全部,Windows会向窗口函数发送一条WM_PAINT消息。
另外,当 Windows删除覆盖窗口部分区域的对话框或消息框时和菜单下拉出来又被释放时窗口用户区被临时覆盖,系统会试图保存显示区域,但是不一定能成功,可能向窗口函数发送一条WM_PAINT消息,要求应用程序刷新其用户区。
需要说明的是:光标或图符穿过窗口用户区时,也可能覆盖显示内容,但这种情况下,系统一定能保留并恢复被覆盖的区域,所以此时并不会发送WM_PAINT消息来要求应用程序去刷新其显示区。