用 GDI+ 开发矢量图形编辑系统的经验之谈
因为一个项目(包装盒型矢量图形编辑器),在经过对GDI、GDI+、OpenGL、DirectX以及项目的实际需求综合评估之后,我决定选择 GDI+ (GDI Plus) 作为图形开发的工具,因为以前从来没有深入涉及过图形的开发,项目开始的时候有点无从下手,经过一段时间的摸索,终于有了一点眉目,于是就开始了下面各个方面的工作:
1. GDI+如何擦除图形以及其速度测试。
在网上看到很多文章,决大部分图形编辑器使用的都是C++之类的开发工具,然后直接调用GDI Windows API进行操作,图形绘制的时候多数采用异或的方法,这样速度很快,但是绘图时有闪烁,也有采用双缓存的方式,避免图形闪烁,但是速度稍微慢,占内存,而GDI+不支持异或绘图,在微软有关技术文档中,他们的理由是:硬件愈来愈高级,速度不成为问题,而视觉享受是第一位的,以后的图形编辑器都会采用双缓存,这是一个趋势。
1)从CodeProject等其他源码网站上搜索了很多源代码如何通过双缓存结合鼠标的操作进行图像的擦除,经过无数次的试验,终于形成自己的代码风格。大概原理如下:在内存中声明两个Bitmap,一个Bitmap用来进行及时的图形绘制,一个Bitmap用来进行存放已有的图形,在用鼠标绘图的过程中,直接把Bitmap拷贝到绘图窗口所对应的Graphics上,这样能提升绘图的速度。
2)速度测试就比较麻烦了,前后经过很多次,贯穿了整个开发过程,在我印象中因为速度问题引起的对象建模的基础修改次数不下5次,在图形编辑器中,速度不仅仅取决与GDI+还是GDI,更重要的是开发人员结合你所用的开发工具选择合适的算法。算法大概要求,尽量避免用三角函数、平方根、除法;尽量用整数、加法、乘法。
在开发中还发现一个有趣的现象,就是如果大量使用Structure也会导致数据赋值速度变慢,而Class则能在一定程度上提升数据赋值速度。但是看到.NET2.0中的提供的Point, Rectangle等都是结构,为什么微软会这样做呢?后来插到有关的资料(一个曾经参与.NET开发,后来离开微软,并攻击.NET的人),微软的.NET有很多对象简单的API的封装,而且这些.NET对象的实现也是用C++来做的,就是说这些工具可以调用指针,而我所用的VB.NET不能操作指针,所以导致用Structure速度变慢。这个发现也导致了我的许多工作推倒重来。
2. 对象建模。
这个很麻烦,一直到现在还在不断修改对象模型,因为没有经验的原因,走了很多弯路。曾经参考过一个控件VectorDraw,其帮助里面有完整的对象说明,图元、坐标系、标注等,看了以后很受启发,经过不断的摸索,也终于形成了自己的对象风格,现在这个Object Model可以很好地处理图形的结构(文件、图层、图元)、鼠标操作、图形参数化、零件、显示和操作的速度。
1)图元确定。一般情况下基本图元包含直线、矩形、(椭)圆、(椭)圆弧、曲线、标注(作为设计器一定要标注)
2)图元参数的保存和处理。经过若干次的测试,发现最终直接把象素值保存在图元的参数中比较方便,但是像.NET2.0中的Rectangle,Point,Line等图元只支持Single类型,而在实际操作过程中,比如缩放、旋转、坐标系变换后,Single数据类型的还原性很差,必须用Double才能处理,所以我把所有图元参数全部用Double类型处理,虽然对计算性能有影响,但总的速度还可以接收。
3)坐标系。GDI+中的设备坐标系和我们正常使用的笛卡儿坐标系有点不一样,为了方便用户在绘图时的操作,我们必须在界面上给用户显示笛卡儿坐标系,而在实际绘图时使用设备坐标系。坐标系对象也负责对这两个坐标系的数据进行一对一的转换。
4)图像拾取。不同的图元,有不同最优的拾取算法,比如线段,怎么判断鼠标线段上,这个很有技巧性,算法慢的和快的差几十倍,后来选择了一般常用的矢量计算方法,据说1秒可以判断100万条直线,这个很牛,以此类推,很快解决了各个图元的拾取算法。
《未完待续》