当前位置:文档之家› 绘图的双缓冲技术

绘图的双缓冲技术

绘图的双缓冲技术
绘图的双缓冲技术

绘图的双缓冲技术

简介

幸运的是当编写一个典型的Windows 窗体程序时,窗体和控件的绘制、效果等操作是不需要特别加以考虑的。这是为什么呢?因为通过使用.Net 框架,开发人员可以拖动一系列的控件到窗体上,并书写一些简单的与事件相关

联的代码然后在IDE中按F5,一个完完全全的窗体程序就诞生了!所有控件都

将自己绘制自己,窗体或者控件的大小和缩放都调整自如。在这里经常会用到的,且需要引起一点注意的就是控件效果。游戏,自定义图表控件以及屏幕保

护程序的编写会需要程序员额外撰写用于响应Paint 事件的代码。

本文针对那些Windows 窗体开发人员并有助于他们在应用程序编制过程中使用简单的绘图技术。首先,我们会讨论一些基本的绘图概念。到底谁在负责

进行绘制操作?Windows 窗体程序是如何知道何时该进行绘制的?那些绘制代码究竟被放置在哪里?之后,还将介绍图像绘制的双重缓冲区技术,你将会看

到它是怎样工作的,怎样通过一个方法来实现缓存和实际显示的图像间的交替。最后,我们将会探讨”智能无效区域”,实际就是仅仅重绘或者清除应用程序

窗体上的无效部分,加快程序的显示和响应速度。希望这些概念和技术能够引

导读者阅读完本文,并且有助于更快和更有效的开发Windows 窗体程序。

Windows 窗体使用GDI+图像引擎,在本文中的所有绘图代码都会涉及使用托管的.Net 框架来操纵和使用Windows GDI+图像引擎。

尽管本文用于基本的窗体绘图操作,但是它同样提供了快速的、有效的且

有助于提高程序性能的技术和方法。所以,在通读本文之前建议读者对.Net框

架有个基本的了解,包括Windows 窗体事件处理、简单的GDI+对象譬如Line,Pen和Brush等。熟悉Visual Basic .Net或者C#编程语言。

概念

Windows 应用程序是自己负责绘制的,当一个窗体”不干净”了,也就是

说窗体改变了大小,或者部分被其它程序窗体遮盖,或者从最小化状态恢复时,程序都会收到需要绘制的信息。Windows把这种”不干净”状态称为”无效的(Invalidated)”状态,我们理解为:需要重绘,当Windows 窗体程序需要重

绘窗体时它会从Windows消息队列中获取绘制的信息。这个信息经过.Net框架

封装然后传递到窗体的PaintBackground 和Paint 事件中去,在上述事

件中适当的书写专门用于绘制的代码即可。

简单的绘图示例如下:

using System;

using System.Drawing;

using System.Windows.Forms;

public class BasicX : Form.

{

public BasicX()

{

InitializeComponent();

}

private void BasicX_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;

Pen p = new Pen(Color.Red);

int width = ClientRectangle.Width;

int height= ClientRectangle.Height;

g.DrawLine(p, 0,0, width, height);

g.DrawLine(p, 0, height, width, 0);

p.Dispose();

}

private void InitializeComponent()

{

this.SetStyle(ControlStyles.ResizeRedraw, true);

this.ClientSize = new System.Drawing.Size(300, 300);

this.Text = "BasicX";

this.Paint += new PaintEventHandler(this.BasicX_Paint);

}

[System.STAThreadAttribute()]

public static void Main()

{

Application.Run(new BasicX());

}

}

上述代码分成两个基本的步骤来创建示例程序。首

先InitializeComponent 方法包含一些属性的设置和附加窗体Paint 事

件的处理过程。注意,在方法中控件的样式也同时被设置,设置控件的样式也

是自定义Windows 窗体及控件行为的一种有效途径,譬如:控件的"ResizeRedraw"属性指示当窗体的大小变化发生以后需要对其完全进行重绘,

也就是说重绘时总是需要对整个窗体的客户区域进行重绘。窗体的“客户区域”是指除了标题栏和边框的所有窗体区域。可以进行一个有趣的试验,取消该控

件的属性然后再运行程序,我们可以很明显的看出为什么该属性会被经常的设置,因为窗体调整大小后的无效区域根本不会被重绘。

好了,我们需要注意一下BasicX_Paint方法,正如先前所提到的,

Paint 事件在程序需要重绘时被激活,程序窗体利用Paint事件来负责回应需要重绘的系统消息,BasicX_Paint方法的调用需要一个对象sender 和一个PaintEventArgs类型的变量,PaintEventArgs类的实例或称之为变量 e 封

装了两个重要的数据,第一个就是窗体的Graphics 对象,该对象表示窗体

可绘制的表面也称之为画布用于绘制诸如线、文本以及图像等,第二个数据就

是ClipRectangle,该Rectangle对象表示窗体上无效的的矩形围,或者说就

是窗体需要重绘的区域。记住,当窗体的ResizeRedDraw设置后,调整大小后

该ClipRectangle的大小实际就等于窗体整个客户区域的大小,或者是被其它

