NDK開發 從入門到放棄(六:JAVA與C++灰化圖片的效率對比)

前言

前面我們提及了NDK開發的一些優勢,也講解了NDK開發的一些入門基礎。在此,我們將分別使用java代碼以及C++代碼對同一張圖片做同樣的灰化處理,來比較兩種方式的耗時以驗證NDK開發的高效性。
該實例中,將只給出關鍵部分代碼(Android.mk以及Application.mk以後都不再給出)。爲了鞏固之前的知識,我們使用動態註冊JNI的方式。

簡單實例

同樣,我們先上JNI類:

public class JNIDynamicUtils {
    /**
     * 調用C++代碼的方法,灰化圖片
     * @param buf
     * @param w
     * @param h
     * @return
     */
    public static native int[] grayPic(int[] buf, int w, int h);

    /**
     * 加載so庫或jni庫
     */
    static {
        System.loadLibrary("JNI_DYNAMIC_ANDROID_TEST");
    }
}

動態註冊JNI的C++代碼:

#include <stdio.h>
#include <jni.h>
#include <stdlib.h>

// C++灰化圖片的方法
jintArray nativeGrayPic(JNIEnv *env, jclass clazz, jintArray buf, jint w, jint h) {
    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, JNI_FALSE);
    if (cbuf == NULL) {
        return 0; /* exception occurred */
    }
    jint alpha = 0xFF << 24;
    for (jint i = 0; i < h; i++) {
        for (jint j = 0; j < w; j++) {
            // 獲得像素的顏色
            jint color = cbuf[w * i + j];
            jint red = ((color & 0x00FF0000) >> 16);
            jint green = ((color & 0x0000FF00) >> 8);
            jint blue = color & 0x000000FF;
            color = (red + green + blue) / 3;
            color = alpha | (color << 16) | (color << 8) | color;
            cbuf[w * i + j] = color;
        }
    }
    jint size=w * h;
    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, cbuf);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return result;
}

/**
 * JNINativeMethod由三部分組成:
 * (1)Java中的函數名;
 * (2)函數簽名,格式爲(輸入參數類型)返回值類型;
 *  ([III)[I ([III)表示三個參數,依次爲int[]、int、int;[I 表示返回值類型爲int[]
 * (3)native函數名
 */
static JNINativeMethod gMethods[] = {
    {"grayPic", "([III)[I", (void *) nativeGrayPic }
                                    };

//System.loadLibrary過程中會自動調用JNI_OnLoad,在此進行動態註冊
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = JNI_FALSE;

    //獲取env指針
    if (jvm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
        return result;
    }
    if (env == NULL) {
        return result;
    }
    //獲取類引用
    jclass clazz = env->FindClass("<包名>/JNIDynamicUtils");
    if (clazz == NULL) {
        return result;
    }
    //註冊方法
    if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
        return result;
    }
    //成功
    result = JNI_VERSION_1_6;
    return result;
}

測試方法很簡單,當點擊java方法時使用java代碼對圖片進行灰化,且記錄耗時:

long javaTimeBefore = System.currentTimeMillis();
// 使用java代碼把彩色像素轉爲灰度像素
Bitmap img = ConvertGrayImg(R.mipmap.bg_header);
long javaTime = System.currentTimeMillis() - javaTimeBefore;
//顯示灰度圖
ivPic1.setImageBitmap(img);

tvInfo.setText(tvInfo.getText().toString() + "\n" + "--->w:" + img.getWidth() + ",h:"
 + img.getHeight() + " JAVA TIME: " + javaTime + " ms");

其中,ConvertGrayImg方法的代碼如下:

/**
 * 把資源圖片轉爲灰度圖
 *
 * @param resID 資源ID
 * @return
 */
public Bitmap ConvertGrayImg(int resID) {
    Bitmap img1 = ((BitmapDrawable) getResources().getDrawable(resID)).getBitmap();

    int w = img1.getWidth(), h = img1.getHeight();
    int[] pix = new int[w * h];
    img1.getPixels(pix, 0, w, 0, 0, w, h);

    int alpha = 0xFF << 24;
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            // 獲得像素的顏色
            int color = pix[w * i + j];
            int red = ((color & 0x00FF0000) >> 16);
            int green = ((color & 0x0000FF00) >> 8);
            int blue = color & 0x000000FF;
            color = (red + green + blue) / 3;
            color = alpha | (color << 16) | (color << 8) | color;
            pix[w * i + j] = color;
        }
    }
    Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
    result.setPixels(pix, 0, w, 0, 0, w, h);
    return result;
}

當點擊NDK方法時使用C++代碼對圖片進行灰化,且記錄耗時:

long current = System.currentTimeMillis();
// 先打開圖像並讀取像素
Bitmap img1 = ((BitmapDrawable) ContextCompat.getDrawable(activity, R.mipmap.bg_header)).getBitmap();
int w = img1.getWidth();
int h = img1.getHeight();
int[] pix = new int[w * h];
img1.getPixels(pix, 0, w, 0, 0, w, h);
// 通過C++把彩色像素轉爲灰度像素
int[] resultInt = JNIDynamicUtils.grayPic(pix, w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
long ndkTime = System.currentTimeMillis() - current;
// 顯示灰度圖
ivPic2.setImageBitmap(resultImg);

tvInfo.setText(tvInfo.getText().toString() + "\n" + "--->w:" + img1.getWidth() + ",h:"
 + img1.getHeight() + " NDK  TIME: " + ndkTime+ " ms");

我們看到,兩種代碼的灰化處理方式是一致的,那麼執行效率如何呢?結果是java代碼完敗,C++代碼的耗時不到java代碼的一半。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章