vb.net 双缓冲绘图具体如何设置双缓冲

双缓冲实现图形液晶的快速显示
22:19:49&&&来源:21ic
在嵌入式设备中,液晶已经成为显示器件的首选。液晶类型有字符和图形之分,字符液晶便宜且显示速度较快。在中文显示或者复杂图表显示的场合,需要使用。
在液晶接口电路设计时,需要正确使用液晶控制器。典型应用时,特别是在嵌入式系统中,常常采用端口寻址的液晶控制器,如SED1335、T6963C等。这种寻址方式的好处是只占用很少的几个地址空间,并且控制方便。液晶控制器一方面通过外部总线和微处理器相连;一方面通过内部总线与显示缓冲RAM相连;还有一些驱动的控制线和数据线与液晶相连。微处理器通过液晶控制器,采用地址译码方式来执行液晶控制、显示、绘制,以及存贮操作等指令。常用指令有:初始化,显示方式,移动光标,光标处的数据读和写等等。更改显示RAM中的数据,也就更改了液晶屏的相应内容。
作为现在流行的显示器件,液晶有其固有的优越性,但也有严重不足之处。相对CRT等显示器件而言,最主要缺陷是视觉效果差,亮度不足,视角偏小,响应速度慢(“拖尾”现象)。同时,根据前面的叙述,我们可以看到:由于硬件系统的局限性,显示RAM通过内部总线与控制器相连,微处理器对液晶显示RAM
的寻址采用了间接的端口译码方式,而不是速度更快的直接内存寻址。译码的延时会进一步减缓显示速度。表现在用户界面上,很可能出现刷新的延迟,屏幕的闪烁,以及余辉现象,都会引起视觉的不舒适。一方面降低了产品的档次,另外用户长时间操作时会导致眼睛的疲劳,产生厌倦情绪。
2. 基本介绍
无论是LCD还是CRT,进行绘图,都是在“画布”上做清除旧位置的图和重新画新位置的图的操作。当程序执行速度不够快时,我们会看到清除的操作与重画的操作,这就造成了闪烁现象。解决这个问题的方法之一,就是把清除与重画的操作放在另一张“画布”上完成,等到画完后再复制到最后要显示的“画布”上。这种绘图方式就是。
实际上,图形的双缓冲显示方式,对于Windows程序设计员来说,一点也不陌生。以MFC绘图机制来说,CPaintDC就是要显示的画布,如果我们使用另外一个兼容的DC来做清除与重画的操作,再把整个DC的图像复制到CPaintDC,就可以避免屏幕闪烁的问题。对于多数嵌入式设备来说,由于MCU的处理速度要慢许多,因此在软件设计中,更需借鉴这种方法,以期提高显示速度,最大限度地降低液晶的显示缺陷。
3. 实现方法
双缓冲方式不仅可以提高液晶显示速度,还可以改善软件模块的封装性和可移植性。实现时,就是在系统存贮器中开辟一块显示RAM
的镜像缓冲区域,此区域保存了预备显示图像的“快照”。当更改屏幕内容时,先更改镜像区域的内容;需要显示时,才把“快照”数据刷新到显示RAM。这样对于用户界面而言,显示速度只取决于“快照”的送入速度。
下面,我们以两种常用的液晶控制器(T6963C和SED1335)为例,说明如何使用双缓冲的方式进行液晶的。为了简单起见,我们直接选用内藏驱动和控制器的典型图形液晶显示模块。
基于T963C的液晶显示模块SMG24064B
SMG24064B是显示点阵为240*64、控制器为T6963C的液晶显示模块,工作环境是5V/10mA,生产厂家为长沙太阳人。许多液晶生产厂家都有和这款相兼容的产品,如大连东显、深圳拓普微、台湾晶采等。其接口信号说明如下:
表1 SMG24064B信号线说明
SMG24064B应用时,可以采用总线方式或者模拟口线方式。双缓冲方式只能采用总线方式,通过端口译码来实现。下图是一种典型的接口电路。图中的GAL实现地址译码,当然也可以采用74138等完成译码。译码输出信号连接到T6963C控制器的片选使能端CE,而数据命令选择端C/D可以接地址线A0。
图1 SMG24064B与8031的总线连接
这样,就得到了两个译码端口,一个端口作为控制口,向T6963C送入命令;一个端口作为数据口,向T6963C送入数据。采用地址译码方式来执行液晶控制、显示、绘制,以及存贮操作等指令。
显而易见,240×64液晶的显示RAM
空间为240/8×64=1920字节。而T6963C控制器内部自带8KB的RAM缓冲区,其中显示缓冲区首地址寄存器对应的后续1920字节的内容就映射到LCD屏幕的相应位置。这样,我们从嵌入式系统MCU扩充的外部RAM中分配1920字节作为镜像显示缓冲(软件编程也就是从外部RAM中分配1920字节的数组),来实现双缓冲显示。对于应用系统而言,如果扩展的外部RAM较大(如32KB)时,分配不到2KB的镜像显示缓冲,开销不算大;但却可以大大改善显示的速度。
这样,LCD的绘图程序,比如画点、画线、填充、字符串显示等,都是在镜像缓冲RAM中操作的;只有两个程序,LCD的初始化程序InitLCD(初始化液晶,设置液晶的显示模式等)和显示程序DisplayLCD(把镜像缓冲数据刷新到T6963C内的显示RAM缓冲),与底层硬件相关。下面是显示程序DisplayLCD的实现流程:
设置液晶显示的起始0地址(数据口写入数据0x00和0x00,命令口写入数据0x24);
设置液晶连续写模式(命令口写入数据0xb0);
把镜像缓冲中的1920个数据连续送入数据口(数据口写入显示数据);
中止液晶连续写模式(命令口写入数据0xb2)。
上述命令指令含义请参见液晶模块或者控制器相关资料。显示程序只在要最终显示的时候才调用,它决定了显示速度,我们可以专门对它进行优化,或者用汇编代码完成,实现最快的显示速度。
当然,如果系统扩展的RAM空间足够时,我们还可以采用三缓冲的方式,也就是在系统RAM中分配两块镜像区域。一块缓冲保存当前显示图形数据,另一块保存下一个图形数据,然后交替地把缓冲区的数据送入显示RAM,完成显示。这种方式适用于以下情况:用户界面的更新多数只是局部更新,因此通过比较两块缓冲数据的不同,显示时只需要送入当前缓冲中不相同的部分数据,可以进一步提高显示速度。
基于SED1335的液晶显示模块EDM
EDM是显示点阵为320*240、控制器为SED1335的液晶显示模块,RAM的显示容量为32KB,生产厂家为大连东显。许多320*240的液晶,如DMF50081、LM32019P/T等,都可采用SED1335控制器。
实际上,对于较大的图形液晶,通常采用SED系列控制器。此控制器功能强大,具有丰富指令集,与MPU接口有较强的I/O缓冲器,可以管理64K显示缓冲区。具备文本/图形显示特性。可以显示文本区和三个图形显示区的内容(通过软件初始化实现),其中三个图形显示区L1、L2、L3可以单独显示,也可以合成显示。如下图所示:
图2 EDM的图形显示区
显而易见,320×240的图形液晶显示缓存至少需要320/8×240=9600字节。而利用三区显示特性时,通过内部总线需要扩充显示RAM为00字节。因此,对于液晶模块厂家来说,硬件设计时通常扩充了32K的显示RAM,如62256。实际上,多数时候我们只用单区来显示就足够了。
在嵌入式应用中,如果需要液晶显示程序有较好的移植性,并且系统RAM足够大,足够分配9600字节的镜像缓冲的话,那么,最好就象上面的T6963C应用示例一样处理吧。分配9600字节的镜像缓冲后,所有的画点、画线、位图等操作,都是在镜像缓冲RAM中完成,你只需要在显示的时候,把镜像缓冲RAM中数据送到显示区。
如果应用系统没有足够的RAM,也不要紧。我们可以巧妙地应用SED1335提供的特性,把显示区L1,L2当成双缓冲,同样实现双缓冲的显示,一样可以使得屏幕画面变化时没有延时和闪烁现象。不过,此时的画点画线操作都与硬件相关,软件模块的移植性不够好。
具体叙述如下:首先关闭显示区L1,L2,L3;在显示区L1上完成“快照”(即画点画线操作),然后打开L1,即单独显示L1;画面变化时,就把变化后的画面“快照”到L2。当需要刷新显示时,关闭L1,打开L2即可。下次再关闭L2,打开L1。如此交替反复。
当然,SED1335具备3个显示区,有效利用可以完成更加有趣的应用,在此不多谈。下图为定时刷新显示的示意图:
图3 EDM的图形显示
4. 结束语
采用这种方法,具有很多好处:
显示内容更改快。表现在用户界面上,几乎感觉不到刷新和闪烁。
软件模块封装好。菜单编程模块和刷新显示模块分开。菜单编程模块与具体硬件无关,只对镜像RAM操作。刷新模块才和硬件相关。软件的更改和移植方便。
可以轻松完成许多特技效果,如画面的滚动,平移推拉,交错,百叶窗等。
由于用户界面保存于镜像RAM中,还方便实现远程诊断等功能。
综上所述,双缓冲方式不仅可以实现液晶的快速显示,尽可能地避免屏幕闪烁,延迟,余辉等现象,还可以实现许多有趣和特殊的显示。这种思想也适用于文本型液晶的快速显示,同样也适用于CRT等其他有较大显示容量的显示器件。运用之妙,在乎各人了。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:276396次
积分:5468
积分:5468
排名:第3963名
原创:283篇
转载:103篇
评论:16条
(1)(1)(2)(2)(1)(4)(4)(1)(5)(2)(4)(1)(2)(10)(14)(7)(9)(7)(1)(8)(8)(10)(2)(6)(2)(10)(14)(5)(16)(52)(7)(2)(16)(9)(5)(3)(3)(11)(6)(3)(5)(1)(9)(3)(45)(7)(13)(25)(2)C#基础(125)
GDI+的双缓冲问题终于搞定了, 真是松了一口气!
一直以来的误区:.net1.1 和 .net 2.0 在处理控件双缓冲上是有区别的。
.net 1.1 中,使用:this.SetStyle(ControlStyles.DoubleBuffer, true);&
.net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
怪不说老是提示参数无效,一直也不知道是这个问题,呵呵
要知道,图元无闪烁的实现和图元的绘制方法没有多少关系,只是绘制方法可以控制图元的刷新区域,使双缓冲性能更优!
导致画面闪烁的关键原因分析:
&&&&& 一、绘制窗口由于大小位置状态改变进行重绘操作时
&&&& 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint事件一次。窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。
&&&& 所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。
&&&& 根据以上分析可知,当图元数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重。特别是图元比较大绘制时间比较长时,闪烁问题会更加严重,因为时间延迟会更长。
&&&& 解决上述问题的关键在于:窗口刷新一次的过程中,让所有图元同时显示到窗口。
&&&&& 二、进行鼠标跟踪绘制操作或者对图元进行变形操作时
&&&& 当进行鼠标跟踪绘制操作或者对图元进行变形操作时,Paint事件会频繁发生,这会使窗口的刷新次数大大增加。虽然窗口刷新一次的过程中所有图元同时显示到窗口,但也会有时间延迟,因为此时窗口刷新的时间间隔远小于图元每一次显示到窗口所用的时间。因此闪烁现象并不能完全消除!
&&&& 所以说,此时导致窗口闪烁现象的关键因素在于Paint事件发生的次数多少。
&&&&&解决此问题的关键在于:设置窗体或控件的几个关键属性。
&下面来介绍解决办法的具体细节:
解决双缓冲的关键技术:
1、设置显示图元控件的几个属性:& 必须要设置,否则效果不是很明显!
this.SetStyle(ControlStyles.OptimizedDoubleBuffer |   
&&&&&&&&&&&&&&&&&&& ControlStyles.ResizeRedraw |
&&&&&&&&&&&&&&&&&&& ControlStyles.AllPaintingInWmPaint, true);
2、窗口刷新一次的过程中,让所有图元同时显示到窗口。
&&& 可以通过以下几种方式实现,这几种方式都涉及到Graphics对象的创建方式。
Graphics对象的创建方式:
&a、在内存上创建一块和显示控件相同大小的画布,在这块画布上创建Graphics对象。
&&&& 接着所有的图元都在这块画布上绘制,绘制完成以后再使用该画布覆盖显示控件的背景,从而达到“显示一次仅刷新一次”的效果!
  实现代码(在OnPaint方法中):
  Rectangle rect = e.ClipR
  Bitmap bufferimage = new Bitmap(this.Width, this.Height);