程序窗体遮盖的那部分剪切区域。关于部分剪切区域的用处我们会在智能重绘

章节作更详细的阐述。

BasicX 示例程序的运行界面

双重缓冲区绘图技术

双重缓冲区技术能够使程序的绘图更加快速和平滑,有效减少绘制时的图

像闪烁。该技术的基本原理是先将图像绘制到存中的一块画布上,一旦所有的

绘制操作都完成了,再将存中的画布推到窗体的或者控件的表面将其显示出来。通过这种操作后的程序能使用户感觉其更加快速和美观。

下面提供的示例程序能够阐明双重缓冲区的概念和实现方法,这个示例所

包含的功能已相当完整,且完全可以在实际应用中使用。在该章节后面还会提

及该技术应该配合控件的一些属性设置才能达到更好的效果。

要想领略双重缓冲区绘图技术所带来的好处就请运行SpiderWeb示例程序吧。程序启动并运行后对窗口大小进行调整,你会发现使用这种绘图算法的效

率不高,并且在调整大小的过程中有大量的闪烁出现。

不具备双重缓冲区技术的SpiderWeb示例程序

纵观程序的源码你会发现在程序Paint事件激活后是通过调用LineDrawRoutine方法来实现线的绘制的。LineDrawRoutine方法有两个参数,第一个是Graphics对象是用于绘制线条的地方,第二个是绘图工具Pen对象用来画线条。代码相当简单,一个循环语句,LINEFREQ常量等,程序从窗体表面

的左下一直划线到其右上。请注意,程序使用浮点数来计算在窗体上的绘制位置,这样做的好处就是当窗体的大小发生变化时位置数据会更加精确。

private void LineDrawRoutine(Graphics g, Pen p)

{

float width = ClientRectangle.Width;

float height = ClientRectangle.Height;

float xDelta = width / LINEFREQ;

float yDelta = height / LINEFREQ;

for (int i = 0; i < LINEFREQ; i++)

{

g.DrawLine(p, 0, height - (yDelta * i), xDelta * i, 0);

}

}

撰写很简单的用于响应Paint事件SpiderWeb_Paint的代码,正如前面所提到的,Graphics对象就是从Paint事件参数PaintEventArgs对象中提取出来的表示窗体的绘制表面。这个Graphics对象连同新创建Pen对象一起传递给LineDrawRoutine方法来画出蜘蛛网似的线条,使用完Graphics对象和Pen对象后释放其占用的资源,那么整个绘制操作就完成了。

private void SpiderWeb_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;

Pen redPen = new Pen(Color.Red);

//call our isolated drawing routing

LineDrawRoutine(g, redPen);

redPen.Dispose();

g.Dispose();

}

那么到底作怎么样的改动才能使上面的SpiderWeb程序实现简单的双重缓冲区技术呢?原理其实相当简单,就是将应该画到窗体表面的绘制操作改成先画到存中的位图上,LineDrawRoutine向这个在存中隐藏的画布执行同样的蜘蛛网绘制操作,等到绘制完毕再通过调用Graphics.DrawImage方法将隐藏的画布上容推到窗体表面来显示出来,最后,再加上一些小的改动一个高性能的绘图窗体程序就完成了。

请比较下面双重缓冲区绘图事件与前面介绍的简单绘图事件间的区别:private void SpiderWeb_DblBuff_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;

Pen bluePen = new Pen(Color.Blue);

//create our offscreen bitmap

Bitmap localBitmap = new

Bitmap(ClientRectangle.Width,ClientRectangle.Height);

Graphics bitmapGraphics = Graphics.FromImage(localBitmap);

//call our isolated drawing routing

LineDrawRoutine(bitmapGraphics, bluePen);

//push our bitmap forward to the screen

g.DrawImage(localBitmap, 0, 0);

bitmapGraphics.Dispose();

bluePen.Dispose();

localBitmap.Dispose();

g.Dispose();

}

上面的示例代码创建了存位图对象,它的大小等于窗体的客户区域(就是绘图表面)的大小,通过调用Graphics.FromImage将存中位图的引用传递给Graphics对象,也就是说后面所有对该Graphics对象的操作实际上都是对存中的位图进行操作的,该操作在C++中等同于将位图对象的指针复制给Graphics对象,两个对象使用的是同一块存地址。现在Graphics对象表示的是屏幕后方的一块画布,而它在双重缓冲区技术中起到至关重要的作用。所有的线条绘制操作都已经针对于存中的位图对象,下一步就通过调用DrawImage 方法将该位图复制到窗体,蜘蛛网的线条就会立刻显示在窗体的绘制表面而且丝毫没有闪烁出现。

这一系列的操作完成后还不是特别有效,因为我们先前提到了,控件的样式也是定义Windows 窗体程序行为的一条途径,为了更好的实现双重缓冲区

GDI双缓冲实现与GDI+双缓冲实现

