绘图的双缓冲技术

合集下载

如何实现双缓冲

如何实现双缓冲

如何实现双缓冲双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。

双缓冲实现过程如下:1、在内存中创建与画布一致的缓冲区2、在缓冲区画图3、将缓冲区位图拷贝到当前画布上4、释放内存缓冲区(1)在内存中创建与画布一致的缓冲区CDC dc;//这是窗口的DC,假设已加载好CDC MemDC; //创建内存中的一个临时dc- MemDC, MemDC用来向窗口绘图的“草稿”//随后建立与屏幕显示兼容的内存显示设备MemDC.CreateCompatibleDC(&dc); //这时还不能绘图,因为没有地方画 ^_^//创建的临时空白bitmap作为“画布”,至于位图的大小,可以用窗口的大小CBitmap MemBitmap;MemBitmap.CreateCompatibleBitmap(&dc,nWidth,nHeight);//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //将上面创建的临时“画布”MemBitmap与MemDC连接,注意此处的MemBitmap为一个空白临时画布,可以在这个空白画布上自绘图,也可以在这个画布上加载图片//先用背景色将位图清除干净,这里我用的是白色作为背景//你也可以用自己应该用的颜色MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));(2)在缓冲区画图MemDC.MoveTo(……);MemDC.LineTo(……);(2)'在第(2)步中,如果不是自绘图,而是加载一个位图,则需要再定义一个临时dc- MemDC2,用来将位图加载到上面建立的空白画布MemDC中CBitmap p1;//这是要画的位图,假设已加载好CDC MemDC2;MemDC2.CreateCompatibleDC(&dc);MemDC2.SelectObject(&p1);// MemDC2与图片链接//在这里,p1保存的是要加载到临时空白画布上的图片,MemDC2是与p1链接的dc(3)将缓冲区位图拷贝到当前画布(屏幕)上dc.BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);(3)’如果是位图的话首先,将与MemDC2链接的位图p1拷贝到临时空白画布MemDC中MemDC.BitBlt(x,y,width,height,& MemDC2,0,0,SRCCOPY); //向草稿绘制第一张图片,x,y,width,height请自行设置其次,将草稿绘制到屏幕上dc.BitBlt(0,0,width,height,&MemDC,0,0,SRCCOPY);(4)释放内存缓冲区//绘图完成后的清理MemBitmap.DeleteObject();MemDC.DeleteDC();MemDC2.DeleteDC();下面是一个不使用和使用双缓存的例子使用双缓存//CPoint ptCenter;//CRect rect, ellipseRect;//GetClientRect(&rect); //获得窗口客户区的大小//ptCenter = rect.CenterPoint(); //获得矩形的中心点,目的是为了确定后面同心圆图像的圆心//CDC dcMem; // 创建用于缓冲作图的内存DC对象dcMem//CBitmap bmp; // 创建内存中存放临时图像的位图对象bmp//dcMem.CreateCompatibleDC(pDC); // 依附窗口DC(窗口对象为pDC),创建兼容内存DC(就是创建一个内存DC,所有图形先画在这上面)//bmp.CreateCompatibleBitmap(&dcMem, rect.Width(), rect.Height());// 在兼容内存DC上,创建兼容位图//dcMem.SelectObject(&bmp); // 将位图选入内存DC//dcMem.FillSolidRect(rect, pDC->GetBkColor());// 按照原有背景色填充客户区,否则会成为黑色,同时也使内存DC的背景色保持一致//// 绘图操作//for (int i = 60; i > 0; --i)//{// ellipseRect.SetRect(ptCenter, ptCenter);// ellipseRect.InflateRect(i * 5, i * 5);// dcMem.Ellipse(ellipseRect); // 在内存DC上绘图,做同心圆图像//}//pDC->BitBlt(0, 0, rect.Width(), rect.Height(),// &dcMem, 0, 0, SRCCOPY); // 将内存DC上的图像复制到前台pDC,即实际屏幕对象pDC//dcMem.DeleteDC(); // 删除内存DC//bmp.DeleteObject(); // 删除内存位图不使用双缓存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);}下面的例子是加载两幅图片CBitmap p1,p2;//这是要画的位图,假设已加载好CDC dc;//这是窗口的DC,假设已加载好//创建两个临时dc,dc1为向窗口绘图的“草稿”,dc2为与源位图连接的dc(实际上dc2也可以用别的方法代替,这只是我的癖好)CDC dc1,dc2;dc1.CreateCompatibleDC(&DC);dc2.CreateCompatibleDC(&DC);//创建一个临时bitmap作为“画布”,与dc1连接CBitmap bm;CBitmap *Oldbm1,Oldbm2bm.CreateCompatibleBitmap(pDC,width,height); //长度宽度设置成与绘图面积一样大dc1.SelectObject(&bm);dc2.SelectObject(&p1);//dc2与第一张图片链接dc1.BitBlt(x,y, width,height,&dc2,0,0,SRCCOPY); //向草稿绘制第一张图片,x,y,width,height请自行设置dc2.SelectObject(&p2);//dc2与第一张图片链接dc1.BitBlt(x,y, width,height,&dc2,0,0,SRCCOPY); //向草稿绘制第二张图片//将草稿转移至窗口dc.BitBlt(0,0, width,height,&dc1,0,0,SRCCOPY);//清理工作...。

