绘图效率完整解决方案——三种手段提高GDI/GDI+绘图效率
现在的cpu飞快,其实数学计算一般很快,cpu大部分时间是在处理绘图,而绘图有三种境界:1>每次重绘整体Invalidate(); 2>每次局部绘制Invalidate(Rect); 3>有选择的局部绘制。
不能说,一定是第三种方式好,得视情况,境界高程序肯定就复杂,如果对效率要求不高或者绘图量小当然直接用第一种方式。然而,稍微专业点的绘图程序,第一第二种方式肯定满足不了要求,必须选用第三种方式。而第三种方式的手段多样,也得根据实际情况拿相应的解决之道。这里讲解一般的三种手段,他们可以联合使用。
1. 缓存——Bitmap或者DoubleBuffer。缓存就是先把绘制的图形绘制到一张内存位图上,然后在一次性的贴位图,他可以提高绘图速度,也能避免闪烁。DoubleBuffer=true是C#窗体的属性,设置了此属性估计系统本身会起用无效区的内存位图缓存,而不需要程序员Bitmap处理。
2. 合理利用无效区域。无效区域就是系统保存当前变化需要重绘的区域,可以在OnPaint()中,e.ClipRectangle直接获得,也可以通过其他方式获得。Windows系统只会重绘无效区域内的绘图信息,然而我们用户的绘制代码一般是绘制整个区域的,很多时候无效区域只是一小部分区域,虽然执行了所有的绘图代码,但是Windows系统只会重新更新无效区域内的绘图。这里有两个利用点:1>用户请求重绘时,只请求重绘指定区域的,而不是整个区域,如Invalidate(Rect);2>在用户绘图代码Graphics g; g.DrawLine\g.DrawString\g.FillRectangle...前,先判断绘图的内容是否在无效区域,如果不是就不直接g.Draw...绘图代码。
3. 直接贴图。一般绘图或者重绘是Windows根据无效区域绘制的,如果在鼠标移动时需要重绘通过Windows系统处理Paint消息,有时满足不了要求,比如①鼠标移动绘制十字测量线就得用异或线而不是Paint消息,又比如②鼠标移动绘制跟随的信息提示框需要频繁擦除上次覆盖的背景,又比如③台球滚动时台球与球桌背景的关系。类似的这些问题如何解决?首先肯定不能利用Windows原来的绘图机制。其中一种解决方式是,不断的帧间变化区域贴内存位图——②中的信息框每次鼠标位置变化时可以重新g.Draw...或者贴早生成的信息框内存位图,②中被信息框覆盖的背景应该把本来的大背景截取此需要擦除区域的位置大小位图贴回来就是擦除背景了。由于每次大背景发生变化时,都应会重新生成大背景内存位图,所以可以是变化的背景。
这三种方式可以一起使用,应该可以解决中等的绘图项目的效率问题。中大型的绘图,必须记住两点1>只绘制电脑屏幕能显示的部分;2>只绘制变化的部分。