&&&&&Graphics g = Graphics.FromImage(bufferimage);
  g.Clear(this.BackColor);
&&&&&g.SmoothingMode = SmoothingMode.HighQ //高质量
&&&&&g.PixelOffsetMode = PixelOffsetMode.HighQ //高像素偏移质量
  foreach (IShape drawobject in doc.drawObjectList)
&&&&&&&&&&&&&&&&&if (rect.IntersectsWith(drawobject.Rect))
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& drawobject.Draw(g);
&&&&&&&&&&&&&&&&&&& if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
&&&&&&&&&&&&&&&&&&&&&&& && this.CurrentOperator == Enum.Operator.Transfrom)//仅当编辑节点操作时显示图元热点
&&&&&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&&&&&& drawobject.DrawTracker(g);
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }
    using (Graphics tg = e.Graphics)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& tg.DrawImage(bufferimage, 0, 0);  //把画布贴到画面上
&&&&&&&&&&& }
&b、直接在内存上创建Graphics对象:
     Rectangle rect = e.ClipR
     BufferedGraphicsContext currentContext = BufferedGraphicsManager.C
&&&&&&&&&&& BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics, e.ClipRectangle);
&&&&&&&&&&& Graphics g = myBuffer.G
&&&&&&&&&&& g.SmoothingMode = SmoothingMode.HighQ
&&&&&&&&&&& g.PixelOffsetMode = PixelOffsetMode.HighS
&&&&&&&&&&& g.Clear(this.BackColor);
&&&&&&&&&&& foreach (IShape drawobject in doc.drawObjectList)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& if (rect.IntersectsWith(drawobject.Rect))
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& drawobject.Draw(g);
&&&&&&&&&&&&&&&&&&& if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
&&&&&&&&&&&&&&&&&&&&&&& && this.CurrentOperator == Enum.Operator.Transfrom)//仅当编辑节点操作时显示图元热点
&&&&&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&&&&&& drawobject.DrawTracker(g);
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }
    myBuffer.Render(e.Graphics);
