作者: Jonathan Knudsen
时间: July 2003
原文出处: http://developers.sun.com/techtopics/mobility/midp/articles/picture/
源码下载:
http://developers.sun.com/techtopics/mobility/midp/articles/picture/src/Snapper.zip
译者注:因为该文成文时间很早,而J2ME技术发展很快,文中提到的 开发工具和运行测试环境现在都有较大变动,请读者注意。
构建MMAPI应用程序的第一个难题是找到正确的 开发工具.J2ME Wireless Toolkit 2.0 支持MIDP2.0,MMAPI 1.0,WMA1.1.乍一看,它包括了你所要的一切.但是Nokia 3650只能运行MIDP1.0,不能运行 MIDP2.0.为了开发MIDP1.0的应用程序,你需要J2ME Wireless Toolkit的1.0.4版,外带一个支持 MMAPI的模拟 器。
Sun提供这样一个模拟器,你可以在MMAPI的主页上找到( http://java.sun.com/products/mmapi/index.jsp).尽管你可以利用该模拟器构建MMAPI应用程序,但是它不支持拍照,因此你不能用它来测试拍照的应用程序。
Nokia有一个支持拍照的模拟器,它是Nokia Series 60 MIDP Concept SDK Beta 0.2的一个组成部分.你可以在Nokia的开发者论坛( http://www.forum.nokia.com)上找到.文件的名字是nS60_jme_concept_sdk_b0_2_014.zip,Linux开发者应该是用2.0版的Nokia Developer's Suite for J2ME( http://www.forum.nokia.com/nds_for_j2me.html),下面的安装指南只适用于windows环境。
首先,在<J2ME Wireless Toolkit>/wtklib/devices目录下安装Nokia SDK(把 Nokia_Series_60_MIDP_Concept_SDK_Beta_0_2的安装目录拷贝到这个目录亦可).接下来,运行 J2ME Wireless Toolkit,你就有一个额外的模拟器-- Nokia_Series_60_MIDP_Concept_SDK_Beta_0_2可用了。选择该模拟器后,你就可以构建和测试你的MMAPI应用程 序了。该模拟器和真实的Nokia 3650看起来非常相似。
没有什么能替代真机测试。从长远观点来看,将会有很多设备支持MMAPI,但是 Nokia 3650是目前唯一支持MMAPI的设备.有好几种方式可以在Nokia 3650真机上发布MIDlet.我使用红外接口在我的笔记本和手 机上传输文件。你也可以使用蓝牙和OTA.
MIDlet中拍照(正式的说法叫视频捕捉)的第一步是从Manager获取一个Player。一个特殊的定位符:capture://video,指示照相机使用缺省的图片大小来拍照。
mPlayer = Manager.createPlayer("capture://video");
如果设备不支持视频捕捉,这将会抛出一个MediaException。你可以预先检查一下设备是否支持拍照,如果系统属性supports.video.capture的值为真,那么该设备就支持照相功能。
为了获取拍照所需资源,Player需要被realize.如果你没有学过或者忘记了realized是什么意思,请参考<Mobile Media API Overiew>( http://developers.sun.com/techtopics/mobility/apis/articles/mmapi_overview/)或者J2ME Mobile Media API.
mPlayer.realize();
来源于照相机的视频可以作为一个Item添加到Form上,也可以作为Canvas的一部分。一个VideoControl可以让你实现这个功能,你可以采用如下语句获取VideoControl.
mVideoControl = (VideoControl)
mPlayer.getControl("VideoControl");
如果你希望在Canvas里面显示来源于相机的视频,初始化VideoControl,然后在Canvas里面设置视频的大小和位置,最后使视频可见。下 面的例子(一个Canvas字类的构造函数)显示了如何把视频摆放在离Canvas上下边框2个像素宽的位置。如果不能这样放置,构造函数试着使用整个屏 幕,最后,它调用setVisible()使视频可见.
public CameraCanvas(SnapperMIDlet midlet,
VideoControl videoControl) {
int width = getWidth();
int height = getHeight();
mSnapperMIDlet = midlet;
videoControl.initDisplayMode(
VideoControl.USE_DIRECT_VIDEO, this);
try {
videoControl.setDisplayLocation(2, 2);
videoControl.setDisplaySize(width - 4, height - 4);
}
catch (MediaException me) {
try { videoControl.setDisplayFullScreen(true); }
catch (MediaException me2) {}
}
videoControl.setVisible(true);
}
在Form里面显示视频稍微有所不同.我们不再用USE_DIRECT_VIDEO参数调用VideoControl的initDisplayMode(),而是代之以USE_GUI_PRIMITIVE参数。在MIDP设备上,你可以获得一个Item显示在Form上
Form form = new Form("Camera form");
Item item = (Item)mVideoControl.initDisplayMode(
GUIControl.USE_GUI_PRIMITIVE, null);
form.append(item);
一旦相机视频显示在设备上,拍照就很容易了.你所需要的只是调用一下VideoControl的getSnapshot ()方法。为了调用该方法,你需要传递一个图片类型参数,如果你使用null作参数,那么相机就会适用缺省的图片类型?PNG.通过获取用空格隔开的系统 属性video.snapshot.encodings的值,你可以预先获取相机支持的图片类型。
The getSnapshot ()方法返回一个字节数组,里面是按你所指定的图片格式的图片数据。接下来,你就可以随意行事了,你可以把这个字节数组存储在数据库里,也可以把它发到服 务器上去,或者依据这个字节数组构造一个Image对象,这样你就可以把刚才所拍照片显示在用户面前,如下例所示:
byte[] raw = mVideoControl.getSnapshot(null);
Image image = Image.createImage(raw, 0, raw.length);
在这篇文章中,我想要谈谈如何创建微缩图片,也就是使所拍的相片缩小化。这事看起来简单,其实却并不轻松。 MIDP2.0中包含一些方法,这些方法可以从Image对象中获取原始的像素数据,有可能实现图像的伸缩变换。不幸的是,MIDP1.0并不提供对像素 数据的直接访问。
从图像处理角度来看,我这里采用的解决办法既不优雅,也不严格正确。但是这种办法接近我所需要的效果,并且不涉及到手工解析 PNG格式。我创建了一个新的空白的图片作为微缩图,对于图片的每个像素,我设置一个围绕该像素的极小的剪切区。然后把原始图片绘制在一个适当的位置上, 这样就设置了微缩图的每一个像素.
private Image createThumbnail(Image image) {
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
int thumbWidth = 64;
int thumbHeight = -1;
if (thumbHeight == -1)
thumbHeight = thumbWidth * sourceHeight / sourceWidth;
Image thumb = Image.createImage(thumbWidth, thumbHeight);
Graphics g = thumb.getGraphics();
for (int y = 0; y < thumbHeight; y++) {
for (int x = 0; x < thumbWidth; x++) {
g.setClip(x, y, 1, 1);
int dx = x * sourceWidth / thumbWidth;
int dy = y * sourceHeight / thumbHeight;
g.drawImage(image, x - dx, y - dy,
Graphics.LEFT | Graphics.TOP);
}
}
Image immutableThumb = Image.createImage(thumb);
return immutableThumb;
}
从本文下载的源代码是一个叫做Snapper的程序。Snapper让你可以把所拍照片的所有微缩图都显示 在一个Form里.本文所引用的代码来源于Snapper.Snapper程序仅包含2个类。SnapperMIDlet提供应用逻辑和视图流程控制,与 绝大部分的MMAPI代码相似,需要创建Player,获取snapshot.CameraCanvas是一个Canvas的子类,它包含一个 VideoControl来显示相机视频。
Snapper能聪明的发现目标设备是否支持视频捕捉,如果你在Sun的MME模拟器上运行该程序,你会收到一个优雅的反馈信息:
在不支持视频捕捉的设备上运行Snapper
如果你使用Nokia的Concept模拟器,你将看到如下效果:
Snapper的主屏
选择Camera命令(Nokia机型把该命令放在Options菜单里),在一台真机上,你可以看到来源于摄像头的视频,模拟器只能显示单幅的静态图像。但是在这2种情况下,CameraCanvas都会在视频周围画上一个绿框。
Snapper的摄像机屏幕
要拍照的话,你只需选择Capture命令或者按开火键(位于方向键中心),Snapper将创建一个微缩图片并显示在MIDlet的主屏上。
放置了微缩图的Snapper主屏
Snapper是一个非常简单的例子,但是有很多有趣的地方你可以改进。
1.你可以把整个相片保存在数据库里,这样你可以在今后浏览。
2.你可以允许玩家删除相片,或者改变其次序
3.你可以把整个相片发送到一个服务器上存储起来,并且提供一个Web视图进行浏览。
4.使用MIDP2.0,你可以对图片做标准的变换处理:模糊,锐化,颜色反转,几何变换等等。
Jonathan Knudsen出了好几本书,包括《Wireless Java (second edition)》,《The Unofficial Guide to LEGO MINDSTORMS Robots》, 《Learning Java (second edition)》,《Java 2D Graphics》。Jonathan在Java 和 Lego robots方面广泛写作,他还为JavaWorld, EXE, NZZ Folio, and the O'Reilly Network写文章. Jonathan在普林斯顿大学获得了机械工程学位.
时间: July 2003
原文出处: http://developers.sun.com/techtopics/mobility/midp/articles/picture/
源码下载:
http://developers.sun.com/techtopics/mobility/midp/articles/picture/src/Snapper.zip
译者注:因为该文成文时间很早,而J2ME技术发展很快,文中提到的 开发工具和运行测试环境现在都有较大变动,请读者注意。
译文
概述
Mobile Media API(MMAPI)让MIDlet能够播放和录制音频和视频数据.但是设备的真实能力还是依赖于其具体实现。目前,支持MMAPI的唯一设备是Nokia 3650.本文描述了如何利用MMAPI来构建一个简单的拍照程序。开发MMAPI应用程序所用工具
构建MMAPI应用程序的第一个难题是找到正确的 开发工具.J2ME Wireless Toolkit 2.0 支持MIDP2.0,MMAPI 1.0,WMA1.1.乍一看,它包括了你所要的一切.但是Nokia 3650只能运行MIDP1.0,不能运行 MIDP2.0.为了开发MIDP1.0的应用程序,你需要J2ME Wireless Toolkit的1.0.4版,外带一个支持 MMAPI的模拟 器。
Sun提供这样一个模拟器,你可以在MMAPI的主页上找到( http://java.sun.com/products/mmapi/index.jsp).尽管你可以利用该模拟器构建MMAPI应用程序,但是它不支持拍照,因此你不能用它来测试拍照的应用程序。
Nokia有一个支持拍照的模拟器,它是Nokia Series 60 MIDP Concept SDK Beta 0.2的一个组成部分.你可以在Nokia的开发者论坛( http://www.forum.nokia.com)上找到.文件的名字是nS60_jme_concept_sdk_b0_2_014.zip,Linux开发者应该是用2.0版的Nokia Developer's Suite for J2ME( http://www.forum.nokia.com/nds_for_j2me.html),下面的安装指南只适用于windows环境。
首先,在<J2ME Wireless Toolkit>/wtklib/devices目录下安装Nokia SDK(把 Nokia_Series_60_MIDP_Concept_SDK_Beta_0_2的安装目录拷贝到这个目录亦可).接下来,运行 J2ME Wireless Toolkit,你就有一个额外的模拟器-- Nokia_Series_60_MIDP_Concept_SDK_Beta_0_2可用了。选择该模拟器后,你就可以构建和测试你的MMAPI应用程 序了。该模拟器和真实的Nokia 3650看起来非常相似。
MMAPI的测试设备
没有什么能替代真机测试。从长远观点来看,将会有很多设备支持MMAPI,但是 Nokia 3650是目前唯一支持MMAPI的设备.有好几种方式可以在Nokia 3650真机上发布MIDlet.我使用红外接口在我的笔记本和手 机上传输文件。你也可以使用蓝牙和OTA.
获取照相机
MIDlet中拍照(正式的说法叫视频捕捉)的第一步是从Manager获取一个Player。一个特殊的定位符:capture://video,指示照相机使用缺省的图片大小来拍照。
mPlayer = Manager.createPlayer("capture://video");
如果设备不支持视频捕捉,这将会抛出一个MediaException。你可以预先检查一下设备是否支持拍照,如果系统属性supports.video.capture的值为真,那么该设备就支持照相功能。
为了获取拍照所需资源,Player需要被realize.如果你没有学过或者忘记了realized是什么意思,请参考<Mobile Media API Overiew>( http://developers.sun.com/techtopics/mobility/apis/articles/mmapi_overview/)或者J2ME Mobile Media API.
mPlayer.realize();
显示相机视频
来源于照相机的视频可以作为一个Item添加到Form上,也可以作为Canvas的一部分。一个VideoControl可以让你实现这个功能,你可以采用如下语句获取VideoControl.
mVideoControl = (VideoControl)
mPlayer.getControl("VideoControl");
如果你希望在Canvas里面显示来源于相机的视频,初始化VideoControl,然后在Canvas里面设置视频的大小和位置,最后使视频可见。下 面的例子(一个Canvas字类的构造函数)显示了如何把视频摆放在离Canvas上下边框2个像素宽的位置。如果不能这样放置,构造函数试着使用整个屏 幕,最后,它调用setVisible()使视频可见.
public CameraCanvas(SnapperMIDlet midlet,
VideoControl videoControl) {
int width = getWidth();
int height = getHeight();
mSnapperMIDlet = midlet;
videoControl.initDisplayMode(
VideoControl.USE_DIRECT_VIDEO, this);
try {
videoControl.setDisplayLocation(2, 2);
videoControl.setDisplaySize(width - 4, height - 4);
}
catch (MediaException me) {
try { videoControl.setDisplayFullScreen(true); }
catch (MediaException me2) {}
}
videoControl.setVisible(true);
}
在Form里面显示视频稍微有所不同.我们不再用USE_DIRECT_VIDEO参数调用VideoControl的initDisplayMode(),而是代之以USE_GUI_PRIMITIVE参数。在MIDP设备上,你可以获得一个Item显示在Form上
Form form = new Form("Camera form");
Item item = (Item)mVideoControl.initDisplayMode(
GUIControl.USE_GUI_PRIMITIVE, null);
form.append(item);
拍照
一旦相机视频显示在设备上,拍照就很容易了.你所需要的只是调用一下VideoControl的getSnapshot ()方法。为了调用该方法,你需要传递一个图片类型参数,如果你使用null作参数,那么相机就会适用缺省的图片类型?PNG.通过获取用空格隔开的系统 属性video.snapshot.encodings的值,你可以预先获取相机支持的图片类型。
The getSnapshot ()方法返回一个字节数组,里面是按你所指定的图片格式的图片数据。接下来,你就可以随意行事了,你可以把这个字节数组存储在数据库里,也可以把它发到服 务器上去,或者依据这个字节数组构造一个Image对象,这样你就可以把刚才所拍照片显示在用户面前,如下例所示:
byte[] raw = mVideoControl.getSnapshot(null);
Image image = Image.createImage(raw, 0, raw.length);
创建微缩图
在这篇文章中,我想要谈谈如何创建微缩图片,也就是使所拍的相片缩小化。这事看起来简单,其实却并不轻松。 MIDP2.0中包含一些方法,这些方法可以从Image对象中获取原始的像素数据,有可能实现图像的伸缩变换。不幸的是,MIDP1.0并不提供对像素 数据的直接访问。
从图像处理角度来看,我这里采用的解决办法既不优雅,也不严格正确。但是这种办法接近我所需要的效果,并且不涉及到手工解析 PNG格式。我创建了一个新的空白的图片作为微缩图,对于图片的每个像素,我设置一个围绕该像素的极小的剪切区。然后把原始图片绘制在一个适当的位置上, 这样就设置了微缩图的每一个像素.
private Image createThumbnail(Image image) {
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
int thumbWidth = 64;
int thumbHeight = -1;
if (thumbHeight == -1)
thumbHeight = thumbWidth * sourceHeight / sourceWidth;
Image thumb = Image.createImage(thumbWidth, thumbHeight);
Graphics g = thumb.getGraphics();
for (int y = 0; y < thumbHeight; y++) {
for (int x = 0; x < thumbWidth; x++) {
g.setClip(x, y, 1, 1);
int dx = x * sourceWidth / thumbWidth;
int dy = y * sourceHeight / thumbHeight;
g.drawImage(image, x - dx, y - dy,
Graphics.LEFT | Graphics.TOP);
}
}
Image immutableThumb = Image.createImage(thumb);
return immutableThumb;
}
Snapper例子
从本文下载的源代码是一个叫做Snapper的程序。Snapper让你可以把所拍照片的所有微缩图都显示 在一个Form里.本文所引用的代码来源于Snapper.Snapper程序仅包含2个类。SnapperMIDlet提供应用逻辑和视图流程控制,与 绝大部分的MMAPI代码相似,需要创建Player,获取snapshot.CameraCanvas是一个Canvas的子类,它包含一个 VideoControl来显示相机视频。
Snapper能聪明的发现目标设备是否支持视频捕捉,如果你在Sun的MME模拟器上运行该程序,你会收到一个优雅的反馈信息:
![Running Two Copies of the Whisper Example](http://www.sf.org.cn/j2me/j2me/200711/20071105143117306.gif)
在不支持视频捕捉的设备上运行Snapper
如果你使用Nokia的Concept模拟器,你将看到如下效果:
![Snapper's Main Screen](http://www.sf.org.cn/j2me/j2me/200711/20071105143120214.gif)
Snapper的主屏
选择Camera命令(Nokia机型把该命令放在Options菜单里),在一台真机上,你可以看到来源于摄像头的视频,模拟器只能显示单幅的静态图像。但是在这2种情况下,CameraCanvas都会在视频周围画上一个绿框。
![Snapper's Camera Screen](http://www.sf.org.cn/j2me/j2me/200711/20071105143120911.gif)
Snapper的摄像机屏幕
要拍照的话,你只需选择Capture命令或者按开火键(位于方向键中心),Snapper将创建一个微缩图片并显示在MIDlet的主屏上。
![Snapper's Main Screen With a Thumbnail](http://www.sf.org.cn/j2me/j2me/200711/20071105143123574.gif)
放置了微缩图的Snapper主屏
未来的改进
Snapper是一个非常简单的例子,但是有很多有趣的地方你可以改进。
1.你可以把整个相片保存在数据库里,这样你可以在今后浏览。
2.你可以允许玩家删除相片,或者改变其次序
3.你可以把整个相片发送到一个服务器上存储起来,并且提供一个Web视图进行浏览。
4.使用MIDP2.0,你可以对图片做标准的变换处理:模糊,锐化,颜色反转,几何变换等等。
总结
MMAPI是一个灵活而强大的API,它让开发者能表现和捕获音频和视频数据。本文描述了MMAPI的拍照功能,所需工具, 以及如何编写拍照的MIDlet. Snapper例子实现了拍照功能和微缩图功能。你只要稍作加工,该例子就可以成为一个网络相片共享程序的基础.关于作者
Jonathan Knudsen出了好几本书,包括《Wireless Java (second edition)》,《The Unofficial Guide to LEGO MINDSTORMS Robots》, 《Learning Java (second edition)》,《Java 2D Graphics》。Jonathan在Java 和 Lego robots方面广泛写作,他还为JavaWorld, EXE, NZZ Folio, and the O'Reilly Network写文章. Jonathan在普林斯顿大学获得了机械工程学位.