使用大图时如何使用JNI位图操作避免OOM? [英] How to use JNI bitmap operations for helping to avoid OOM when using large images?

查看:18
本文介绍了使用大图时如何使用JNI位图操作避免OOM?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

大多数时候,在 android 上出现 OOM 是由于使用了太多位图和/或创建了大位图.

最近我决定尝试 JNI,以便通过将数据本身存储在 JNI 端来避免 OOM.

在搞砸了 JNI 一段时间后,我在 SO 上创建了一些帖子寻求帮助并分享我的知识,现在我决定与您分享更多代码.如果有人有兴趣阅读调查结果或贡献,这里是帖子:

这一次,我添加了存储、恢复、裁剪和旋转位图的功能.添加更多选项应该很容易,如果这里的其他人将自己的代码添加到更有用的功能中,我会很高兴.

所以我要展示的代码实际上是合并了我创建的所有东西.

使用示例代码:

位图位图=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);最终 int 宽度=bitmap.getWidth(),height=bitmap.getHeight();//将位图存储在 JNI世界"中最终 JniBitmapHolder bitmapHolder=new JniBitmapHolder(位图);//不需要javaworld"上的位图,因为操作是在JNIworld"上完成的bitmap.recycle();//从位图裁剪一个中心正方形,从位图的 (0.25,0.25) 到 (0.75,0.75).bitmapHolder.cropBitmap(width/4,height/4,width*3/4,height*3/4);//旋转位图:bitmapHolder.rotateBitmapCcw90();//获取输出java位图,并释放JNI世界"上的位图位图=bitmapHolder.getBitmapAndFree();

项目在 github 上可用

  • 项目页面可在 github 这里上找到.p>

  • 随时提供建议和贡献.

重要提示

此处所示的注释相同,另外:

  • 此处编写的当前功能(在项目页面上进行了更多更新):

    • 商店

    • 恢复

    • 逆时针旋转 90 度

    • 裁剪.

  • 我对这段代码采取的方法是内存效率(只使用我需要的内存,并在不需要时释放它)和 CPU 效率(我尝试使用指针和 CPU 内存缓存优化)可能).

  • 为了获得最佳性能,我做了很少的验证,尤其是在 JNI 部分.最好管理 java世界"上的验证.

  • 我认为还有很多缺少的功能需要补充,希望有时间可以补充.如果有人愿意贡献,我也很乐意添加他们的代码.以下是我认为可能有用的功能:

    • 获取当前位图信息

    • 缩放位图,包括选择使用哪种算法(最近邻和双线性插值就足够了).

    • 使用不同的位图格式

    • 在 JNI 中进行解码,以避免从一开始就创建 java 位图(而不是使用 java 世界上的堆),只有在结束时,当您完成所有操作时.

    • 人脸检测

    • 任何角度的旋转,或者至少是明显的旋转.目前我只添加了逆时针旋转 90 度.

解决方案

注意:这是一个有点旧的代码.如需最新版本,请查看 github 上的项目页面.

jni/Android.mk

LOCAL_PATH := $(call my-dir)#位图操作模块包括 $(CLEAR_VARS)LOCAL_MODULE := JniBitmapOperationsLOCAL_SRC_FILES := JniBitmapOperations.cppLOCAL_LDLIBS := -llogLOCAL_LDFLAGS += -ljnigraphics包括 $(BUILD_SHARED_LIBRARY)APP_OPTIM := 调试LOCAL_CFLAGS := -g#如果您需要添加更多模块,请与我们开始使用的模块相同(带有 CLEAR_VARS 的模块)

jni/JniBitmapOperations.cpp

