做過Java的同學可能經常會遇到一些關於圖片處理的
例如類似QQ離線頭像顯示灰的。最快的算法是用colorMatrix來實現。這里通過Java調用JNI來處理每一個像素來實現。
- 對每一個像素點取出RGB每個通道的值R,G,B
- cololr=(R+G+B)/3;這個值是需要修改的值
- 將原來GRB的通道全設置成color的值
首先先看用Java怎么實現這個功能
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, Config.RGB_565); result.setPixels(pix, 0, w, 0, 0,w, h); return result; }
下面是JNI來處理
c++代碼如下
#include <jni.h> #include <string> extern "C"{ jintArray JNICALL Java_com_tmf_test_ndk_bitmap_MainActivity_getImgToGray(JNIEnv *env, jobject instance, jintArray data_, jint w, jint h); } JNIEXPORT jintArray JNICALL Java_com_tmf_test_ndk_bitmap_MainActivity_getImgToGray(JNIEnv *env, jobject instance, jintArray data_, jint w, jint h) { jint *data; data = env->GetIntArrayElements(data_, NULL); if (data == NULL) { return 0; /* exception occurred */ } int alpha = 0xFF << 24; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { // 獲得像素的顏色 int color = data[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; data[w * i + j] = color; } } int size=w * h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, data); env->ReleaseIntArrayElements(data_, data, 0); return result; }
activity實現如下
package com.tmf.test.ndk.bitmap; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ImageView; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method ImageView tv = (ImageView) findViewById(R.id.image); tv.setImageBitmap(getJniBitmap()); } public Bitmap getJniBitmap(){ Bitmap bitmap=((BitmapDrawable) getResources().getDrawable(R.mipmap.timg)).getBitmap(); int w=bitmap.getWidth(),h=bitmap.getHeight(); int[] pix = new int[w * h]; bitmap.getPixels(pix, 0, w, 0, 0, w, h); //通過ImgToGray.so把彩色像素轉為灰度像素 int[] resultInt=getImgToGray(pix, w, h); Bitmap resultImg=Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565); resultImg.setPixels(resultInt, 0, w, 0, 0,w, h); return resultImg; } public native int[] getImgToGray(int[] data,int w,int h); private int[] bitmapToArray(int resID) { Bitmap bitmap=((BitmapDrawable) getResources().getDrawable(resID)).getBitmap(); int w=bitmap.getWidth(),h=bitmap.getHeight(); int[] pix = new int[w * h]; bitmap.getPixels(pix, 0, w, 0, 0, w, h); return pix; } }
效果如下

bitmap.h
目錄android-ndk-r10e\platforms\android-21\arch-arm\usr\include\android里面
bitmap.h頭文件中的內容並不多,主要有這些部分組成:
- 結果狀態定義。
- 位圖格式枚舉。
- 位圖信息結構體。
- 位圖操作函數聲明。
define ANDROID_BITMAP_RESULT_SUCCESS 0 #define ANDROID_BITMAP_RESULT_BAD_PARAMETER -1 #define ANDROID_BITMAP_RESULT_JNI_EXCEPTION -2 #define ANDROID_BITMAP_RESULT_ALLOCATION_FAILED -3 /* Backward compatibility: this macro used to be misspelled. */ #define ANDROID_BITMAP_RESUT_SUCCESS ANDROID_BITMAP_RESULT_SUCCESS
這里定義了對Bitmap進行操作時的結果,分別對應成功,錯誤的參數,JNI異常,內存分配錯誤,至於最后一個,這是個梗。Google工程師在定義NDK的時候寫錯一個單詞,居然沒有檢查就發布了,然后就233333333了。看來IDE的拼寫檢查對自己人也有好處。
位圖格式枚舉
enum AndroidBitmapFormat { ANDROID_BITMAP_FORMAT_NONE = 0, ANDROID_BITMAP_FORMAT_RGBA_8888 = 1, ANDROID_BITMAP_FORMAT_RGB_565 = 4, ANDROID_BITMAP_FORMAT_RGBA_4444 = 7, ANDROID_BITMAP_FORMAT_A_8 = 8, };
一般而言,常見的位圖格式有RGB_565 、RGBA_8888、 ARGB_8888、 RGBA_4444、 ARGB_4444、 ALPHA_8
位圖信息結構體
typedef struct {
uint32_t width;
uint32_t height;
uint32_t stride;
int32_t format;
uint32_t flags; // 0 for now
} AndroidBitmapInfo;
width表示圖片的寬度(列數),height表示圖片的高度(行數),stride為行跨度,具體含義后面會進行介紹。最后一個參數已經被棄用,其值始終為0。
位圖操作函數聲明
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info); int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr); int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
- AndroidBitmap_getInfo:獲取當前位圖信息。
- AndroidBitmap_lockPixels:鎖定當前位圖像素,在鎖定期間該Bitmap對象不會被回收,使用完成之后必須調用AndroidBitmap_unlockPixels函數來解除對像素的鎖定。
- AndroidBitmap_unlockPixels:解除像素鎖定。
JNIEXPORT jboolean JNICALL Java_com_tmf_test_ndk_bitmap_MainActivity_getImgToGray1(JNIEnv *env, jclass type, jobject jsrcBitmap, jobject desBitmap) { AndroidBitmapInfo srcInfo, dstInfo; if (ANDROID_BITMAP_RESULT_SUCCESS != AndroidBitmap_getInfo(env, jsrcBitmap, &srcInfo) || ANDROID_BITMAP_RESULT_SUCCESS != AndroidBitmap_getInfo(env, desBitmap, &dstInfo)) { LOGE("get bitmap info failed"); return false; } void *srcBuf, *dstBuf; if (ANDROID_BITMAP_RESULT_SUCCESS != AndroidBitmap_lockPixels(env, jsrcBitmap, &srcBuf)) { LOGE("lock src bitmap failed"); return false; } if (ANDROID_BITMAP_RESULT_SUCCESS != AndroidBitmap_lockPixels(env, desBitmap, &dstBuf)) { LOGE("lock dst bitmap failed"); return false; } int w = srcInfo.width; int h = srcInfo.height; int32_t *srcPixs = (int32_t *) srcBuf; int32_t *desPixs = (int32_t *) dstBuf; int alpha = 0xFF << 24; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { // 獲得像素的顏色 int color = srcPixs[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; desPixs[w * i + j] = color; } } AndroidBitmap_unlockPixels(env, jsrcBitmap); AndroidBitmap_unlockPixels(env, desBitmap); return true; }
Java層代碼
public native boolean getImgToGray1(Bitmap src,Bitmap des);
效果和上面都一樣的,只是這個直接在底層處理bitmap對象
如果有以下異常
E:\test\NdkBitmap\app\src\main\cpp/native-lib.cpp:52: undefined reference to `AndroidBitmap_getInfo'
E:\test\NdkBitmap\app\src\main\cpp/native-lib.cpp:53: undefined reference to `AndroidBitmap_getInfo'
E:\test\NdkBitmap\app\src\main\cpp/native-lib.cpp:59: undefined reference to `AndroidBitmap_lockPixels'
E:\test\NdkBitmap\app\src\main\cpp/native-lib.cpp:64: undefined reference to `AndroidBitmap_lockPixels'
E:\test\NdkBitmap\app\src\main\cpp/native-lib.cpp:86: undefined reference to `AndroidBitmap_unlockPixels'
E:\test\NdkBitmap\app\src\main\cpp/native-lib.cpp:87: undefined reference to `AndroidBitmap_unlockPixels'
我用的是Android studio自帶的cmake打包的,需要在
在CMakeLists.txt 添加 -ljnigraphics
target_link_libraries( # Specifies the target library.
native-lib
-ljnigraphics
# Links the target library to the log library
# included in the NDK.
${log-lib} )