GDI双缓冲实现与GDI+双缓冲实现 分类:C++ 2013-03-21 13:32 57人阅读评论(0) 收藏举报 我们看电视时,看到的屏幕称为OSD层,也就是说,只有在OSD层上显示图像我们才能看到。现在,我需要创建一个虚拟的、看不见但是可以在上面画图(比如说画点、线)的OSD层,我称之为offscreen(后台缓冲区)。这个offscreen存在于内存中,我们在上面画图,这个offscreen上面的东西可以显示在OSD层上,需要一个创建这个offscreen的函数,返回这个offscreen的句柄(整型指针)、宽度、高度、指向新建offscreen数据缓冲区的指针,该缓冲区是一个在函数外创建的offscreen的数据缓冲区,大小是offscreen的高度*宽度*每个像素点数据的大小。闪烁是图形编程的一个常见问题。需要多重复杂绘制操作的图形操作会导致呈现的图像闪烁或具有其他不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当启用双缓冲时,所有绘制操作首先呈现到内存缓冲区,而不是屏幕上的绘图图面。所有绘制操作完成后,内存缓冲区直接复制到与其关联的绘图图面。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁。 如何实现双缓冲 首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中: CDC MemDC; //首先定义一个显示设备对象 CBitmap MemBitmap;//定义一个位图对象 //随后建立与屏幕显示兼容的内存显示设备 MemDC.CreateCompatibleDC(NULL); //这时还不能绘图,因为没有地方画^_^ //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小,也可以自己定义(如:有滚动条时就要大于当前窗口的大小,在BitBlt时决定拷贝内存的哪部分到屏幕上) MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight); //将位图选入到内存显示设备中 //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上 CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //先用背景色将位图清除干净,这里我用的是白色作为背景 //你也可以用自己应该用的颜色 MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255)); //绘图 MemDC.MoveTo(……);

CDC双缓冲防闪屏绘图总结

CDC双缓冲防闪屏绘图总结 CDC在屏幕绘图可以用以下方法: CDC dc; dc.CreateCompatibleDC(NULL); CBitmap m_bitmap; m_bitmap.LoadBitmap(IDB_BITMAP1); //载入资源文件 dc.SelectObject(&m_bitmap); CDC *pDC = GetDC(); pDC->BitBlt(0,0,200,100,&dc,0,0,SRCCOPY); m_bitmap.DeleteObject(); dc.DeleteDC(); 上面虽然可以在屏幕绘图,但是如里需要绘多张图,并且有重叠的部份,当鼠标改变窗口大小时,重叠部份有严重的闪烁。非常难看 CDC *pDC = GetDC(); pDC->BitBlt(0,0,200,100,&dc,0,0,SRCCOPY); 这个方式是直接在屏幕绘图,如果多张图,即多次调用此方法。 为了避免闪烁,可采用双缓冲的方法,不管你要绘多少张图,先把它们绘在一个内存DC,之后再在屏幕显示

DC小知识点: CDC dc; 这个是内存dc, 使用BitBlt只会在内存中,不会在屏幕显示 一旦dc=GetDC(); 此时dc就与屏幕有关了,BitBlt方法就会输出的屏幕 CDC双缓冲防闪屏绘图实例 下面的nWidth,nHeight 是屏幕的宽高 CDC *pDC=this->GetDC(); //此DC负责在屏幕显示 CDC dc,sub_dc; //dc 负责组建对话框整张大图,sub_dc负责里面小块 dc.CreateCompatibleDC(NULL); sub_dc.CreateCompatibleDC(&dc); //注意参数,&dc就指定了sub_dc是基于dc的。 CBitmap bg_bmp,load_bmp; // bg_bmp负责构造背景,load_bmp负责从资源文件载入位图 bg_bmp.CreateCompatibleBitmap(pDC,nWidth,nHeight); //先创建一张与对话框当前大小一致的位图 dc.SelectObject(&bg_bmp); dc.FillSolidRect(0,0,nWidth,nHeight,RGB(236,236,234)); //构造背景 bg_bmp.DeleteObject(); //构造第一张图

图片控件上实现双缓冲绘图防止闪烁

1.添加图片控件 2.对话框初始化函数(或其他函数中获得控件的绘图指针) /*******获得图片区绘图指针*******/ pWnd = GetDlgItem(IDC_STA TIC_SHOWPICTURE);//获得控件窗口 //获得控件大小,PictureRect是全局变量,用于存放控件大小 pWnd->GetClientRect(&PictureRect); pDC = pWnd->GetDC();//定义pDC为控件绘图设备指针 3.内存中绘图并将图形复制到当前区域中 void CShockSensorConfiguratorDlg::OnPaint() { …; else { if(m_iWitchPicture==3)//如果要显示的是第三张也就是自己绘制的图形 { CBitmap memBitmap; CBitmap* pOldBmp = NULL; memDC.CreateCompatibleDC(pDC); memBitmap.CreateCompatibleBitmap(pDC,PictureRect.right,PictureRect.bottom); pOldBmp = memDC.SelectObject(&memBitmap); memDC.BitBlt(PictureRect.left,PictureRect.top,PictureRect.right,PictureRect.bottom,pDC, 0,0,SRCCOPY); memDC.FillSolidRect(1,1,PictureRect.right-2,PictureRect.bottom-2,RGB(255,255,255)); DrawPicture(&memDC);//自绘函数,传入的是内存指针,也就是在内存中绘图pDC->BitBlt(PictureRect.left,PictureRect.top,PictureRect.right,PictureRect.bottom,&m emDC,0,0,SRCCOPY); memDC.SelectObject(pOldBmp); memDC.DeleteDC(); memBitmap.DeleteObject(); } else if(m_iWitchPicture==2) { CDC *pDC1; pDC1=GetDC(); CBitmap m_bmpBK;