基于IOS双缓冲绘图技术的研究

基于IOS双缓冲绘图技术的研究
的绘 图奠 定 了扎 实的 基 础 。 关键词 : i o s ; 绘 图 ;重 绘 ; 双 缓 冲 中图 分 类 号 : T N 8 7 文献标识码 : A 文 章编 号 :1 6 7 4 — 6 2 3 6 ( 2 0 1 3 ) 2 1 — 0 0 4 6 — 0 3
St u d y o n t h e d o u bl e bu fe r d r a wi ng t e c hn o l o g y b a s e d o n I OS
r e a l i z i n g mo r e d a z z l i n g ra g p h i c s o n t h e o p e r a t i n g s y s t e m p l a f t o r m ,v e i r i f e d ,d o u b l e b u f f e r i n g g ra p h i c s o n t h e l OS o p e r a t i n g
Ab s t r a c t :I OS i s a h a n d h e l d d e v i c e o p e r a t i n g s y s t e m h u ma n i z e d i n t e fa r c e ,p o we f r u l f u n c t i o n,wi d e ma r k e t b y t h e Ap p l e Re s e a r c h a n d d e v e l o p me n t ,t h e o p e r a t i n g s y s t e m c a n a c h i e v e t h e d r a wi n g f u n c t i o n v e r y d a z z l e ,B u t i t ma y b e a p p e a r t h e p h e n o me n o n o f s c r e e n l f i c k e r a n d wa v e f o r m d i s c o n t i n u i t y i n t h e r e d r a w,i n o r d e r t o s o l v e t h i s p r o b l e m,t h e p a p e r p r o p o s e d

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

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

C# WinForm利用GDI+的双缓冲技术来提高绘图效率前言进入.NET时代,Windows的绘图技术也从GDI升级到了GDI+,从名字就能知道GDI+是对以前传统GDI 绘图技术的一次升级,不过在微软几乎把所有的新技术都冠之.NET的情况下,GDI+竟然不叫做,还真让我感到有点意外了。

:)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;TimeSpan sp = t2 - t1;float per = 1000 / liseconds;bel1.Text = "速度:" + per.ToString() + "帧/秒";运行后,我点击“开始”按纽,效果如下图所示:应用双缓冲以前的效果图(帧数:5帧/秒)正如大家所看到的,我在程序中使用循环画了几百个圆形,然后在每次的定时器脉冲事件中使用不同方向的线性渐变来对它们进行填充,形成了一个动画效果。

[Winodows图形编程]初识双缓冲技术

[Winodows图形编程]初识双缓冲技术

[Winodows图形编程]初识双缓冲技术为完成PaintBoardDemo(本人设计的一个基于.NET Framework的WinForm的画图程序),过程中遇到的技术难点之一就是就是要显示任何图形绘制过程中的轨迹,也即需要在pictureBox控件的MouseMove事件中添加相应的Graphics对象的DrawXX Methods.在设计之初,仅仅能够在MouseMove事件中写出一行代码:g.DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y)(ptLast为Point类型的全局变量,用以保存MouseDown事件的坐标)。

可想而知,鼠标的每一次Move就将触发MouseMove事件,即执行一次g.DrawLine(画一条直线)。

因此程序运行的结果就是,画板上显示了绘制过程中的所有轨迹,如下图,显然,这是与我们的设计目的相不符的。

经过对Bitmap与Graphics的学习及经过同学开导,终于解决了不显示绘制过程中多余轨迹的问题。

