Android で OpenGL ES 2.0 事始め JNI 編 〜android/bitmap.h を使ってみる〜
先程のサンプルにちょっと手お加えて今度は、ビットマップを JNI 側から読み込むように変更してみます。(この方式は Java 側からピクセルデータを渡す方式と比べて JNI 側で画像形式のハンドリングができる特徴があります)
今回は前回のサンプルからの差分のみを紹介します。変更が発生するソースは
- SimpleRenderer
- JniBridge
- Android.mk
- Application.mk
- main.cpp
です。
SimpleRenderer
onSurfaceCreated を変更しています。今回は JNI でビットマップを読み込むので onSurfaceCreated でビットマップからピクセルデータを読み込まず Bitmap オブジェクトを渡すように変更しています。
/* * Copyright (c) 2012 OrangeSignal.com All Rights Reserved. */ package com.orangesignal.android.example.gles20.gl; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLSurfaceView; import com.orangesignal.android.example.gles20.R; /** * ビットマップをテクスチャとして読み込んで {@link GLSurfaceView} の描画領域いっぱいに描画するだけのシンプルな {@link GLSurfaceView.Renderer} を提供します。 * * @author 杉澤 浩二 */ public final class SimpleRenderer implements GLSurfaceView.Renderer { : 略 : @Override public void onSurfaceCreated(final GL10 gl, final EGLConfig config) { final Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.sample); JniBridge.nativeOnSurfaceCreated(bitmap); bitmap.recycle(); } : 略 : }
JniBridge
nativeOnSurfaceCreated を変更しています。
/* * Copyright (c) 2012 OrangeSignal.com All Rights Reserved. */ package com.orangesignal.android.example.gles20.gl; import android.graphics.Bitmap; /** * JNI への橋渡しをするブリッジクラスを提供します。 * * @author 杉澤 浩二 */ public final class JniBridge { : 略 : public static native void nativeOnSurfaceCreated(Bitmap bitmap); : 略 : }
Android.mk
JNI で Bitmap から読み込むので、jnigraphics ライブラリを追加しています。
# # Copyright (c) 2012 OrangeSignal.com All Rights Reserved. # LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := example LOCAL_SRC_FILES := main.cpp # see ${ANDROID_NDK_HOME}/docs/STABLE-APIS.html LOCAL_LDLIBS := -llog # ログ機能を使用するので Android-specific Log Support を追加します。 LOCAL_LDLIBS += -lGLESv2 # OpenGL ES 2.0 を使用するので OpenGL ES 2.0 ライブラリを追加します。 LOCAL_LDLIBS += -ljnigraphics # Java ビットマップオブジェクトへアクセスするので jnigraphics ライブラリを追加します。(ソースコード上で使用するには <android/bitmap.h> ヘッダファイルをインクルードする必要があります) include $(BUILD_SHARED_LIBRARY)
Application.mk
jnigraphics ライブラリは API レベル 8 から使用可能なので、APP_PLATFORM で明確化しています。
#APP_STL := gnustl_static #APP_CPPFLAGS := -frtti -fexceptions APP_ABI := all #APP_ABI := armeabi armeabi-v7a APP_PLATFORM := android-8
main.cpp
JNI で Bitmap から読み込むので、
- インクルードに android/bitmap.h を追加しています。
- nativeOnSurfaceCreated のパラメータと実装を変更しています。
- nativeOnDrawFrame での背景ブレンド方法を GL_ONE, GL_ONE_MINUS_SRC_ALPHA へ変更しています。
尚、ピクセルデータ使用時と異なり ARGB ⇒ RGBA 変換をしていないことにも注意して下さい。
/* * Copyright (c) 2012 OrangeSignal.com All Rights Reserved. */ #include <jni.h> #include <stdio.h> #include <stdlib.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <android/bitmap.h> #include <android/log.h> ////////////////////////////////////////////////////////////////////////////// : 略 : ////////////////////////////////////////////////////////////////////////////// #ifdef __cplusplus extern "C" { #endif JNIEXPORT void JNICALL Java_com_orangesignal_android_example_gles20_gl_JniBridge_nativeOnSurfaceCreated(JNIEnv* env, jobject thiz, jobject bitmap); JNIEXPORT void JNICALL Java_com_orangesignal_android_example_gles20_gl_JniBridge_nativeOnSurfaceChanged(JNIEnv* env, jobject thiz, jint width, jint height); JNIEXPORT void JNICALL Java_com_orangesignal_android_example_gles20_gl_JniBridge_nativeOnDrawFrame(JNIEnv* env, jobject thiz); #ifdef __cplusplus } #endif ////////////////////////////////////////////////////////////////////////////// : 略 : JNIEXPORT void JNICALL Java_com_orangesignal_android_example_gles20_gl_JniBridge_nativeOnSurfaceCreated(JNIEnv* env, jobject thiz, jobject bitmap) { LOGI("nativeOnSurfaceCreated"); printGLString("Version", GL_VERSION); printGLString("Vendor", GL_VENDOR); printGLString("Renderer", GL_RENDERER); printGLString("Extensions", GL_EXTENSIONS); // プログラムを生成して使用可能にします。 program = createProgram(VERTEX_SHADER, FRAGMENT_SHADER); if (!program) { LOGE("Could not create program."); } glUseProgram(program); checkGlError("glUseProgram"); // シェーダで使用する変数のハンドルを取得し使用可能にします。 position = glGetAttribLocation(program, "position"); checkGlError("glGetAttribLocation position"); glEnableVertexAttribArray(position); texcoord = glGetAttribLocation(program, "texcoord"); checkGlError("glGetAttribLocation texcoord"); glEnableVertexAttribArray(texcoord); textures[0] = glGetUniformLocation(program, "texture"); checkGlError("glGetUniformLocation texture"); // テクスチャを作成します。(サーフェスが作成される度にこれを行う必要があります) int ret; AndroidBitmapInfo info; // ビットマップオブジェクトに関する情報を取得します。 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return; } // ビットマップの画像データをロックして画像データを操作できるようにします。 void* pixels; if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); } // テクスチャオブジェクトを作成して画像を与えます。 glGenTextures(1, textures); glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // ビットマップの画像データをアンロックします。 AndroidBitmap_unlockPixels(env, bitmap); // テクスチャを拡大/縮小する方法を設定します。 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // 縮小するときピクセルの中心に最も近いテクスチャ要素で補完 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 拡大するときピクセルの中心付近の線形で補完 } : 略 : JNIEXPORT void JNICALL Java_com_orangesignal_android_example_gles20_gl_JniBridge_nativeOnDrawFrame(JNIEnv* env, jobject thiz) { : 略 : // 背景とのブレンド方法を設定します。 glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // see http://ga29.blog.fc2.com/blog-entry-37.html : 略 : } : 略 :