绘图的双缓冲技术

绘图的双缓冲技术 简介 幸运的是当编写一个典型的Windows 窗体程序时,窗体和控件的绘制、效 果等操作是不需要特别加以考虑的。这是为什么呢?因为通过使用 .Net 框架,开发人员可以拖动一系列的控件到窗体上,并书写一些简单的与事件相关联的 代码然后在IDE中按F5,一个完完全全的窗体程序就诞生了!所有控件都将自 己绘制自己,窗体或者控件的大小和缩放都调整自如。在这里经常会用到的, 且需要引起一点注意的就是控件效果。游戏,自定义图表控件以及屏幕保护程 序的编写会需要程序员额外撰写用于响应 Paint 事件的代码。 本文针对那些Windows 窗体开发人员并有助于他们在应用程序编制过程中 使用简单的绘图技术。首先,我们会讨论一些基本的绘图概念。到底谁在负责 进行绘制操作?Windows 窗体程序是如何知道何时该进行绘制的?那些绘制代 码究竟被放置在哪里?之后,还将介绍图像绘制的双重缓冲区技术,你将会看 到它是怎样工作的,怎样通过一个方法来实现缓存和实际显示的图像间的交替。最后,我们将会探讨”智能无效区域”,实际就是仅仅重绘或者清除应用程序 窗体上的无效部分,加快程序的显示和响应速度。希望这些概念和技术能够引 导读者阅读完本文,并且有助于更快和更有效的开发Windows 窗体程序。 Windows 窗体使用GDI+图像引擎,在本文中的所有绘图代码都会涉及使用 托管的.Net 框架来操纵和使用Windows GDI+图像引擎。 尽管本文用于基本的窗体绘图操作,但是它同样提供了快速的、有效的且 有助于提高程序性能的技术和方法。所以,在通读本文之前建议读者对.Net框 架有个基本的了解,包括Windows 窗体事件处理、简单的GDI+对象譬如Line,Pen和Brush等。熟悉Visual Basic .Net或者C#编程语言。 概念 Windows 应用程序是自己负责绘制的,当一个窗体”不干净”了,也就是 说窗体改变了大小,或者部分被其它程序窗体遮盖,或者从最小化状态恢复时,程序都会收到需要绘制的信息。Windows把这种”不干净”状态称为”无效的(Invalidated)”状态,我们理解为:需要重绘,当Windows 窗体程序需要重 绘窗体时它会从Windows消息队列中获取绘制的信息。这个信息经过.Net框架 封装然后传递到窗体的 PaintBackground 和 Paint 事件中去,在上述事件中 适当的书写专门用于绘制的代码即可。 简单的绘图示例如下: using System;

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(屏蔽掉系统的自动刷新)

C#绘图双缓冲技术总结

C#绘图双缓冲技术总结 GDI+的双缓冲问题 一直以来的误区:.net1.1 和.net 2.0 在处理控件双缓冲上是有区别的。 .net 1.1 中,使用:this.SetStyle(ControlStyles.DoubleBuffer, true); .net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); VS2005 是2.0 怪不说老是提示参数无效,一直也不知道是这个问题,呵呵 要知道,图元无闪烁的实现和图元的绘制方法没有多少关系,只是绘制方法可以控制图元的刷新区域,使双缓冲性能更优! 导致画面闪烁的关键原因分析: 一、绘制窗口由于大小位臵状态改变进行重绘操作时 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint事件一次。窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位臵,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。 所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。

根据以上分析可知,当图元数目不多时,窗口刷新的位臵也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位臵都在刷新,闪烁现象自然就会越来越严重。特别是图元比较大绘制时间比较长时,闪烁问题会更加严重,因为时间延迟会更长。 解决上述问题的关键在于:窗口刷新一次的过程中,让所有图元同时显示到窗口。 二、进行鼠标跟踪绘制操作或者对图元进行变形操作时 当进行鼠标跟踪绘制操作或者对图元进行变形操作时,Paint事件会频繁发生,这会使窗口的刷新次数大大增加。虽然窗口刷新一次的过程中所有图元同时显示到窗口,但也会有时间延迟,因为此时窗口刷新的时间间隔远小于图元每一次显示到窗口所用的时间。因此闪烁现象并不能完全消除! 所以说,此时导致窗口闪烁现象的关键因素在于Paint事件发生的次数多少。解决此问题的关键在于:设臵窗体或控件的几个关键属性。 下面来介绍解决办法的具体细节: 解决双缓冲的关键技术: 1、设臵显示图元控件的几个属性:必须要设臵,否则效果不是很明显!this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true);

c++双缓冲程序设计

