绪论
这篇文章将介绍可升级2d矢量图形api(scalable 2d vector graphics api),它是j2me的一个可选包。这些api非常易于使用,它们可以用来渲染和转换体积小和伸缩性好的二维(2d)图形,这个可选包在java社区进程(jcp)中以jsr-226开发。我将会给出api的总括,并且突出典型的用户案例,使用代码演示这些案例。
慨述
jsr-226将渲染和重放矢量2d图形引入到java语言的移动应用中。由nokia领导的专家组,是由移动行业的主要厂商组成,他们都很有兴趣将矢量多媒体内容引入到他们的设备中。在写这篇文章时,这个规范刚刚被核准,大家正在热切期待着一个参考实现。
矢量图对移动开发者来说,有两大好处:占用空间小和伸缩性好。考虑下面这个简单的图像:

图1 一个简单的示例图像
使用gif格式,这个图片的大小会达到7386字节。使用向量格式,大小仅仅为693字节,不到十分之一。这种减少是如何发生的呢?
以光栅为基础的图像格式,像gif,对组成图像的矩形区域中的每一个像素的色彩内容进行编码。而矢量图,仅仅包含决定像素应该如何被着色的绘画指令。一个图像的向量表示法可以更大程度上进行压缩,这对资源受限的移动设备是一个很大的有利因素。
伸缩性好是另外一个重要的优点。矢量图可以很清楚的进行变换,因为它们的绘画指令是和分辨率无关的。图2显示前一张图片进行等比例缩小、翻转和旋转后的图像。

