Android NDK功能很强大,今天就来学习下jni用bitmap实现Imageview的上下翻转和左右镜像
效果图:

在 Android 通过 JNI 去调用 Bitmap,通过 CMake 去编 so 动态链接库的话,需要添加 jnigraphics 图像库。

target_link_libraries( # Specifies the target library.                       native-operation                       jnigraphics                       ${log-lib} )

在 Android 中关于 JNI Bitmap 的操作,都定义在 bitmap.h 的头文件里面了,主要就三个函数,明白它们的含义之后就可以去实践体会了。
检索 Bitmap 对象信息

AndroidBitmap_getInfo 函数允许原生代码检索 Bitmap 对象信息,如它的大小、像素格式等,函数签名如下:

/** * Given a java bitmap object, fill out the AndroidBitmapInfo struct for it. * If the call fails, the info parameter will be ignored. */int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,                          AndroidBitmapInfo* info);

其中,第一个参数就是 JNI 接口指针,第二个参数就是 Bitmap 对象的引用,第三个参数是指向 AndroidBitmapInfo 结构体的指针。

AndroidBitmapInfo 结构体如下:/** Bitmap info, see AndroidBitmap_getInfo(). */typedef struct {    /** The bitmap width in pixels. */    uint32_t    width;    /** The bitmap height in pixels. */    uint32_t    height;    /** The number of byte per row. */    uint32_t    stride;    /** The bitmap pixel format. See {@link AndroidBitmapFormat} */    int32_t     format;    /** Unused. */    uint32_t    flags;      // 0 for now} AndroidBitmapInfo;

其中,width 就是 Bitmap 的宽,height 就是高,format 就是图像的格式,而 stride 就是每一行的字节数。

图像的格式有如下支持:

/** Bitmap pixel format. */enum AndroidBitmapFormat {    /** No format. */    ANDROID_BITMAP_FORMAT_NONE      = 0,    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/    ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,    /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/    ANDROID_BITMAP_FORMAT_RGB_565   = 4,    /** Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead. **/    ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,    /** Alpha: 8 bits. */    ANDROID_BITMAP_FORMAT_A_8       = 8,};

如果 AndroidBitmap_getInfo 执行成功的话,会返回 0 ,否则返回一个负数,代表执行的错误码列表如下:

/** AndroidBitmap functions result code. */enum {    /** Operation was successful. */    ANDROID_BITMAP_RESULT_SUCCESS           = 0,    /** Bad parameter. */    ANDROID_BITMAP_RESULT_BAD_PARAMETER     = -1,    /** JNI exception occured. */    ANDROID_BITMAP_RESULT_JNI_EXCEPTION     = -2,    /** Allocation failed. */    ANDROID_BITMAP_RESULT_ALLOCATION_FAILED = -3,};

访问原生像素缓存

AndroidBitmap_lockPixels 函数锁定了像素缓存以确保像素的内存不会被移动。

如果 Native 层想要访问像素数据并操作它,该方法返回了像素缓存的一个原生指针,

/** * Given a java bitmap object, attempt to lock the pixel address. * Locking will ensure that the memory for the pixels will not move * until the unlockPixels call, and ensure that, if the pixels had been * previously purged, they will have been restored. * * If this call succeeds, it must be balanced by a call to * AndroidBitmap_unlockPixels, after which time the address of the pixels should * no longer be used. * * If this succeeds, *addrPtr will be set to the pixel address. If the call * fails, addrPtr will be ignored. */int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);

其中,第一个参数就是 JNI 接口指针,第二个参数就是 Bitmap 对象的引用,第三个参数是指向像素缓存地址的指针。

AndroidBitmap_lockPixels 执行成功的话返回 0 ,否则返回一个负数,错误码列表就是上面提到的。
释放原生像素缓存

对 Bitmap 调用完 AndroidBitmap_lockPixels 之后都应该对应调用一次 AndroidBitmap_unlockPixels 用来释放原生像素缓存。

当完成对原生像素缓存的读写之后,就应该释放它,一旦释放后,Bitmap Java 对象又可以在 Java 层使用了,函数签名如下:

/** * Call this to balance a successful call to AndroidBitmap_lockPixels. */int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);

其中,第一个参数就是 JNI 接口指针,第二个参数就是 Bitmap 对象的引用,如果执行成功返回 0,否则返回 1。