#include <jni.h>#include <jni.h>#include <android/log.h>#include <stdio.h>#include <android/bitmap.h>#include <cstring>#include <unistd.h>#define LOG_TAG "调试"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)外部C"{JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap);JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle);JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle);JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle);JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom);}JniBitmap 类{上市:uint32_t* _storedBitmapPixels;AndroidBitmapInfo _bitmapInfo;JniBitmap(){_storedBitmapPixels = NULL;}};/** 将其中的位图裁剪为更小.请注意,没有进行任何验证*///JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom){JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);如果(jniBitmap->_storedBitmapPixels == NULL)返回;uint32_t* previousData = jniBitmap->_storedBitmapPixels;uint32_t oldWidth = jniBitmap->_bitmapInfo.width;uint32_t newWidth = 右 - 左,newHeight = 底部 - 顶部;uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];uint32_t* whereToGet = previousData + left + top * oldWidth;uint32_t* whereToPut = newBitmapPixels;for (int y = top; y < bottom; ++y){memcpy(whereToPut, whereToGet, sizeof(uint32_t) * newWidth);whereToGet += oldWidth;whereToPut += newWidth;}//完成复制,所以用新数据替换旧数据删除[] 以前的数据;jniBitmap->_storedBitmapPixels = newBitmapPixels;jniBitmap->_bitmapInfo.width = newWidth;jniBitmap->_bitmapInfo.height = newHeight;}/** 将内部位图数据顺时针旋转 90 度 *///JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject 句柄){JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);如果(jniBitmap->_storedBitmapPixels == NULL)返回;uint32_t* previousData = jniBitmap->_storedBitmapPixels;AndroidBitmapInfo bitmapInfo = jniBitmap->_bitmapInfo;uint32_t* newBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];int whereToPut = 0;//A.D.C.//...>...//B.C.A.Bfor (int x = bitmapInfo.width - 1; x >= 0; --x)for (int y = 0; y < bitmapInfo.height; ++y){uint32_t 像素 = previousData[bitmapInfo.width * y + x];newBitmapPixels[whereToPut++] = 像素;}删除[] 以前的数据;jniBitmap->_storedBitmapPixels = newBitmapPixels;uint32_t temp = bitmapInfo.width;bitmapInfo.width = bitmapInfo.height;bitmapInfo.height = temp;}/**免费位图*///JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject 句柄){JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);如果(jniBitmap->_storedBitmapPixels == NULL)返回;删除[] jniBitmap->_storedBitmapPixels;jniBitmap->_storedBitmapPixels = NULL;删除 jniBitmap;}/**恢复java位图(从JNI数据)*///JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject 句柄){JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);如果(jniBitmap->_storedBitmapPixels == NULL){LOGD("没有存储位图数据.返回 null...");返回空值;}////创建一个新的位图以将像素放入其中 - 使用 Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :////LOGD("正在创建新的位图...");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, jniBitmap->_bitmapInfo.width, jniBitmap->_bitmapInfo.height, bitmapConfig);////将像素放入新的位图中://诠释;无效*位图像素;if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0){LOGE("AndroidBitmap_lockPixels() 失败!错误=%d", ret);返回空值;}uint32_t* newBitmapPixels = (uint32_t*) 位图像素;int pixelCount = jniBitmap->_bitmapInfo.height * jniBitmap->_bitmapInfo.width;memcpy(newBitmapPixels, jniBitmap->_storedBitmapPixels, sizeof(uint32_t) * pixelCount);AndroidBitmap_unlockPixels(env, newBitmap);//LOGD("返回新的位图");返回新位图;}/**将java位图存储为JNI数据*///JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap){AndroidBitmapInfo 位图信息;uint32_t* 存储位图像素 = NULL;//LOGD("读取位图信息...");诠释;if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0){LOGE("AndroidBitmap_getInfo() 失败!错误=%d", ret);返回空值;}LOGD("宽度:%d 高度:%d 步幅:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);如果(位图信息格式!= ANDROID_BITMAP_FORMAT_RGBA_8888){LOGE("位图格式不是 RGBA_8888!");返回空值;}////将位图的像素读入本机内存:////LOGD("读取位图像素...");无效*位图像素;if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0){LOGE("AndroidBitmap_lockPixels() 失败!错误=%d", ret);返回空值;}uint32_t* src = (uint32_t*) 位图像素;storedBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];int pixelCount = bitmapInfo.height * bitmapInfo.width;memcpy(storedBitmapPixels, src, sizeof(uint32_t) * pixelCount);AndroidBitmap_unlockPixels(env, bitmap);JniBitmap *jniBitmap = new JniBitmap();jniBitmap->_bitmapInfo = bitmapInfo;jniBitmap->_storedBitmapPixels = storedBitmapPixels;return env->NewDirectByteBuffer(jniBitmap, 0);}

