GDI+性能优化
每个 Windows 控件都可以拥有一个 paint 事件处理程序和一个表示此控件是绘图画布的 Graphics 对象。这意味着我们可以使用一个按钮或一个列表框作为绘图画布。
如果在菜单或按钮的 Click 事件处理程序中绘制图形对象,则必须最后调用 this.Invalidate() 方法。如果不调用,窗体将不会重新绘制自身,但是如果我们在窗体的 OnPaint 或 paint 事件处理程序中编写
一些代码,则不需要使窗体无效。
释放图形对象
如果一个 Graphics 对象是通过 CreateGraphics 方法或其他 CreateFrom 方法创建的,则必须释放此对对象,即手动调用 g.Dispose() 方法 。
如果我们在 paint 事件中或从 PaintEventArgs.Graphics 属性的 OnPaint 方法使用 Graphics 对象,则不需要释放此对象。
关于 Pen、Brush 等,如果将频繁地重新绘制窗体,则将这些变量定义为全局变量可以提高性能,因为在每一次重新绘制时都不需要再重新创建这些对象上浪费时间,但另一方面,定义全局变量将占用更多的内存资源。
注意:有时使用浮点数据类型代替整数类型可以改善绘图的质量,尽管浮点数据会占用更多的资源。
双缓存和无抖动绘图
双缓存技术用于通过减少抖动来提供更快、更平滑的绘图。在这种技术中,所有的对象都是使用一个临时的图像和一个 Graphics 对象在一个屏幕外的画布上绘制的。然后这个图像将复制到控件上。
如果绘图操作很简单,并且只包括绘制一些矩形或直线等简单的对象,则不需要使用双缓存技术,因为技术会降低性能。如果操作包含很多计算或绘图元素,则通过使用双缓存技术可以极大地改善性能
和外观。
理解 SetStyle 方法
Windows Forms 控件为双缓存提供了内建的支持,而 Control 类的 SetStyle 方法在这个过程中起到了非常重要的作用。
this.SetStyle(ControlStyles.UserPaint, true); this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.DoubleBuffer, true); this.SetStyle(ControlStyles.ResizeRedraw, true);
注意:很多控件(如 PictureBox)都是自动双缓存的,这意味着在 PictureBox 控件中查看图象时,我们不需要编写任何附加的代码。
绘制图象的质量和性能
//质量优先:较低的绘图性能和较高的质量 g.SmoothingMode= SmoothingMode.HighQuality; g.SmoothingMode = SmoothingMode.AntiAlias; //性能优先:较低的质量和较高的性能 g.SmoothingMode = SmoothingMode.HighSpeed; g.SmoothingMode = SmoothingMode.None; //文本的质量 g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
只重新绘制所需的区域
使用区域和剪辑矩形可能会有些帮助。如果需要使用搞锯齿处理绘制一个对象,则只为此对象设置抗锯齿处理,而不要为整个绘图表面(窗体)设置此选项。使用区域是只重新绘制所需区域的最佳技术之一。如何使用特定区域无效和剪辑特定区域的详细信息,请看第6章。
使用图形路径绘制
Graphics g = this.CreateGraphics(); g.Clear(this.BackColor); Pen blackPen = new Pen(Color.Black, 2); GraphicsPath path = new GraphicsPath(); path.AddLine(50, 50, 200, 50); path.AddLine(50, 50, 50, 200); path.AddRectangle(new Rectangle(60, 60, 150, 150)); path.AddRectangle(new Rectangle(70, 70, 100, 100)); path.AddEllipse(90, 90, 50, 50); g.DrawPath(blackPen, path); g.SmoothingMode = SmoothingMode.HighQuality; blackPen.Dispose(); g.Dispose();
使用一条图形路径绘制语句来代替多条绘制语句,在性能上有优势
但是有所限制,即我们不能使用不同的钢笔或画笔来绘制图形路径的每个元素(直线、矩形或椭圆等)。
同时绘制同一类元素的多个时,应该使用 绘制数组方法
例如:一次绘制三个矩形
Graphics g = this.CreateGraphics(); g.Clear(this.BackColor); Pen blackPen = new Pen(Color.Black, 2); RectangleF[] rectArray = { new RectangleF(5.0F,5.0F,100.0F,200.0F), new RectangleF(20.0F,20.0F,80.0F,40.0F), new RectangleF(60.0F,80.0F,140F,50.0F), }; g.DrawRectangles(blackPen, rectArray); blackPen.Dispose(); g.Dispose();
使用系统画笔和钢笔
Graphics g = this.CreateGraphics(); g.Clear(this.BackColor); SolidBrush brush = (SolidBrush)SystemBrushes.ActiveCaption; Pen pn = SystemPens.ControlDarkDark; g.DrawLine(pn, 20, 20, 20, 100); g.DrawLine(pn, 20, 20, 100, 20); g.FillRectangle(brush, 30, 30, 50, 50); g.Dispose();
注意:系统画笔和钢笔不需要释放,如果释放会报异常。
避免图像的自动缩放
自动缩放会导致性能降低,尽量避免使用自动缩放。
Graphics g = this.CreateGraphics(); g.DrawImage(image, 10, 10, image.Width, image.Height); g.Dispose();
绘制时指定图像的宽和高。