绘图的双缓冲技术
如何实现双缓冲
如何实现双缓冲双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。
双缓冲实现过程如下: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双缓冲绘图技术的研究
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+的双缓冲技术来提高绘图效率前言进入.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图形编程]初识双缓冲技术为完成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# 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+双缓冲绘图
下面是实现本示例的几个步骤:一、创建一个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>///当窗体改变大小时,重新初始化图片。
利用双缓存技术提高绘图速率
此 fr o 循环将 srfa otnme中的球队按净 胜球排序 ,并将信 j 息存A jq x 0 和 t sr 0 sp e ot mp
f a f I smerm ( q x ( =0T e o jp ) s ) hn
c u i ta ( ) =t sr 1 h xa e m 2 n e o t() mp
(பைடு நூலகம்在屏幕上显示设备对 象中的图形 : 4
p C - Bt h ( 0 , d ,H i t Me oD ,, S C D >i B , n t n eg , m r C 00 R - 0 Wi h h& ,
完成 ,而后将其保 存为位 图的方式 ,所有操 作完成 后再 一次 性地将 其粘贴 到屏幕 画布上 。因为 整个绘 制图形 的过程 都是 在虚 拟画布上 完成 的 ,对 屏幕 画布 只有一 次操作 ,只需 要刷
I s 2 < s 3 T e f t() > t() h n
s m eo = 2 a t
o o n s tnr ) F r = 1 oUB u d ( rfa e J T oj n I rs ( =srfa j T e f otq i s j ) otnme f j ) hn tmpot() =srs ( e sr k otq i j ) jq x () =t jq ( sp k e s i mp )
3 两种 方 法的测 试与 比较
通过 一个小实验 ,对两 种方法 的绘 图速率进 行 比较 ,实 验结 果 ( 如表 1 所示) 表明利用双 缓存技术 大约 提高 了 5 % 0
表 1实 验 结 果
方法 速率
传统方法 传统的绘制方法 双缓存绘制方法
于双缓冲技术的GDI无闪烁绘图
摘
Flickerfree GDI + drawing based on double buffering
JIANG Jianguo , WEN Shaoying, ZHANG Ruinan
( School of Mathematies, Liaoning Normal University, Dalian Liaoning 116029 , China)
基于双缓冲技术的 GDI + 无闪烁绘图
江建国 ,温少营,张瑞楠
( 辽宁师范大学 数学学院, 辽宁 大连 116029 ) ( * 通信作者电子邮箱 jjgbox@ sina. com)
*
要: 图形闪烁是使用 GDI + 编写 Windows 窗体应用程序时经 常 遇 到的 一 个 问题。 在 深 入 分析 窗 体重 绘 模型 阐述了图形闪烁的本质原因。利用. NET框架 中 内 置 的 双 缓 冲 技术, 提出了 两 种 GDI + 无 闪烁 绘 图方 法。 以 基础上, 模拟时钟程序为例, 说明了这两种方法都非常简便且有效。 关键词: 双缓冲; 无闪烁绘图; 图形闪烁; GDI + ; . NET框架; 模拟时钟 中图分类号: TP391. 41 文献标志码: A
this. SetStyle( ControlStyles. OptimizedDoubleBuffer | ControlStyles. UserPaint | ControlStyles. AllPaintingInWmPaint, true) ;
向 Windows 窗体注册一个名为 MyPaintHandler 的 Paint 事件 处理程序, 将重绘窗体的代码添加到方法 MyPaintHandler 中:
双缓冲技术实现VC绘图共28页文档
********************所有的GDI绘图函数使用的都是逻辑坐标(逻辑范围)**************************************系统默认情况下物理范围和逻辑范围是1:1 的对应关系*******************1. 首先定义类成员:CDC *m_pDC;CDC MemDC;CBitmap MemBitmap;CBitmap *pOldbitmap;LONG xRange; // 逻辑范围,x方向宽度LONG yRange; // 逻辑范围,y方向高度LONG nWidht; // 物理范围,x方向宽度LONG nHeight; // 物理范围,y方向高度2. 在类初始化函数中:m_pDC = this->GetDC(); // 获取设备上下文句柄CWnd *wnd = GetDlgItem(IDC_SHOWGRAPH); // 获取界面上显示图形的ID控件的句柄wnd->GetWindowRect(&rect); // 获取显示/画图区域大小(物理范围)ScreenToClient(&rect); // 转换为客户区坐标nWidth = rect.Width(); // 显示/画图区域x方向物理宽度nHeight = rect.Height(); // 显示/画图区域y方向物理高度3. 在自定义函数中,设置视口与窗口的比例关系:m_pDC->SetMapMode(MM_ANISOTROPIC); // 注意MM_ANISOTROPIC和MM_ISOTROPIC的区别m_pDC->SetWindowExt(XRange,-yRange); // 设定窗口尺寸范围,画图使用的逻辑范围,实现放大或是缩小,坐标方向↑和→为正向m_pDC->SetViewportExt(nWidth,nHeight); // 设定视口尺寸范围,客户区实际图形显示的区域范围,大小固定m_pDC->SetViewportOrg(rect.left,rect.bottom); //设定画图的逻辑原点坐标(0,0)在物理坐标的(rect.left,rect.bottom)点上4. 在自定义函数中,双缓冲技术的使用:MemDC.CreateCompatibleDC(m_pDC);// 创建内存兼容设备上下文MemBitmap.CreateCompatibleBitmap(m_pDC,xRange,yRange); // 创建内存兼容画布,大小由逻辑范围决定pOldbitmap =MemDC.SelectObject(&MemBitmap); // 将画布选入内存设备上下文MemDC.FillSolidRect(0,0,xRange,yRange,RGB(123,213,132)); // 对内存中的画布填充背景颜色,否则是默认的黑色// 画图操作,如画一条对角直线MemDC.MoveTo(0,0);MemDC.LineTo(xRange*0.9,yRange*0.9);// 将内存中的画图区域拷贝到界面的控件区域上去// 第1和第2个参数若是0时,则从物理坐标的(rect.left,rect.bottom)点上开始按上述指定的方向贴图m_pDC->BitBlt(0,0,xRange,yRange,&MemDC,0,0,SRCCOPY);5. 在类的析构函数中:MemDC.SelectObject(pOldbitmap);bitmap.DeleteObject();this->ReleaseDC(m_pDC);6. 至此,就完成了双缓冲及坐标缩放绘图的功能用VC做的画图程序,当所画的图形大于屏幕时,在拖动滚动条时屏幕就会出现严重的闪烁,为了解决这一问题,就得使用双缓冲来解决。
[转载]MFC绘制动态曲线,用双缓冲绘图技术防闪烁
[转载]MFC绘制动态曲线,⽤双缓冲绘图技术防闪烁先上效果图随着时间的推移,曲线向右平移,同时X轴的时间坐标跟着更新。
⼀、如何绘制动态曲线 所谓动画,都是⼀帧⼀帧的图像连续呈现在⽤户⾯前形成的。
所以如果你掌握了如何绘制静态曲线,那么学会绘制动态曲线也不远啦,只需要创建⼀个定时器(⽐如调⽤MFC中的SetTimmer函数),每隔⼀定时间(⽐如1ms),调⽤OnPaint或者OnDraw函数,绘制当前帧图像即可。
这⾥需要注意的是,绘制图像的代码需要写在OnPaint或者OnDraw函数中,因为当窗⼝失效(⽐如最⼩化)恢复后,会重新绘制当前窗⼝,窗⼝之前的⾃绘图像会丢失。
⽽把绘图代码写在OnPaint或者OnDraw中就是为了让窗⼝每次重绘时也能重绘你⾃⼰画的图像,避免出现窗⼝最⼩化再恢复后,⾃⼰画的图像丢失的尴尬情况。
另外绘制当前帧图像之前,记得⽤InvalidateRect函数清除上⼀帧图像,不然各帧图像会背景的堆叠。
⽐如我想清除窗⼝中(0,0)和(100,100)这两点确定的矩形中的图像,代码如下:CRect Rect;Rect.top = 0;Rect.left = 0;Rect.bottom = 100;Rect.right = 100;InvalidateRect(Rect); 根据上⾯的思路,我们每隔⼀定时间绘制⼀幅图像,可是如果每次绘制的图像都是完全相同的,那么图像看起来也是静态的。
如何让曲线动起来呢?我们需要为⾃⼰绘图的代码设计⼀个输⼊,即在当前时刻曲线上各个点的坐标信息。
随着时间的推移,令曲线上各个点的坐标随之变化,这样每次绘图都是基于当前时刻的曲线坐标绘制的,控制好曲线坐标的变化,也就能让你绘制的曲线乖乖的动起来。
上⾯提到了曲线上各个点的坐标信息,这个信息可以⽤多种数据结构储存,不过笔者推荐使⽤STL中的deque数据结构储存。
为什么呢?需求决定选择。
让我们先想想在绘制图像的过程中需要对这个数据进⾏哪些操作。
java图像显示双缓冲技术
在Java编写具有连贯变化的窗口程序时,通常的办法是在子类中覆盖父类的paint(Graphics)方法,在方法中使用GUI函数实现窗口重绘的过程。
连贯变换的窗口会不断地调用update(Graphics)函数,该函数自动的调用paint(Graphics)在Java编写具有连贯变化的窗口程序时,通常的办法是在子类中覆盖父类的paint(Graphics)方法,在方法中使用GUI函数实现窗口重绘的过程。
连贯变换的窗口会不断地调用update(Graphics)函数,该函数自动的调用paint(Graphics)函数。
这样就会出现闪烁的情况。
为了解决这一问题,可以应用双缓冲技术。
可以通过截取上述过程,覆盖update(Graphics)函数,在内存中创建一个与窗口大小相同的图形,并获得该图形的图形上下文(Graphics),再将图片的图形上下文作为参数调用paint(Graphics)函数(paint(Graphics)中的GUI函数会在图片上画图),再在update(Graphics)函数调用drawImage函数将创建的图形直接画在窗口上。
01.Image ImageBuffer = null;02.Graphics GraImage = null;03.04.public void update(Graphics g){ //覆盖update方法,截取默认的调用过程05. ImageBuffer = createImage(this.getWidth(), this.getHeight());//创建图形缓冲区06. GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的图形上下文07. paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲区绘图08. GraImage.dispose(); //释放图形上下文资源09. g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏幕上10.}11.12.public void paint(Graphics g){ //在paint方法中实现绘图过程13. g.drawLine(0, 0, 100, 100);14.}Image ImageBuffer = null;Graphics GraImage = null;public void update(Graphics g){ //覆盖update方法,截取默认的调用过程ImageBuffer = createImage(this.getWidth(), this.getHeight()); //创建图形缓冲区GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的图形上下文paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲区绘图GraImage.dispose(); //释放图形上下文资源g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏幕上}public void paint(Graphics g){ //在paint方法中实现绘图过程g.drawLine(0, 0, 100, 100);}因为大部分绘图过程是在内存中进行,所以有效地消除了闪烁。
C++实现双缓冲
C++实现双缓冲⾸先声明下,这篇资料也是整理别⼈的资料的基础上,总结来的。
在图形图像处理过程中,双缓冲技术是⼀种⽐较常见的技术。
窗体在响应WM_PAINT消息时,需要对图像进⾏绘制处理。
如果图像绘制次数过多,重绘过于频繁时,或者当要绘制的对象太复杂,尤其是含有位图时,⼀般计算机便⼒不从⼼了。
显⽰器上就会因为刷新过频或者过慢⽽闪烁。
双缓冲就是解决这种问题的技术。
窗体在刷新前,会⾸先擦除(OnEraseBkgnd)之前的内容,然后利⽤背景⾊填充,再调⽤绘制代码进⾏绘制。
⼀擦⼀填⼀写,就会形成颜⾊的反差,当反差过于明显且频繁时,闪烁就来了。
擦除绘制需要时间去处理。
如果不在窗体上直接绘制,⽽是在“别的地⽅”绘制好,然后再直接搬过来,就不会有这种问题了。
这就是双缓冲的基本原理。
双缓冲技术中,内存就充当了“别的地⽅”。
双缓冲技术分为五步:1、在内存中申请缓冲区,创建兼容内存;2、创建位图,并将位图与缓冲区内存相关联起来;3、在兼容内存⾥绘制;4、将绘制好的位图拷贝到当前设备;5、释放兼容内存。
具体代码实现如下(这是⼀个绘制同⼼圆的例⼦):CPoint ptCenter;CRect rect,ellipseRect;GetClientRect(&rect);ptCenter = rect.CenterPoint();CDC dcMem; //⽤于缓冲作图的内存DCCBitmap cbBmp; //内存中承载临时图象的位图dcMem.CreateCompatibleDC(pDC); //申请缓冲区,依附窗⼝DC创建兼容内存DCcbBmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//创建兼容位图dcMem.SelectObject(&cbBmp); //将位图选择进内存DC//按原来背景填充客户区,不然会是⿊⾊dcMem.FillSolidRect(rect,pDC->GetBkColor());for(int i=20;i>0;i--) //在内存DC上做同样的同⼼圆图象{ellipseRect.SetRect(ptCenter,ptCenter);ellipseRect.InflateRect(i*10,i*10);dcMem.Ellipse(ellipseRect);}/*//提供下绘制⽅框、画线等⽅法dcMem.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));//绘图dcMem.MoveTo(……);dcMem.LineTo(……);*/pDC->BitBlt(0,0,rect.Width(),rect.Height(), &dcMem,0,0,SRCCOPY);//将内存DC上的图象拷贝到前台dcMem.DeleteDC(); //删除DC该段代码中已经提供了填充客户区的⽅法,为了提⾼绘制效率,可以继承OnEraseBkgnd,然后直接返回true就⾏。
单缓冲与双缓冲的区别
单缓冲与双缓冲的区别单缓冲,实际上就是将所有的绘图指令在窗⼝上执⾏,就是直接在窗⼝上绘图,这样的绘图效率是⽐较慢的,如果使⽤单缓冲,⽽电脑⽐较慢,你回到屏幕的闪烁。
双缓冲,实际上的绘图指令是在⼀个缓冲区完成,这⾥的绘图⾮常的快,在绘图指令完成之后,再通过交换指令把完成的图形⽴即显⽰在屏幕上,这就避免了出现绘图的不完整,同时效率很⾼。
⼀般⽤OpenGL绘图都是⽤双缓冲,单缓冲⼀般只⽤于显⽰单独的⼀副⾮动态的图像OpenGL的消隐与双缓冲2008-08-12 16:24:45| 分类: | 标签: |字号⼤中⼩⾸先是⼤家可能已经发现,在我们之前提到的所有例⼦中,在图形的旋转过程中整个图形都有⼀定程度的闪烁现象,显得图形的过渡极不平滑,这当然不是我们所要的效果,幸好opengl ⽀持⼀个称为双缓存的技术,可以有效的帮助我们解决这个问题。
我们知道在我们电脑中,屏幕中显⽰的东西都会被放在⼀个称为显⽰缓存的地⽅,在通常情况下我们只有⼀个这样的缓冲区,也就是单缓冲,在单缓冲中任何绘图的过程都会被显⽰在屏幕中,这也就是我们为什么会看到闪烁,⽽所谓双缓冲就是再这个显⽰的缓冲区之外再建⽴⼀个不显⽰的缓冲区,我们所有的绘图都将在这个不显⽰的缓冲区中进⾏,只有当⼀帧都绘制完了之后才会被拷贝到真正的现实缓冲区显⽰出来,这样中间过程对于最终⽤户就是不可见的了,那即使是速度⽐较慢也只会出现停顿⽽不会有闪烁的现象出现。
在OpenGL 中我们可以通过glut 库中的函数void glutSwapBuffers(void) 来实现从⾮显⽰缓冲区到显⽰缓冲区的复制当然在使⽤双缓冲之前我们也要在 glutInitDisplayMode 设定参数的时候选择GLUT_DOUBLE ⽽不是之前我们⽤的 GLUT_SINGLE 相信这两个参数的意思也应该很明⽩了在解决了闪烁的问题之后我们来解决另⼀个问题⾸先我们在窗⼝中画两个交叉的分别位于ZOX 和 ZOY 平⾯的长⽅形并⽤不同的颜⾊填充它们,然后让它们旋转很快我们发现有个⽐较严重的问题发⽣了,因为我们发现opengl显然没有空间的位置观念因为它根本不能分辨物体的前后关系,当然也不能做出适当的显⽰,在opengl中我们使⽤深度缓冲区来解决这个问题,在这个缓冲区中存放着每个象素深度信息,当有⼀个新的象素需要显⽰的时候,我们可以通过⼀个被称为深度测试的函数来确定它们的先后关系,要使⽤深度缓冲区有以下⼏个步骤:1.在函数 glutInitDisplayMode(mode) 中将GLUT_DEPTH置位表明要使⽤深度缓冲区2.使⽤函数glEnable(GL_DEPTH_TEST) 打开深度测试函数void glEnable(GLenum cap);void glDisable(GLenum cap);这两个函数属于基本状态管理函数⽤来打开和关闭⼀些常⽤的功能,常⽤的参数有以下⼏个GL_BLEND GL_DEPTH_TEST GL_FOG GL_LINE_STIPPLE GL_LIGHTING3.是⽤函数glDepthFunc()来设置深度测试函数void glDepthFunc(GLenum func)这⾥我们⽐较常⽤的深度测试函数有 GL_LESS 和 GL_LEQUAL 两者的区别在于当深度相同时是显⽰新的象素还是⽼的象素4.使⽤下⾯的函数来设置深度缓冲区的值void glClearDepth(GLclampd depth)这⾥的参数是背景的深度⼀般来说我们都将背景深度设成最⼤值15.最后在我们使⽤glClear(bits) 刷新背景的同时我们要将GL_DEPTH_BUFFER_BIT置位表⽰我们在刷新背景颜⾊的同时⽤刷新背景深度#include "stdafx.h"#include <math.h>#include <gl/glut.h>#include <gl/gl.h>bool mouseisdown=false;bool loopr=false;int mx,my;void timer(int p){ry-=5;glutPostRedisplay();if (loopr)glutTimerFunc(200,timer,0);}void mouse(int button, int state, int x, int y){if(button == GLUT_LEFT_BUTTON){if(state == GLUT_DOWN){ mouseisdown=true; loopr=false;}elsemouseisdown=false;}if (button== GLUT_RIGHT_BUTTON)if(state == GLUT_DOWN){loopr=true; glutTimerFunc(200,timer,0);} }void motion(int x, int y){if(mouseisdown==true){ry+=x-mx;rx+=y-my;glutPostRedisplay();}}void special(int key, int x, int y){switch(key){case GLUT_KEY_LEFT:ry-=5;glutPostRedisplay();break;case GLUT_KEY_RIGHT:ry+=5;glutPostRedisplay();break;case GLUT_KEY_UP:rx+=5;glutPostRedisplay();break;case GLUT_KEY_DOWN:rx-=5;glutPostRedisplay();break;}}void init()//设置OpenGL的⼀些状态变量的初始值{glEnable(GL_DEPTH_TEST); //深度测试glDepthFunc(GL_LESS); //设置深度测试函数glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); //设置多边形显⽰模式为正反⾯都是填充显⽰ glClearColor(1,1,1,1); //设置刷新背景⾊glClearDepth(1); //设置清除深度缓冲区所⽤的值}void display(){float red[3]={1,0,0};float blue[3]={0,1,0};float green[3]={0,0,1};float yellow[3]={1,1,0};glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();glRotatef(ry,0,1,0);glRotatef(rx,1,0,0);glBegin(GL_QUADS);glColor3fv(green);glVertex3f(0.5,0.5,0);glVertex3f(-0.5,0.5,0);glVertex3f(-0.5,-0.5,0);glVertex3f(0.5,-0.5,0);glEnd();glBegin(GL_QUADS);glColor3fv(red);glVertex3f(0.5,0.5,0.3);glVertex3f(-0.5,0.5,-0.3);glVertex3f(-0.5,-0.5,-0.3);glVertex3f(0.5,-0.5,0.3);glEnd();glBegin(GL_QUADS);glColor3fv(yellow);glVertex3f(0.5,0.5,-0.3);glVertex3f(-0.5,0.5,0.3);glVertex3f(-0.5,-0.5,0.3);glVertex3f(0.5,-0.5,-0.3);glEnd();glFlush();glutSwapBuffers();}int main(int argc, char** argv){glutInit(&argc, argv);glutInitDisplayMode (GLUT_DOUBLE| GLUT_RGBA|GLUT_DEPTH); //设置显⽰模式:单缓冲区, RGBA颜⾊模式 glutInitWindowSize (400, 400); //设置窗⼝宽度、⾼度glutInitWindowPosition(100,100); //设置窗⼝位置glutCreateWindow ("double"); //弹出窗⼝init();glutDisplayFunc (display); //设置窗⼝刷新的回调函数glutMouseFunc(mouse); //设置⿏标器按键回调函数glutMotionFunc(motion);glutSpecialFunc(special);glutMainLoop(); //开始主循环return 0;}。
双缓冲技术(基于GDI+实现)
双缓冲技术(基于GDI+实现)一、双缓冲技术双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。
当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图。
双缓冲实现过程如下:1、在内存中创建与画布一致的缓冲区2、在缓冲区画图3、将缓冲区位图拷贝到当前画布上4、释放内存缓冲区在图形图象处理编程过程中,双缓冲是一种基本的技术。
我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。
解决这一问题的有效方法就是双缓冲技术。
因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。
当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。
于是我们就看到了闪烁现象。
我们会很自然的想到,避免背景色的填充是最直接的办法。
但是那样的话,窗体上会变的一团糟。
因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。
所以单纯的禁止背景重绘是不够的。
我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。
它可以支持图形块的复制,速度很快。
我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。
以上也就是双缓冲绘图的基本的思路。
二、基于GDI+实现双缓冲技术1.CPaintDC dc(this); // device context for painting2.Graphics gr(dc.m_hDC); // Graphics to paint1.Rect rGdi;2.gr.GetVisibleClipBounds(&rGdi); // The same as the clip rect3.4.//创建缓冲区5.Bitmap clBmp(rGdi.Width, rGdi.Height); // Mem bitmap6.Graphics* grPtr = Graphics::FromImage(&clBmp); // As memDC7.8.//利用grPtr在clBmp缓冲区绘图9.grPtr->DrawImage(m_PngMeter,clock_Rect);10.grPtr->TranslateTransform(Pcenter.X,Pcenter.Y);11.grPtr->RotateTransform(i);12.grPtr->TranslateTransform(-Pcenter.X,-Pcenter.Y);13.grPtr->DrawImage(m_PngArrow,picRect);14.grPtr->ResetTransform();15.16.//将clBmp缓冲区绘制到窗口17.gr.DrawImage(&clBmp, rGdi);<pre class="cpp" name="code"> delete grPtr;//注:使用完缓冲区后,一定要及时释放内才能,尤其是在OnPaint中,否则程序很快占满物理内存,崩溃!这里是在WM_PAINT消息里面处理的。
双缓冲的基本原理
双缓冲的基本原理
双缓冲是一种图形显示技术,其基本原理是利用两个缓冲区来实现平滑的图形显示。
在传统的单缓冲模式下,图形的绘制是直接在屏幕上进行的。
这意味着绘制过程是可见的,并且可能会出现图像闪烁、图像残留等问题,特别是在动态图形的情况下。
而双缓冲技术通过使用两个缓冲区来解决这些问题。
其中一个缓冲区被称为前缓冲区,用于保存即将显示在屏幕上的图像;另一个缓冲区被称为后缓冲区,用于保存正在绘制或已经绘制完成但还未显示的图像。
绘制操作首先在后缓冲区进行,然后才将绘制完成的图像复制到前缓冲区,最后再将前缓冲区的图像显示在屏幕上。
这样,当图像绘制完成时,前缓冲区中的图像会一次性地显示在屏幕上,避免了直接在屏幕上进行绘制时可能出现的闪烁和残留问题。
双缓冲技术有效降低了图像绘制的可见性,提升了图形显示的平滑性和性能。
Qt双缓冲绘图
双缓冲绘图在Qt4中,所有的窗口部件默认都使用双缓冲进行绘图。
使用双缓冲,可以减轻绘制的闪烁感。
在有些情况下,用户要关闭双缓冲,自己管理绘图。
下面的语句设置了窗口部件的Qt::WA_PaintOnScreen属性,就关闭了窗口部件的双缓冲.mywidget->setAttribute(Qt::WA_PaintOnScreen);由于Qt4不再提供异或笔,组合模式QPainter::CompostionMode_Xor()并不是异或笔,Qt4只提供了QRubberBand实现矩形和直线的绘图反馈。
因此要实现在绘图中动态反馈必须使用其他方法。
程序中使用双环冲来解决这个问题。
在绘图过程中,一个缓冲区绘制临时内存,一个缓冲区保存绘制好的内容,最后进行合并。
在交互绘图过程中,程序将图像缓冲区复制到临时缓冲区,并在临时缓冲区上绘制,绘制完毕在将结果复制到图像缓冲区,如果没有交互复制,则直接将图像缓冲区绘制显示到屏幕上。
Qt组件中的双缓冲无闪烁绘图闪烁首先,要想把闪烁减弱,请设置组件的背景模式为NoBackground. setBackgroundMode(NoBackground);其次,重载组件的paintEvent()函数,如下改写:void MyWidget::paintEvent(QPaintEvent *e){QRect ur=e->rect();//得到组件尺寸QPixmap pix(ur.size());//以此为参数创建一个位图变量pix.fill(this,ur.topLeft());//填充位图QPainter p(&pic);//以位图为参数创建一个QPainter 对象p.translate(-ur.x(),-ur.y());//在QPainter 上绘画//......//Drawingp.End();//绘画完毕bitBlt(this,ur.topLeft().&pix);//把位图贴到组件上//注从qt4开始,bitBlt函数不在使用,取而代之的是drawPixmap。
android surfaceview双缓冲机制原理
Android Surfaceview双缓冲机制原理1. 介绍在Android开发中,使用SurfaceView可以实现复杂的图像绘制和动画效果。
而SurfaceView的双缓冲机制能够提高绘制性能和流畅度。
2. SurfaceView概述SurfaceView是一种特殊的View,允许我们在一个单独的线程中进行UI更新和渲染工作,与普通的View不同,SurfaceView拥有一个独立的Surface图层,可以直接与底层硬件进行交互,从而提供更好的绘制性能。
3. SurfaceView的工作原理SurfaceView的绘制工作是在一个后台线程中完成的,这个线程被称为“渲染线程”或“绘制线程”。
SurfaceView内部会创建一个Surface,该Surface用于绘制图像。
3.1. Surface的创建和回调Surface是通过SurfaceHolder接口创建的,并在SurfaceHolder.Callback中监听Surface的生命周期。
当Surface被创建时,渲染线程会自动开始绘制工作。
3.2. 绘制工作的流程渲染线程在循环中不断地执行绘制操作,持续更新Surface上的图像。
具体的绘制流程如下:1.锁定Surface的像素缓冲区,获取Canvas对象。
2.执行绘制操作,比如绘制图形、文本或者位图。
3.通过Canvas对象将绘制的内容提交到Surface上。
4.解锁Surface的像素缓冲区,将绘制的内容显示在屏幕上。
3.3. Surface双缓冲机制SurfaceView的双缓冲机制是通过两个Surface来实现的,一个用于显示,称为”前面缓冲区”,另一个用于绘制,称为”后面缓冲区”。
双缓冲机制的工作原理如下:1.绘制线程先在后面缓冲区进行绘制操作。
2.绘制完毕后,将后面缓冲区的内容复制到前面缓冲区。
3.前面缓冲区的内容立即显示在屏幕上。
4.继续绘制线程的绘制操作,不断更新后面缓冲区的内容。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 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对象,两个对象使用的是同一块存地址。