DXライブラリ Android版を使用した Androidアプリで Java のコードでカメラを使用する
( Visual Studio編 )


 すべての処理を C++ で実行できれば良いのですが、Android の基本言語が Java である関係で、
今のところ Java を使用しないと Android端末に搭載されているカメラを使用することができません。

 なので、ここでは『DXライブラリ Android版』には無いカメラの映像の取得を Java を使用して
行うための手順を記します。


 1.『AndroidManifest.xml』の内容をカメラの使用と Java のコードを実行できるように変更する

 2.プロジェクトのターゲット API レベルを 23 に変更する

 3.プロジェクトに Java のソースファイルを追加する

 4.Java のコードを入力する

 5.Java の情報を取得する C++ のコードを入力する



1.プロジェクトの設定と『『AndroidManifest.xml』の内容をカメラの使用と Java のコードを実行できるように変更する

  Visual Studio『DXライブラリ Android版』を使用する Androidアプリのプロジェクトを開き
 『ソリューションエクスプローラー』と書かれているリストに表示されている『AndroidManifest.xml』
 クリックして、内容を表示します。

  『AndroidManifest.xml』の内容は、使い方ページに沿って編集した場合は以下のようになっていると思いますが、
<?xml version="1.0" encoding="utf-8"?> <!-- Changes made to Package Name should also be reflected in the Debugging - Package Name property, in the Property Pages --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.$(ApplicationName)" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="false" android:theme="@android:style/Theme.NoTitleBar"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:configChanges="orientation|screenSize" android:launchMode="singleInstance"> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="$(AndroidAppLibName)"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>

  この中の以下のように変更します。(色が緑や赤の部分が変更箇所や追加箇所で、5箇所です)
<?xml version="1.0" encoding="utf-8"?> <!-- Changes made to Package Name should also be reflected in the Debugging - Package Name property, in the Property Pages --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.$(ApplicationName)" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="23"/> <uses-permission android:name="android.permission.CAMERA"/> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="true" android:theme="@android:style/Theme.NoTitleBar"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="com.(プロジェクト名).Packaging.(プロジェクト名)" android:label="@string/app_name" android:configChanges="orientation|screenSize" android:launchMode="singleInstance"> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="$(AndroidAppLibName)"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



2.プロジェクトのターゲット API レベルを 23 に変更する

  次に 以下の手順で Java プロジェクトのAPIレベルを 23 に変更します。
  初期状態では APIレベルが 19 ですが、アプリがカメラを使う際に必要な権限取得の処理が、Android 6.0 ( APIレベル 23 )のときに
 大きく変更されたので、それに対応する処理を行うためには APIレベルを 23 以上にする必要がある為です。

  ① 『ソリューションエクスプローラー』と書かれているリストに表示されている『(プロジェクト名).Packaging』
    を右クリックして、表示されるプルダウンメニューから『プロパティ(R)』を選択して、
    『(プロジェクト名) プロパティページ』ダイアログを表示します。

  ② 次にダイアログの左側のリストから『構成プロパティ』→『全般』を選びます。

  ③ ダイアログ右側に表示されている『ターゲット API レベル』の項目を『KitKat 4.4 - 4.4.4、(android-19)』から
    『Marshmallow 6.0、(android-23)』に変更した後、ダイアログ右下にある『OK』ボタンを押してダイアログを閉じます。

  これでプロジェクトで使用する Java の API レベルが 23 になります。



