LoginSignup
3
2

More than 5 years have passed since last update.

Create Native Method on Android App

Last updated at Posted at 2013-03-26

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'.

jni/Android.mk
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
src/zyxw/project/cocopuri/util/Yuv420SpDecoder.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
Terminal
javah -classpath bin/classes -d jni zyxw.project.cocopuri.util.Yuv420SpDecoder
  • Generated A File

Auto generated a following file in 'jni'

jni/zyxw_project_cocopuri_util_Yuv420SpDecoder.h
/* 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...

jni/yuv420spDecoder.cpp
#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.
src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
    static {
        System.loadLibrary("yuv420spDecoder");
    }
  • Invoke native method from Java
src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
nYuv420spToRgb(oBuf, iBuf, width, height);

Entire Java Code…

src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
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

src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
    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)

jni/zyxw_project_cocopuri_util_Yuv420SpDecoder.h
/*
 * 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

Terminal
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)

Terminal
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

Terminal
<Path to Java 6>/javah -classpath bin/classes -d jni <Target Java class>
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2