src/com/jni/bitmap_operations/JniBitmapHolder.java

包com.jni.bitmap_operations;导入 java.nio.ByteBuffer;导入android.graphics.Bitmap;导入android.util.Log;公共类 JniBitmapHolder{ByteBuffer _handler =null;静止的{System.loadLibrary("JniBitmapOperations");}private native ByteBuffer jniStoreBitmapData(Bitmap bitmap);私有本机位图 jniGetBitmapFromStoredBitmapData(ByteBuffer handler);私有本机无效 jniFreeBitmapData(ByteBuffer 处理程序);私有本机无效 jniRotateBitmapCcw90(ByteBuffer 处理程序);private native void jniCropBitmap(ByteBuffer handler,final int left,final int top,final int right,final int bottom);公共 JniBitmapHolder(){}public JniBitmapHolder(最终位图位图){存储位图(位图);}公共无效存储位图(最终位图位图){if(_handler!=null)自由位图();_handler=jniStoreBitmapData(位图);}公共无效rotateBitmapCcw90(){如果(_handler==null)返回;jniRotateBitmapCcw90(_handler);}公共无效cropBitmap(最终int左,最终int顶部,最终int右,最终int底部){如果(_handler==null)返回;jniCropBitmap(_handler,left,top,right,bottom);}公共位图 getBitmap(){如果(_handler==null)返回空值;返回 jniGetBitmapFromStoredBitmapData(_handler);}公共位图 getBitmapAndFree(){最终位图 bitmap=getBitmap();自由位图();返回位图;}公共无效freeBitmap(){如果(_handler==null)返回;jniFreeBitmapData(_handler);_handler=null;}@覆盖protected void finalize() 抛出 Throwable{super.finalize();如果(_handler==null)返回;Log.w("DEBUG","JNI 位图没有很好地释放.请记住尽快释放位图");自由位图();}}

Background

most of the times, getting OOM on android is due to using too many bitmaps and/or creating large bitmaps.

recently i've decided to try out JNI in order to allow avoiding OOM by storing the data itself on the JNI side.

after messing around with JNI for a while, i've created some posts on SO asking for help and sharing my knowledge, and i've now decided to share some more code with you. here are the posts in case anyone is interested in reading the findings or contributing :

this time, i've added the ability to store,restore, crop and rotate bitmaps. it should be easy to add more options and I would be happy if other people here would add their own code to more useful functions .

so the code i'm about to show is actually merging of all the things i've created.

Sample code of usage:

Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
final int width=bitmap.getWidth(),height=bitmap.getHeight();
// store the bitmap in the JNI "world"
final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
// no need for the bitmap on the java "world", since the operations are done on the JNI "world"
bitmap.recycle();
// crop a center square from the bitmap, from (0.25,0.25) to (0.75,0.75) of the bitmap.
bitmapHolder.cropBitmap(width/4,height/4,width*3/4,height*3/4);
//rotate the bitmap:
bitmapHolder.rotateBitmapCcw90();
//get the output java bitmap , and free the one on the JNI "world"
bitmap=bitmapHolder.getBitmapAndFree();

The project is available on github

  • project page is available on github here .

  • feel free to give advises and contribute.

Important notes

same notes as shown here, plus:

  • current features that are written here (more updated on the project page) :

    • store

    • restore

    • rotate 90 degrees CCW

    • crop.

  • the approach i've taken for this code is both memory efficiency (use only memory that i need, and free it when not needed), and CPU efficiency (i tried to use pointers and CPU memory cache optimizations whenever possible).

  • for best performance, i've done really few validations, especially on the JNI part. it might be best to manage the validations on the java "world".

  • there are still many missing features that i think should be added, and i hope that i will have the time to add them . if anyone wishes to contribute, i will be glad to add they code too. here are the functions that i think could be useful:

    • get current bitmap info

    • scale bitmaps, including choice of which algorithm to use (nearest neighbour and bilinear interpolation should be enough).

    • use different bitmap formats

    • do the decoding within JNI, to avoid creation of the java bitmap (and not use the heap on the java world) from the beginning, only at the end, when you finished with all of the operations.

    • face detection

    • rotation in any angle, or at least the obvious ones . currently i only added rotation of 90 degrees counter clock wise .