3.プロジェクトに Java のソースファイルを追加する

  次に Java のソースファイルを以下の手順でプロジェクトに追加します。

  ① 『ソリューションエクスプローラー』と書かれているリストに表示されている『(プロジェクト名).Packaging』
    を右クリックして、表示されるプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、名前を『src』としてください。

  ② 次に追加した『src』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『com』としてください。

  ③ 次に追加した『com』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『(プロジェクト名)』( (プロジェクト名) はお手元のプロジェクトの名前に置き換えてください )としてください。

  ④ 次に追加した『(プロジェクト名)』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『Packaging』としてください。

    と、要は『(プロジェクト名).Packaging』以下に『src』→『com』→『(プロジェクト名)』→『Packaging』というフォルダ
    を作ります。次に漸く Java ファイルの追加です。

  ⑤ 追加した『Packaging』フォルダーを右クリックして、今度は『新しい項目(W)...』を選択して『新しい項目の追加』ダイアログを表示します。

  ⑥ ダイアログ左側のリストから『インストール済み』→『Cross Platform』→『Android』を選び、
    右側のリストから『Java』を選びます。

  ⑦ ダイアログ下側の『名前(N):』の項目に『(プロジェクト名).java』と入力して『追加(A)』ボタンを押します。

  ⑧ 次に追加された『(プロジェクト名).java』を開いた状態でメニューから『ファイル(F)』→『名前を付けて(プロジェクト名).javaを保存(A)...』を
    選択して、『名前を付けてファイルを保存』ダイアログを表示します。
    ( VisualStudio 2015 の場合はメニューから『ファイル(F)』→『保存オプションの詳細設定(V)...』を選択して、『保存オプションの詳細設定』ダイアログを表示します )

  ⑨ 『名前を付けてファイルを保存』ダイアログの右下にある『上書き保存(S)』の項目を『エンコード付きで保存(V)...』
    に変更します。すると変更した瞬間に『名前を付けて保存の確認』ダイアログが表示されますので『はい(Y)』を選択してください。
    ( VisualStudio 2015 の場合はこの工程が無いので、手順⑩に進んでください )

  ⑩ 次に『保存オプションの詳細設定』ダイアログの『エンコード(E):』の項目を『日本語 (シフト JIS) - コードページ 932』から
    『Unicode (UTF-8 シグネチャなし) - コードページ 65001』に変更して『OK』ボタンを押します。
    ( 『Unicode (UTF-8 シグネチャ付き) - コードページ 65001』と間違えないようにしてください、
     『Unicode (UTF-8 シグネチャなし) - コードページ 65001』はリストのかなり下の方にあります )


 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )


  これで java ファイルの追加は完了です。



4.Java のコードを入力する

  追加した『(プロジェクト名).java』には最初から数行書かれていますが、それを全部消して、代わりに
 今回の『カメラの映像の取得』を行うための以下のコードを入力します。