图2 示例图片的变换
矢量图的度量单位是任意的和相对的,它们可以被一些常量因子乘或除,以变换图像从而适应特定设备的屏幕。通过对这个坐标系统使用简单的数学操作,你可以创造出无限的效果,包括翻转、旋转、拉伸和扭曲。使用矢量图像,应用程序开发者不再需要为图像和图标创建不同的集合,来支持不同的屏幕分辨率。
矢量图的简洁和伸缩性在动画领域大放异彩。以光栅为基础的动画,像网页中的gif和midp游戏中的精灵,在动画的每一个帧中包含整个图片都很费力。与之相比,以矢量为基础的动画仅仅包含一些指令,而这些指令规定了当需要改变动画时的图像哪些元素应该如何改变。如果矢量图的大小比它们相对应的光栅图小一个数量级,那么矢量动画的大小就比他们相对应的光栅动画小两个数量级,这使得它们成为在移动设备上变换和展示多媒体内容的理想选择。
当以私有的macromedia flash播放器为基础的矢量图和矢量动画变得流行时,可升级矢量图形(scalable vector graphics,svg)文件格式以开放的标准和免费的专利权,成为业界的另外一个选择。它由w3c标准化。svg-tiny提供了svg全部功能的一个子集,适合在移动设备上使用。jsr-226采用svg-tiny的1.1版本作为j2me矢量图形的官方文件格式。w3c称svg-tiny为一个“profile”,但是为了避免和j2me的profile混淆,我称之为“格式”。
svg-tiny标准也可以支持动画。图像文件内部的指令可以自己修改图像元素的位置和属性,来回应时钟事件和用户输入。
为了更加复杂的交互,jsr-226引入了兼容microdom的api,microdom是svg1.2完整的文档对象模型(dom)的一个子集。
svg是一种可扩展标记语言(xml)格式,它使用一个公共的schema充分的注释和系统化。svg-tiny可能很小,但是它们仍然是svg文件,仍然包含xml。下面是代表一个“brave world”图像的完整的svg-tiny文件:
<?xml version="1.0" standalone="no"?>
<!doctype svg public "-//w3c//dtd svg 1.1 tiny//en"
"http://www.w3.org/graphics/svg/1.1/dtd/svg11-tiny.dtd">
<svg preserveaspectratio="xmidymid meet"
viewbox="10 10 130 55" width="150.0" height="75.0">
<rect x="25" y="25" transform="translate(75 36.375)
rotate(15) translate(-70 -35.375)" fill="#00007e"
width="100" height="22.75" stroke="#000000" stroke-width=".5"/>
<rect x="25" y="25" fill="#ff9800" width="108.5"
height="22.75" stroke="#000000" stroke-width=".5"/>
<text x="30" y="40" fill="#00007e" stroke="#ffffff"
stroke-width=".33" xml:space="preserve">hello brave world.</text>
</svg>这个文档声明了两个矩形和一些文字。第一个矩形绕它的中心旋转15度,这一系列的转换都包含在rect标记的转换属性中。第二个矩形在第一个之上绘制,然后文本在第二个矩形之上绘制。
即时未经修饰的文本不是一个特别的简洁格式,这个文档仍旧比一个压缩的光栅图像小的多,并且易于人工导入。因为xml文本可以充分地压缩,所以svg在应用程序的jar文件中就占用相对较少的空间。jsr-226要求支持svg文档的实现使用gzip格式压缩。后缀名.svgz表示gzip压缩的文档。
mobile 2d api
jsr-226的一个主要的目的就是定义移动2d图形(mobile 2d graphics,m2g)api,这个api是一小组和svg-tiny格式功能结合很紧密的类。它没有被规定为普通的2d图形绘制工具箱,就像j2se的graphics2d类提供的那样。m2g api很清楚地定位于播放和在运行期操纵svg内容。
m2g由高级类和低级类组成,高级类用来创建和渲染矢量图形,而低级类负责以dom树的部分来操纵一个矢量图形的xml成分。矢量图形是scalableimage的实例,你可以通过这个的静态createimage()方法获得它们。你使用scalablegraphics的一个实例将scalableimages绘制到一个midp图形上下文中。svgimage是scalableimage的一个子类,它提供事件处理和底层dom文档访问之间的联系。
为了使渲染动画svg内容的普通情况更加方便,这个api提供了svganimator类。对midp应用程序,svganimator创建和控制一个canvas对象,这个对象自动处理屏幕更新,以回应动画事件和对这个图像的计划性的修改。svganimator提供一个类似播放器接口来控制动画的播放。
这些类都定义在javax.miroedition.m2g和org.w3c.dom.svg包中,在表1中进行了总结,并且在jsr-226规范中也作了充分的注释。svg dom是在标准的org.w3c.domorg.w3c.dom.events包中定义dom类和接口的扩展。
| class或interface | 描述 |
| javax.microediton.m2g | |
| scalablegraphics | 2d渲染的基础类 |
| svganimator | 这个类对一个目标用户接口组件,处理svgimage的更新和动画的自动渲染 |
| svgeventlistener | 这个接口用来处理一个应用程序的平台相关事件 |
| svgimage | 这个类代表符合w3c svg tiny1.1 profile的svg图像 |
| externalresourcehandler | 这个接口用来同步加载装入svg内容所需要的外部资源 |
| org.w3c.dom.svg | |
| svganimatorelement | 这个接口代表一个动画元素,包含控制动画时间的方法 |
| svgelement | 这个接口代表文档树中一个svg元素 |
| svglocatableelement | 这个接口代表一个可绘制的svg元素,典型的有形状、图像或者文本 |
| svgmatrix | 这个接口代表一个svg matrix数据类型,由一个仿射矩阵定义,等价于一个平移的线性变换 |
| svgpath | 这个接口代表一个svg path数据类型,用来定义几何路径 |
| svgpoint | 这个接口代表一个svg point数据类型,由它的x和y标示 |
| svgrect | 这个接口代表svg rectangle数据类型,由最小x、最小y、宽度和高度组成 |
| svgrgbcolor | 这个接口代表svg rgb color数据类型,由红、绿、蓝组成 |
| svgsvgelement | 这个接口代表在svg文档树中的一个元素 |
为了创建一个m2g应用程序,你需要可以工作的jsr-226实现。在写这篇文章时,这个规范才刚刚被定案;没有设备实现它,并且没有官方的参考实现可用。你可以下载一个有限的、实现的实现,作为mpowerplayer开发者工具箱的附加包。
你还需要一些svg-tiny内容。这篇文章是用上面的“brave world”图像,但是更多的svg-tiny示例可以从移动开发工具制造者tinyline上获得。你也可以使用一个商用java应用程序创建自己的内容,这个应用程序被称为sketsa,它可以像一个插图工具一样工作,并且使用svg作为它的文件格式。
只有很少的方法用来将你的svg内容绘制到屏幕上。最简单的是为你的图像创建一个svganimator,然后把它canvas放到屏幕上。另外一个方法是创建一个scalableimage的实例,然后使用scalablegraphics的实例将它绘制到你自己的canvas或者customitem的图形上下文中。你将会获得除了展现外的更多控制,但是你必须负责处理输入事件,并且为动画效果或者用户交互进行重绘。
使用一个svganimator显示2d图像
你可以通过svganimator的静态方法createanimator()获得一个它的实例,需要提供你自己的图像。使用svganimator是一个不错的选择,因为它处理所有的用户交互,和动态内容的动画。play()、pause()、stop()和settimeincrement()方法会给你除了重放之外的对动画的可编程控制。实现可能提供一个本地用户接口,可以同样的处理动态行为。
一旦你获得svganimator,就可以很容易的将它嵌入到你的应用程序中。调用gettargetcomponent()会一个canvas实例,它可以被放置在屏幕上。下面是一个例子:
public void animationtest()
{
// create an animator to load the content
svganimator animator =
svganimator.createanimator( image );
// add our custom event listener
animator.seteventlistener(
new customeventlistener( animator ) );
// get the canvas for this player; requires a cast
canvas canvas = (canvas) animator.gettargetcomponent();
// add a "back" command
canvas.addcommand( backcommand );
canvas.setcommandlistener( this );
// show it
display.getdisplay(this).setcurrent( canvas );
// start it
animator.play();
}
在midp环境中,从gettargetcomponent()返回的值是一个midp canvas。在非midp环境中,返回值将适合本地窗口系统,例如在awt环境中,返回值是component。如果当前环境拥有一个以上的可用的选择,那么就提供需要的类型的名字作为createanimator()的第二个参数。
图3显示了运行时结果。根据不同的实现,和笔触设备的可用性,动画绘制器可能允许用户和动画内容进行交互。在“brave world”例子中没有动态的内容,但是文本是可选的。

