C#高效绘图.docx
- 文档编号:6227350
- 上传时间:2023-01-04
- 格式:DOCX
- 页数:19
- 大小:27.96KB
C#高效绘图.docx
《C#高效绘图.docx》由会员分享,可在线阅读,更多相关《C#高效绘图.docx(19页珍藏版)》请在冰豆网上搜索。
C#高效绘图
C#高效绘图
来源:
双缓冲技术
双缓冲是将图片在显示到DC前,现在要内存建一个DC,也就是用于存储这张图片的内存区,然后在将这部分update到你要显示的地方
这样,可以防止画面抖动很大
这样和你说吧,如果要实现你要的效果,你必须用指针访问内存
比如,把程序声明成unsafe的,然后按照上面的操作进行
this.clear(this.BackColor)不行的invalidate(),闪的厉害所以不行
我再来详细解释一下刚才实现双缓冲的具体步骤:
1、 在内存中建立一块“虚拟画布”:
Bitmap bmp = new Bitmap(600, 600);
2、 获取这块内存画布的Graphics引用:
Graphics g = 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|
ControlStyles.AllPaintingInWmPaint,true);
this.UpdateStyles();
上述方式适合直接在窗体上绘制图形,并且很容易做到。
但有时我们需要在某个控件上绘制图形,那该怎么办呢?
原理跟直接在窗体上绘制图形采用双缓冲是一样的,也要在控件的构造函数里设置上述代码一或代码二。
那么又怎么设置呢?
在MicrosoftVisualStudio2005环境下的,用的C#语言,并采用GDI+。
目标是实现简单的鼠标拖动画线,并且要把之前画过的线都重新画出来。
整个程序使用了三个控件:
一个SplitContainer控件、一个自定义的Panel控件和一个VS自带的Panel控件。
SplitContainer控件的大小设置成窗体宽、半窗体高并定位在窗体的下半部分。
自定义的Panel控件和VS自带的Panel控件都是通过设置它们的Dock属性使它们绑定到SplitContainer控件的Panel1和Panel2上。
附录中会说到自定义的Panel控件是怎么定义的。
窗体的上半部分采用双缓冲。
自定义的Panel控件采用了双缓冲,是通过在自定义Panel控件时设置样式来做到的(设置方法与窗体的双缓冲设置方法一样,如下面三条语句),这不能够在面板的Paint方法里直接设置,因为SetStyle()在Panel类中不是public方法。
VS自带的Panel控件没有采用双缓冲。
SetStyle(ControlStyles.AllPaintingInWmPaint,true);
SetStyle(ControlStyles.UserPaint,true);
SetStyle(ControlStyles.OptimizedDoubleBuffer,true);
把三者结合
有两种方式来创建Graphics对象:
第一是在内存上创建一块和显示区域或控件相同大小的画布,在这块画布上创建Graphics对象。
接着所有的图元都在这块画布上绘制,绘制完成以后再使用该画布覆盖显示控件的背景,从而达到“显示一次仅刷新一次”的效果!
第二是直接在内存上创建Graphics对象。
使用双缓冲的图形可以减少或消除重绘显示图面时产生的闪烁。
使用双缓冲时,更新的图形首先被绘制到内存的缓冲区中,然后,此缓冲区的内容被迅速写入某些或所有显示的图面中。
显示图形的重写相对简短,这通常可以减少或消除有时在更新图形时出现的闪烁。
双缓冲技术(C#GDI)
c#如何实现防窗体闪烁的功能。
大家都会想到运用双缓冲技术,那么在c#中是如何做的?
1、利用默认双缓冲
(1)在应用程序中使用双缓冲的最简便的方法是使用.NETFramework为窗体和控件提供的默认双缓冲。
通过将DoubleBuffered属性设置为true。
this.DoubleBuffered=true;
(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个随机生成的小圆。
BufferedGraphicsContextcurrent=BufferedGraphicsManager.Current;//
(1)
BufferedGraphicsbg=current.Allocate(this.CreateGraphics(),this.DisplayRectangle);//
(2)
Graphicsg=bg.Graphics;//(3)
//随机宽400高400
System.Randomrnd=newRandom();
intx,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)
BufferedGraphicsContextcurrent=BufferedGraphicsManager.Current;//
(1)
BufferedGraphicsbg;
bg=current.Allocate(this.CreateGraphics(),this.DisplayRectangle);//
(2)
Graphicsg=bg.Graphics;//(3)
//随机宽400高400
System.Randomrnd=newRandom();
intx,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对象),在其中绘制完成后,再一次性显示。
完整代码如下:
viewplaincopytoclipboardprint?
Bitmapbt=newBitmap(400,400);
Graphicsbg=Graphics.FromImage(bt);
System.Randomrnd=newRandom();
intx,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);
bg.DrawEllipse(Pens.Blue,x,y,w,h);
}
this.CreateGraphics().DrawImage(bt,newPoint(0,0));
BufferedGraphicsContext的构造函数
∙BufferedGraphicsContext
初始化BufferedGraphicsContext类的新实例。
BufferedGraphicsContext的方法
∙Allocate(Graphics,Rectangle)
使用指定的Graphics的像素格式,创建指定大小的图形缓冲区。
∙Allocate(IntPtr,Rectangle)
使用指定的Graphics的像素格式,创建指定大小的图形缓冲区。
∙Dispose
ReleasesallresourcesusedbytheBufferedGraphicsContext.
∙Equals(Object)
确定指定的Object是否等于当前的Object。
(继承自Object。
)
∙Finalize
允许Object在“垃圾回收”回收Object之前尝试释放资源并执行其他清理操作。
(继承自Object。
)
∙GetHashCode
用作特定类型的哈希函数。
(继承自Object。
)
∙GetType
获取当前实例的Type。
(继承自Object。
)
∙Invalidate
如果某个缓冲区已被分配但尚未释放,则释放当前的图形缓冲区。
∙MemberwiseClone
创建当前Object的浅表副本。
(继承自Object。
)
∙ToString
返回表示当前Object的String。
(继承自Object。
)
BufferedGraphicsContext的属性
∙MaximumBuffer
获取或设置要使用的缓冲区的最大大小。
双缓冲技术绘图
本文主要介绍.Net框架的基本绘图技术。
通过简要的介绍和示例程序来探讨绘图技术的
优势、劣势以及其它相关注意事项。
简介
幸运的是当编写一个典型的Windows窗体程序时,窗体和控件的绘制、效果等操作是
不需要特别加以考虑的。
这是为什么呢?
因为通过使用.Net框架,开发人员可以拖动一
系列的控件到窗体上,并书写一些简单的与事件相关联的代码然后在IDE中按F5,一个完完
全全的窗体程序就诞生了!
所有控件都将自己绘制自己,窗体或者控件的大小和缩放都调
整自如。
在这里经常会用到的,且需要引起一点注意的就是控件效果。
游戏,自定义图表
控件以及屏幕保护程序的编写会需要程序员额外撰写用于响应Paint事件的代码。
本文针对那些Windows窗体开发人员并有助于他们在应用程序编制过程中使用简单的
绘图技术。
首先,我们会讨论一些基本的绘图概念。
到底谁在负责进行绘制操作?
Window
s窗体程序是如何知道何时该进行绘制的?
那些绘制代码究竟被放置在哪里?
之后,还将
介绍图像绘制的双重缓冲区技术,你将会看到它是怎样工作的,怎样通过一个方法来实现
缓存和实际显示的图像间的交替。
最后,我们将会探讨”智能无效区域”,实际就是仅仅
重绘或者清除应用程序窗体上的无效部分,加快程序的显示和响应速度。
希望这些概念和
技术能够引导读者阅读完本文,并且有助于更快和更有效的开发Windows窗体程序。
Windows窗体使用GDI+图像引擎,在本文中的所有绘
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 高效 绘图