北京理工大学 20981 陈罡
上一篇讲到目前可以很容易就找到的支持svg显示的几个库,像librsvg这个比较成熟的svg渲染库,
移植起来肯定难度不小,而且其本身是针对linux环境做的,对于手机或者其它嵌入式平台而言,
它本身认为理所当然系统应该具有的lib,在实际的环境中往往没有。这就更加增大了移植的难度。
偶个人认为最好从最直接能够解决问题的库入手,于是就选中了libsvg这个库。以下的全部工作
都是围绕着libsvg这个库的编译、使用展开的。而且,很不幸,libsvg库也是基于librsvg的一个
移植,因此,它也是要依赖于其它的lib的。好在,依赖的不多,主要有libexpat或者libxml2,
以及libpng, libjpeg, zlib。
大概的浏览了以下libsvg的代码,普通的svg格式肯定都没有问题(这一点明显要强于svgt和svgb)。
从svg_image.c来看,这个版本的libsvg是假定svg中的图片都是“引用”得svg文件外部的图片的,
目前可以支持的格式为jpeg和png。偶看到较新的svg文件一般都可以将jpeg或png用base64编码,
做为svg文件中的内嵌的图像数据,这样显然要比外部引用要好一些,至少一个svg把自己需要的资源
全都“自包含”了。当然可能很多人会有不同的意见,主要是svg的xlink可以引用远程的其它主机的
资源。这一点当然很好,不过目前我们的目标是在手机平台上能够运行,所以要尽量避免一下“远程”
这个概念,这会导致联网,以及糟糕的用户体验。
另外,在svg.h中,libsvg定义了一个硕大的结构体:
typedef struct svg_render_engine
这个所谓的svg_render_engine就是一个包含着多个指向函数的指针的结构体,由于整个libsvg
是采用纯c语言编写的,因此,想要实现“OO”编程不得不借助大量的包含指向函数的指针的结构体。
这里的svg_render_engine就充当了这样一个角色,结构体中的每个指向函数的指针就相当于c++
语言中的“纯虚函数”。整个libsvg就是利用这些“纯虚函数”完成所有的svg数据操作和显示的,当然
前提是在我们使用这个lib的时候,已经自己实现了这些“纯虚函数”。整个libsvg其实就是一个svg
文件的“渲染引擎”,它留出来了绘图函数、以及坐标变换的接口函数(就是这个svg_render_engine),
等着libsvg库的调用者来实现具体的操作函数。我想,这就是libsvg的核心思想了,让“渲染引擎”与
具体的实现的绘图、坐标变换等平台相关函数分开,做到平台独立。这样就可以很方便地将libsvg
移植到各种嵌入式或者pc的平台上面去了。
libsvg的作者的思路是很好的,这一点相比librsvg而言已经进步了一大步,但是作者还需要考虑一点
的是——大多数开发者还是很难看明白他的良苦用心,毕竟是30多个函数啊,而且里面还有各种矩阵变换,
图形缩放、坐标变换平移、字体操作等等。这些东西实现起来并非易事。单就xml解析器而言,就很麻烦,
幸好现在最新版本的libsvg已经可以同时支持libexpat和libxml2两个库了。对于symbian手机平台而言,
已经有国外的大侠们移植的syexpat这个库了。相对还是比较容易一些,于是就采用libexpat做为libsvg
库的xml解析器了(syexpat是将expat库移植到symbian上之后,又用c++给expat api做了一个wrapper,
这个wrapper虽然很好用,但是libsvg是采用没有wrapper的expat api的,呵呵,所以需要手工把syexpat
修改一下,让它支持如XML_Parser,XML_SetStartElementHandler之类的调用方式)。
移植的时候可以先关注svg_parser.c和svg_parser_expat.c这两个文件。偶个人建议,如果不打算让移植
的库支持不同的xml解析器的话,最好把这两个文件给合并起来,成为一个文件。这样会免去编译时候的
很多问题。然后就是关于zlib、libpng、libjpeg这几个了,从代码上来看,libsvg还是使用了zlib的api
函数的,主要是在svg_parse_file, svg_parse,这里需要说明一点的是,zlib中有几个关于打开文件的
api,叫做gzopen()和gzdopen(),这里面还有点让人郁闷的东西,就是gzdopen()这个函数。在linux
环境中,是可以通过dup()函数来复制一个文件的句柄id的,然后通过文件的id号(或句柄号)来打开这个
文件,但是在手机或者嵌入式平台,这一招就有可能不适用。因此,还是建议将gzdopen都换成gzopen()
利用传入的文件名来打开文件。
好了,有了这些再加上心细一些。肯定能够成功地将libsvg移植到win32上去(有朋友可能会问,为什么要
移植到win32上?不是要移植到手机上吗?呵呵,主要原因是win32的调试和编译比较快,在移植的前期问题
多多的时候,先在win32上移植,效率要比直接上手机开发环境移植高得多,而且单步调试之类的工具也是
非常丰富)。这些移植好之后,就会发现,我们已经把libsvg这个渲染引擎移植到pc上了(偶目前也成功地
将其移植到手机上了,效果很令人满意),但还不能够做任何事情——我们还没有实现svg_render_engine中
定义的那些“纯虚函数”(其实就是指向函数的指针了,那些指针所指向的函数是需要我们自己去实现的)。
大概看了一下,简单的move_to,line_to之类的简直轻而易举。但复杂一些的,如文字类型、大小、字体处理
坐标变换之类的,实现起来就很麻烦。于是就有了在《让s60支持svg显示吧-(1)》文章最后,展示的那张
图片,只实现了move_to,line_to函数的libsvg渲染引擎的效果图——只用线条绘制出来的svg图形。但是这样
的效果是远远不够的。
为了达到perfect的效果,我们有必要继续移植一个“完全”的libsvg渲染引擎的实现函数,于是目光转向了
libsvg-cairo这个库。该库是基于libcairo 2d绘图库实现的libsvg引擎的应用库。值得一提的是,该库的
作者就是libsvg的作者,呵呵,想必作者实现了libsvg库以后,一时技痒就依赖于cairo把libsvg的调用库
也顺手写出来了。这对我们来说可是一个天大的好消息,毕竟不用自己去处理那么复杂的坐标变换之类的移植
工作了。libsvg-cairo本身也就五、六个文件而已,移植起来易如反掌,在此不再赘述了。
移植完libsvg和libsvg-cairo以后,随之而来的是cairo这个二维绘图库的问题(当时偶是欲哭无泪)。可以
肯定,手机上是绝对不会有cairo这个库的,如果想在手机上使用libsvg-cairo,就必须要自己动手去移植
cairo库。于是乎,新的一轮dig又开始了。很快就会发现,这个cairo号称支持xlib,pdf,png,svg,win32,xcb多种平台和格式的文件输出,又是一个“大块头”。
这时候,有两条路摆在面前:
(1)放弃移植libcairo,转而仔细阅读libsvg-cairo的实现代码,自己实现libsvg的svg_render_engine。
(2)咬牙硬啃libcairo,将其不必要的win32, xlib等等相关的代码去掉,只把运算和内存渲染图片部分代码
保留并移植出来。
偶是个懒人,不喜欢动脑筋(没办法,睡觉要紧啊!),所以决定采用方案(2)。很快在cairo的关于内存
图片操作的函数中(主要是cairo_image_surface_xxxxx系列函数)发现cairo竟然大量使用了pixman这个库,
shit,又多了一个库,意味着又多了工作量。。。偶怎么这么命苦啊。。。于是转过头来看pixman这个库都有
啥特殊的功能,不看还好,一看就看到了很多诸如mmx之类的让人头大的东西。这些东西在win32和linux等基于
pc硬件的系统中都是可以大大加快处理速度的,但是对于嵌入式设备来说,多数arm芯片都不支持这些东西。
越靠近底层,越为移植制造麻烦。看了一下libcairo的log,里面说pixman原本是属于libcairo的一个“工具”类
的共享库,后来越做越完善,最终独立出去成为一套底层面向pixel,pixbuf,这个级别的图像操作库。呵呵,
换句话来说,早期的libcairo应该包含了这个pixman的库才对。赶紧回头去看看libsvg-cairo库的readme,发现
libsvg-cairo是track到libcairo 0.5.0的,也就是说0.5.0这么低的版本的cairo都可以完成libsvg-cairo的
绘图要求。呵呵,心中暗喜。然后马上找libcairo的低版本的代码,鉴于sourceforge还无法正常访问,所以
只好找到哪个算哪个了。
于是,找到了libcairo 1.0.0虽然版本高了一点,但毕竟也是相对最新的1.6.x而言
还是比较“老”的。打开一看,果然,pixman是直接包含在libcairo的库代码里面的。
好了,这下可以放心一些了,只要把pixman关于pixman_image_xxx的函数提取出来移植即可。这里需要说明
的是在pixman的代码目录里面有一个叫做pixman_remap.h的函数,天知道这东西是干嘛使的,把大多数的
pixman_xxxxx的函数都改名为_cairo_xxxxx。可能对于这个版本的作者来说,他自己实现了一组底层操作函数,
在没有确定pixman库完全可用之前,尽管在代码中写着是调用了pixman的库,但实际上还是调用自己的cairo
库中的函数,可能为以后移植或者将pixman全面引入创造条件吧。总之,这个remap,偶是不需要的,直接剔除
之,然后就是cairo的移植了。可以先加入少量文件,然后根据编译的错误逐渐添加缺失的文件。去掉字体相关
的代码和engine(这是为了尽量少跟freetype之类的打交道,要不又要增加工作量了,不过对于字体的处理,逃
估计是逃不过的,早晚要面对)。
呵呵,初次编译会有1800多个error,只要耐心一些都能够一一解决。
这里值得特别注意的是cairo的surface大爷的竟然不提供诸如surface->data或者cairo_image_surface_get_data()
之类的函数,那样的话,即使svg_cairo_render()函数将svg图像画入surface以后,也弄不出来,不能一个像素
一个像素的绘制到屏幕上去。于是,这部分需要自己动手,把surface中的二进制数据提取出来,然后进行绘制,
这一步操作可以通过跟踪cairo-png.c的实现过程得到想要的二进制数据。
最后就是久违了的成功!!
抓图秀一下结果,呵呵,熟悉svg的朋友一定对这个经典的老虎记忆犹新吧:
有了这些经验,将其在手机上跑起来,只是一个时间上的问题而已。
困了,睡了。。。
阅读(4405) | 评论(0) | 转发(0) |