《1》普通绘图就是直接在我们看得到的黑板上绘图 《2》双缓冲就是先在一个虚拟的黑板上画完,等用到的时候在把虚拟黑板上的图画复制到我们看得到的黑板上去; 利用双缓冲的优点就是能够使画面流畅,可以想象把画好的图直接粘贴到黑板上一定比在黑板上重新画要快的多。 ——————————————————————开始 第一步:新建一个对话框工程 第二步:添加两个按钮: 一个命名为双缓冲绘图; 一个命名为普通绘图; 第三步:声明变量: 在CMyDlg类上右击添加变量如下: CDC MyDC; CBitmap bmp; CBitmap *oldbmp; 首先声明一个与窗口DC兼容的内存DC(MyDC)和两个与内存相兼容的位图(bmp,*oldbmp) 第四步:在OnInitDialog()函数中添加以下代码: //窗口DC CDC *dc=GetDC(); //创建与窗口DC兼容的内存DC(MyDC)及位图(bmp,*oldbmp ) MyDC.CreateCompatibleDC(dc); bmp.CreateCompatibleBitmap(dc,200,200); //把内存位图选进内存DC中用来保存在内存DC中绘制的图形 oldbmp=MyDC.SelectObject(&bmp); //在内存DC中绘制一些小的圆形,数量要多(体现双缓存的优点) for(int i=0;i<200;i+=6) for(int j=0;j<200;j+=6) MyDC.Ellipse(i-3,j-3,i+3,j+3); 第五步:右击CMyDlg类添加windows消息响应函数WM_CLOSE,添加以下代码:MyDC.SelectObject(oldbmp); bmp.DeleteObject(); MyDC.DeleteDC(); //选进原来的位图,删除内存位图对象和内存DC 第六步:双击”双缓冲“按钮添加以下代码: GetDC()->StretchBlt(0,0,200,200,&MyDC,0,0,200,200,SRCCOPY); //把内存DC中的图形粘贴到窗口中; 第七步:双击“普通绘图”按钮添加以下代码: for(int i=0;i<200;i+=6) for(int j=0;j<200;j+=6) GetDC()->Ellipse(i-3,j-3,i+3,j+3);

vc双缓冲

双缓冲:VC++双缓冲实现方法 在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。 因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。 我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。 一、普通方法: 先按普通做图的方法进行编程。即在视类的OnDraw函数中添加绘图代码。在此我们绘制若干同心圆,代码如下: CBCDoc* pDoc = GetDocument(); ASSERT_V ALID(pDoc); CPoint ptCenter; CRect rect,ellipseRect; GetClientRect(&rect); ptCenter = rect.CenterPoint(); for(int i=20;i>0;i--){ ellipseRect.SetRect(ptCenter,ptCenter); ellipseRect.InflateRect(i*10,i*10); pDC->Ellipse(ellipseRect); } 编译运行程序,尝试改变窗口大小,可以发现闪烁现象。 二、双缓冲方法: 在双缓冲方法中,首先要做的是屏蔽背景刷新。背景刷新其实是在响应WM_ERASEBKGND消息。我们在视类中添加对这个消息的响应,可以看到缺省的代码如下:

GDI+测井曲线绘图中效率提升的研究

龙源期刊网 https://www.doczj.com/doc/501507774.html, GDI+测井曲线绘图中效率提升的研究 作者:王宇飞赵正文李瑶 来源:《数字技术与应用》2013年第03期 摘要:GDI+提供了快速、简单、有效的程序开发方式。大量测井原始数据生成测井曲线时,绘制对象的增加严重制约了GDI+的绘图效率。双缓冲技术的使用,可以有效避免图形的闪烁;使用内存中已有图形,可以减少测井曲线的绘制过程,显著提高图形绘制效率。 关键词:测井曲线双缓冲内存图形绘图效率 中图分类号:P631.84 文献标识码:A 文章编号:1007-9416(2013)03-0083-02 1 前言 GDI(Graphics Device Interface,图形设备接口)的主要任务是负责系统与绘图程序之间 的信息交换,处理所有Windows程序的图形输出。通过GDI众多函数,软件开发人员不需要关心设备驱动及具体硬件设备,就可以将应用程序的输出转化为硬件设备上的输出,实现程序与硬件设备的隔离,方便开发工作。 GDI+是GDI的升级版本,在GDI的基础上进行了了大量的优化和改进工作,使得它的易用性更好。GDI其中的一个好处就是用户不必知道任何关于数据怎样在设备上绘制图像的细节,GDI+更好地拓展了这一优点,GDI是一个中低层API,用户必须需要知道设备情况,而GDI+是一个高层的API,用户可以不必知道设备情况,GDI+的体系结构如图1。此外,GDI+不但在功能上比GDI强大很多,而且在代码编写方面也要显得更加简单方便,这将使得其很 快成为Windows图形图像程序开发的首选。 2 测井曲线绘制中存在问题 测井是油田勘探与开发过程中确定和评价油、气层的重要方法之一,是解决地质问题的重要手段。通过测井能直接为石油地质和工程技术人员提供各项资料和数据,以指导油田生产。然而,通过测井设备测量出的大多是一系列离散或连续的数值型数据,同时这也是它们在数据库中的存储形式,即使经验丰富的测井解释人员要想在几百甚至几千米井深的海量测井数据中获得解释结论也是相当困难的,不利于测井数据发挥其功用。将各类测井数据以图像的形式直观的展示在测井解释人员面前,更方便于测井数据的解释与分析。 由于油田勘探与开发过程中,井深一般都在几百米甚至几千米。以一米八个采样点,井深1000米就有8000个采样点。按照1280×800的分辨率进行绘制,也需要10个屏幕长度的界面来绘制。同时,测井曲线图中的对象需要进行、调整显示顺序等操作。如果在绘制过程中,将所有对象进行绘制,图形会出现严重闪烁和操作延迟现象。因此,有效提升绘图效率的方法是必需的,以此达到测井解释人员的使用要求。