package com.(プロジェクト名).Packaging; import android.content.Context; import android.content.res.Configuration; import android.content.pm.PackageManager; import android.app.NativeActivity; import android.widget.Toast; import android.Manifest; import android.util.Size; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCaptureSession.CaptureCallback; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraDevice.StateCallback; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.TotalCaptureResult; import android.media.Image; import android.media.ImageReader; import android.media.ImageReader.OnImageAvailableListener; import android.view.Surface; public class (プロジェクト名) extends NativeActivity { String mCameraId; boolean mIsPortraitDevice; CameraManager mCameraManager; CameraCaptureSession mCameraCaptureSession; CaptureRequest.Builder mCaptureRequestBuilder; ImageReader mImageReader; // カメラ映像の解像度 int mCameraImageSizeX; int mCameraImageSizeY; // カメラ映像のYUVイメージのY要素を格納する配列とUV要素を格納する配列 byte mCameraYImage[]; byte mCameraUVImage[]; // カメラ映像のYUVフォーマット 1:NV21 2:NV12 3:I420 int mImageFormat; // カメラの回転角度 0:0度 1:90度 2:180度 3:270度 int mCameraRotation; // カメラの状態( 0:準備中 1:イメージ取得開始 2:エラー ) int mCameraState; // カメラ情報取得の権限をリクエストする際に使用する識別番号 static final int PERMISSIONS_REQUEST_CAMERA = 2 ; // ByteBuffer の実効アドレスを取得する関数 private static long GetByteBufferAddress( final ByteBuffer buffer ) { try { // Buffer クラスのメンバー変数 effectiveDirectAddress へアクセスする為の Field を取得 final java.lang.reflect.Field field = java.nio.Buffer.class.getDeclaredField( "effectiveDirectAddress" ); // 値へのアクセスを可能にする field.setAccessible( true ); // 実効アドレスを取得して返す return field.getLong( buffer ); } catch( NoSuchFieldException e ) { e.printStackTrace(); // 例外が発生したらコールスタックをログに出力する } catch( IllegalAccessException e ) { e.printStackTrace(); // 例外が発生したらコールスタックをログに出力する } return 0; } // カメラマネージャの初期化を行う private void InitializeCameraManager() { // スマートフォンかどうかを調べておく boolean isPortraitApp = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; int orientation = getWindowManager().getDefaultDisplay().getRotation(); if( isPortraitApp ) { mIsPortraitDevice = ( orientation == Surface.ROTATION_0 || orientation == Surface.ROTATION_180 ); } else { mIsPortraitDevice = ( orientation == Surface.ROTATION_90 || orientation == Surface.ROTATION_270 ); } // カメラマネージャの取得 mCameraManager = ( CameraManager )getSystemService( Context.CAMERA_SERVICE ); try { // 有効なカメラが見つかったかどうかのフラグを倒しておく boolean Flag = false; // 搭載されているカメラのID配列を取得 String[] CameraIDs = mCameraManager.getCameraIdList(); // カメラのIDの数だけループ for( String cameraId : CameraIDs ) { // カメラの情報を取得 CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics( cameraId ); // 背面カメラ以外はキャンセルする if( characteristics.get( CameraCharacteristics.LENS_FACING ) == CameraCharacteristics.LENS_FACING_BACK ) { StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP ); // カメラが対応しているサイズの配列を取得 Size[] CameraImageSizes = map.getOutputSizes( SurfaceTexture.class ); // カメラのサイズ 640x480 か 320x240 を使用する boolean Valid640x480 = false; boolean Valid320x240 = false; for( Size cameraSize : CameraImageSizes ) { if( cameraSize.getWidth() == 320 && cameraSize.getHeight() == 240 ) { Valid320x240 = true; } else if( cameraSize.getWidth() == 640 && cameraSize.getHeight() == 480 ) { Valid640x480 = true; } } // 640x480 が有効な場合は 640x480 を、そうでない場合は 320x240 を使用する if( Valid640x480 ) { mCameraImageSizeX = 640; mCameraImageSizeY = 480; } else if( Valid320x240 ) { mCameraImageSizeX = 320; mCameraImageSizeY = 240; } else { // 640x480 にも 320x240 にも対応していなかったらキャンセル continue; } // カメラのIDを保存 mCameraId = cameraId; // 有効なカメラがあったかどうかのフラグを立てる Flag = true; break; } } // 有効なカメラが無かったらここで終了 if( Flag == false ) { return; } } catch( CameraAccessException e ) { e.printStackTrace(); // 例外が発生したらコールスタックをログに出力する } // カメラのYUVイメージのY要素を保存する配列を作成 mCameraYImage = new byte[ mCameraImageSizeX * mCameraImageSizeY ]; // カメラのYUVイメージのUV要素を保存する配列を作成 mCameraUVImage = new byte[ ( mCameraImageSizeX / 2 ) * ( mCameraImageSizeY / 2 ) * 2 ]; try { // カメラへの接続を開始 mCameraManager.openCamera( mCameraId, new StateCallback() { // カメラへの接続が完了したら呼ばれる関数 @Override public void onOpened( CameraDevice cameraDevice ) { // カメラのイメージを取得する為の ImageReader を作成 mImageReader = ImageReader.newInstance( mCameraImageSizeX, mCameraImageSizeY, ImageFormat.YUV_420_888, 1 ); // カメラのプレビューイメージの取得リクエストの作成 try { mCaptureRequestBuilder = cameraDevice.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW ); } catch( CameraAccessException e ) { e.printStackTrace(); // 例外が発生したらコールスタックをログに出力する } // カメラのプレビューイメージの取得リクエスト先に作成した ImageReader を追加 mCaptureRequestBuilder.addTarget( mImageReader.getSurface() ); // カメラのプレビューイメージの取得を開始 try { List< Surface > outputs = Arrays.asList( mImageReader.getSurface() ); cameraDevice.createCaptureSession( outputs, new CameraCaptureSession.StateCallback() { // イメージの取得が開始されたら呼ばれる関数 @Override public void onConfigured( CameraCaptureSession session ) { mCameraCaptureSession = session; // オートフォーカスモードを設定 mCaptureRequestBuilder.set( CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE ); // フラッシュの設定 mCaptureRequestBuilder.set( CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH ); // イメージの取得を繰り返し行う処理の開始 try { mCameraCaptureSession.setRepeatingRequest( mCaptureRequestBuilder.build(), new CaptureCallback() { @Override public void onCaptureCompleted( CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result ) { super.onCaptureCompleted( session, request, result ); } }, null ); } catch( CameraAccessException e ){ e.printStackTrace(); // 例外が発生したらコールスタックをログに出力する } } @Override public void onConfigureFailed( CameraCaptureSession session ) { } }, null ); } catch( CameraAccessException e ) { e.printStackTrace(); // 例外が発生したらコールスタックをログに出力する } // ここまで来たらイメージの取得が開始されている mCameraState = 1; // ImageReader のイメージが更新されたら呼ばれる処理の設定 mImageReader.setOnImageAvailableListener( new OnImageAvailableListener() { // ImageReader のイメージが更新された呼ばれる関数 @Override public void onImageAvailable( ImageReader reader ) { // このタイミングでカメラの向きも取得 { int orientation = getWindowManager().getDefaultDisplay().getRotation(); switch( orientation ) { case Surface.ROTATION_0: mCameraRotation = mIsPortraitDevice ? 3 : 0; break; case Surface.ROTATION_90: mCameraRotation = mIsPortraitDevice ? 0 : 1; break; case Surface.ROTATION_180: mCameraRotation = mIsPortraitDevice ? 1 : 2; break; case Surface.ROTATION_270: mCameraRotation = mIsPortraitDevice ? 2 : 3; break; } } // ImageReader から Image を取得 Image image = mImageReader.acquireLatestImage(); // カメラのイメージのフォーマットを取得 if( mImageFormat == 0 ) { // Uイメージのストライドが1byteの場合は I420フォーマット if( image.getPlanes()[ 1 ].getPixelStride() == 1 ) { mImageFormat = 3; } else { ByteBuffer uBuffer = image.getPlanes()[ 1 ].getBuffer(); ByteBuffer vBuffer = image.getPlanes()[ 2 ].getBuffer(); if( GetByteBufferAddress( uBuffer ) < GetByteBufferAddress( vBuffer ) ) { // UVの順番でイメージが格納されている場合は NV12フォーマット mImageFormat = 2 ; } else { // VUの順番でイメージが格納されている場合は NV21フォーマット mImageFormat = 1 ; } } } // YUVイメージをクラスの配列にコピーする { ByteBuffer ys = image.getPlanes()[ 0 ].getBuffer(); ByteBuffer us = image.getPlanes()[ 1 ].getBuffer(); ByteBuffer vs = image.getPlanes()[ 2 ].getBuffer(); // Yイメージは全フォーマット共通 int i ; int loopNum = mCameraImageSizeX * mCameraImageSizeY ; for( i = 0 ; i < loopNum ; i ++ ) { mCameraYImage[ i ] = ys.get( i ); } // UVイメージはフォーマットによって処理が異なる int j = 0 ; loopNum = ( mCameraImageSizeX / 2 ) * ( mCameraImageSizeY / 2 ) ; switch( mImageFormat ) { case 1: // I420 case 2: // NV12 for( i = 0 ; i < loopNum ; i ++ ) { mCameraUVImage[ j + 0 ] = us.get( j ); mCameraUVImage[ j + 1 ] = vs.get( j ); j += 2 ; } break; case 3: // NV21 for( i = 0 ; i < loopNum ; i ++ ) { mCameraUVImage[ j + 0 ] = us.get( i ); mCameraUVImage[ j + 1 ] = vs.get( i ); j += 2 ; } break; } } // Image の解放 image.close(); } }, null ); } @Override public void onError(CameraDevice camera, int error) { } @Override public void onDisconnected(CameraDevice camera) { } }, null ); } catch( CameraAccessException e ) { e.printStackTrace(); // 例外が発生したらコールスタックをログに出力する } } // 権限の許可を求めるダイアログで許可か不許可が選択されたら呼ばれる関数 public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults ) { // カメラ情報取得の権限を求めるリクエストに対する結果の場合のみ処理を行う if( requestCode == PERMISSIONS_REQUEST_CAMERA ) { // 許可されたのかどうかを判定 if( grantResults[ 0 ] == PackageManager.PERMISSION_GRANTED ) { // 許可されたらカメラマネージャを初期化 Toast.makeText( (プロジェクト名).this, "カメラ情報取得が許可されました", Toast.LENGTH_SHORT ).show() ; InitializeCameraManager() ; } else { // 許可されなかったらその旨を表示する Toast.makeText( (プロジェクト名).this, "カメラ情報取得が拒否されました", Toast.LENGTH_SHORT ).show() ; // カメラ使用不可の状態にする mCameraState = 2 ; } } } // カメラの処理を開始する public void StartCamera() { // UIスレッドで実行する処理を登録する runOnUiThread( new Runnable() { // UIスレッドで呼ばれる関数 @Override public void run() { // Android のバージョンチェック if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M ) { // Android 6.0以上の場合はアプリ実行中に位置情報取得の権限があるかをチェックする // カメラ情報取得の権限があるか判定 if( checkSelfPermission( Manifest.permission.CAMERA ) == PackageManager.PERMISSION_GRANTED ) { // カメラ情報取得の権限があればカメラマネージャを初期化 Toast.makeText( (プロジェクト名).this, "このアプリはカメラ情報取得の権限が既にあります", Toast.LENGTH_SHORT ).show() ; InitializeCameraManager() ; } else { // カメラ情報取得の権限が無ければ権限を求めるダイアログを表示 requestPermissions( new String[]{ Manifest.permission.CAMERA }, PERMISSIONS_REQUEST_CAMERA ) ; } } else { // Android 6.0未満の場合はアプリ実行時にはカメラ情報取得の権限が許可されているので // 無条件でカメラマネージャの初期化を行う InitializeCameraManager() ; } } } ) ; } }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



5.Java の情報を取得する C++ のコードを入力する

  次に Java のカメラの映像取得の処理を開始する関数を呼び出したり、カメラの状態やカメラの映像を画面に表示したりする C++ 側のプログラムです。
  『DXライブラリ Android版』でのみ使用できる GetNativeActivity という関数を使用しています。
#include "DxLib.h" // Java のクラスの int型のメンバー変数を取得する関数 int GetJavaClassIntValue( const ANativeActivity *NativeActivity, JNIEnv *env, jclass Class, const char *Name ) { return env->GetIntField( NativeActivity->clazz, env->GetFieldID( Class, Name, "I" ) ) ; } int android_main( void ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; int CameraState = 0 ; int CameraImageSizeX = 0 ; int CameraImageSizeY = 0 ; int CameraRotation = -1 ; int CameraGraphHandle = -1 ; int CameraSoftImageHandle = -1 ; // 背景を灰色にする SetBackgroundColor( 128,128,128 ) ; SetGraphMode( 640, 960, 32 ) ; // DXライブラリの初期化 if( DxLib_Init() < 0 ) return -1 ; // 描画先を裏画面に変更 SetDrawScreen( DX_SCREEN_BACK ) ; // アプリの NativeActivity を取得しておく NativeActivity = GetNativeActivity() ; // Java の関数 StartCamera の呼び出し { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス (プロジェクト名) のメンバー関数 StartCamera の ID を取得 jmethodID jmethodID_StartCamera = env->GetMethodID( jclass_(プロジェクト名), "StartCamera", "()V" ) ; // Java のクラス (プロジェクト名) のメンバー関数 StartCamera の呼び出し env->CallVoidMethod( NativeActivity->clazz, jmethodID_StartCamera ) ; // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; } // メインループ while( ProcessMessage() == 0 ) { // 裏画面の内容をクリア ClearDrawScreen() ; // JNI 関連の処理 { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス (プロジェクト名) のメンバー変数 CameraState の値を取得 CameraState = GetJavaClassIntValue( NativeActivity, env, jclass_(プロジェクト名), "mCameraState" ) ; // カメラが使用可能な状態になっていたら更に情報を取得する if( CameraState == 1 ) { // Java のクラス (プロジェクト名) の int型のメンバー変数の値を取得 CameraImageSizeX = GetJavaClassIntValue( NativeActivity, env, jclass_(プロジェクト名), "mCameraImageSizeX" ) ; CameraImageSizeY = GetJavaClassIntValue( NativeActivity, env, jclass_(プロジェクト名), "mCameraImageSizeY" ) ; CameraRotation = GetJavaClassIntValue( NativeActivity, env, jclass_(プロジェクト名), "mCameraRotation" ) ; // Java のクラス (プロジェクト名) のメンバー変数 mCameraYImage と mCameraUVImage の ID を取得 jfieldID jfieldID_mCameraYImage = env->GetFieldID( jclass_(プロジェクト名), "mCameraYImage", "[B" ) ; jfieldID jfieldID_mCameraUVImage = env->GetFieldID( jclass_(プロジェクト名), "mCameraUVImage", "[B" ) ; // Java のクラス (プロジェクト名) のメンバー変数 mCameraYImage と mCameraUVImage の値を取得 { // ソフトイメージハンドルがまだ作成されていなかったらここで作成する if( CameraSoftImageHandle == -1 ) { CameraSoftImageHandle = MakeARGB8ColorSoftImage( CameraImageSizeX, CameraImageSizeY ) ; } // YイメージとUVイメージが格納されている配列のJNIオブジェクトを取得 jbyteArray jbyteArray_CameraYImage = ( jbyteArray )env->GetObjectField( NativeActivity->clazz, jfieldID_mCameraYImage ); jbyteArray jbyteArray_CameraUVImage = ( jbyteArray )env->GetObjectField( NativeActivity->clazz, jfieldID_mCameraUVImage ); // JNIオブジェクトからYイメージとUVイメージが格納されているメモリアドレスを取得 jbyte *jbyte_CameraYImage = env->GetByteArrayElements( jbyteArray_CameraYImage, NULL ) ; jbyte *jbyte_CameraUVImage = env->GetByteArrayElements( jbyteArray_CameraUVImage, NULL ) ; // ソフトイメージハンドルにYUVイメージをRGBイメージに変換しながら転送 BYTE *yimg = ( BYTE * )jbyte_CameraYImage ; BYTE *dest = ( BYTE * )GetImageAddressSoftImage( CameraSoftImageHandle ) ; for( int i = 0 ; i < CameraImageSizeY ; i ++ ) { BYTE *uvimg = ( BYTE * )jbyte_CameraUVImage + ( i / 2 ) * ( CameraImageSizeX / 2 ) * 2 ; int k = 0 ; for( int j = 0 ; j < CameraImageSizeX ; j ++ ) { int b = ( int )( *yimg + ( 1.732446f * ( ( int )uvimg[ 0 ] - 128 ) ) ); int g = ( int )( *yimg - ( 0.698001f * ( ( int )uvimg[ 1 ] - 128 ) ) - ( 0.337633f * ( ( int )uvimg[ 0 ] - 128 ) ) ); int r = ( int )( *yimg + ( 1.370705f * ( ( int )uvimg[ 1 ] - 128 ) ) ); dest[ 0 ] = ( BYTE )( b < 0 ? 0 : ( b > 255 ? 255 : b ) ) ; dest[ 1 ] = ( BYTE )( g < 0 ? 0 : ( g > 255 ? 255 : g ) ) ; dest[ 2 ] = ( BYTE )( r < 0 ? 0 : ( r > 255 ? 255 : r ) ) ; dest[ 3 ] = 0xff ; dest += 4 ; yimg ++ ; k ++ ; if( k == 2 ) { k = 0 ; uvimg += 2 ; } } } // YイメージとUVイメージが格納されている配列へのアクセスの終了 env->ReleaseByteArrayElements( jbyteArray_CameraYImage, jbyte_CameraYImage, 0 ) ; env->ReleaseByteArrayElements( jbyteArray_CameraUVImage, jbyte_CameraUVImage, 0 ) ; } } // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; } // ソフトイメージハンドルが作成されている場合のみ画像の描画処理を行う if( CameraSoftImageHandle != -1 ) { // 既にカメラから取得した画像を描画する為のグラフィックハンドルが作成されているかどうかで処理を分岐 if( CameraGraphHandle == -1 ) { // まだグラフィックハンドルが作成されていなかったらソフトイメージハンドルからグラフィックハンドルを作成する CameraGraphHandle = CreateGraphFromSoftImage( CameraSoftImageHandle ) ; } else { // 既にグラフィックハンドルが作成されていたらソフトイメージハンドルのイメージをグラフィックハンドルに転送 ReCreateGraphFromSoftImage( CameraSoftImageHandle, CameraGraphHandle ) ; } // カメラの向きに応じて回転させつつ描画 switch( CameraRotation ) { case 0: DrawRotaGraph( 320, 480, 1.0, DX_PI * 2.0, CameraGraphHandle, FALSE ) ; break; case 1: DrawRotaGraph( 320, 480, 1.0, DX_PI * 1.5, CameraGraphHandle, FALSE ) ; break; case 2: DrawRotaGraph( 320, 480, 1.0, DX_PI * 1.0, CameraGraphHandle, FALSE ) ; break; case 3: DrawRotaGraph( 320, 480, 1.0, DX_PI * 0.5, CameraGraphHandle, FALSE ) ; break; } } // カメラの状態と解像度を描画 DrawFormatString( 0, 0, GetColor( 255,255,255 ), "CameraState : %s Size : %d x %d", CameraState == 0 ? "準備中" : ( CameraState == 1 ? "使用可能" : "使用不可能" ), CameraImageSizeX, CameraImageSizeY ) ; // 裏画面の内容を表画面に反映 ScreenFlip() ; } // DXライブラリの後始末 DxLib_End() ; // ソフトの終了 return 0 ; }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )

  以上です。
  これでプロジェクトを実行すると、画面にカメラが使用可能かや、使用可能な場合はカメラの映像が表示されます。

  詳細は割愛しますが、C++ から Java にアクセスする基本的な流れは。

   ① AttachCurrentThreadAsDaemon でソフト実行用スレッドと JavaVM を関連付け、JNIEnv も取得。

   ② GetObjectClass でアプリの Java クラスを取得。

   ③ GetMethodID や GetFieldID で Java の関数や変数の ID を取得。

   ④ Call???Method や Get???Field で Java の関数の呼び出しや変数の値を取得。

   ⑤ DeleteLocalRef で Get???Field で取得したオブジェクト系の変数の参照を削除。

   ⑥ DeleteLocalRef で GetObjectClass で取得したアプリの Java クラスの参照を削除。

   ⑦ DetachCurrentThread でソフト実行用スレッドと JavaVM の関連付けを終了。

  となります。
  C++ と Java でのやり取りを行う機能( JNI( Java Native Interface ) ) の詳しい扱い方については、検索サイトで
 『Android Java JNI』などのキーワードで検索して調べてみてください。





戻る