解决方案

NOTE: this is a bit old code. for the most updated one, check out the project page on github.

jni/Android.mk

LOCAL_PATH := $(call my-dir)

#bitmap operations module
include $(CLEAR_VARS)

LOCAL_MODULE    := JniBitmapOperations
LOCAL_SRC_FILES := JniBitmapOperations.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDFLAGS += -ljnigraphics

include $(BUILD_SHARED_LIBRARY)
APP_OPTIM := debug
LOCAL_CFLAGS := -g

#if you need to add more module, do the same as the one we started with (the one with the CLEAR_VARS)

jni/JniBitmapOperations.cpp

#include <jni.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <android/bitmap.h>
#include <cstring>
#include <unistd.h>

#define  LOG_TAG    "DEBUG"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

extern "C"
  {
  JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap);
  JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle);
  JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle);
  JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle);
  JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom);
  }

class JniBitmap
  {
  public:
    uint32_t* _storedBitmapPixels;
    AndroidBitmapInfo _bitmapInfo;
    JniBitmap()
      {
      _storedBitmapPixels = NULL;
      }
  };

/**crops the bitmap within to be smaller. note that no validations are done*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom)
  {
  JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
  if (jniBitmap->_storedBitmapPixels == NULL)
    return;
  uint32_t* previousData = jniBitmap->_storedBitmapPixels;
  uint32_t oldWidth = jniBitmap->_bitmapInfo.width;
  uint32_t newWidth = right - left, newHeight = bottom - top;
  uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
  uint32_t* whereToGet = previousData + left + top * oldWidth;
  uint32_t* whereToPut = newBitmapPixels;
  for (int y = top; y < bottom; ++y)
    {
    memcpy(whereToPut, whereToGet, sizeof(uint32_t) * newWidth);
    whereToGet += oldWidth;
    whereToPut += newWidth;
    }
  //done copying , so replace old data with new one
  delete[] previousData;
  jniBitmap->_storedBitmapPixels = newBitmapPixels;
  jniBitmap->_bitmapInfo.width = newWidth;
  jniBitmap->_bitmapInfo.height = newHeight;
  }

/**rotates the inner bitmap data by 90 degress counter clock wise*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle)
  {
  JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
  if (jniBitmap->_storedBitmapPixels == NULL)
    return;
  uint32_t* previousData = jniBitmap->_storedBitmapPixels;
  AndroidBitmapInfo bitmapInfo = jniBitmap->_bitmapInfo;
  uint32_t* newBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
  int whereToPut = 0;
  // A.D D.C
  // ...>...
  // B.C A.B
  for (int x = bitmapInfo.width - 1; x >= 0; --x)
    for (int y = 0; y < bitmapInfo.height; ++y)
      {
      uint32_t pixel = previousData[bitmapInfo.width * y + x];
      newBitmapPixels[whereToPut++] = pixel;
      }
  delete[] previousData;
  jniBitmap->_storedBitmapPixels = newBitmapPixels;
  uint32_t temp = bitmapInfo.width;
  bitmapInfo.width = bitmapInfo.height;
  bitmapInfo.height = temp;
  }

/**free bitmap*/  //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle)
  {
  JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
  if (jniBitmap->_storedBitmapPixels == NULL)
    return;
  delete[] jniBitmap->_storedBitmapPixels;
  jniBitmap->_storedBitmapPixels = NULL;
  delete jniBitmap;
  }