C#高效绘图

C#高效绘图 来源:https://www.doczj.com/doc/501507774.html,/suncherrydream/article/details/17585201 双缓冲技术 双缓冲是将图片在显示到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>有选择的局部绘制。 不能说,一定是第三种方式好,得视情况,境界高程序肯定就复杂,如果对效率要求不高或者绘图量小当然直接用第一种方式。然而,稍微专业点的绘图程序,第一第二种方式肯

利用双缓存技术解决GDI画图的刷新问题

利用双缓存技术提高GDI+的绘图效果 摘要 在进行图像处理的软件中,一般的图形处理程序需要大量的绘图操作,首要解决的就是绘图的效率和效果问题:前者表现在性能方面,是否有延迟,一个小小的交互操作是否要等上几秒才能看到结果;后者表现在重新绘制或刷新的过程中出现闪烁?本文主要介绍GDI+程序中引用双缓冲技术以解决重绘时出现的闪烁问题—提高绘图效果。 关键字:GDI+;双缓存;快速;缓存位图;效果 引言 在使用GDI+的实际的绘图中,绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。这样的效率非常低,不仅达不到性能要求,而且还可能会出现闪烁的现象。刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint 事件一次。窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。 根据以上分析可知,当图元数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重。特别是图元比较大绘制时间比较长时,闪烁问题会更加严重,因为时间延迟会更长。 1、双缓存技术 为了解决画图的效率问题,我们引进了双缓存技术,双缓冲技术早在GDI的程序开发中,就已经开始使用。在普通的绘图模式下,图像处理程序是按照设计的顺序,将被绘制的元素一个接一个地在目标设备上进行绘制。何谓“双缓冲”?它的基本原理就是:先在内存中开辟一块虚拟画布,然后将所有需要画的图形先画在这块“虚拟画布”上,最后在一次性将整块画布画到真正的窗体上。因为所有的单个图形的绘制都不是真正的调用显示系统“画”,所以不会占用显示系统的开销,极大的提高的绘图效率。其原理如图1所示。[1][3]

vc双缓冲VC 双缓冲实现方法

vc双缓冲:VC++双缓冲实现方法 疯狂代码 https://www.doczj.com/doc/501507774.html,/ ?:http:/https://www.doczj.com/doc/501507774.html,/VC/Article22436.html 在图形图象处理编程过程中,双缓冲是种基本技术我们知道,如果窗体在响应WM_PAINT消息时候要进行复杂图形处理那么窗体在重绘时由于过频刷新而引起闪烁现象解决这问题有效思路方法就是双缓冲技术 窗体在刷新时总要有个擦除原来图象过程OnEraseBkgnd它利用背景色填充窗体绘图区然后在新绘图代码进行重绘这样擦写造成了图象颜色反差当WM_PAINT响应很频繁时候这种反差也就越发明显于是我们就看到了闪烁现象 我们会很自然想到避免背景色填充是最直接办法但是那样话窗体上会变团糟每次绘制图象时候都没有将原来图象清除造成了图象残留于是窗体重绘时画面往往会变乱 7 8糟所以单纯禁止背景重绘是不够我们还要进行重新绘图但要求速度很快于是我们想到了使用BitBlt它可以支持图形块复制速度很快我们可以先在内存中作图然后用此将做好图复制到前台同时禁止背景刷新这样就消除了闪烁以上也就是双缓冲绘图基本思路 、普通思路方法: 先按普通做图思路方法进行编程即在视类OnDraw中添加绘图代码在此我们绘制若干同心圆代码如下: CBCDoc* pDoc = GetDocument; ASSERT_VALID(pDoc); CPo ptCenter; CRect rect,ellipseRect; GetClientRect(&rect); ptCenter = rect.CenterPo; for( i=20;i>0;i--) {

C# WinForm利用GDI+的双缓冲技术来提高绘图效率