基本实现的思想可以总结如下:1.建立一个位图对象,即Bitmap _bitmapTemp,和一个Graphics对象用来实现一系列绘制操作,即Graphics g(_bitmapTemp,g均为全局变量)。

2.在主窗体的Shown事件中,对_bitmapTemp和g进行初始化:_bitmapTemp=new Bitmap(pictureBox.Width,pictureBox.Height);//初始化位图大小和pictureBox一致Graphics.FromImage(_bitmapTemp).FillRectangle(Brushes.White,0,0,pictureBox.Width,pictureB ox.Height);//并且将位图_bitmapTemp以白色填充全部区域,以作绘制之用。

g=pictureBox.CreateGraphics();//实例化g对象,表明以后对g的所有绘制操作均在pictureBox之上。

双缓冲

双缓冲
三、双缓冲图形刷新技术的原理
双缓冲图形刷新技术顾名思义是采用双缓存实现的。传统的绘图方式实际上是一种单缓冲。在windows中每一种设备都在内存中有一个设备描述表与其对应,这个设备描述表实际上就是一个内存缓冲区。传统的绘图中我们是将图形绘制在设备描述表缓冲区中,然后由gdi自动的将设备描述表中的图像拷贝到显存中进行显示。这样一个自动的拷贝过程屏蔽了传统的绘图方式是单缓冲的实质,使我们感觉到我们是在直接操纵显存一样。双缓冲图形刷新技术在内存中有两片缓存,除了设备描述表以外还有一个需要手动建立的与设备描述表缓冲区(前端缓冲区)相兼容的后备缓冲区。绘图过程中,首先将图形绘制在后备缓冲区中,然后在手动的将后备缓冲区中的图像拷贝到前端缓冲区中,再由gdi自动将前端缓冲区中的图像拷贝到显存完成图形的显示过程。
//先用背景色将位图清除干净,这里我用的是白色作为背景
//你也可以用自己应该用的颜色
MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));
//绘图
MemDC.MoveTo(……);
MemDC.LineTo(……);
//将后备缓冲区中的图形拷贝到前端缓冲区
1、编写一个刷新速度很慢的应用程序,可以设计为通过点击鼠标来进行屏幕刷新。通过该试验可以发现即使屏幕的刷新速度很慢,但是在每次刷新的时候仍然存在屏幕的问题,只是闪烁不是很明显。
2、编写一个刷新速度很快的应用程序,并在程序中应用双缓冲图形刷新技术。通过该试验可以发现虽然屏幕刷新速度很快,但是采用了双缓冲图新刷新技术以后,屏幕不存在闪烁。
CBitmap MemBitmap;
//建立与屏幕设备描述表(前端缓冲区)兼容的内存设备描述表句柄(后备缓冲区)

双缓冲绘图技术

双缓冲绘图技术

由双缓冲绘图技术谈起到Delphi源码实现摘要:双缓冲绘图技术在Delphi中的实现关键字:Delphi,双缓冲,Canvas作者:上海翰博数码科技实业有限公司沈小云说明:假设读者熟悉VCL双缓冲绘图也不是什么新技术,简单的说:在绘图实现时不直接绘在窗口上,而是先绘在内存里,再一起“拷贝”至窗口。

实现起来也不复杂,创建一兼容HDC,在此兼容HDC上绘图,最后拷贝到窗口HDC就行了。

本人前段时间把一C++实现该技术的代码改成了Delphi代码,都是用Win32API写的。

今改成了使用Delphi自带的类,试了一下(窗口类Canvas与TImage 的Canvas)。

实现方式大同小异,但不得不提的是在窗口中直接使用Canvas绘图与TImage.Canvas却不相同。

使用TImage.Canvas绘图时,自动使用了双缓冲技术,而窗口的Canvas对像却未实现。

怎么回事呢?看一下代码吧,“源码面前没有秘密”!一.TImage类的CanvasTImage = class(TGraphicControl)...property Canvas: TCanvas read GetCanvas;...function TImage.GetCanvas: TCanvas;varBitmap: TBitmap;beginif Picture.Graphic = nil thenbeginBitmap := TBitmap.Create;tryBitmap.Width := Width;Bitmap.Height := Height;Picture.Graphic := Bitmap;finallyBitmap.Free;end;end;if Picture.Graphic is TBitmap thenResult := TBitmap(Picture.Graphic).Canvaselseraise EInvalidOperation.Create(SImageCanvasNeedsBitmap); end;可知TImage.Canvas来自Bitmap.Canvas,好,那来看看TBitmap.Canvas function TBitmap.GetCanvas: TCanvas;beginif FCanvas = nil thenbeginHandleNeeded;if FCanvas = nil then // possible recursionbeginFCanvas := TBitmapCanvas.Create(Self);FCanvas.OnChange := Changed;FCanvas.OnChanging := Changing;end;end;Result := FCanvas;end;显而易见TBitmap.Canvas = TBitmapCanvas.Create;也就是说TImage.Canvas=TBitmapCanvas.Create.即使用TImage.Canvas绘图时,实际是在TBitmapCanvas上绘图的。