对 Bitmap 的操作,最重要的就是 AndroidBitmap_lockPixels 函数拿到所有像素的缓存地址,然后对每个像素值进行操作,从而更改 Bitmap 。

具体代码如下:
Activity 部分:

 private void initview(){        source_img = (ImageView)findViewById(R.id.source_img);        operation_img = (ImageView)findViewById(R.id.operation_img);        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.newpic);        operation_img.setImageBitmap(rotateBitmap(bitmap));    }      /*    * 上下翻转和左右镜像     */    public native Bitmap rotateBitmap(Bitmap bitmap);

2.jni部分:

#include #include #include #include #include #include "AndroidLog.h"#include jobject generateBitmap(JNIEnv *env, uint32_t width, uint32_t height) {    jclass bitmapCls = env->FindClass("android/graphics/Bitmap");    jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls,                                                            "createBitmap",                                                            "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");    jstring configName = env->NewStringUTF("ARGB_8888");    jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");    jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(            bitmapConfigClass, "valueOf",            "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");    jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass,                                                       valueOfBitmapConfigFunction, configName);    jobject newBitmap = env->CallStaticObjectMethod(bitmapCls,                                                    createBitmapFunction,                                                    width,                                                    height, bitmapConfig);    return newBitmap;}extern "C" JNIEXPORT jobject JNICALLJava_com_xinrui_ndkapp_MainActivity_rotateBitmap(                JNIEnv *env,                jobject /* this */,jobject bitmap) {    AndroidBitmapInfo bitmapInfo;    int ret;    if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0) {        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);        return NULL;    }    // 读取 bitmap 的像素内容到 native 内存    void *bitmapPixels;    if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0) {        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);        return NULL;    }    // 旋转操作,新 Bitmap 的宽等于原来的高,新 Bitmap 的高等于原来的宽    uint32_t newWidth = bitmapInfo.width;    uint32_t newHeight = bitmapInfo.height;    // 创建一个新的数组指针,把这个新的数组指针填充像素值    uint32_t *newBitmapPixels = new uint32_t[newWidth * newHeight];    int whereToGet = 0;//上下翻转    for (int y = 0; y < newHeight; ++y) {        for (int x = 0; x < newWidth; x++) {            uint32_t pixel = ((uint32_t *) bitmapPixels)[whereToGet++];            newBitmapPixels[newWidth * (newHeight - 1 - y) + x] = pixel;        }    }//左右镜像//    for (int y = 0; y < newHeight; ++y) {//        for (int x = newWidth - 1; x >= 0; x--) {//            uint32_t pixel = ((uint32_t *) bitmapPixels)[whereToGet++];//            newBitmapPixels[newWidth * y + x] = pixel;//        }//    }    AndroidBitmap_unlockPixels(env, bitmap);    jobject newBitmap = generateBitmap(env,newWidth,newHeight);    void *resultBitmapPixels;    if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &resultBitmapPixels)) < 0) {        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);        return NULL;    }    int pixelsCount = newWidth * newHeight;    memcpy((uint32_t *) resultBitmapPixels, newBitmapPixels, sizeof(uint32_t) * pixelsCount);    AndroidBitmap_unlockPixels(env, newBitmap);    delete []newBitmapPixels;    return newBitmap;}

更多相关文章

  1. Android(安卓)文件夹介绍
  2. ps图片黑白调整算法——Android实现及性能优化
  3. Android快速开发框架ZBLibrary源码分享
  4. Android(安卓)本地缓存开源库 Reservoir 替代SharedPreferences
  5. xUtils 源码解析
  6. 关于手机的像素
  7. 【Android(安卓)Developers Training】 85. 不要有冗余的下载
  8. Android(安卓)studio rebuild之后找不到R文件
  9. Picasso的使用

随机推荐

  1. Android 下移植WIFI 驱动
  2. 在Android模拟机上跑javaeye android 客
  3. Cocos2d-x for android 使用Box2d报错解
  4. Android之Application引用方式
  5. Android特色开发之桌面组件
  6. 转 Android监听键盘弹出收起
  7. Maven In Android
  8. Android(安卓)Accessibility 辅助功能简
  9. android 按钮点击效果 imageview button
  10. Android以太网框架情景分析之EthernetSer