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

	:
	略
	:

}

	:
	略
	: