tdnki @ ウィキ
NativeCode
最終更新:
tdnki
-
view
2. nativeコードへの移行
前回のコードによる描画処理には、Xperia VLで50ms~70ms程度かかっていた(15~20fps)。
これからさらに重い画像処理を追加しながらも、30fps程度は実現したいため、
C/C++による実装に切り替えて高速化を図っていく。
これからさらに重い画像処理を追加しながらも、30fps程度は実現したいため、
C/C++による実装に切り替えて高速化を図っていく。
前回、Javaで書いたpreviewBufferRgbaを書き込む処理を、C++で置き換えることを目標とする。
2-1. nativeサポートの追加
プロジェクト上で右クリックし、[Android Tools] > [Add Native Support...] を選択する。
適当なライブラリ名を入力して決定すると、プロジェクトルートにjniフォルダが作成される。
以降、ツールバーのとんかちのアイコンや、プロジェクトのビルドのタイミングでnativeコードがビルドされる。
適当なライブラリ名を入力して決定すると、プロジェクトルートにjniフォルダが作成される。
以降、ツールバーのとんかちのアイコンや、プロジェクトのビルドのタイミングでnativeコードがビルドされる。
早速ビルドしてみようととんかちを押すと、次の警告が出る。
APP_PLATFORM android-** is larger than android:minSdkVersion ** in ./AndroidManifest.xml
自動で設定されたnativeコードのターゲットが、アプリ側で設定したminSdkVersionより大きいことを示す警告で、
アプリ側ではエラーとして扱われる。
jniフォルダにApplication.mkファイルを追加し、minSdkVersionを超えない値をターゲットとすることで解決できる。
アプリ側ではエラーとして扱われる。
jniフォルダにApplication.mkファイルを追加し、minSdkVersionを超えない値をターゲットとすることで解決できる。
Application.mk
APP_PLATFORM := android-8
2-2. native関数を呼ぶ準備
関数を呼びたいクラスに、関数定義と、staticイニシャライザでsoを読み込む処理を追加する。
MainActivity.java
private native boolean processImage(byte[] src, int[] dst, int width, int height); static { System.loadLibrary("ComicFinder"); }
次にヘッダファイルを作成する。
自分で書こうとすると、何らかのミスで実行時にUnsatisfiedLinkErrorが出るのがオチなので、javahで自動生成しておきたい。
コマンドは次のようになるか。(カレントがプロジェクトルートであることを想定)
自分で書こうとすると、何らかのミスで実行時にUnsatisfiedLinkErrorが出るのがオチなので、javahで自動生成しておきたい。
コマンドは次のようになるか。(カレントがプロジェクトルートであることを想定)
javah -classpath ".\bin\classes; <android-sdkのパス>\platforms\android-14\data\layoutlib.jar" -o .\jni\ComicFinder.hpp com.example.comicfinder.MainActivity
ヘッダファイルができたら、実体もコーディングする。
ひとまずは呼び出しが上手くいくかを確認したいため、空の関数を書いておく。
ひとまずは呼び出しが上手くいくかを確認したいため、空の関数を書いておく。
ComicFinder.cpp
#include "ComicFinder.hpp" JNIEXPORT bool JNICALL Java_com_example_comicfinder_MainActivity_processImage (JNIEnv *env, jobject me, jbyteArray src, jintArray dst, jint width, jint height) { return false; }
アプリ側で呼び出し。
MainActivity.java
private void updateFrame(byte[] data) { processImage(data, previewBufferRgba, previewSize.width, previewSize.height);
これを実行してエラーが起きなければ成功。
2-3. 関数の実装
GetPrimitiveArrayCritical()で、Javaの配列にnative側から触れるポインタが取得できる。
ComicFinder.cpp
#include <stdlib.h> #include "ComicFinder.hpp" JNIEXPORT bool JNICALL Java_com_example_comicfinder_MainActivity_processImage (JNIEnv *env, jobject me, jbyteArray src, jintArray dst, jint width, jint height) { unsigned char *p_src = reinterpret_cast<unsigned char *>(env->GetPrimitiveArrayCritical(src, NULL)); if (NULL == p_src) { return false; } unsigned char *p_dst = reinterpret_cast<unsigned char *>(env->GetPrimitiveArrayCritical(dst, NULL)); if (NULL == p_dst) { env->ReleasePrimitiveArrayCritical(src, p_src, 0); return false; } for (int k = 0; k < width*height; k++) { p_dst[k*4+0] = p_dst[k*4+1] = p_dst[k*4+2] = p_src[k]; p_dst[k*4+3] = 0xff; } env->ReleasePrimitiveArrayCritical(dst, p_dst, 0); env->ReleasePrimitiveArrayCritical(src, p_src, 0); return true; }
2-4. 目標達成
描画処理の時間は30ms程度となった。
が、複雑な画像処理をしていないにも関わらず、すでに30fpsぎりぎりである。
画像処理自体は5msで終わっているため、Surfaceへの描画が完全にボトルネックとなっている。
まずこちらの処理の高速化や並列化を検討する必要があるかもしれない。
が、複雑な画像処理をしていないにも関わらず、すでに30fpsぎりぎりである。
画像処理自体は5msで終わっているため、Surfaceへの描画が完全にボトルネックとなっている。
まずこちらの処理の高速化や並列化を検討する必要があるかもしれない。
ここまでのソース
ComicFinder.zip