tdnki @ ウィキ
http://w.atwiki.jp/tdnki/
tdnki @ ウィキ
ja
2014-02-05T11:20:39+09:00
1391566839
-
Android/TIPS
https://w.atwiki.jp/tdnki/pages/51.html
***&spanclass(headline){ライブラリにjavadocを関連付ける}
Androidプロジェクトのlibsフォルダに配置したライブラリは、自動的にビルドパス(Android Private Libraries)に追加されるが、ソースやjavadocのロケーションを変更できない。
回避策は以下。
1. ソースとjavadocを適当な場所に置く。
2. ライブラリのファイル名(拡張子.jarを含む)に.propertiesを付加したテキストファイルを作り、ライブラリと同階層に置く。
(ex.
libs
├ library.jar
├ library.jar.properties
├ src
│ └ library_sources.jar
└ doc
└ library_javadoc.jar
3. propertiesファイルに、ソースとjavadocのパスをそれぞれ"src","doc"で指定する。
library.jar.properties
src=src/library_sources.jar
doc=doc/library_javadoc.jar
4. プロジェクトを開きなおす。
反映されるタイミングがよくわからないが、プロジェクトを開きなおせば確実。
2014-02-05T11:20:39+09:00
1391566839
-
メニュー
https://w.atwiki.jp/tdnki/pages/2.html
***[[トップページ]]
----
Android
***[[漫画風カメラファインダーアプリ作成>ComicFinder]]
***[[TIPS>Android/TIPS]]
----
***[[Eclipse TIPS]]
***[[Android旧記事>Android]]
----
***リンク
-[[@wiki>>http://atwiki.jp]]
-[[@wikiご利用ガイド>>http://atwiki.jp/guide/]]
2014-01-09T12:34:41+09:00
1389238481
-
ComicFinder/OpenGLDrawing
https://w.atwiki.jp/tdnki/pages/49.html
***&spanclass(headline){7. OpenGLによる描画}
これまでの実装では、画像処理から描画までをメインスレッドでシーケンシャルに実行しているだけなので、FPSが安定しない。
描画をOpenGLに委譲することで、FPSの安定化を図っていく。
***&spanclass(bold){7-1. }
2013-10-27T13:42:12+09:00
1382848932
-
ComicFinder
https://w.atwiki.jp/tdnki/pages/40.html
目標:C++を利用した高速なAndroidカメラアプリを作る。
ポイント:
1. Cameraハードウェアを利用する。
2. JNIによりJavaからC++を呼び出す。
3. NEONを使用して高速化する。
目次:
[[0. 準備>ComicFinder/Preparation]]
[[1. カメラプレビューの表示>ComicFinder/CameraPreview]]
[[2. nativeコードへの移行>ComicFinder/NativeCode]]
[[3. 描画の高速化>ComicFinder/FasterDrawing]]
[[4. 画像処理>ComicFinder/ImageProcessing]]
[[5. 画像処理の高速化>ComicFinder/Optimization]]
[[6. 画像処理の高速化(NEON編)>ComicFinder/OptimizationNeon]]
[[7. OpenGLによる描画(作成中)>ComicFinder/OpenGLDrawing]]
2013-10-24T14:12:35+09:00
1382591555
-
ComicFinder/OptimizationNeon
https://w.atwiki.jp/tdnki/pages/48.html
***&spanclass(headline){6. 画像処理の高速化(NEON編)}
NEON(ARMのCPU拡張命令)を使用して、さらなる高速化を図る。
***&spanclass(bold){6-1. 準備}
NEONを有効にする。
Android.mk
LOCAL_ARM_NEON := true
これだけだと、NEONはv7aでしかサポートしていない、とビルドエラーが出るので
v7a向けのビルドを明示する。
Application.mk
APP_ABI := armeabi-v7a
Android.mk
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
LOCAL_ARM_NEON := true
LOCAL_CFLAGS += -DENABLE_NEON
endif
もし APP_ABI をデフォルトの armeabi に戻してもビルドエラーとならないよう、TARGET_ARCH_ABI を見るようにした。
LOCAL_CFLAGS に渡したのは、今後、ビルド対象によってコード側も ifdef で切り分けるための define なので、
わかりやすい名前なら何でも良い。
ちなみに、これだけでかなり最適化がかかり、速くなってしまった。
40ms → 30ms
***&spanclass(bold){6-2. 方針}
NEONでは64bitか128bitのベクタを使用できるので、unsigned charのピクセルデータは16個を並列処理できる。
ただし、今回はSobelフィルタの途中計算で16bit精度が必要となるので、8個ずつループを回して処理していく。
画像幅が8で割り切れない場合は端数処理が必要なので、水平方向のループカウンタjを外に出した。
ComicFinder.cpp
for (int i = 0; i < height; i++) {
int j = 0;
#ifdef ENABLE_NEON
for ( ; j < width-7; j+=8) {
// NEONコード
}
#endif
for ( ; j < width; j++) {
// これまでのコード
}
}
本来はアセンブラを直で書かなければならないが、arm_neon.hで定義されているintrinsicsを使えば、
比較的高い可読性を持ちながら、NEON命令にコンパイルされるコードを書くことができる。
使用できる関数は[[ARM Complier Reference>http://infocenter.arm.com/help/topic/com.arm.doc.dui0491c/DUI0491C_arm_compiler_reference.pdf]]を参照のこと。
ComicFinder.cpp
#ifdef ENABLE_NEON
#include <arm_neon.h>
#endif
***&spanclass(bold){6-3. NEONコード}
ラインの冒頭でスクリーントーンのパターンを定義する。座標の偶奇によって白黒を反転させる。
uint8x8_t v_tone = vcreate_u8((i % 2) == 0 ? 0xff00ff00ff00ff00 : 0x00ff00ff00ff00ff);
三値化の置き換え。
ベクタの要素単位にif文を書くような事はできないので、vcge等の比較結果のビットマスクを返す関数を使用する。
黒、灰、白それぞれについて、各々の値を表すベクタとビットマスクの論理積を取れば、
これらの論理和が三値化後のベクタとなる。
uint8x8_t v_src = vld1_u8(p_src+i*width+j);
uint8x8_t v_mask_white = vcge_u8(v_src, vdup_n_u8(threshold_gray_and_white));
uint8x8_t v_mask_gray = veor_u8(v_mask_white, vcge_u8(v_src, vdup_n_u8(threshold_black_and_gray)));
uint8x8_t v_lum = vorr_u8(v_mask_white, vand_u8(v_tone, v_mask_gray));
比較が真なら全てのビットが1、偽なら全てのビットが0となるが、
これはそのまま白ピクセルおよび黒ピクセルと解釈できるので、不要な論理演算は排除した。
(例えば最後の行の v_mask_white は、正確には vand_u8(vdup_n_u8(0xff), v_mask_white) とするべきだが、
白ピクセルの値が0xffであることから、この演算の結果は v_mask_white に一致する。
同様の理由により、黒ピクセルに関してはマスクを作る必要すらない。)
Sobelフィルタの置き換え。
近傍8ピクセルをラインバッファからロードする。
この後で符号付き演算のオペランドに使うのでsignedに変換するが、wrap aroundが起きないよう16bitへの拡張も済ませておく。
int16x8_t v_src00 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line0+j+0)));
int16x8_t v_src01 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line0+j+1)));
int16x8_t v_src02 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line0+j+2)));
int16x8_t v_src10 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line1+j+0)));
int16x8_t v_src12 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line1+j+2)));
int16x8_t v_src20 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line2+j+0)));
int16x8_t v_src21 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line2+j+1)));
int16x8_t v_src22 = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(line2+j+2)));
エッジ判定。
int16x8_t v_fx = vdupq_n_s16(0);
v_fx = vsubq_s16(v_fx, v_src00);
v_fx = vsubq_s16(v_fx, vshlq_n_s16(v_src10, 1));
v_fx = vsubq_s16(v_fx, v_src20);
v_fx = vaddq_s16(v_fx, v_src02);
v_fx = vaddq_s16(v_fx, vshlq_n_s16(v_src12, 1));
v_fx = vaddq_s16(v_fx, v_src22);
int16x8_t v_fy = vdupq_n_s16(0);
v_fy = vsubq_s16(v_fy, v_src00);
v_fy = vsubq_s16(v_fy, vshlq_n_s16(v_src01, 1));
v_fy = vsubq_s16(v_fy, v_src02);
v_fy = vaddq_s16(v_fy, v_src20);
v_fy = vaddq_s16(v_fy, vshlq_n_s16(v_src21, 1));
v_fy = vaddq_s16(v_fy, v_src22);
uint8x8_t v_edge = vclt_u8(vqmovun_s16(vaddq_s16(vabsq_s16(v_fx), vabsq_s16(v_fy))), vdup_n_u8(threshold_edge));
三値化の結果とマージ。
v_lum = vand_u8(v_lum, v_edge);
RGBAの順にインターリーブする形でストア。
uint8x8x4_t v_dst;
v_dst.val[0] = v_lum;
v_dst.val[1] = v_lum;
v_dst.val[2] = v_lum;
v_dst.val[3] = vdup_n_u8(0xff);
vst4_u8(p_dst+(i*width+j)*4, v_dst);
NEON化により、画像処理時間は 30ms → 10ms となった。
ここまでのソース
#ref(ComicFinder.zip)
2013-10-24T14:07:32+09:00
1382591252
-
ComicFinder/Optimization
https://w.atwiki.jp/tdnki/pages/47.html
***&spanclass(headline){5. 画像処理の高速化}
30fps(33ms)を目指して高速化していく。
現時点で1フレームの画像処理にかかる時間は80ms。
画像処理とは別に、描画にすでに10ms消費しているので、画像処理は23ms以下を目標とする。
***&spanclass(bold){5-1. ループ統合}
画像の3値化と輪郭抽出で、2回に分けて全ピクセルをなめているが、
これは統合可能。
ComicFinder.cpp
int lum, fx, fy, im, ip, jm, jp;
for (int i = 0; i < height; i++) {
im = i-1;
ip = i+1;
im = im < 0 ? 0 : im;
ip = ip > height-1 ? height-1 : ip;
for (int j = 0; j < width; j++) {
lum = p_src[i*width+j];
if (lum < threshold_black_and_gray) {
lum = 0x00;
}
else if (lum < threshold_gray_and_white) {
lum = ((i+j) % 2) == 0 ? 0x00 : 0xff;
}
else {
lum = 0xff;
}
if (lum != 0x00) {
jm = j-1;
jp = j+1;
jm = jm < 0 ? 0 : jm;
jp = jp > width-1 ? width-1 : jp;
fx = 0;
fx += p_src[im*width+jm] * -1;
fx += p_src[i *width+jm] * -2;
fx += p_src[ip*width+jm] * -1;
fx += p_src[im*width+jp] * 1;
fx += p_src[i *width+jp] * 2;
fx += p_src[ip*width+jp] * 1;
fy = 0;
fy += p_src[im*width+jm] * -1;
fy += p_src[im*width+j ] * -2;
fy += p_src[im*width+jp] * -1;
fy += p_src[ip*width+jm] * 1;
fy += p_src[ip*width+j ] * 2;
fy += p_src[ip*width+jp] * 1;
lum = abs(fx) + abs(fy);
lum = lum < threshold_edge ? 0xff : 0x00;
}
p_dst[(i*width+j)*4+0] = p_dst[(i*width+j)*4+1] = p_dst[(i*width+j)*4+2] = lum;
p_dst[(i*width+j)*4+3] = 0xff;
}
}
80ms → 50ms。
***&spanclass(bold){5-2. ラインバッファ}
上下左右のピクセルを参照するSobelフィルタは、領域外を参照しないよう端点で分岐処理(jm, jp等の座標計算に三項演算を使用)しているので、
今後予定しているNEONコードを利用した並列処理に置き換えにくい。
そのため、入力画像の1ラインに対して、両端のピクセルをコピーした
長さ(width+2)のラインバッファを3本用意する形に変更し、端点の条件分岐を消しておく。
(NEON化しないとしても、条件分岐が消えることで高速化に繋がる可能性が高い。)
ComicFinder.cpp
// ラインバッファの初期化
unsigned char line_buf[(width+2)*3];
unsigned char *line0 = line_buf;
unsigned char *line1 = line0 + width+2;
unsigned char *line2 = line1 + width+2;
memcpy(line1+1, p_src, width);
line1[0] = line1[1];
line1[width+1] = line1[width];
memcpy(line2+1, p_src+width, width);
line2[0] = line2[1];
line2[width+1] = line2[width];
memcpy(line0, line1, width+2);
// ラインバッファを参照する形に変更
fx = 0;
fx += line0[j+0] * -1;
fx += line1[j+0] * -2;
fx += line2[j+0] * -1;
fx += line0[j+2] * 1;
fx += line1[j+2] * 2;
fx += line2[j+2] * 1;
fy = 0;
fy += line0[j+0] * -1;
fy += line0[j+1] * -2;
fy += line0[j+2] * -1;
fy += line2[j+0] * 1;
fy += line2[j+1] * 2;
fy += line2[j+2] * 1;
lum = abs(fx) + abs(fy);
lum = lum < threshold_edge ? 0xff : 0x00;
// heightのループの最後でラインバッファを更新
unsigned char *tmp = line0;
line0 = line1;
line1 = line2;
line2 = tmp;
memcpy(line2+1, p_src+(i+1 > height-1 ? height-1 : i+1)*width, width);
line2[0] = line2[1];
line2[width+1] = line2[width];
50ms → 40ms。
ここまでのソース
#ref(ComicFinder.zip)
2013-10-21T00:05:50+09:00
1382281550
-
Android
https://w.atwiki.jp/tdnki/pages/16.html
-[[Proguardについて>Android/Proguardについて]]
-[[リンク>Android/リンク]]
2013-10-17T00:45:59+09:00
1381938359
-
ComicFinder/ImageProcessing
https://w.atwiki.jp/tdnki/pages/45.html
***&spanclass(headline){4. 画像処理}
漫画風の画像処理を実装していく。
***&spanclass(bold){4-1. 三値化}
グレースケールの画像を、白、黒、グレーの三値に変換する。
閾値は動かしながら良いものを選ぶ。
ComicFinder.cpp
const int threshold_black_and_gray = 60;
const int threshold_gray_and_white = 120;
int lum;
for (int k = 0; k < width*height; k++) {
lum = p_src[k];
if (lum < threshold_black_and_gray) {
lum = 0x00;
}
else if (lum < threshold_gray_and_white) {
lum = 0x80;
}
else {
lum = 0xff;
}
p_dst[k*4+0] = p_dst[k*4+1] = p_dst[k*4+2] = lum;
p_dst[k*4+3] = 0xff;
}
より漫画っぽくするために、グレー(128/255)を、白と黒の市松模様で表現する。
グレーと判定されたピクセルのx座標とy座標のパリティが一致したら黒、不一致なら白とした。
ComicFinder.cpp
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
lum = p_src[i*width+j];
if (lum < threshold_black_and_gray) {
lum = 0x00;
}
else if (lum < threshold_gray_and_white) {
lum = ((i+j) % 2) == 0 ? 0x00 : 0xff;
}
else {
lum = 0xff;
}
p_dst[(i*width+j)*4+0] = p_dst[(i*width+j)*4+1] = p_dst[(i*width+j)*4+2] = lum;
p_dst[(i*width+j)*4+3] = 0xff;
}
}
***&spanclass(bold){4-2. 輪郭抽出}
三値化前の画像の輪郭を抽出し、主線として重ねあわせる。
輪郭抽出のアルゴリズムにはSobelフィルタを使用し、この結果を適当な閾値で二値化したものを主線とした。
ComicFinder.cpp
const int threshold_edge = 32;
int fx, fy, im, ip, jm, jp;
for (int i = 0; i < height; i++) {
im = i-1;
ip = i+1;
im = im < 0 ? 0 : im;
ip = ip > height-1 ? height-1 : ip;
for (int j = 0; j < width; j++) {
jm = j-1;
jp = j+1;
jm = jm < 0 ? 0 : jm;
jp = jp > width-1 ? width-1 : jp;
fx = 0;
fx += p_src[im*width+jm] * -1;
fx += p_src[i *width+jm] * -2;
fx += p_src[ip*width+jm] * -1;
fx += p_src[im*width+jp] * 1;
fx += p_src[i *width+jp] * 2;
fx += p_src[ip*width+jp] * 1;
fy = 0;
fy += p_src[im*width+jm] * -1;
fy += p_src[im*width+j ] * -2;
fy += p_src[im*width+jp] * -1;
fy += p_src[ip*width+jm] * 1;
fy += p_src[ip*width+j ] * 2;
fy += p_src[ip*width+jp] * 1;
lum = abs(fx) + abs(fy);
lum = lum < threshold_edge ? 0xff : 0x00;
p_dst[(i*width+j)*4+0] = p_dst[(i*width+j)*4+1] = p_dst[(i*width+j)*4+2] = lum & p_dst[(i*width+j)*4+0];
p_dst[(i*width+j)*4+3] = 0xff;
}
}
#ref(comicfinder_04_01.png)
アップロードする際に画像縮小したため、スクリーントーン部分が補間処理によってグレーに戻ってしまった。
アプリ内でも拡縮により同様のことが起き得るので、コード上でトーンを適用する位置については再考の余地がある。
ここまでのソース
#ref(ComicFinder.zip)
2013-09-04T01:51:39+09:00
1378227099
-
ComicFinder/CameraPreview
https://w.atwiki.jp/tdnki/pages/42.html
***&spanclass(headline){1. カメラプレビューの表示}
プレビュー画像に使用されるNV21フォーマットは、輝度のみのデータを取り出しやすいこと、
最終的な画像処理にも輝度しか使用しない予定であることから、モノクロのプレビュー表示をゴールとする。
***&spanclass(bold){1-1. Permissionを付加する}
AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA"/>
***&spanclass(bold){1-2. Activityの向きを固定する}
端末の回転を考慮したくないので、Orientationはlandscape固定とする。
AndroidManifest.xml
<activity
android:name="com.example.comicfinder.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
***&spanclass(bold){1-3. フルスクリーン}
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView()より先に呼ぶこと。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
***&spanclass(bold){1-4. Cameraオブジェクトの取得と破棄}
MainActivity.java
@Override
protected void onResume() {
super.onResume();
camera = Camera.open();
if (camera == null) {
finish();
return;
}
}
@Override
protected void onPause() {
super.onPause();
if (camera != null) {
camera.release();
}
}
***&spanclass(bold){1-5. プレビュー表示用Surfaceの準備}
activity_main.xml
<SurfaceView
android:id="@+id/surface"
android:layout_width="match_parent"
android:layout_height="match_parent" />
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
surface = (SurfaceView)findViewById(R.id.surface);
}
***&spanclass(bold){1-6. プレビュー画像の取得}
プレビューを表示する方法は大きく2通り。
A) Camera#setPreviewDisplay()で描画用のSurfaceを渡して、描画までの一切を依頼する。プレビュー画像には触れない。
B) 用意したバッファにCamera#addCallbackBuffer()でプレビュー画像を書き込んでもらい、(任意の加工をして)描画する。
今回はプレビュー画像を編集したいため、Bを選択する。
この場合は、描画処理まで自分で責任を持たなければいけない。
プレビュー画像書き込み依頼は非同期であり、Camera#setPreviewCallbackWithBuffer()により指定したcallbackが呼ばれるのを待つことになる。
MainActivity.java
@Override
protected void onResume() {
super.onResume();
camera = Camera.open();
if (camera == null) {
finish();
return;
}
Camera.Parameters params = camera.getParameters();
// プレビューフォーマットをNV21に設定
if (ImageFormat.NV21 != params.getPreviewFormat()) {
if (params.getSupportedPreviewFormats().contains(ImageFormat.NV21)) {
finish();
return;
}
params.setPreviewFormat(ImageFormat.NV21);
}
// プレビューサイズを(横幅が)最も大きいものに設定
for (Size s : params.getSupportedPreviewSizes()) {
if (params.getPreviewSize().width < s.width) {
params.setPreviewSize(s.width, s.height);
}
}
camera.setParameters(params);
previewSize = params.getPreviewSize();
// プレビューのrawデータ。8bit * (画サイズ) * 1.5
byte[] previewBuffer = new byte[previewSize.width * previewSize.height * 3 / 2];
// 描画用バッファ。32bit * (画サイズ)
previewBufferRgba = new int[previewSize.width * previewSize.height];
camera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
updateFrame(data);
}
});
camera.startPreview();
camera.addCallbackBuffer(previewBuffer);
}
@Override
protected void onPause() {
super.onPause();
if (camera != null) {
camera.stopPreview();
camera.release();
}
}
***&spanclass(bold){1-7. 描画画像の作成}
プレビュー画像のbyte列(NV21)をもとに、intの配列(RGBA)を書き込んで描画する。
NV21には、初めのwidth*heightピクセルに輝度が詰まっているので、これをそのまま読み出してRGBにコピーすればよい。
MainActivity.java
private void updateFrame(byte[] data) {
for (int k = 0; k < previewSize.width * previewSize.height; k++) {
int lum = data[k] & 0xff;
previewBufferRgba[k] = (0xff << 24) | (lum << 16) | (lum << 8) | lum;
}
Canvas c = surface.getHolder().lockCanvas();
if (c != null) {
try {
c.drawBitmap(previewBufferRgba, 0, previewSize.width, 0, 0, previewSize.width, previewSize.height, true, null);
}
finally {
surface.getHolder().unlockCanvasAndPost(c);
}
}
camera.addCallbackBuffer(data);
}
***&spanclass(bold){1-8. ディスプレイサイズに合わせてストレッチ}
float scale = Math.max((float)surface.getWidth() / previewSize.width, (float)surface.getHeight() / previewSize.height);
c.scale(scale, scale);
c.drawBitmap(previewBufferRgba, 0, previewSize.width, 0, 0, previewSize.width, previewSize.height, true, null);
変化していない公算が高い値を、除算を使って毎フレーム求めるのもどうかと思うが、
プレビューサイズ決定とSurfaceのサイズ確定を待ち合わせてcacheするのも面倒なので、この実装とする。
scaleの計算自体は1msecを切っており、パフォーマンスへの影響はほぼない。
***&spanclass(bold){1-9. 目標達成}
モノクロのプレビューを表示することができた。
#ref(comicfinder_01.png)
プレビュー表示に成功したエリエールタワー
ここまでのソース
#ref(ComicFinder.zip)
2013-09-04T00:20:48+09:00
1378221648
-
ComicFinder/FasterDrawing
https://w.atwiki.jp/tdnki/pages/44.html
***&spanclass(headline){3. 描画の高速化}
前回、Canvas#drawBitmap()が意外に遅いことがわかってしまった。
***&spanclass(bold){3-1. drawBitmap(Bitmap, float, float, Paint)を使う}
このメソッドに、今まではintの配列を渡していたが、Bitmapを渡すオーバーロードを呼んでみたところ、int[]に比べて3倍ほど高速であった。
そこで、描画データはBitmapで持つように修正する。
MainActivity.java
@Override
protected void onResume() {
:
:
// 描画用Bitmap
previewBitmap = Bitmap.createBitmap(previewSize.width, previewSize.height, Bitmap.Config.ARGB_8888);
:
:
}
@Override
protected void onPause() {
super.onPause();
if (camera != null) {
camera.stopPreview();
camera.release();
}
if (previewBitmap != null) {
previewBitmap.recycle();
}
}
***&spanclass(bold){3-2. nativeコードでBitmapを扱う}
int[]からBitmapを生成する処理もそこそこ重く、描画データをBitmapに変更することで浮くであろうマージンを食い潰してしまう。
幸いnativeからBitmapのピクセルデータに触る方法があるので、Bitmapをそのまま渡し、書き換える形に変更する。
C++がJavaのクラスを意識するのは気持ち悪いが、速度にはかえられない。
ComicFinder.hpp
JNIEXPORT bool JNICALL Java_com_example_comicfinder_MainActivity_processImage
(JNIEnv *, jobject ,jbyteArray, jobject, jint, jint);
ComicFinder.cpp
#include <stdlib.h>
#include <android/bitmap.h>
#include "ComicFinder.hpp"
JNIEXPORT bool JNICALL Java_com_example_comicfinder_MainActivity_processImage
(JNIEnv *env, jobject me, jbyteArray src, jobject dst, jint width, jint height)
{
int ret;
unsigned char *p_dst;
if ((ret = AndroidBitmap_lockPixels(env, dst, reinterpret_cast<void **>(&p_dst))) < 0) {
return false;
}
unsigned char *p_src = reinterpret_cast<unsigned char *>(env->GetPrimitiveArrayCritical(src, NULL));
if (NULL == p_src) {
AndroidBitmap_unlockPixels(env, dst);
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(src, p_src, 0);
AndroidBitmap_unlockPixels(env, dst);
return true;
}
jnigraphicsをリンクする。
Android.mk
LOCAL_LDLIBS := -ljnigraphics
***&spanclass(bold){3-3. まとめ}
描画が10msで回るようになった。
ここまでのソース
#ref(ComicFinder.zip)
2013-08-31T23:32:14+09:00
1377959534