图3 由svganimator控制的canvas
和动画绘制器交互
尽管“brave world”图像不是一个动画,一些程序设计还是可以增加交互的。
jsr-226的dom支持一个程序改变一个图像的结构。和任何其他的xml文档一样,一个svgimage可以用一个dom对象树代表。m2g api允许你使用类似熟悉的xml处理技术来修改图像。树中的每一个节点是一个svgelement,提供操纵临近的父节点和子节点的方法。你可以通过添加和删除子节点修改节点,或者转换成一个合适的元素类,然后调用那个类的方法进行改变。
svgeventlistener结构使你的应用程序可以从svganimator的canvas接收用户的输入事件。这个例子是用dom api来等比例变化和旋转这个图像,用户可以通过箭头键输入:{
int gameaction = canvas.getgameaction( keycode );
switch ( gameaction )
{
case canvas.up:
scale( 0.10f );
break;
case canvas.left:
rotate( -10.0f );
break;
case canvas.down:
scale( -0.10f );
break;
case canvas.right:
rotate( 10 );
break;
default:
// leave unchanged
}
}
private void rotate( final float delta )
{
// put ourselves on the animator's thread
animator.invokelater( new runnable()
{
public void run()
{
// execute the transformation
document document = svgimage.getdocument();
svgsvgelement root = (svgsvgelement)
document.getdocumentelement();
root.setcurrentrotate( root.getcurrentrotate() + delta );
}
} );
}
private void scale( final float delta )
{
// put ourselves on the animator's thread
animator.invokelater( new runnable()
{
public void run()
{
// execute the transformation
document document = svgimage.getdocument();
svgsvgelement root = (svgsvgelement)
document.getdocumentelement();
root.setcurrentscale( root.getcurrentscale() + delta );
}
} );
}
svgimage.getdocument()提供了动画绘制器渲染的文档的引用。因为这个播放器可能正在运行一个或者更新一个动态的内容,所以你必须小心地将你的变化和动画线程同步,否则将会发生不可确定的错误。svganimator's invokeandwait() 和invokelater()方法就像它们的awt副本一样,可以保证你的代码在动画线程上运行。
如果你想除了你的svg图像在哪里和怎样被渲染外获得完全的控制,你可以使用scalablegraphics类直接绘制一个scalableimage到graphics对象之上,这个graphics对象被传递到canvas、layer或者customitem实例的paint()方法中。
你通过调用scalablegraphics的静态createinstance()方法创建一个它的实例。在绘制之前,你必须首先使用bindtarget()方法将scalablegraphics实例和传递到绘制方法中的graphics对象绑定。在完成以后,请调用releasetarget()。一旦建立绑定,你就可以使用render()方法绘制你的每一个图像。
这个例子以不同的大小重复绘制“brave world”图像,例子通过在每一次调用render()方法前调用setviewportheight()和setviewportwidth()方法改变图像大小:
private static class m2gcanvas extends canvas
{
// retain a reference the specified image
scalableimage scalableimage;
// retain an instance of a scalable graphics
scalablegraphics scalablegraphics;
public m2gcanvas( scalableimage inimage )
{
scalableimage = inimage; // create the scalable graphics instance
scalablegraphics = scalablegraphics.createinstance();
}
public void paint( graphics g )
{
// clear the display
g.setcolor( 255, 255, 255 );
g.fillrect( 0, 0, getwidth(), getheight() );
// bind our scalable graphics to the given graphics
scalablegraphics.bindtarget( g );
// render at fixed position and size
scalableimage.setviewportwidth( 50 );
scalableimage.setviewportheight( 75 );
scalablegraphics.render( 5, 50, scalableimage );
// again at different position and size
scalableimage.setviewportwidth( 100 );
scalableimage.setviewportheight( 150 );
scalablegraphics.render( 80, 5, scalableimage );
// again at size that varies with the canvas size
scalableimage.setviewportwidth( getwidth()-20 );
scalableimage.setviewportheight( getheight()-20 );
scalablegraphics.render( 0, 0, scalableimage );
// release the graphics context
scalablegraphics.releasetarget();
}
}
canvas的子类在不同的位置以不同的大小显示这个图像三次。在第三次,图像填满了这个画布。在支持可重设屏幕大小的设备上,图像可以适当的等比例变化。图4显示了画布的效果。在这个实现中,较大的图像在窗口大小被重置时等比例得到。
图4 在一个canvas上等比例变换scalableimages
如果你正在渲染的图像是动态的,你应该允许用户和他们交互。当你不用svganimator渲染时,你需要检测用户指针事件,得到图像是否和在何处被点击,然后在svgimage上调用dispatchmouseevent()方法,触发脚本定义好的动作。对一个customitem,表单移动应该允许激活和焦点导航,通过图像中的元素,分别使用svgimage的activate() 和focuson()方法。
你的程序不仅仅可以操纵一个已经存在的svgimage的dom,也可以从头建立一个图像。svgimage的静态方法createemptyimage()返回一个具有空文档的图像,这个文档的架构可以有程序创建的代表形状和变换的svgelement填充。这种可以和scalablegraphics联合使用,实现一个普通的2d绘制工具箱,但是正像我前面提到的那样,这样用法不是最初的意向,不推荐使用。
总结
j2me的可升级2d矢量图形api提供了渲染和操作以矢量为基础的图像和动画的能力。矢量图像的占用空间小和伸缩性好使得它天生的适合移动应用程序。jsr-226的移动2d图形api使应用程序能够使用midp和awt工具箱创建、显示和修改svg内容。
闽公网安备 35060202000074号