&&&&&&&&&&&g.Dispose();
&&&&&&&&&&&myBuffer.Dispose();//释放资源
至此,双缓冲问题解决,两种方式的实现效果都一样,但最后一种方式的占有的内存很少,不会出现内存泄露!
参考资料:
2、GDI+图形程序设计&& [美]Mahesh Chand 著&&&&&& 电子工业出版社
3、C#高级编程(第三版)
原文链接:http://blog.csdn.net/ifooler/article/details/1598447
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:55326次
排名:千里之外
原创:24篇
转载:114篇
(1)(1)(2)(2)(4)(1)(1)(2)(2)(1)(3)(3)(4)(3)(13)(38)(1)(1)(10)(9)(5)(8)(23)2012年12月 总版技术专家分月排行榜第一2012年10月 总版技术专家分月排行榜第一2012年9月 总版技术专家分月排行榜第一2012年8月 总版技术专家分月排行榜第一
2012年11月 总版技术专家分月排行榜第三
2011年11月 专题开发/技术/项目大版内专家分月排行榜第二2011年8月 专题开发/技术/项目大版内专家分月排行榜第二
2012年12月 总版技术专家分月排行榜第一2012年10月 总版技术专家分月排行榜第一2012年9月 总版技术专家分月排行榜第一2012年8月 总版技术专家分月排行榜第一
2012年11月 总版技术专家分月排行榜第三
2012年12月 总版技术专家分月排行榜第一2012年10月 总版技术专家分月排行榜第一2012年9月 总版技术专家分月排行榜第一2012年8月 总版技术专家分月排行榜第一
2012年11月 总版技术专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。& 双缓冲区绘图操作的实现
双缓冲区绘图操作的实现
在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。
因为窗体在刷新时,总要有一个擦除原来图像的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图像颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显,于是我们就看到了闪烁现象。
我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图像的时候都没有将原来的图像清除,造成了图像的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。
下面来看一个简单的例子,在CProjectNameView的OnDraw函数进行稍微复杂的绘图操作(复杂一点闪烁效果明显)
// 普通的画图操作
CPoint ptCenter;
CRect rect, ellipseRect;
GetClientRect(&rect);
ptCenter = rect.CenterPoint();
for(int i = 60; i & 0; --i)
ellipseRect.SetRect(ptCenter, ptCenter);
ellipseRect.InflateRect(i * 5, i * 5);
pDC-&Ellipse(ellipseRect);
下面是双缓冲区的绘图代码:
// 双缓冲区画图
CPoint ptCenter;
CRect rect, ellipseRect;
GetClientRect(&rect);
ptCenter = rect.CenterPoint();
CDC dcMem;
// 用于缓冲作图的内存CD
CBitmap bmp;
// 内存中存在临时图像的位图
dcMem.CreateCompatibleDC(pDC); // 依附窗口DC创建兼容DC
// 创建兼容位图
bmp.CreateCompatibleBitmap(&dcMem, rect.Width(), rect.Height());
dcMem.SelectObject(&bmp); // 将位图选入内存DC
dcMem.FillSolidRect(rect, pDC-&GetBkColor());// 按照原有背景色填充客户区
// 绘图操作
for (int i = 60; i & 0; --i)
ellipseRect.SetRect(ptCenter, ptCenter);
ellipseRect.InflateRect(i * 5, i * 5);
dcMem.Ellipse(ellipseRect);
// 在内存DC上绘图
// 将内存DC上的东西复制到pDC
pDC-&BitBlt(0, 0, rect.Width(), rect.Height(),
&dcMem, 0, 0, SRCCOPY);
dcMem.DeleteDC(); // 删除DC
bmp.DeleteObject(); // 删除位图
可以看到,当拖动窗口大小时,使用普通绘图操作会不停的闪烁,而双缓冲区的几乎看不到闪烁。
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!
您可能对下面的文章也感兴趣:
(推荐使用
(关于作者 Wins0n/代码疯子)
免责声明:本站所有内容仅代表个人观点,无法保证100%准确,如有错误请联系指正,谢谢!
2016年十月 &(1)
2016年三月 &(1)
2016年二月 &(1)
2016年一月 &(1)
2015年十月 &(1)
2015年六月 &(2)
2015年四月 &(1)
2015年一月 &(1)
2014年十一月 &(5)
2014年十月 &(1)
2014年九月 &(1)
2014年八月 &(2)
2014年七月 &(3)
2014年六月 &(4)
2014年四月 &(1)
2014年三月 &(2)
2014年二月 &(1)
2014年一月 &(1)
2013年十二月 &(1)
2013年十一月 &(2)
2013年十月 &(2)
2013年九月 &(3)
2013年八月 &(2)
2013年七月 &(3)
2013年六月 &(2)
2013年五月 &(1)
2013年四月 &(4)
2013年三月 &(2)
2013年二月 &(1)
2013年一月 &(2)
2012年十二月 &(5)
2012年十一月 &(3)
2012年十月 &(3)
2012年九月 &(4)
2012年八月 &(4)
2012年七月 &(3)
2012年六月 &(3)
2012年五月 &(6)
2012年四月 &(4)
2012年三月 &(6)
2012年二月 &(4)
2012年一月 &(7)
2011年十二月 &(9)
2011年十一月 &(9)
2011年十月 &(13)
2011年九月 &(18)
2011年八月 &(8)
2011年七月 &(7)
2011年六月 &(16)
2011年五月 &(13)
2011年四月 &(21)
2011年三月 &(22)
2011年二月 &(15)
2011年一月 &(7)
2010年十二月 &(23)
2010年十一月 &(33)
2010年十月 &(35)
2010年九月 &(42)3489人阅读
C#绘图双缓冲
C#双缓冲解释:
简单说就是当我们在进行画图操作时,系统并不是直接把内容呈现到屏幕上,而是先在内存中保存,然后一次性把结果输出来,如果没用双缓冲的话,你会发现在画图过程中屏幕会闪的很厉害,因为后台一直在刷新,而如果等用户画完之后再输出就不会出现这种情况,具体的做法,其实也就是先创建一个位图对象,然后把内容保存在里面,最后把图呈现出来。
GDI+的双缓冲问题
一直以来的误区:.net1.1 和 .net 2.0 在处理控件双缓冲上是有区别的。
.net 1.1 中,使用:this.SetStyle(ControlStyles.DoubleBuffer, true);&
.net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
导致画面闪烁的关键原因分析:
&&&&& 一、绘制窗口由于大小位置状态改变进行重绘操作时
&&&& 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint事件一次。窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。
&&&& 所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。
&&&& 根据以上分析可知,当图元数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重。特别是图元比较大绘制时间比较长时,闪烁问题会更加严重,因为时间延迟会更长。
&&&& 解决上述问题的关键在于:窗口刷新一次的过程中,让所有图元同时显示到窗口。
&&&&& 二、进行鼠标跟踪绘制操作或者对图元进行变形操作时
&&&& 当进行鼠标跟踪绘制操作或者对图元进行变形操作时,Paint事件会频繁发生,这会使窗口的刷新次数大大增加。虽然窗口刷新一次的过程中所有图元同时显示到窗口,但也会有时间延迟,因为此时窗口刷新的时间间隔远小于图元每一次显示到窗口所用的时间。因此闪烁现象并不能完全消除!
&&&& 所以说,此时导致窗口闪烁现象的关键因素在于Paint事件发生的次数多少。
&&&&&解决此问题的关键在于:设置窗体或控件的几个关键属性。
使用双缓冲
解决双缓冲的关键技术:
1、设置显示图元控件的几个属性:& 必须要设置,否则效果不是很明显!
this.SetStyle(ControlStyles.OptimizedDoubleBuffer |   
&&&&&&&&&&&&&&&&&&& ControlStyles.ResizeRedraw |
&&&&&&&&&&&&&&&&&&& ControlStyles.AllPaintingInWmPaint, true);
2、窗口刷新一次的过程中,让所有图元同时显示到窗口。
&&& 可以通过以下几种方式实现,这几种方式都涉及到Graphics对象的创建方式。
1、& 利用默认双缓冲
(1)在应用程序中使用双缓冲的最简便的方法是使用 .NET Framework 为窗体和控件提供的默认双缓冲。通过将 DoubleBuffered 属性设置为 true。
&&&&& this.DoubleBuffered=
(2)使用 SetStyle 方法可以为 Windows 窗体和所创作的 Windows 控件启用默认双缓冲。
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
2、& 手工设置双缓冲
.netframework提供了一个类BufferedGraphicsContext负责单独分配和管理图形缓冲区。每个应用程序域都有自己的默认 BufferedGraphicsContext 实例来管理此应用程序的所有默认双缓冲。大多数情况下,每个应用程序只有一个应用程序域,所以每个应用程序通常只有一个默认 BufferedGraphicsContext。默认 BufferedGraphicsContext 实例由 BufferedGraphicsManager 类管理。通过管理BufferedGraphicsContext实现双缓冲的步骤如下:
(1)获得对 BufferedGraphicsContext 类的实例的引用。
(2)通过调用 BufferedGraphicsContext.Allocate 方法创建 BufferedGraphics 类的实例。
(3)通过设置 BufferedGraphics.Graphics 属性将图形绘制到图形缓冲区。
(4)当完成所有图形缓冲区中的绘制操作时,可调用 BufferedGraphics.Render 方法将缓冲区的内容呈现到与该缓冲区关联的绘图图面或者指定的绘图图面。
(5)完成呈现图形之后,对 BufferedGraphics 实例调用释放系统资源的 Dispose 方法。
完整的例子,在一个400*400的矩形框内绘制10000个随机生成的小圆。
&&&&&&&&&& BufferedGraphicsContext current = BufferedGraphicsManager.C //(1)
&&&&&&&&&& BufferedG
&&&&&&&&&& bg = current.Allocate(this.CreateGraphics(),this.DisplayRectangle); //(2)
&&&&&&&&&& Graphics g = bg.G//(3)
&&&&&&&&&& //随机 宽400 高400
&&&&&&&&&& System.Random rnd = new Random();
&&&&&&&&&& int x,y,w,h,r,i;
&&&&&&&&&& for (i = 0; i & 10000; i++)
&&&&&&&&&& {
&&&&&&&&&&&&&& x = rnd.Next(400);
&&&&&&&&&&&&&& y = rnd.Next(400);
&&&&&&&&&&&&&& r = rnd.Next(20);
&&&&&&&&&&&&&& w = rnd.Next(10);
&&&&&&&&&&&&&& h = rnd.Next(10);
&&&&&&&&&&&&&& g.DrawEllipse(Pens.Blue, x, y, w, h);
&&&&&&&&&& }
&&&&&&&&&& bg.Render();//(4)
&&&&&&&&&& //bg.Render(this.CreateGraphics());
&&&&&&&&&& bg.Dispose();//(5)
3、&& 自己开辟一个缓冲区(如一个不显示的Bitmap对象),在其中绘制完成后,再一次性显示。
完整代码如下:
&&&&&&&&&& Bitmap bt = new Bitmap(400, 400);
&&&&&&&&&& Graphics bg = Graphics.FromImage(bt);
&&&&&&&&&& System.Random rnd = new Random();
&&&&&&&&&& int x, y, w, h, r,
&&&&&&&&&& for (i = 0; i & 10000; i++)
&&&&&&&&&& {
&&&&&&&&&&&&&& x = rnd.Next(400);
&&&&&&&&&&&&&& y = rnd.Next(400);
&&&&&&&&&&&&&& r = rnd.Next(20);
&&&&&&&&&&&&&& w = rnd.Next(10);
&&&&&&&&&&&&&& h = rnd.Next(10);
&&&&&&&&&&&&&& bg.DrawEllipse(Pens.Blue, x, y, w, h);
&&&&&&&&&& }
&&&&&&&&&& this.CreateGraphics().DrawImage(bt, new Point(0, 0));&
另外一个例子,差不多
Graphics对象的创建方式:
&a、在内存上创建一块和显示控件相同大小的画布,在这块画布上创建Graphics对象。
&&&& 接着所有的图元都在这块画布上绘制,绘制完成以后再使用该画布覆盖显示控件的背景,从而达到“显示一次仅刷新一次”的效果!
  实现代码(在OnPaint方法中):
  Rectangle rect = e.ClipR
  Bitmap bufferimage = new Bitmap(this.Width, this.Height);
&&&&&Graphics g = Graphics.FromImage(bufferimage);
  g.Clear(this.BackColor);
&&&&&g.SmoothingMode = SmoothingMode.HighQ //高质量
&&&&&g.PixelOffsetMode = PixelOffsetMode.HighQ //高像素偏移质量
  foreach (IShape drawobject in doc.drawObjectList)
&&&&&&&&&&&&&&&&&if (rect.IntersectsWith(drawobject.Rect))
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& drawobject.Draw(g);
&&&&&&&&&&&&&&&&&&& if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
&&&&&&&&&&&&&&&&&&&&&&& && this.CurrentOperator == Enum.Operator.Transfrom)//仅当编辑节点操作时显示图元热点
&&&&&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&&&&&& drawobject.DrawTracker(g);
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }
    using (Graphics tg = e.Graphics)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& tg.DrawImage(bufferimage, 0, 0);  //把画布贴到画面上
