##Add Native Support
On Eclipse package explorer, right click on the project, and select following menu
Android Tools > Add Native Support
Here, type to a form 'yuv420spDecoder'
Now, a 'jni' directory has been created at the project root. Also auto generated Android.mk in 'jni'.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := yuv420spDecoder
LOCAL_SRC_FILES := yuv420spDecoder.cpp
include $(BUILD_SHARED_LIBRARY)
##Create Method
- Define the native method in the class of JAVA
private native static final void nYuv420spToRgb(ByteBuffer oBuf, ByteBuffer iBuf, int width, int height);
Also ok 'public', 'non-static', 'final', etc…
- Run 'javah' Comand
javah -classpath bin/classes -d jni zyxw.project.cocopuri.util.Yuv420SpDecoder
- Generated A File
Auto generated a following file in 'jni'
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class zyxw_project_cocopuri_util_Yuv420SpDecoder */
#ifndef _Included_zyxw_project_cocopuri_util_Yuv420SpDecoder
#define _Included_zyxw_project_cocopuri_util_Yuv420SpDecoder
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: zyxw_project_cocopuri_util_Yuv420SpDecoder
* Method: nYuv420spToRgb
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;II)V
*/
JNIEXPORT void JNICALL Java_zyxw_project_cocopuri_util_Yuv420SpDecoder_nYuv420spToRgb
(JNIEnv *, jclass, jobject, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
- Write implementation
Write implementation code normally...
#include "zyxw_project_cocopuri_util_Yuv420SpDecoder.h"
#define LOG_TAG ("yuv420spDecoder")
static const int YUV_ELEMENT_MASK = 0xff;
static const int RED_ELEMENT_MASK = 0xff0000;
static const int GREEN_ELEMENT_MASK = 0xff00;
static const int BLUE_ELEMENT_MASK = 0xff;
static const int RED_ELEMENT_SHIFT = 6;
static const int GREEN_ELEMENT_SHIFT = 2;
static const int BLUE_ELEMENT_SHIFT = 10;
static const int ALPHA_ELEMENT_FULL = 0xff000000;
int max(int, int);
int min(int, int);
int getRgb(int, int, int);
int getRgbValueInRange(int);
void Java_zyxw_project_cocopuri_util_Yuv420SpDecoder_nYuv420spToRgb(JNIEnv* env, jclass thiz, jobject oBuf,
jobject iBuf, jint width, jint height) {
typedef char byte;
int* rgb = reinterpret_cast<int*>(env->GetDirectBufferAddress(oBuf));
byte* yuv = reinterpret_cast<byte*>(env->GetDirectBufferAddress(iBuf));
const int frameSize = width * height;
for (int i = 0; i < height; i++) {
int uvp = frameSize + (i >> 1) * width;
int u = 0x0, v = 0x0;
for (int j = 0; j < width; j++) {
int y = max(0x0, yuv[i * width + j] & YUV_ELEMENT_MASK) - 0x10;
if ((j & 1) == 0) {
v = (yuv[uvp++] & YUV_ELEMENT_MASK) - 0x80;
u = (yuv[uvp++] & YUV_ELEMENT_MASK) - 0x80;
}
*(rgb++) = getRgb(y, u, v);
}
}
return;
}
int max(int a, int b) {
return (a > b) ? a : b;
}
int min(int a, int b) {
return (a < b) ? a : b;
}
int getRgb(int y, int u, int v) {
int y0x4a8 = 0x4a8 * y;
int r = getRgbValueInRange(y0x4a8 + 0x662 * v);
int g = getRgbValueInRange(y0x4a8 - 0x341 * v - 0x190 * u);
int b = getRgbValueInRange(y0x4a8 + 0x812 * u);
return ALPHA_ELEMENT_FULL | ((r << RED_ELEMENT_SHIFT) & RED_ELEMENT_MASK)
| ((g >> GREEN_ELEMENT_SHIFT) & GREEN_ELEMENT_MASK) | ((b >> BLUE_ELEMENT_SHIFT) & BLUE_ELEMENT_MASK);
}
int getRgbValueInRange(int value) {
return max(0x0, min(value, 0x3ffff));
}
Invoke The Native Method
- Load the native library before invoke it.
static {
System.loadLibrary("yuv420spDecoder");
}
- Invoke native method from Java
nYuv420spToRgb(oBuf, iBuf, width, height);
Entire Java Code…
package zyxw.project.cocopuri.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
/**
* Don't move this class to other package or location!!
* Using native library {@value #NLIB_NAME}.
*
* @author Y
* @category Factory
* @since 10/08/2012
*/
public class Yuv420SpDecoder {
private static final int RANGE_MIN = 0x0;
private static final int RANGE_MAX = 0x3ffff;
private static final int YUV_ELEMENT_MASK = 0xff;
private static final int RED_ELEMENT_MASK = 0xff0000;
private static final int GREEN_ELEMENT_MASK = 0xff00;
private static final int BLUE_ELEMENT_MASK = 0xff;
private static final int RED_ELEMENT_SHIFT = 6;
private static final int GREEN_ELEMENT_SHIFT = 2;
private static final int BLUE_ELEMENT_SHIFT = 10;
private static final int ALPHA_ELEMENT_FULL = 0xff000000;
private static final int INT_CAPA = Integer.SIZE / Byte.SIZE;
private static final String NLIB_NAME = "yuv420spDecoder";
static {
System.loadLibrary(NLIB_NAME);
}
private Yuv420SpDecoder() {} // no ctor
/**
* Decode yuv420sp.
*
* @param yuv420sp
* This byte array (YUV420SP data) will be converted to Bitmap.
* @param width
* Width of the YUV420SP graphic
* @param height
* Height of the YUV420SP graphic
* @param useNative
* Whether to use native library {@value #NLIB_NAME}
* @return Decoded result.
*/
public static final Bitmap decodeYuv420sp(final byte[] yuv420sp, final int width, final int height, boolean useNative) {
int[] rgb = new int[width * height];
if (useNative) {
ByteBuffer oBuf = ByteBuffer.allocateDirect(INT_CAPA * rgb.length).order(ByteOrder.nativeOrder());
ByteBuffer iBuf = ByteBuffer.allocateDirect(yuv420sp.length).order(ByteOrder.nativeOrder()).put(yuv420sp);
nYuv420spToRgb(oBuf, iBuf, width, height);
oBuf.asIntBuffer().get(rgb);
} else {
yuv420spToRgb(rgb, yuv420sp, width, height);
}
Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
result.setPixels(rgb, 0, width, 0, 0, width, height);
return result;
}
/**
* @param rgb
* This int array will recieve the result.
* @param yuv420sp
* This byte array (YUV420SP data) will be converted to RGB.
* @param width
* Width of the YUV420SP graphic.
* @param height
* Height of the YUV420SP graphic.
*/
private static final void yuv420spToRgb(int[] rgb, final byte[] yuv420sp, final int width, final int height) {
final int frameSize = width * height;
for (int i = 0; i < height; i++) {
int uvp = frameSize + (i >> 1) * width;
int u = 0x0, v = 0x0;
for (int j = 0; j < width; j++) {
int y = Math.max(0x0, yuv420sp[i * width + j] & YUV_ELEMENT_MASK) - 0x10;
if ((j & 1) == 0) {
v = (yuv420sp[uvp++] & YUV_ELEMENT_MASK) - 0x80;
u = (yuv420sp[uvp++] & YUV_ELEMENT_MASK) - 0x80;
}
rgb[i * width + j] = getRgb(y, u, v);
}
}
}
private static int getRgb(int y, int u, int v) {
int y0x4a8 = 0x4a8 * y;
int r = getRgbValueInRange(y0x4a8 + 0x662 * v);
int g = getRgbValueInRange(y0x4a8 - 0x341 * v - 0x190 * u);
int b = getRgbValueInRange(y0x4a8 + 0x812 * u);
return ALPHA_ELEMENT_FULL
| ((r << RED_ELEMENT_SHIFT) & RED_ELEMENT_MASK)
| ((g >> GREEN_ELEMENT_SHIFT) & GREEN_ELEMENT_MASK)
| ((b >> BLUE_ELEMENT_SHIFT) & BLUE_ELEMENT_MASK);
}
private static int getRgbValueInRange(int value) {
return Math.max(RANGE_MIN, Math.min(value, RANGE_MAX));
}
/**
* This method will be called in {@link #decodeYuv420sp(byte[], int, int, boolean)} when that method's boolean param
* is true. This use native library {@value #NLIB_NAME}.
*/
private native static final void nYuv420spToRgb(ByteBuffer oBuf, ByteBuffer iBuf, int width, int height);
}
Try to compare performances of Java and C/C++ with this code.
##About Overload
private native final void toNegative(ByteBuffer buf, int w, int h);
private native boolean toNegative(String inputFileName, String outputFileName);
If overload method exists, signatures would be added to native method's name. (Of course, auto generated with javah)
/*
* Class: com_example_opencv_nativetest_FullscreenActivity
* Method: toNegative
* Signature: (Ljava/nio/ByteBuffer;II)V
*/
JNIEXPORT void JNICALL Java_com_example_opencv_nativetest_FullscreenActivity_toNegative__Ljava_nio_ByteBuffer_2II
(JNIEnv *, jobject, jobject, jint, jint);
/*
* Class: com_example_opencv_nativetest_FullscreenActivity
* Method: toNegative
* Signature: (Ljava/lang/String;Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_com_example_opencv_nativetest_FullscreenActivity_toNegative__Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv *, jobject, jstring, jstring);
However, don't care of them.
Write implementation code normally...
Difference of JDK version
- Difference of JDK 6.x and 7.x
Case of java 6.x, javah command is
javah -classpath bin/classes -d jni <Target Java class>
But, run same command on Java 7.x,
Error: cannot access android.app.Activity
class file for android.app.Activity not found
This error returned…
So, try the following command (on Java 7.x)
javah -classpath bin/classes;<Path to Android-SDK>/platforms/android-<Target SDK version>/android.jar -d jni <Target Java class>
This time, Permission denied is returned…
- Solution
Use Java 6!
If wanna use Java 7, Install both, and run following command
<Path to Java 6>/javah -classpath bin/classes -d jni <Target Java class>