/**restore java bitmap (from JNI data)*/  //
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle)
  {
  JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
  if (jniBitmap->_storedBitmapPixels == NULL)
    {
    LOGD("no bitmap data was stored. returning null...");
    return NULL;
    }
  //
  //creating a new bitmap to put the pixels into it - using Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :
  //
  //LOGD("creating new bitmap...");
  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, jniBitmap->_bitmapInfo.width, jniBitmap->_bitmapInfo.height, bitmapConfig);
  //
  // putting the pixels into the new bitmap:
  //
  int ret;
  void* bitmapPixels;
  if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
    {
    LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    return NULL;
    }
  uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
  int pixelsCount = jniBitmap->_bitmapInfo.height * jniBitmap->_bitmapInfo.width;
  memcpy(newBitmapPixels, jniBitmap->_storedBitmapPixels, sizeof(uint32_t) * pixelsCount);
  AndroidBitmap_unlockPixels(env, newBitmap);
  //LOGD("returning the new bitmap");
  return newBitmap;
  }

/**store java bitmap as JNI data*/  //
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap)
  {
  AndroidBitmapInfo bitmapInfo;
  uint32_t* storedBitmapPixels = NULL;
  //LOGD("reading bitmap info...");
  int ret;
  if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0)
    {
    LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
    return NULL;
    }
  LOGD("width:%d height:%d stride:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);
  if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
    {
    LOGE("Bitmap format is not RGBA_8888!");
    return NULL;
    }
  //
  //read pixels of bitmap into native memory :
  //
  //LOGD("reading bitmap pixels...");
  void* bitmapPixels;
  if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0)
    {
    LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    return NULL;
    }
  uint32_t* src = (uint32_t*) bitmapPixels;
  storedBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
  int pixelsCount = bitmapInfo.height * bitmapInfo.width;
  memcpy(storedBitmapPixels, src, sizeof(uint32_t) * pixelsCount);
  AndroidBitmap_unlockPixels(env, bitmap);
  JniBitmap *jniBitmap = new JniBitmap();
  jniBitmap->_bitmapInfo = bitmapInfo;
  jniBitmap->_storedBitmapPixels = storedBitmapPixels;
  return env->NewDirectByteBuffer(jniBitmap, 0);
  }

src/com/jni/bitmap_operations/JniBitmapHolder.java

package com.jni.bitmap_operations;
import java.nio.ByteBuffer;
import android.graphics.Bitmap;
import android.util.Log;

public class JniBitmapHolder
  {
  ByteBuffer _handler =null;
  static
    {
    System.loadLibrary("JniBitmapOperations");
    }

  private native ByteBuffer jniStoreBitmapData(Bitmap bitmap);

  private native Bitmap jniGetBitmapFromStoredBitmapData(ByteBuffer handler);

  private native void jniFreeBitmapData(ByteBuffer handler);

  private native void jniRotateBitmapCcw90(ByteBuffer handler);

  private native void jniCropBitmap(ByteBuffer handler,final int left,final int top,final int right,final int bottom);

  public JniBitmapHolder()
    {}

  public JniBitmapHolder(final Bitmap bitmap)
    {
    storeBitmap(bitmap);
    }

  public void storeBitmap(final Bitmap bitmap)
    {
    if(_handler!=null)
      freeBitmap();
    _handler=jniStoreBitmapData(bitmap);
    }

  public void rotateBitmapCcw90()
    {
    if(_handler==null)
      return;
    jniRotateBitmapCcw90(_handler);
    }

  public void cropBitmap(final int left,final int top,final int right,final int bottom)
    {
    if(_handler==null)
      return;
    jniCropBitmap(_handler,left,top,right,bottom);
    }

  public Bitmap getBitmap()
    {
    if(_handler==null)
      return null;
    return jniGetBitmapFromStoredBitmapData(_handler);
    }

  public Bitmap getBitmapAndFree()
    {
    final Bitmap bitmap=getBitmap();
    freeBitmap();
    return bitmap;
    }

  public void freeBitmap()
    {
    if(_handler==null)
      return;
    jniFreeBitmapData(_handler);
    _handler=null;
    }

  @Override
  protected void finalize() throws Throwable
    {
    super.finalize();
    if(_handler==null)
      return;
    Log.w("DEBUG","JNI bitmap wasn't freed nicely.please rememeber to free the bitmap as soon as you can");
    freeBitmap();
    }
  }

这篇关于使用大图时如何使用JNI位图操作避免OOM?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