本文仅作为记录之用
案例: BitmapBlur 图片虚化库.
编译JAVA
package com.a.b.c; import android.content.Context; import android.graphics.Bitmap; import android.os.Build.VERSION; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RenderScript; import android.renderscript.ScriptIntrinsicBlur; public class BitmapBlur { static{ System.loadLibrary("BitmapBlur"); } /** * blur src bitmap * @param src * @param cxt * @param radius 0 <= radius <= 25 * @return */ public static Bitmap getBlurBitmap(Bitmap src, Context cxt, int radius){ return getBlurBitmap(src, cxt, radius, false); } /** * blur src bitmap by JNI * @param src * @param radius * @return */ public static Bitmap getBlurBitmapJNI(Bitmap src, int radius){ return getBlurBitmap(src, null, radius, true); } /** * blur src, if SDK > 16, use SDK, else use JNI; * @param src * @param cxt * @param radius radius 0 <= radius <= 25 * @param forceJNI * @return */ public static Bitmap getBlurBitmap(Bitmap src, Context cxt, int radius, boolean forceJNI){ if (VERSION.SDK_INT > 16 && !forceJNI) { Bitmap bitmap = src.copy(src.getConfig(), true); final RenderScript rs = RenderScript.create(cxt); final Allocation input = Allocation.createFromBitmap(rs, src, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); final Allocation output = Allocation.createTyped(rs, input.getType()); final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); script.setRadius(radius /* e.g. 3.f */); script.setInput(input); script.forEach(output); output.copyTo(bitmap); return bitmap; }else{ blur(src, radius); } return src; } public native static void blurArray(int[] bm, int w, int h, int radius); public native static void blur(Bitmap bm, int radius); }
拷贝BitmapBlur.class到JNI目录下:
mkdir -p com/a/b/c/
cp xxx/BitmapBlur.class com/a/b/c/
生成JNI 头文件
javah -classpath . -d jni com.a.b.c.BitmapBlur 生成jni/com_a_b_c_BitmapBlur.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_a_b_c_BitmapBlur */ #ifndef _Included_com_a_b_c_BitmapBlur #define _Included_com_a_b_c_BitmapBlur #ifdef __cplusplus extern "C" { #endif /* * Class: com_a_b_c_BitmapBlur * Method: blurArray * Signature: ([IIII)V */ JNIEXPORT void JNICALL Java_com_a_b_c_BitmapBlur_blurArray (JNIEnv *, jclass, jintArray, jint, jint, jint); /* * Class: com_a_b_c_BitmapBlur * Method: blur * Signature: (Landroid/graphics/Bitmap;I)V */ JNIEXPORT void JNICALL Java_a_b_c_BitmapBlur_blur (JNIEnv *, jclass, jobject, jint); #ifdef __cplusplus } #endif #endif
编写CPP文件com_a_b_c_BitmapBlur.cpp
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <com_a_b_c_BitmapBlur.h> #include <android/bitmap.h> #include <android/log.h> #include <blur.c> #include <stdio.h> JNIEXPORT void JNICALL Java_a_b_c_BitmapBlur_blurArray (JNIEnv *env, jclass thiz, jintArray arrIn, jint w, jint h, jint r) { jint *pix; pix = env->GetIntArrayElements(arrIn, 0); if (pix == NULL) return; //Start pix = blurIntArray(pix, w, h, r); //End //int size = w * h; //jintArray result = env->NewIntArray(size); //env->SetIntArrayRegion(result, 0, size, pix); env->ReleaseIntArrayElements(arrIn, pix, 0); //return result; } JNIEXPORT void JNICALL Java_a_b_c_BitmapBlur_blur (JNIEnv *env, jclass thiz, jobject bitmapIn, jint r) { /***************************************/ AndroidBitmapInfo infoIn; void* pixelsIn; int ret; // Get image info if ((ret = AndroidBitmap_getInfo(env, bitmapIn, &infoIn)) < 0) return; // Check image if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888) return; // Lock all images if ((ret = AndroidBitmap_lockPixels(env, bitmapIn, &pixelsIn)) < 0) { //AndroidBitmap_lockPixels failed! } //height width int h = infoIn.height; int w = infoIn.width; //Start pixelsIn = blurIntArray((int*)pixelsIn, w, h, r); //End // Unlocks everything AndroidBitmap_unlockPixels(env, bitmapIn); /**********************************/ }
blur.c
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> #include <math.h> #include <android/bitmap.h> #include <android/log.h> #include <stdio.h> /* Header for class com_a_b_c_BitmapBlur */ #include<malloc.h> #define ABS(a) ((a)<(0)?(-a):(a)) #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b)) /* * Class: com_a_b_c_BitmapBlur * Method: blur * Signature: (Landroid/graphics/Bitmap;)V */ static int* blurIntArray(int* pix, int w, int h, int radius) { int wm = w - 1; int hm = h - 1; int wh = w * h; int div = radius + radius + 1; int *r = (int *)malloc(wh * sizeof(int)); int *g = (int *)malloc(wh * sizeof(int)); int *b = (int *)malloc(wh * sizeof(int)); int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int *vmin = (int *)malloc(MAX(w,h) * sizeof(int)); int divsum = (div + 1) >> 1; divsum *= divsum; int *dv = (int *)malloc(256 * divsum * sizeof(int)); for (i = 0; i < 256 * divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int(*stack)[3] = (int(*)[3])malloc(div * 3 * sizeof(int)); int stackpointer; int stackstart; int *sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum; int rinsum, ginsum, binsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = pix[yi + (MIN(wm, MAX(i, 0)))]; sir = stack[i + radius]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rbs = r1 - ABS(i); rsum += sir[0] * rbs; gsum += sir[1] * rbs; bsum += sir[2] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (y == 0) { vmin[x] = MIN(x + radius + 1, wm); } p = pix[yw + vmin[x]]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; yp = -radius * w; for (i = -radius; i <= radius; i++) { yi = MAX(0, yp) + x; sir = stack[i + radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; rbs = r1 - ABS(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (x == 0) { vmin[y] = MIN(y + r1, hm) * w; } p = x + vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi += w; } } free(r); free(g); free(b); free(vmin); free(dv); free(stack); return(pix); }
编写Android.mk文件
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libBitmapBlur LOCAL_SRC_FILES := com_a_b_c_BitmapBlur.cpp LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libcutils \ libjnigraphics include $(BUILD_SHARED_LIBRARY)
普通的JNI编译到此结束.
接下来只需要通过mm / mmm命令编译即可生成相应lib库.
以下补充一些遇到的问题:
1. 引用让部lib库.
在编译当前的lib时, 需要依赖到libExt1.so, libExt2.so, 我们把两个lib库放在编译目录的lib/目录下, 同时, 新建lib/Android.mk文件, 内容如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libExt1.so LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_MODULE_STEM := $(LOCAL_MODULE) LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_MODULE_SUFFIX := include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_MODULE := libExt2.so LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_MODULE_STEM := $(LOCAL_MODULE) LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_MODULE_SUFFIX := include $(BUILD_PREBUILT)
在需要编译的模块的Android.mk中, 添加如下:
LOCAL_SHARED_LIBRARIES := libExt1 libExt2
在这之前曾错误的使用了: include $(call all-subdir-makefiles), 导致编译出现很多问题:
最常见的make: *** No rule to make target 'packages/jni/BTP_S80/lib/com_example_testscanprn_Printer.cpp', needed by 'out/target/product/XXX/obj/SHARED_LIBRARIES/libScanPrnWrap_intermediates/com_example_testscanprn_Printer.o'. Stop.
2. 源码文件,
LOCAL_SRC_FILES := com_a_b_c_BitmapBlur.cpp
使用错误的文件名, 也同样可以编译通过, 并生成so库, 但so库在JAVA调用时, 会提示
java.lang.UnsatisfiedLinkError: No implementation found for 后面紧跟的是函数名.
3. 关于javah.
javah -classpath . -d jni a.b.c.Printer 这里需注意classpath后面的参数
如, 刚开始是把native函数写在Activity中, 后面发现, 会出现找不到android.app.Activity类的错误
网上也有对应的解决方案, 但没时间去细细研究, 建议, 把native函数放在单的类中定义, 这样用javah生成.h文件时会少走些弯路.