C#使用BufferedGraphics实现GDI+双缓冲绘图

C#使用BufferedGraphics实现GDI+双缓冲绘图

使用双缓冲的图形可以减少或消除重绘显示图面时产生的闪烁。

使用双缓冲时,更新的图形首先被绘制到内存的缓冲区中,然后,此缓冲区的内容被迅速写入某些或所有显示的图面中。

显示图形的重写相对简短,这通常可以减少或消除有时在更新图形时出现的闪烁。

使用C# GDI+绘图,实现双缓冲绘图有几种方法,在这篇文章中,将介绍其中的一种——使用BufferedGraphics实现GDI+双缓冲绘图。

下面的代码示例演示如何使用BufferedGraphics对象绘制以下图形,这些图形使用几种类型的缓冲实现。

单击窗体将启动或停止一个计时器,该计时器将引起绘制更新。

绘制更新使您可以观察双缓冲的效果。

右击窗体将循环使用下列绘制模式:∙对于Form,直接绘制到Handle。

∙对使用OptimizedDoubleBuffer控件样式的OnPaint方法进行重写,以进行绘制∙对于不使用OptimizedDoubleBuffer控件样式的窗体方法,重写OnPaint方法以进行绘制。

在每种模式下都将绘制文本,以标识当前模式并描述按下每个鼠标按钮时发生的行为。