C# WinForm利用GDI+的双缓冲技术来提高绘图效率 前言 进入.NET时代,Windows的绘图技术也从GDI升级到了GDI+,从名字就能知道GDI+是对以前传统GDI 绘图技术的一次升级,不过在微软几乎把所有的新技术都冠之.NET的情况下,GDI+竟然不叫做https://www.doczj.com/doc/501507774.html,,还真让我感到有点意外了。:) GDI+在一种与设备无关的环境下提供了一套统一的绘图编程模型,极大的提高了Windows绘图编程的方便性,我们再也不用创建什么各种各样复杂的设备环境了,说实话,我现在想起来都头疼。 题归正传,关于如何进行GDI+的基本编程,我不能过多的加以描述,如果有对此概念还不太清楚的朋友,建议先去了解一下相关的资料,我们在这里主要讨论的是一种提高绘图效率(主要是动画效率)的双缓冲技术在GDI+中的应用和实现。 实现目的 为了能清楚的对比应用双缓冲技术前后的效果,我编写了一段程序来进行测试。首先,我创建了一个普通的Windows Application,在主Form中,我放置了一个定时器:timer1,然后将它的Interval属性设置为10,然后在Form上放置两个按纽,分别用来控制定时器的开启和关闭,最后,我还放置了一个label控件,用来显示绘图的帧数。 测试程序 在timer1的timer1_Tick事件中,我写下了如下的代码(其中flag是一个bool型标志变量):DateTime t1 = DateTime.Now; Graphics g = this.CreateGraphics(); if(flag) { brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(700.0f, 300.0f), Color.Red, Color.Blue); flag = false; } else { brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(700.0f, 300.0f), Color.Blue, Color.Red); flag = true; } for(int j = 0; j < 60; j ++) { for(int i = 0; i < 60; i++) { g.FillEllipse(brush, i * 10, j * 10, 10, 10); } } DateTime t2 = DateTime.Now;

基于双缓冲技术解决游戏开发中画面更新问题_张哲源

中图分类号:TP311文献标识码:A文章编号:1009-2552(2011)08-0017-03 基于双缓冲技术解决游戏开发中画面更新问题 张哲源,熊庆国,李文翔 (武汉科技大学信息科学与工程学院,武汉430081) 摘要:在基于Visual C++的游戏开发中,当游戏画面实时性要求较高时,则需要在短时间内快速更新画面,而运用传统单缓冲技术绘图则会出现画面闪烁、停顿等现象,严重影响游戏画面质量以及游戏开发的后期制作。采用双缓冲技术能够解决此问题,提高游戏的操作性和观赏性。本文分析双缓冲技术与传统的单缓冲技术区别,给出了双缓冲技术的实现部分关键程序,为游戏开发中处理大量数据,频繁画面更新提供指导意义。 关键词:游戏开发;实时性;双缓冲技术;单缓冲技术 Based on double buffering technology to solve game development in pictures updated problem ZHANG Zhe-yuan,XIONG Qing-guo,LI Wen-xiang (Department of Information Science and Engineering,Wuhan University of Science and Technology, Wuhan430081,China) Abstract:During the peroid of the game development which is based on Visual C++,when the game screen in real-time requirement is high,quickly updating the screen in a short time is need,while the use of traditional single-buffering graphics flickering occurs,pause and so on,seriously affect the game screen quality post-production and game development.Using the double-buffering technology can solve this problem,improve operational and entertaining game.This paper analyzes the distinction between double-buffering technology with the traditional single-buffer technology,given the realization of the double buffering some of the key procedures for the game development deal with large amounts of data,frequent updates to provide guidance screen. Key words:game development;real-time;double buffering technology;single buffer technology 0引言 Visual C++是一个功能强大的可视化软件开发工具。自1993年Microsoft公司推出Visual C++ 1.0后,随着其新版本的不断问世,实际中,更多的是以Visual C++6.0或者更新的版本为平台。Visu-al C++6.0不仅是一个C++编译器,而且是一个基于Windows操作系统的可视化集成开发环境(in-tegrated development environment,IDE)。Visual C++ 6.0由许多组件组成,包括编辑器、调试器以及程序向导AppWizard、类向导Class Wizard等开发工具。这些组件通过一个名为Developer Studio的组件集成为和谐的开发环境。它已成为专业程序员进行软件开发的首选工具。 MFC借助C++的优势为Windows开发开辟了一片新天地,同时也借助Application Wizard使开发者摆脱离了那些每次都必写基本代码,借助Class Wizard和消息映射使开发者摆脱了定义消息处理时那种混乱和冗长的代码段。更令人兴奋的是利用C++的封装功能使开发者摆脱Windows中各种句柄的困扰,只需要面对C++中的对象,这样一来使 收稿日期:2010-12-13 基金项目:湖北省教育厅科学技术研究项目(B20101101) 作者简介:张哲源(1987-),男,硕士研究生,研究领域为控制理论与控制工程。 — 71 —

双缓冲技术