&&&&&&&&&&& }
&b、直接在内存上创建Graphics对象:
     Rectangle rect = e.ClipR
     BufferedGraphicsContext currentContext = BufferedGraphicsManager.C
&&&&&&&&&&& BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics, e.ClipRectangle);
&&&&&&&&&&& Graphics g = myBuffer.G
&&&&&&&&&&& g.SmoothingMode = SmoothingMode.HighQ
&&&&&&&&&&& g.PixelOffsetMode = PixelOffsetMode.HighS
&&&&&&&&&&& g.Clear(this.BackColor);
&&&&&&&&&&& foreach (IShape drawobject in doc.drawObjectList)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& if (rect.IntersectsWith(drawobject.Rect))
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& drawobject.Draw(g);
&&&&&&&&&&&&&&&&&&& if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
&&&&&&&&&&&&&&&&&&&&&&& && this.CurrentOperator == Enum.Operator.Transfrom)//仅当编辑节点操作时显示图元热点
&&&&&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&&&&&& drawobject.DrawTracker(g);
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }
    myBuffer.Render(e.Graphics);
&&&&&&&&&&&g.Dispose();
&&&&&&&&&&&myBuffer.Dispose();//释放资源
至此,双缓冲问题解决,两种方式的实现效果都一样,但最后一种方式的占有的内存很少,不会出现内存泄露!
接下来是对acdsee拖动图片效果的实现。开始不懂双缓冲,以为双缓冲可以解决这个问题,结果发现使用了双缓冲没啥效果,请教了高人,然后修改了些代码,完成这个效果。
图片是在pictureBox1里。
&&&&&&& Bitmap currentM
&&&&&&& bool first =
&&&&&&& private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
&&&&&&&&&&& if (zoom == 0)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& if (e.Button == MouseButtons.Left) //dragging
&&&&&&&&&&&&&&&&&&& mousedrag = e.L
&&&&&&&&&&&&&&& Image myImage = myMap.GetMap();
&&&&&&&&&&&&&&& currentMap = new Bitmap(myImage);
&&&&&&&&&&&&&&& first =
&&&&&&&&&&& }&&&
&&&&&&& private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
&&&&&&&&&&& if (zoom == 0&&!first)
&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& Image img = new Bitmap(Size.Width, Size.Height);
&&&&&&&&&&&&&&&&&&& Graphics g = Graphics.FromImage(img);
&&&&&&&&&&&&&&&&&&& g.Clear(Color.Transparent);//图片移动后显示的底色
&&&&&&&&&&&&&&&&&&& g.SmoothingMode = SmoothingMode.HighQ //高质量
&&&&&&&&&&&&&&&&&&& g.PixelOffsetMode = PixelOffsetMode.HighQ //高像素偏移质量
&&&&&&&&&&&&&&&&&&& g.DrawImageUnscaled(currentMap, new System.Drawing.Point(e.Location.X - mousedrag.X, e.Location.Y - mousedrag.Y));//在g中移动图片,原图在(0,0)画的,所以直接用new System.Drawing.Point(e.Location.X - mousedrag.X, e.Location.Y - mousedrag.Y)就好。
&&&&&&&&&&&&&&&&&&& g.Dispose();
&&&&&&&&&&&&&&&&&&& pictureBox1.Image =//img是在鼠标这个位置时生成被移动后的暂时的图片
&&&&&&&&&&& }
&&&&&&& private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
&&&&&&&&&&& if (zoom == 0)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& System.Drawing.Point pnt = new System.Drawing.Point(Width / 2 + (mousedrag.X - e.Location.X),
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Height / 2 + (mousedrag.Y - e.Location.Y));
&&&&&&&&&&&&&&&& myMap.Center = myMap.ImageToWorld(pnt);
&&&&&&&&&&&&&&& pictureBox1.Image = myMap.GetMap();
&&&&&&&&&&&&&&& first =
&&&&&&&&&&& }
说说思路,在鼠标点下时创建一个bitmap,currentMap,用它来存放当前图像。鼠标移动时,根据鼠标位置画图,最后,鼠标up时,重新画图。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:244812次
积分:2295
积分:2295
排名:第13732名
转载:95篇
评论:12条
(2)(1)(1)(1)(1)(1)(1)(2)(2)(2)(6)(4)(2)(1)(3)(6)(5)(8)(6)(1)(1)(2)(3)(7)(19)(8)(1)(1)(5)(1)(7)(3)(15)(17)}

我要回帖

更多关于 双缓冲绘图 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信