using System;using ponentModel;using System.Drawing;using System.Windows.Forms;namespace BufferingExample{public class BufferingExample : Form{private BufferedGraphicsContext context;private BufferedGraphics grafx;private byte bufferingMode;private string[] bufferingModeStrings ={ "Draw to Form without OptimizedDoubleBufferring control style","Draw to Form using OptimizedDoubleBuffering control style","Draw to HDC for form" };private System.Windows.Forms.Timer timer1;private byte count;public BufferingExample() : base(){// Configure the Form for this example.this.Text = "User double buffering";this.MouseDown += newMouseEventHandler(this.MouseDownHandler);this.Resize += new EventHandler(this.OnResize);this.SetStyle( ControlStyles.AllPaintingInWmPaint | erPaint, true );// Configure a timer to draw graphics updates.timer1 = new System.Windows.Forms.Timer();timer1.Interval = 200;timer1.Tick += new EventHandler(this.OnTimer);bufferingMode = 2;count = 0;// Retrieves the BufferedGraphicsContext for the// current application domain.context = BufferedGraphicsManager.Current;// Sets the maximum size for the primary graphics buffer// of the buffered graphics context for the application// domain. Any allocation requests for a buffer larger// than this will create a temporary buffered graphics// context to host the graphics buffer.context.MaximumBuffer = new Size(this.Width+1,this.Height+1);// Allocates a graphics buffer the size of this form// using the pixel format of the Graphics created by// the Form.CreateGraphics() method, which returns a// Graphics object that matches the pixel format of the form.grafx = context.Allocate(this.CreateGraphics(),new Rectangle( 0, 0, this.Width, this.Height ));// Draw the first frame to the buffer.DrawToBuffer(grafx.Graphics);}private void MouseDownHandler(object sender, MouseEventArgs e){if( e.Button == MouseButtons.Right ){// Cycle the buffering mode.if( ++bufferingMode > 2 )bufferingMode = 0;// If the previous buffering mode used// the OptimizedDoubleBuffering ControlStyle,// disable the control style.if( bufferingMode == 1 )this.SetStyle( ControlStyles.OptimizedDoubleBuffer, true );// If the current buffering mode uses// the OptimizedDoubleBuffering ControlStyle,// enabke the control style.if( bufferingMode == 2 )this.SetStyle( ControlStyles.OptimizedDoubleBuffer, false );// Cause the background to be cleared and redraw.count = 6;DrawToBuffer(grafx.Graphics);this.Refresh();}else{// Toggle whether the redraw timer is active.if( timer1.Enabled )timer1.Stop();elsetimer1.Start();}}private void OnTimer(object sender, EventArgs e){// Draw randomly positioned ellipses to the buffer.DrawToBuffer(grafx.Graphics);// If in bufferingMode 2, draw to the form's HDC.if( bufferingMode == 2 )// Render the graphics buffer to the form's HDC.grafx.Render(Graphics.FromHwnd(this.Handle));// If in bufferingMode 0 or 1, draw in the paint method.elsethis.Refresh();}private void OnResize(object sender, EventArgs e){// Re-create the graphics buffer for a new window size.context.MaximumBuffer = new Size(this.Width+1,this.Height+1);if( grafx != null ){grafx.Dispose();grafx = null;}grafx = context.Allocate(this.CreateGraphics(),new Rectangle( 0, 0, this.Width, this.Height ));// Cause the background to be cleared and redraw.count = 6;DrawToBuffer(grafx.Graphics);this.Refresh();}private void DrawToBuffer(Graphics g){// Clear the graphics buffer every five updates.if( ++count > 5 ){count = 0;grafx.Graphics.FillRectangle(Brushes.Black, 0, 0, this.Width, this.Height);}// Draw randomly positioned and colored ellipses.Random rnd = new Random();for( int i=0; i<20; i++ ){int px = rnd.Next(20,this.Width-40);int py = rnd.Next(20,this.Height-40);g.DrawEllipse(new Pen(Color.FromArgb(rnd.Next(0, 255),rnd.Next(0,255), rnd.Next(0,255)), 1),px, py, px+rnd.Next(0, this.Width-px-20), py+rnd.Next(0, this.Height-py-20));}// Draw information strings.g.DrawString("Buffering Mode:"+bufferingModeStrings[bufferingMode], new Font("Arial", 8), Brushes.White, 10, 10);g.DrawString("Right-click to cycle buffering mode", new Font("Arial", 8), Brushes.White, 10, 22);g.DrawString("Left-click to toggle timed display refresh", new Font("Arial", 8), Brushes.White, 10, 34);}protected override void OnPaint(PaintEventArgs e){grafx.Render(e.Graphics);}[STAThread]public static void Main(string[] args){Application.Run(new BufferingExample());}}}。

C#使用Bitmap实现GDI+双缓冲绘图

C#使用Bitmap实现GDI+双缓冲绘图

下面是实现本示例的几个步骤:一、创建一个Windows程序项目,在项目里面创建一个窗口,命名为:FormBuffering,在窗口上添加另两个按钮,分别命名为:btnStart和btnStop。

二、复制下面的源码,覆盖自动生成的代码:using System;using System.Collections.Generic;using ponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;namespace ImageDoubleBuffer{public partial class FormBuffering : Form{private Bitmap _bufferImage;private int _count;private Timer _timer;public FormBuffering(){InitializeComponent();_bufferImage = new Bitmap(ClientSize.Width,ClientSize.Height);_timer = new Timer();_timer.Interval = 200;_timer.Tick += delegate(object sender, EventArgs e){_count++;if (_count > 6){_count = 0;}base.Invalidate();};btnStart.Click += delegate(object sender, EventArgs e) {_timer.Start();};btnStop.Click += delegate(object sender, EventArgs e) {_timer.Stop();_count = 0;};}///<summary>///当窗体改变大小时,重新初始化图片。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

绘图的双缓冲技术简介幸运的是当编写一个典型的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 routingLineDrawRoutine(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 bitmapBitmap localBitmap = newBitmap(ClientRectangle.Width,ClientRectangle.Height);Graphics bitmapGraphics = Graphics.FromImage(localBitmap);//call our isolated drawing routingLineDrawRoutine(bitmapGraphics, bluePen);//push our bitmap forward to the screeng.DrawImage(localBitmap, 0, 0);bitmapGraphics.Dispose();bluePen.Dispose();localBitmap.Dispose();g.Dispose();}上面的示例代码创建了存位图对象,它的大小等于窗体的客户区域(就是绘图表面)的大小,通过调用Graphics.FromImage将存中位图的引用传递给Graphics对象,也就是说后面所有对该Graphics对象的操作实际上都是对存中的位图进行操作的,该操作在C++中等同于将位图对象的指针复制给Graphics对象,两个对象使用的是同一块存地址。

相关文档
最新文档