iOSというのはセンサーの塊みたいなもんでして、そのなかでもいちばん面白いセンサーがカメラ。このカメラを世界中の人々が持ち歩いてすぐに取り出せるとき、我々Flasherはどんなびっくりするような世界を作れるだろうとわくわくしたものですが、実際にFlashからアクセス出来るカメラ機能がしょぼすぎていろんな夢を諦めた経験があります。
Cameraクラスがしょぼいなら、NativeCameraにアクセスすればいいじゃないということでANEを使ってiOSのネイティブのカメラ機能をAIRでぞんぶんに味わってみたいと思います。
今回使ったANEがこちら。
diadraw-air-camera-native-extension
ひょんな事からお仕事をご一緒させて頂いている方から教えて頂いたANEです。
リンク先にあるドキュメントを読めば、今からでもすぐ使えるとは思いますが、ちょっと使いやすいようにラッパークラスを作ってみましたYO!
package
{
import com.diadraw.extensions.camera.NativeCameraExtension;
import com.diadraw.extensions.camera.NativeCameraExtensionEvent;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.ByteArray;
/**
* iOSデバイスのネイティブカメラを扱う基本クラスです。
* @author mainUser
*
*/
public class NativeCamera extends EventDispatcher
{
public static const BITMAP_READY:String = 'bitmap_ready';
private static const _MIN_FPS:int = 10;
private static const _MAX_FPS:int = 30;
private static const _USE_FRONT_CAMERA:Boolean = false;
private var MAP_RED : Array = new Array( 256 );
private var MAP_GREEN : Array = new Array( 256 );
private var MAP_BLUE : Array = new Array( 256 );
private var MAP_ALPHA : Array = new Array( 256 );
protected var _camera:NativeCameraExtension;
protected var _lastFrameIndex:int;
protected var _zeroPoint:Point;
protected var _rect:Rectangle;
protected var _matrix:Matrix;
protected var _pointMatrix:Matrix;
protected var _baseBitmapData:BitmapData;
protected var _bitmapData:BitmapData;
protected var _bitmap:Bitmap;
private var _isRunning:Boolean;
/**
* コンストラクター
*
*/
public function NativeCamera()
{
_init();
}
/**
* 初期化
*
*/
protected function _init():void
{
_initChannelMaps();
_camera = new NativeCameraExtension();
}
/**
* ARGBのチャンネルマップを初期化します。
*
*/
private function _initChannelMaps():void
{
for ( var i : uint = 0 ; i < 256 ; ++i )
{
MAP_ALPHA[ i ] = 0xff000000;
MAP_RED[ i ] = i << 16;
MAP_GREEN[ i ] = i << 8;
MAP_BLUE[ i ] = i;
}
}
/**
* カメラをスタートさせて、ネイティブカメラがサポートされているか返します。
* @return ネイティブカメラがサポートされていればtrue
*
*/
public function isSupport():Boolean
{
_isRunning = _camera.startVideoCamera(NativeCameraExtension.Preset1280x720, _MIN_FPS, _MAX_FPS, _USE_FRONT_CAMERA);
return _isRunning;
}
/**
* カメラを開始します。
*
*/
public function start():void
{
_lastFrameIndex = int.MIN_VALUE;
_camera.addEventListener(NativeCameraExtensionEvent.IMAGE_READY, _onReady);
}
/**
* カメラを停止します。
*
*/
public function stop():void
{
_camera.removeEventListener(NativeCameraExtensionEvent.IMAGE_READY, _onReady);
_camera.stopVideoCamera();
_isRunning = false;
}
/**
* カメラが映像取得可能になるたびに実行されます。
* @param e
*
*/
private function _onReady(e:NativeCameraExtensionEvent):void
{
var byte:ByteArray = new ByteArray();
var currentFrameIndex:int = _camera.getFrameBuffer(byte, _lastFrameIndex);
if(currentFrameIndex !== _lastFrameIndex){
//フレームバッファーの大きさを取得
var w:Number = e.frameWidth;
var h:Number = e.frameHeight;
//ゼロ位置を生成
if(!_zeroPoint){
_zeroPoint = new Point(0, 0);
}
//フレームバッファーの矩形を生成
if(!_rect){
_rect = new Rectangle(0, 0, w, h);
}
//変形用マトリクスを生成
if(!_matrix){
_createMatrix();
}
//フレームバッファー読み込み用BitmapDataを生成
if(!_baseBitmapData){
_baseBitmapData = new BitmapData(w, h, true);
}
//フレームバッファーからBitmapDataにピクセルを転写
_baseBitmapData.setPixels(_rect, byte);
//ARGB値が元のバイナリデータと違っているのでリマップ
_baseBitmapData.paletteMap( _baseBitmapData, _rect, _zeroPoint, MAP_GREEN, MAP_RED, MAP_ALPHA, MAP_BLUE );
//
_setBitmapData();
_writeBitmap();
_lastFrameIndex = currentFrameIndex;
}
}
/**
* ベースとなるBitmapDataを変形するMatrixを生成します。
*
*/
protected function _createMatrix():void
{
_matrix = new Matrix();
_matrix.rotate(90 * (Math.PI / 180));
_matrix.translate(_rect.height, 0);
_pointMatrix = new Matrix();
_pointMatrix.translate(-_rect.height, 0);
_pointMatrix.rotate(-90 * (Math.PI / 180));
}
/**
* ベースのBitmapDataから、表示用BitmapDataを生成します。
*
*/
protected function _setBitmapData():void
{
if(!_bitmapData){
_bitmapData = new BitmapData(_rect.height, _rect.width);
}
_bitmapData.draw(_baseBitmapData, _matrix);
}
/**
* 表示用BitmapDataをBitmapに転写します。
*
*/
protected function _writeBitmap():void
{
if(!_bitmap){
_bitmap = new Bitmap(_bitmapData);
_bitmap.addEventListener(Event.ADDED_TO_STAGE, function(e:Event):void{
_bitmap.removeEventListener(Event.ADDED_TO_STAGE, arguments.callee);
_bitmap.stage.addEventListener(MouseEvent.CLICK, _onMouseClick);
});
_bitmap.addEventListener(Event.REMOVED_FROM_STAGE, function(e:Event):void{
_bitmap.removeEventListener(Event.REMOVED_FROM_STAGE, arguments.callee);
_bitmap.stage.removeEventListener(MouseEvent.CLICK, _onMouseClick);
stop();
});
//最初にBitmapを取得可能になったらイベント発生させる
dispatchEvent(new Event(BITMAP_READY));
}
_bitmap.bitmapData = _bitmapData;
}
/**
* 画面クリックで実行。
* @param e
*
*/
protected function _onMouseClick(e:MouseEvent):void
{
if(!(!_camera)){
var sp:Point = new Point(e.localX, e.localY);
// var gp:Point = _bitmap.stage.localToGlobal(sp);
// var lp:Point = _bitmap.globalToLocal(gp);
var lp:Point = _bitmap.globalToLocal(sp);
var tp:Point = _pointMatrix.transformPoint(lp);
var pp:Point = new Point(Math.max(0, Math.min(1, tp.x / _rect.width)), Math.max(0, Math.min(1, tp.y / _rect.height)));
_camera.setFocusMode(NativeCameraExtension.FocusModeContinuousAutoFocus, pp);
}
}
public function get bitmap():Bitmap
{
return _bitmap;
}
public function get isRunning():Boolean
{
return _isRunning;
}
}
}
ライブラリとかそういう大層なもんではないので、適当にコピペしてやってみてください。
特にクリックしたときのFocusの座標とかまだ自信ないし。
使うときはこんな感じで使ってみたり。
var nativeCamera:NativeCamera = new NativeCamera();
nativeCamera.addEventListener(NativeCamera.BITMAP_READY, function(e:Event):void{
nativeCamera.removeEventListener(NativeCamera.BITMAP_READY, arguments.callee);
addChild(nativeCamera.bitmap);
});
nativeCamera.start();
やめるときはこうとか。
nativeCamera.stop();
いろいろ便利なんではないでしょーか!?
ほかにもいろいろ遊びたいときは、このNativeCameraを継承するといいかもしれないです。
例えば正方形のカメラにしたいときは、
package
{
import flash.display.BitmapData;
import flash.geom.Matrix;
public class SquareCamera extends NativeCamera
{
private static const _WIDTH:Number = 600;
private static const _HEIGHT:Number = 600;
private var _cameraWidth:Number;
private var _cameraHeight:Number;
public function SquareCamera()
{
super();
}
override protected function _init():void
{
super._init();
//縦横のサイズを設定
_cameraWidth = _WIDTH;
_cameraHeight = _HEIGHT;
}
override protected function _createMatrix():void
{
_matrix = new Matrix();
//90度回転させて、ずれたぶん横移動
_matrix.rotate(90 * (Math.PI / 180));
_matrix.translate(_rect.height, 0);
//ここから縦横が逆になるので注意
var tX:Number = 0;
var tY:Number = 0;
var scale:Number;
if(_rect.width > _rect.height){
scale = _cameraHeight / _rect.height;
tY = (_cameraWidth - _rect.width * scale) * 0.5;
}else{
scale = _cameraWidth / _rect.width;
tX = (_cameraHeight - _rect.height * scale) * 0.5;
}
_matrix.scale(scale, scale);
_matrix.translate(tX, tY);
_pointMatrix = new Matrix();
_pointMatrix.translate(-tX, -tY);
_pointMatrix.scale(1, 1);
_pointMatrix.translate(-_rect.height, 0);
_pointMatrix.rotate(-90 * (Math.PI / 180));
}
override protected function _setBitmapData():void
{
if(!_bitmapData){
_bitmapData = new BitmapData(_cameraWidth, _cameraHeight);
}
_bitmapData.draw(_baseBitmapData, _matrix);
}
}
}
こんな感じの継承をこしらえるとか。
継承でなんとか済ませようというところがそこはかとなくダサいですが(ダサいのは継承ではなくて、継承で逃げようとする私自身です。念のため)そこらへんがクラスライブラリまでは作りきれなかった設計力の甘さです。
とにかくですね、ANEでネイティブのカメラを扱えるようになると、ネックだった動作の遅さも解消されるしウハウハですわ。みんなにもはよ教えたい。