刚刚用了12个小时的时间解决了这个问题,趁热打铁给大家分享一下。
本文的目的如题,介绍一下如何用ANE实现在AIR中打开iOS照片库中的图片。请注意阅读本文之前先了解ANE的基本概念,以及Flash开发iOS应用的基本流程,在我博客中的前文已经多次提起,这里不再重复。本文只对核心内容做介绍。
除了本文介绍的方法之外,也许还有更好的解决方案,欢迎各位高手补充和纠正。
下面我就单刀直入直接介绍代码和思路。
首先在OBJC里使用UIImagePicker浏览照片库:
//创建mediaUI,这是一个UIViewController的子类,用来显示Native的照片浏览窗口
UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
mediaUI.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
mediaUI.allowsEditing = NO;
//指定UIImagePickerDelegate的接口,observer是我自定义的类,使用Delegate回调接口是OBJC的一种典型的事件回调机制。
mediaUI.delegate = observer;
//在AIR应用上加UIViewController
showModalViewController(mediaUI);
这就是如何在AIR上增加和移除原生独占窗口的例子:
void showModalViewController(UIViewController* viewController){
if(currentModalViewController!=nil){
dismissModalViewController(currentModalViewController);
}
//使用Singleton对象sharedApplication的keyWindow属性可以获取AIR应用的主窗口。
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
//为即将显示的独占窗口创建一个父级UIViewController
UIViewController *parentViewController = [[UIViewController alloc] init];
//在主窗口加上这个父级controller,由于Native的view大多是用UIViewController构建的,
//而AIR应用的基础并没有建立在UIViewController之上,所以需要用这种方式来做桥接。
//这个概念类似于用UIComponent连接MXML Display组件和AS DisplayObject。
[window addSubview:parentViewController.view];
//显示独占窗口
[parentViewController presentModalViewController:viewController animated:YES];
currentModalViewController = viewController;
}
void dismissModalViewController(UIViewController* viewController){
if(viewController !=nil){
UIViewController *parentViewController = [viewController parentViewController];
[parentViewController dismissModalViewControllerAnimated:NO];
[parentViewController.view removeFromSuperview];
currentModalViewController = nil;
}
}
接下来一步是等待用户选择图片,对于程序来说,是等待调用UIImagePickerDelegated的方法didFinishPickingImage,
现在我们就开始介绍整个思路的核心。
ANE的核心在于FlashRuntimeExtension API,这里面包括了一些关键的方法和数据结构。详见这里http://help.adobe.com/zh_CN/air/extensions/WSb464b1207c184b14-62b8e11f12937b86be4-8000.html。虽然通过这套API可以实现两者之间的通信,但是通信的地位是不同的。除了基础数据类型,ActionScript的 BitmapData,ByteArray也能够以形参的方式直接传递到C,但是C传给ActionScript的数据只有基础类型,位图是传递不了的。但是FRE给我们提供了一个比较有趣的方案,可以将AS位图对象直接交给C来处理,通过内存地址直接修改,不需要回传。
基于这个思路,下一步要做的就是先把照片库中的照片拿出来,把照片尺寸传给Flash,让Flash创建一个等大的空位图并且传回来,最后用Objective-C修改这个位图。
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo
{
//标记所选择的图片
self.selectedImage = image;
//取得图片的尺寸并保存在一个XML中
CGSize image_size = image.size;
NSMutableString *retXML = [NSMutableString stringWithFormat:@"<image>"];
[retXML appendFormat:@"<width>%i</width>",(int)image_size.width];
[retXML appendFormat:@"<height>%i</height>",(int)image_size.height];
[retXML appendFormat:@"</image>"];
//通过StatusEvent将图片尺寸派发给Flash
FREDispatchStatusEventAsync(g_ctx, (const uint8_t*)"selectedImageReady", (const uint8_t*)[retXML UTF8String]);
//关掉选取图片的独占窗口
dismissModalViewController(picker);
}
在ActionScript里,用我们熟悉的方式创建一个BitmapData,然后传给C
private function onStatus(e:StatusEvent):void{
switch(e.code){
case "selectedImageReady":
var xml:XMLList = new XMLList(e.level);
var imageWidth:Number = Number(xml.width);
var imageHeight:Number = Number(xml.height);
var bitmapData:BitmapData = new BitmapData(imageWidth,imageHeight,true,0x00);
dispatchEvent(new ExtensionEvent(ExtensionEvent.GET_BITMAPDATA, [drawAsSelectedImage(bitmapData)]));
break;
}
}
//调用C的函数,把BitmapData对象移交出去。
public function drawAsSelectedImage(bitmapData:BitmapData):BitmapData{
ctx.call("getSelectedImage",bitmapData);
return bitmapData;
}
最后就是在C里处理这张图片,代码注释里是详细介绍。
FREObject getSelectedImage(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
//创建一个FREBitmapData对象bitmapData,这个对象是ActionScript中BitmapData的代表,共享一个内存地址。
FREBitmapData bitmapData;
//从参数组argv中拿出AS的BitmapData对象,用FREAcquireBitmapData方法将bitmapData指针指向这个BitmapData对象。
//这样,只要改动bitmapData的位图信息,就会直接影响 AS里面创建的BitmapData对象。
FREResult re = FREAcquireBitmapData(argv[0], &bitmapData);
//找到刚才从照片库中选择并保存的那张图片
UIImage *image = observer.selectedImage;
//取出尺寸
CGSize image_size = image.size;
//用Core Graphic API的CGBitmapContextCreate方法,在bitmapData的指针位置创建一个位图绘制环境。
CGContextRef context = CGBitmapContextCreate(bitmapData.bits32, image_size.width, image_size.height, 8, 4 * image_size.width,
CGColorSpaceCreateDeviceRGB(), CGImageGetBitmapInfo([image CGImage]));
//创建一个位图绘制区域
CGRect rect = {{0,0},{image_size.width,image_size.height}};
//绘制位图
CGContextDrawImage(context, rect, [image CGImage]);
//位图绘制完成后,必须调用FREInvalidateBitmapDataRect方法来重绘位图的修改区域。
FREInvalidateBitmapDataRect(argv[0], 0, 0, (int)image_size.width, (int)image_size.height);
//最后把BitmapData交回给Flash,结束修改动作。这个时候ActionScript里的这个BitmapData对象就已经被绘制成了照片库中选择的那张图片。
FREReleaseBitmapData(argv[0]);
//释放内存
CGContextRelease(context);
return nil;
};
补充一点:
从OBJC获取的照片是BGR图片,在AS中使用之前需要转换成RGB:
public static function convertRGB2BGR(bitmapData:BitmapData):BitmapData{
var outputBitmapData:BitmapData = new BitmapData(bitmapData.width,bitmapData.height,true);
outputBitmapData.copyChannel(bitmapData,bitmapData.rect,new Point(),BitmapDataChannel.BLUE,
BitmapDataChannel.RED);
outputBitmapData.copyChannel(bitmapData,bitmapData.rect,new Point(),BitmapDataChannel.GREEN,
BitmapDataChannel.GREEN);
outputBitmapData.copyChannel(bitmapData,bitmapData.rect,new Point(),BitmapDataChannel.RED,
BitmapDataChannel.BLUE);
return outputBitmapData;
}
Tutorials and samples |
AIR blogs |
More |
AIR Cookbooks |
More |