c高效绘图转.docx
《c高效绘图转.docx》由会员分享,可在线阅读,更多相关《c高效绘图转.docx(20页珍藏版)》请在冰豆网上搜索。
![c高效绘图转.docx](https://file1.bdocx.com/fileroot1/2022-10/10/976cab90-cfdf-46f4-a195-835f07b85729/976cab90-cfdf-46f4-a195-835f07b857291.gif)
c高效绘图转
C#高效绘图(转)
双缓冲技术
双缓冲是将图片在显示到DC前,现在要内存建一个DC,也就是用于存储这张图片的内存区,然后在将这部分update到你要显示的地方
这样,可以防止画面抖动很大
这样和你说吧,如果要实现你要的效果,你必须用指针访问内存
比如,把程序声明成unsafe的,然后按照上面的操作进行
this.clear(this.BackColor)不行的invalidate(),闪的厉害所以不行
我再来详细解释一下刚才实现双缓冲的具体步骤:
1、在内存中建立一块“虚拟画布”:
Bitmapbmp=newBitmap(600,600);
2、获取这块内存画布的Graphics引用:
Graphicsg=Graphics.FromImage(bmp);
3、在这块内存画布上绘图:
g.FillEllipse(brush,i*10,j*10,10,10);
4、将内存画布画到窗口中
this.CreateGraphics().DrawImage(bmp,0,0);
重点:
现在的cpu飞快,其实数学计算一般很快,cpu大部分时间是在处理绘图,而绘图有三种境界:
1>每次重绘整体Invalidate()
2>每次局部绘制Invalidate(Rect);
3>有选择的局部绘制。
不能说,一定是第三种方式好,得视情况,境界高程序肯定就复杂,如果对效率要求不高或者绘图量小当然直接用第一种方式。
然而,稍微专业点的绘图程序,第一第二种方式肯定满足不了要求,必须选用第三种方式。
而第三种方式的手段多样,也得根据实际情况拿相应的解决之道。
这里讲解一般的三种手段,他们可以联合使用。
1.缓存——Bitmap或者DoubleBuffer。
缓存就是先把绘制的图形绘制到一张内存位图上,然后在一次性的贴位图,他可以提高绘图速度,也能避免闪烁。
DoubleBuffer=true是C#窗体的属性,设置了此属性估计系统本身会起用无效区的内存位图缓存,而不需要程序员Bitmap处理。
2.合理利用无效区域。
无效区域就是系统保存当前变化需要重绘的区域,可以在OnPaint()中,e.ClipRectangle(e.ClipRectangle.X)直接获得,也可以通过其他方式获得。
Windows系统只会重绘无效区域内的绘图信息,然而我们用户的绘制代码一般是绘制整个区域的,很多时候无效区域只是一小部分区域,虽然执行了所有的绘图代码,但是Windows系统只会重新更新无效区域内的绘图。
这里有两个利用点:
1>用户请求重绘时,只请求重绘指定区域的,而不是整个区域,如Invalidate(Rect);
2>在用户绘图代码
Graphicsg;g.DrawLine\g.DrawString\g.FillRectangle...前,先判断绘图的内容是否在无效区域,如果不是就不直接g.Draw...绘图代码。
3.直接贴图。
一般绘图或者重绘是Windows根据无效区域绘制的,如果在鼠标移动时需要重绘通过Windows系统处理Paint消息,有时满足不了要求,
比如①鼠标移动绘制十字测量线就得用异或线而不是Paint消息,
又比如②鼠标移动绘制跟随的信息提示框需要频繁擦除上次覆盖的背景,
又比如③台球滚动时台球与球桌背景的关系。
类似的这些问题如何解决?
首先肯定不能利用Windows原来的绘图机制。
其中一种解决方式是,不断的帧间变化区域贴内存位图——②中的信息框每次鼠标位置变化时可以重新g.Draw...或者贴早生成的信息框内存位图,②中被信息框覆盖的背景应该把本来的大背景截取此需要擦除区域的位置大小位图贴回来就是擦除背景了。
由于每次大背景发生变化时,都应会重新生成大背景内存位图,所以可以是变化的背景。
这三种方式可以一起使用,应该可以解决中等的绘图项目的效率问题。
中大型的绘图,必须记住两点1>只绘制电脑屏幕能
显示的部分;2>只绘制变化的部分。
C#GDI双缓冲高效绘图
Rectanglerectangle=e.ClipRectangle;//取出次窗体或者画布的有效区的矩形区域
BufferedGraphicsContextGraphicsContext=BufferedGraphicsManager.Current;//获取程序住缓冲区域的BufferedGraphicsContext(双缓存类,此类用于提供双缓冲的功能)对象
BufferedGraphicsmyBuffer=GraphicsContext.Allocate(e.Graphics,e.ClipRectangle);//获取缓冲区
Graphicsg=myBuffer.Graphics;
指定在呈现期间像素偏移的方式。
g.PixelOffsetMode=PixelOffsetMode.HighQuality;//高质量低速度呈现
指定是否将平滑处理(消除锯齿)应用于直线、曲线和已填充区域的边缘。
g.SmoothingMode=SmoothingMode.HighQuality;//指定高质量、低速度呈现。
g.Clear(BackColor);//或者使用invalidate方法==有效区的擦除
PenbluePen2=newPen(Color.Blue);
LineDrawRoutine(g,bluePen2);
myBuffer.Render(e.Graphics);//将图形缓冲区的内容写入指定的Graphics对象。
g.Dispose();
myBuffer.Dispose();
其实在C#里如果是在Form中绘图的话直接把Form的DoubleBuffered=true就可以了(利用winfrom窗体的默认双缓冲)
把所有的绘图放在一个picturebox里面绘制,
不要直接再在form里面绘
SetStyle(ControlStyles.UserPaint,true);
SetStyle(ControlStyles.ResizeRedraw,true);
SetStyle(ControlStyles.AllPaintingInWmPaint,true);
SetStyle(ControlStyles.OptimizedDoubleBuffer,true);
SetStyle(ControlStyles.Selectable,true);
如果你在Form中绘图的话,不论是不是采用的双缓存,都会看到图片在更新的时候都会不断地闪烁,解决方法就是在这个窗体的构造函数中增加以下三行代码:
请在构造函数里面底下加上如下几行:
SetStyle(ControlStyles.UserPaint,true);
SetStyle(ControlStyles.AllPaintingInWmPaint,true);//禁止擦除背景.
SetStyle(ControlStyles.DoubleBuffer,true);//双缓冲
参数说明:
UserPaint
如果为true,控件将自行绘制,而不是通过操作系统来绘制。
此样式仅适用于派生自Control的类。
AllPaintingInWmPaint
如果为true,控件将忽略WM_ERASEBKGND窗口消息以减少闪烁。
仅当UserPaint位设置为true时,才应当应用该样式。
DoubleBuffer
如果为true,则绘制在缓冲区中进行,完成后将结果输出到屏幕上。
双重缓冲区可防止由控件重绘引起的闪烁。
要完全启用双重缓冲,还必须将UserPaint和AllPaintingInWmPaint样式位设置为true。
GDI的双缓冲问题
我想有很多搞图形方面的朋友都会用到双缓冲技术的时候,而且有的时候她的确是个头疼的问题。
最近我也要用双缓冲技术,程序怎么调试都不合适,当要对图形进行移动时,总是会出现闪烁抖动。
在网上找了些资料,说得都不清不楚的,折腾了一晚上也没弄出来。
第二天觉定自己研究一下。
现在把自己的一些想法拿出来跟大家分享一下。
双缓冲的基本原理:
一直以来的误区:
.net1.1和.net2.0在处理控件双缓冲上是有区别的。
.net1.1中,使用:
this.SetStyle(ControlStyles.DoubleBuffer,true);
.net2.0中,使用:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer,true);
要知道,图元无闪烁的实现和图元的绘制方法没有多少关系,只是绘制方法可以控制图元的刷新区域,使双缓冲性能更优!
导致画面闪烁的关键原因分析:
一、绘制窗口由于大小位置状态改变进行重绘操作时
绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。
刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint事件一次。
窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。
所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。
根据以上分析可知,当图元数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重。
特别是图元比较大绘制时间比较长时,闪烁问题会更加严重,因为时间延迟会更长。
解决上述问题的关键在于:
窗口刷新一次的过程中,让所有图元同时显示到窗口。
二、进行鼠标跟踪绘制操作或者对图元进行变形操作时
当进行鼠标跟踪绘制操作或者对图元进行变形操作时,Paint事件会频繁发生,这会使窗口的刷新次数大大增加。
虽然窗口刷新一次的过程中所有图元同时显示到窗口,但也会有时间延迟,因为此时窗口刷新的时间间隔远小于图元每一次显示到窗口所用的时间。
因此闪烁现象并不能完全消除!
所以说,此时导致窗口闪烁现象的关键因素在于Paint事件发生的次数多少。
解决此问题的关键在于:
设置窗体或控件的几个关键属性。
下面讲具体的实现方法:
(转)
1、在内存中建立一块“虚拟画布”:
Bitmapbmp=newBitmap(600,600);
2、获取这块内存画布的Graphics引用:
Graphicsg=Graphics.FromImage(bmp);
3、在这块内存画布上绘图:
如画线g.DrawLine(添加参数);
4、将内存画布画到窗口中:
this.CreateGraphics().DrawImage(bmp,0,0);
在构造函数中加如下代码
代码一:
SetStyle(ControlStyles.UserPaint,true);
SetStyle(ControlStyles.AllPaintingInWmPaint,true);//禁止擦除背景.
SetStyle(ControlStyles.DoubleBuffer,true);//双缓冲
或代码二:
this.SetStyle(ControlStyles.DoubleBuffer|ControlStyles.UserPaint