2013年6月29日土曜日

[Android] OpenGLの簡易画像ビュアーをAndroid-NDKでビルドする (失敗談)

OpenGL and JNI
AndroidアプリをAndroid-NDKで開発するとネイティブコード(Javaインタープリターを通らないマシン語)で書かれたJNI(Java Native Interface)の動作比率を増やすことができ、Javaだけで開発したアプリに比べて高速な動作やバッテリーの長持ちが期待できます。
Android-NDKで開発するデメリットとして「マルチプラットフォーム(CPUが異なる端末)で動作しなくなる」という点があげられます。
Android-NDK-r8d では
・armeabi
・armeabi-v7a
・MIPS
・x86
の4種類のCPUがサポートされています。
PowerPC系のAndroid端末が普及しない限りマルチプラットフォームの心配はないようです。
そこで、 前回 公開したOpenGLによる簡易画像ビュアーをAndroid-NDKに移植してみました。

Android-NDKの弱点
アプリケーションのresフォルダーに保存された画像や音声ファイルを直接読み込む方法がありません。
SDカードに保存された画像ファイルなら読み込むことはできますが、画像をテクスチャに表示するためには画像ファイルをBitmap(1次元配列)にデコードする必要があります。
ところがAndroid-NDKには画像ファイルをデコードするライブラリーが見当たりません。
Android-NDKで画像を扱うには次の選択肢があります。

1. AndroidのJavaのAPIで画像ファイルをデコードしたint配列をJNIに渡す。
2. 画像ファイルをデコードする処理をC/C++で自作する。
3. C言語で書かれたオープンソースの画像ローダーを利用する。

デバッグのしやすさから1番目の方法で画像ファイルをNDKで扱うことにします。

留意点
Android端末の機種によりますが、ビットマップ画像の扱い中にOutOfMemoryErrorが起きやすいです。
SH-03C(Android2.2)は 1440x1440ピクセルの画像を int配列(約8MB)にバッファーしようとすると必ず次のエラーが発生します。
Out of memory on a 8294416-byte allocation.
試行錯誤した結果、今回は次のような仕様にしました。

1. Javaで画像ファイルを読み込み、512×512に伸縮する。
2. 伸縮した画像をint配列に変換しJNIに渡す。
3. JNIのOpenGL APIで画像をテクスチャとして表示する。

このブログを書いている時点の開発環境は次の通りです。
・Windows8 (64bit)
・Eclipse Juno (32bit) (XP互換モードで起動)
・Android SDK r21.0.1
・Cygwin
・Android-NDK-r8d
※Android-NDKのビルド方法は環境に強く依存するため省略します。

2点以上のタッチ操作が必要なので、Android2.2以降でマルチタッチ対応の端末が必要です
新品で発売されているAndroid端末なら、中華Padも含めてほぼど対応していると思います。

以下ソースです。

MainActivity.java
jni.cpp
glu.h (Android-NDKのsampleの com.example.SanAngeles から抜粋しました)
Android.mk
jni.cpp、glu.h、Android.mk はプロジェクトにjniというフォルダーを追加して、そこにコピーします。
※特別な権限(Permission)は不要なのでマニフェストは省略します。
Android2.2以降で動作します。
Target SDK Version が8以上の設定でビルドできます。
Android-NDKのビルド方法は環境に強く依存するので省略させて頂きます。

留意点でも書きましたが、古いスマートフォン(Android2.2以前)で実際に使ってみると 前回のJavaだけで作ったアプリに比べて
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
が起きやすいです。
画像をint配列に変換するためのメモリーを確保したり、その逆の変換をするためのメモリーを確保したりするためメモリーの空き領域が不足がちになるようです。
さらにJavaだけで書いたアプリと体感速度を比べるても差がありませんでした。
当初の予想とは裏腹にデメリットの方が多かったのでタイトルに「失敗談」と付けました。

Android4.0なら安定して動作しますが体感速度は変わりません。

関連ブログ
[Android] BitmapクラスのOutOfMemoryErrorを防ぐ
[Android] BitmapクラスのOutOfMemoryErrorを防ぐ #2
[Android] フリーサイズの画像をOpenGLのテクスチャに表示する
[Android] OpenGLで簡易画像ビュアーを作る
[Android] ジェスチャーでOpenGL簡易画像ビュアーに拡大縮小機能を追加する
[Android] OpenGLでプロ生ちゃんをアニメーションさせる
[Android] OpenGL ESで文字を表示する
[Android] OpenGLで頂点の多いポリゴンを扱うには
[Android] OpenGLでシャープ製端末用3D(立体視)アプリケーションを作る
[Blender] obj2opengl.plをJava用に改造する
[Windows8][Android] Can't bind to local 8600 for debugger


以上、参考になれば幸いです。

0 件のコメント:

コメントを投稿