本文c代码来自于ndk demo。主要演示在ndk中如何操作bitmap对象,改变bitmap中的某些像素值。利用该功能再结合相应的算法,可实现将图片置灰等功能。
除了常用的rgb8888外,还有rgb565模式。
rgb565:用16位表示一个颜色,从高位到低位如下:R R R R R G G G G G G B B B B B。因此在该种模式下红色为0xf800,绿色为0x7e0,蓝色为0x1f。
第一步:要导入#include <android/bitmap.h>头文件。
第二步:在ldLibs的值中加入"jnigraphics"。如下:
ndk { moduleName "ImageJniUtils" //生成的so名字 ldLibs "log","jnigraphics" //可以使用log与graphics abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无 }
AndroidBitmap_getInfo:获取一个AndroidBitmapInfo的结构体对象。利用该对象可以获取bitmap的format,height,width等信息。只有在该方法返回值>0的时才代表该方法执行成功。
AndroidBitmap_lockPixels:用于获取bitmap像素数组的地址。bitmap的每一个像素值都是一个int数值,它们存储在一个数组中(Bitmap#getPixels()会将这些数值存储到指定的数组中)。获取地址后就可以对每一个像素进行操作了。
AndroidBitmap_unlockPixels:与上面一个方法相反,它是释放地址。
void* pixels; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return; } if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) { LOGE("Bitmap format is not RGB_565 !"); return; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {//此时pixels便指向了像素数组的首地址 LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); } /* Now fill the values with a nice little plasma */ fill_plasma(&info, pixels, time_ms ); AndroidBitmap_unlockPixels(env, bitmap);对于pixels的使用如下(来自于置灰demo):
void *pixel = ((uint16_t *)pixels) + y * info.width + x; uint16_t v = *(uint16_t *)pixel; //ARGB_565共16位,所以使用unit16_t r = RGB565_R(v); //获取对应的r通道值 g = RGB565_G(v); //获取对应的g通道值 b = RGB565_B(v); //获取对应的b通道值 int gray = (r * 38 + g * 75 + b * 15) >> 7; //将获取到的三通道值改变 *((uint16_t *)pixel) = MAKE_RGB565(gray, gray, gray); //将改变后的值存储到原来的位置,这样就改变了这个位置上的像素值
int Edge(int x, int y, int radius, int centerX, int centerY) { return sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)) < radius && abs(y - centerY) <= radius && abs(x - centerX) <= radius; } JNIEXPORT void JNICALL Java_com_baigle_image_ImageUtils_blur(JNIEnv *env, jobject obj, jobject bitmap, jint width, jint height) { AndroidBitmapInfo info; void *pixels; int ret; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); return; } int centerX = info.width / 2; int centerY = info.height / 3; int shortP = info.width > info.height ? info.height : info.width; int radius = shortP / 8;//确定保留半径 int x, y = centerY - radius; void *dst = malloc((2 * radius + 1) * (2 * radius + 1) * sizeof(int)); for (; y <= centerY + radius; y++) {//保留像素值 for (x = centerX - radius; x <= centerX + radius; x++) { if (Edge(x, y, radius, centerX, centerY)){ *((int *) dst + (y - centerY + radius) * (2 * radius + 1) + x - centerX + radius) = *((int *) pixels + y * info.width + x); } } } blur((int *) pixels, info.width, info.height, 100);//调用模糊方法进行模糊 for (y = centerY - radius; y <= centerY + radius; y++) {//还原保留区域内的像素值 for (x = centerX - radius; x <= centerX + radius; x++) { if (Edge(x, y, radius, centerX, centerY)) *((int *) pixels + y * info.width + x) = *((int *) dst + (y - centerY + radius) * (2 * radius + 1) + x - centerX + radius); } } free(dst); AndroidBitmap_unlockPixels(env, bitmap); }在使用图片时,将图片转换成ARGB_8888。如下:
mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.xxxxxxx); mBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888,false);