很久前就听一大牛说起Agg,据说是一个架构极度牛B的2D引擎,沉寂了许久,最后花了两周时间走马观花地把它过了一遍。果然如那大牛所言,这家伙简直就是巧夺天工的艺术品。今天稍稍瞄了一下Google扔出来的,也宣称极度牛B的2D引擎Skia,不过个人感觉整体架构大不如Agg漂亮。至于两者的性能,没做过比较,只是听说skia性能表现优异。
在啃这块骨头之前,有两点建议:
-
稍了解一点2D计算机图形学的知识。这一点在学习Agg的过程中至关重要。
-
至少尝试使用过一种UI框架,诸如Wtl、Mfc等。
很多人曾说Agg将C++模板用到了炉火纯青的地步,不过就我现在的认知,发现其用法也就正规正矩而已,没用像Boost那样到处是奇技淫巧,让人眼花缭乱,找不到北。也许我功底还不行,无法体会高人的用意。
学习代码最好的办法莫过于编译,看运行效果。下载源码见这里【http://www.antigrain.com/download/index.html】。Agg源码包中并没有对Virtual Studio的直接支持,不过建一个Project,然后将文件拖到里面编译即可(为了保证可移植性,Agg对标准的支持还是很得力的)。在源码包中的examples目录有很多sample,可以另建Project编译运行看效果。如果想偷懒,可以去这里【http://www.antigrain.com/demo/index.html 】下载编译好的可执行文件先睹为快。我围观了下,效果很“炫”。
对于开源的东西,找学习资料总是值得先去官网瞄瞄。Agg官网的Reference 不多,不过强烈建议看下这里【http://www.antigrain.com/doc/index.html 】。如果不喜欢看E文,国内有人翻译(感谢之),质量还不错,见这里【http://www.cnblogs.com/liyiwen/category/248229.html 】。认真看完第一篇【Basic Renderers】很重要,这篇文章对Agg的底层架构做了清晰的讲述,包括RenderBuffer和底层Render,以及两者的职责分离。这种设计相当好,因为RenderBuffer并不应该知道上层使用何种颜色模式。
为了对Agg有一个总体的了解(非技术层次),可以参考这里【http://www.cnblogs.com/Kane_zzt/archive/2009/02/15/1271793.html 】。下面这张图是Agg总体框架的一个总体表述
最后一项Screen Output可以不理会,那是OS的事。Agg处理的最底层是Rendering Buffer 。说白了Rendering Buffer就是一块内存块,类似Bitmap。当然这也不一定,它也可以是通过OS API获得窗口屏幕的映射地址(Windows下可以通过GDI函数获得),或者是FrameBuffer,这样整个屏幕就可以任意虐,如果没猜错的话,Android这鸟毛就这么干的。
在讲Renderers之前,有必要区分两个概念,图形和图像。简单地讲,图形就是一些点线组成的形状,可以通过LineTo和MoveTo原语构造。而图像一般包含形状和具体的颜色信息。而Readerers【渲染器】就是将图形信息渲染成图像。
渲染器层实际上包含三层:
-
颜色模式(例如RGBA)相关的底层渲染层,这一层直接跟Rendering Buffer日。在这一层,每一个像素的大小、颜色的表示被确定。
-
Basic Render层,这一层在底层的基础上进行简单的裁剪支持。包括单裁剪和多区域裁剪。多区域裁剪性能和区域数量相关。
-
应用渲染层,这一层负责渲染具体的线段。在后面会讲到,在Agg中任何一个多边形在光栅化后都会分解成一条条水平线(span),然后依次渲染。
Scanline Rasterizer就是将任意多边形(顶点源)分解成水平线,然后交给顶层渲染器渲染。在这一层也可以进行适当的裁剪。反锯齿功能在这一层引入,Agg中的像素存储使用浮点型,小数部分即传说中的亚像素。
Coordinate conversion pipeline对顶点源进行坐标转换,我们熟悉的操作如放大/缩小、旋转等操作都在这一层完成,这部分代码对数学基础和算法要求较高,我直接死不鸟跳过。在反走样插值功能在这一层引入。
Vertex Source提供光栅化的多边形,它通过一个迭代器来传递顶点。顶点源包括普通的图形,例如矩形、椭圆、三角形,也包括字体等复杂多边形。在Linux下被广泛应用的Freetype库就是根据字符编码、其他字符参数来生成一个字体多边形。这在Agg中也被支持。
前面提到的都是一些很简单的矢量绘图操作,那么Agg对贴图、渐变色和字体引擎的支持怎么样呢?
关于Agg对贴图的支持,默认包含对bmp格式的支持,Agg源码内置了对bmp图片格式的解码代码,不过默认并不支持png和jpg等流行格式。我想作者可能是为了保持简单,不想引入libpng和libjpeg。Agg对所有图片格式除了解码部分外,处理是一致的。
前面一直讲到渲染,那何谓渲染呢,说白了就是将一个像素用颜色填充(或混合Blend)。而前面一直忽略了一个问题,就是渲染时用什么颜色来填充(准确的讲,并不是填充,而是混合(Blend))特定的像素呢?在Agg中其实也包含一个虚拟的“颜色源”,这个颜色源中的每一个点和“顶点源“中的点一一对应。可以有多种方式构造这个虚拟的颜色源,首先可以指定一种颜色,即纯色填充,其次可以通过一定算法来生成”渐变“效果,当然也可以从图片中获取。
说到图片,其实任何格式在处理前都必须转化成bitmap格式,所谓的libpng和libjpeg就是干这个事。而bitmap说到底就是一个矩形颜色源。也许有人会疑惑,某些图片不是支持不规则形状么,例如png图片格式。其实png并不支持不规则形状,而是支持Alpha通道。而2D引擎在渲染时通过Alpha Blend后某些地方就成了透明色,用户看起来就是不规则形状了。
说到这里,Agg对贴图的支持就很简单了,从贴图资源中获得颜色源,然后使用它渲染特定多边形(裁剪在这个多边形中完成)。而对图片的放大/缩小、旋转等过程类似Coordinate conversion pipeline对顶点源的操作。
Agg对渐变的支持和贴图一致。
Agg内置对英文字符的支持,我没有仔细研究源码,不过可以推测它将所有英文字符的路径存储在一个全局(静态)变量中,渲染前直接通过这些路径生成顶点,因而效率极高。不过这种方式有几个不足:
-
不支持未存储的字符,很显然不要靠他来支持”我日“。
-
不支持字体,因为字体路径表述都是写死的,除非修改源代码。
更高级的字体支持来自字体引擎,Agg支持Windows Gdi的字体接口,也可以使用第三方的字体引擎,例如Freetype。Agg可以通过字体引擎提供的字模来包装一个顶点源,因而可以将字体渲染成任意效果。此外Agg也可以对字体引擎进一步封装,实现一个类似贴图的接口,然后渲染多边形,不过个人觉得这种方式很别扭,而且渲染时不太灵活。暂时没有测试两种方式的效率差别。
最后,因本人水平有限,错误之处在所难免,欢迎指正。