c# 双缓冲 技术与例子.docx
- 文档编号:24434704
- 上传时间:2023-05-27
- 格式:DOCX
- 页数:17
- 大小:19.72KB
c# 双缓冲 技术与例子.docx
《c# 双缓冲 技术与例子.docx》由会员分享,可在线阅读,更多相关《c# 双缓冲 技术与例子.docx(17页珍藏版)》请在冰豆网上搜索。
c#双缓冲技术与例子
暂时收集,双缓冲解决闪烁问题。
整理:
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、设置显示图元控件的几个属性:
必须要设置,否则效果不是很明显!
this.SetStyle(ControlStyles.OptimizedDoubleBuffer|
ControlStyles.ResizeRedraw|
ControlStyles.AllPaintingInWmPaint,true);
2、窗口刷新一次的过程中,让所有图元同时显示到窗口。
可以通过以下几种方式实现,这几种方式都涉及到Graphics对象的创建方式。
具体实现
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;
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对象),在其中绘制完成后,再一次性显示。
完整代码如下:
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));
另外一个例子,差不多
Graphics对象的创建方式:
a、在内存上创建一块和显示控件相同大小的画布,在这块画布上创建Graphics对象。
接着所有的图元都在这块画布上绘制,绘制完成以后再使用该画布覆盖显示控件的背景,从而达到“显示一次仅刷新一次”的效果!
实现代码(在OnPaint方法中):
Rectanglerect=e.ClipRectangle;
Bitmapbufferimage=newBitmap(this.Width,this.Height);
Graphicsg=Graphics.FromImage(bufferimage);
g.Clear(this.BackColor);
g.SmoothingMode=SmoothingMode.HighQuality;//高质量
g.PixelOffsetMode=PixelOffsetMode.HighQuality;//高像素偏移质量
foreach(IShapedrawobjectindoc.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(Graphicstg=e.Graphics)
{
tg.DrawImage(bufferimage,0,0); //把画布贴到画面上
}
b、直接在内存上创建Graphics对象:
Rectanglerect=e.ClipRectangle;
BufferedGraphicsContextcurrentContext=BufferedGraphicsManager.Current;
BufferedGraphicsmyBuffer=currentContext.Allocate(e.Graphics,e.ClipRectangle);
Graphicsg=myBuffer.Graphics;
g.SmoothingMode=SmoothingMode.HighQuality;
g.PixelOffsetMode=PixelOffsetMode.HighSpeed;
g.Clear(this.BackColor);
foreach(IShapedrawobjectindoc.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里。
BitmapcurrentMap;
boolfirst=true;
privatevoidpictureBox1_MouseDown(objectsender,MouseEventArgse)
{
if(zoom==0)
{
if(e.Button==MouseButtons.Left)//dragging
mousedrag=e.Location;
ImagemyImage=myMap.GetMap();
currentMap=newBitmap(myImage);
first=false;
}
}
privatevoidpictureBox1_MouseMove(objectsender,MouseEventArgse)
{
if(zoom==0&&!
first)
{
Imageimg=newBitmap(Size.Width,Size.Height);
Graphicsg=Graphics.FromImage(img);
g.Clear(Color.Transparent);//图片移动后显示的底色
g.SmoothingMode=SmoothingMode.HighQuality;//高质量
g.PixelOffsetMode=PixelOffsetMode.HighQuality;//高像素偏移质量
g.DrawImageUnscaled(currentMap,newSystem.Drawing.Point(e.Location.X-mousedrag.X,e.Location.Y-mousedrag.Y));//在g中移动图片,原图在(0,0)画的,所以直接用newSystem.Drawing.Point(e.Location.X-mousedrag.X,e.Location.Y-mousedrag.Y)就好。
g.Dispose();
pictureBox1.Image=img;//img是在鼠标这个位置时生成被移动后的暂时的图片
}
}
privatevoidpictureBox1_MouseUp(objectsender,MouseEventArgse)
{
if(zoom==0)
{
System.Drawing.Pointpnt=newSystem.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=true;
}
}
说说思路,在鼠标点下时创建一个bitmap,currentMap,用它来存放当前图像。
鼠标移动时,根据鼠标位置画图,最后,鼠标up时,重新画图。
双缓冲技术(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个随机生成的小圆。
viewplaincopytoclipboardprint?
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)
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));
C#下实现双缓冲描画高频曲线
由于项目需要,要使用c#描画高频实时曲线.
但是在C#下由于描画图像使用的是GDI+,描画效率很有问题.一旦曲线太多,就会造成CPU使用率直线上升,马上飙升到100%.
在GDI+下使用双缓冲也无济于事,双缓冲本身只会解决曲线多的时候全屏闪烁问题,但描画效率还是严重低下.
其间用过多种解决方案:
DRECT3D,DRIRECT2D,GDI,,,,,等等等等
最后从效率出发,最终解决方案如下:
前台显示使用GDI,而后台描画则采用GDI+
后台采用10倍于前台窗口的BUFFER,每次向其中画一条线.然后通过一个RECT视口,每次向前台显示视口里的内容.否则每次重绘的代价太高.
这个方法实现的难点主要在于GDI和GDI+的结合部分,主要代码如下:
1.函数库:
using和WIN32API函数
usingSystem.Runtime.InteropServices;
usingSystem.Drawing;
usingSystem.Drawing.Imaging;
usingSystem.Drawing.Drawing2D;
usingSystem.Drawing.Text;
[DllImport(gdi32)]
publicstaticexternIntPtrCreateCompatibleDC(IntPtrhdc);
[DllImport(gdi32)]
publicstaticexternIntPtrSelectObject(IntPtrhdc,IntPtrhObject);
[DllImport(GDI32.dll)]
publicstaticexternlongBitBlt(IntPtrhdcDest,intnXDest,intnYDest,intnWidth,intnHeight,IntPtrhdcSrc,intnXSrc,intnYSrc,intdwRop);
[DllImport(GDI32.dll)]
publicstaticexternboolDeleteObject(IntPtrhObject);
2.声明对象
publicPictureBox_backgroundGraph;//被描画的控件对象
//publicForm_backgroundGraph;
publicGraphics_backgroundGraphic=null;//背景Graphic
publicGraphics_backgroundRenderGraphic=null;//双缓冲Graphic
privateGraphics_backg
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- c# 双缓冲 技术与例子 缓冲 技术 例子