双缓冲技术及其在VC的GDI环境下的实现 作者:HateMath的网上田园出处:VCKBASE 责任编辑:方舟 昨天在论坛上,有人问起双缓冲的实现问题,想起网上这方面资料比较凌乱,而 且多是 DirectX 相关的,今天特地在这里给大家简要的介绍一下双缓冲技术及其在 VC++ 的 GDI 绘图环境下的实现。 1、Windows 绘图原理 我们在 Windows 环境下看到各种元素,如菜单、按钮、窗口、图像,从根本上说,都是“画”出来的。这时的屏幕,就相当于一块黑板,而 Windows 下的各种 GDI 要素,如画笔、画刷等,就相当于彩色粉笔了。我们在黑板上手工画图时,是一笔一划的,电脑亦然。只不过电脑的速度比手工快的太多,所以在我们看起来好像所有的图 形文字都是同时出现的。 2、普通绘图方式的局限 上述绘图方式我们暂且称之为普通绘图方式吧。虽然这种方式能满足相当一部分 的绘图需要,但是当要绘制的对象太复杂,尤其是含有位图时,电脑便力不从心了。 这时的画面会显示的很慢,对于运动的画面,会给人“卡”住了的感觉,总之一个字:不爽。 3、解决之道:双缓冲 双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板。首先我们在内存 环境中建立一个“虚拟“的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘 制完毕的时候,再一次性的把内存中绘制好的图形“拷贝”到另一块黑板(屏幕)上。采取这种方法可以提高绘图速度,极大的改善绘图效果。下面是原理图:

图一双缓冲原理示意图 4、相关的函数介绍 1)、为屏幕 DC 创建兼容的内存 DC:CreateCompatibleDC() if(!m_dcMemory.CreateCompatibleDC(NULL)) // CDC m_dcMemory; { ::PostQuitMessage(0); } 2)、创建位图:CreateCompatibleBitmap() m_Bmp.CreateCompatibleBitmap(&m_dcMemory, rt.Width(), rt.Height()); // CBitmap m_Bmp; 3)、把位图选入设备环境:SelectObject(),可以理解为选择画布 ::SelectObject(m_dcMemory.GetSafeHdc(), m_Bmp); 4)、把绘制好的图形“拷贝“到屏幕上:BitBlt()

C#绘图双缓冲技术总结

C#绘图双缓冲技术总结 双缓冲技术 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); 还有别的方式 在构造函数中加如下代码: 代码一: SetStyle(https://www.doczj.com/doc/501507774.html,erPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲 代码二: this.SetStyle(ControlStyles.DoubleBuffer | https://www.doczj.com/doc/501507774.html,erPaint | ControlStyles. AllPaintingInWmPaint, true); this.UpdateStyles(); 双缓冲问题 GDI+的双缓冲问题终于搞定了,真是松了一口气! 一直以来的误区:.Net 1.1和.Net 2.0在处理控件双缓冲上是有区别的。 .Net 1.1中,使用:this.SetStyle(ControlStyles.DoubleBuffer,true); .Net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer,true); 怪不说老是提示参数无效,一直也不知道是这个问题,呵呵。 要知道,图元无闪烁的实现和图元的绘制方法没有多少关系,只是绘制方法可以控制图元的刷新区域,使双缓冲性能更优! 导致画面闪烁的关键原因分析: 一、绘制窗口由于大小位置状态改变进行重绘操作时 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint事件一次。窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。 所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。 根据以上分析可知,当图元数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重。特别是图

VC++绘图时,利用双缓冲解决屏幕闪烁

VC++绘图时,利用双缓冲解决屏幕闪烁 最近做中国象棋,绘制界面时遇到些问题,绘图过程中屏幕闪烁,估计都会想到利用双缓冲来解决问题,但查了下网上双缓冲的资料,发现基本是MFC的,转化为VC++后,大概代码如下: void DrawBmp(HDC hDC, HBITMAP hBitmap) { HDC hdcMEM; //用于缓冲作图的内存DC HBITMAP bmp; //内存中承载临时图象的位图 HANDLE hOld; hdcMEM = CreateCompatibleDC(hDC);//依附窗口DC创建兼容内存DC bmp = CreateCompatibleBitmap(hDC, 100, 100);//创建与hDC环境相关的设备兼容的位图 SelectObject(hdcMEM, bmp); hOld = SelectObject(hdcImage, hBitmap); StretchBlt(hDC, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, SRCCOPY); SelectObject(hdcImage, hOld); DeleteObject(hOld); } 但以上代码似乎没有用到hBitmap,当然屏幕上也不会有任何输出,但网上的资料基本一样。查了一番资料,才明白如果hDC中已经有位图数据,BitBlt的时候,就会直接把hDC中的数据画到内存缓冲区里。所以,还需要建一DC,名为hdcImage,把要画的位图选入内存hdcImage中,然后再在内存缓冲区上绘图。 整理代码如下: void DrawBmp(HDC hDC, HBITMAP hBitmap) { HDC hdcImage; HDC hdcMEM; //注意此处,创建了两个HDC hdcMEM = CreateCompatibleDC(hDC); hdcImage = CreateCompatibleDC(hDC); HBITMAP bmp = ::CreateCompatibleBitmap(hDC, nWidth, nHeight);//创建与hDC环境相关的设备兼容的位图 SelectObject(hdcMEM, bmp); SelectObject(hdcImage, hBitmap);//注意此处,将要画的位图选入hdcImage StretchBlt(hdcMEM, 0, 0, 100, 100, hdcImage, 0, 0, 100, 100, SRCCOPY); //这里才能正常画图,将hdcImage中的位图直接复制到内存缓冲区 StretchBlt(hDC, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, SRCCOPY); //再将内存缓冲区中的数据绘制到屏幕上.

相关主题
文本预览
相关文档 最新文档