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

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

问题描述

背景

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

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

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

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

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

使用示例代码:

Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);最终 int width=bitmap.getWidth(),height=bitmap.getHeight();//将位图存储在 JNI世界"中最终 JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);//不需要javaworld"上的位图,因为操作是在JNIworld"上完成的位图.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世界"上释放该位图bitmap=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 #include #include #include #include #include #include #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);if (jniBitmap->_storedBitmapPixels == NULL)返回;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 _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 handle){JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);if (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 _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 handle){JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);if (jniBitmap->_storedBitmapPixels == NULL)返回;delete[] jniBitmap->_storedBitmapPixels;jniBitmap->_storedBitmapPixels = NULL;删除 jniBitmap;}/**恢复java位图(来自JNI数据)*///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("没有存储位图数据.返回空...");返回空;}////创建一个新的位图以将像素放入其中 - 使用 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);////将像素放入新位图中://int ret;void* bitmapPixels;if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) <0){LOGE("AndroidBitmap_lockPixels() 失败!error=%d", ret);返回空;}uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;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 bitmapInfo;uint32_t* storedBitmapPixels = NULL;//LOGD("读取位图信息...");int ret;if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) <0){LOGE("AndroidBitmap_getInfo() 失败!error=%d", ret);返回空;}LOGD("width:%d height:%d stride:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);如果(位图信息格式!= ANDROID_BITMAP_FORMAT_RGBA_8888){LOGE("位图格式不是RGBA_8888!");返回空;}////将位图的像素读入本机内存:////LOGD("读取位图像素...");void* bitmapPixels;if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) <0){LOGE("AndroidBitmap_lockPixels() 失败!error=%d", ret);返回空;}uint32_t* src = (uint32_t*) 位图像素;storageBitmapPixels = 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;返回 env->NewDirectByteBuffer(jniBitmap, 0);}

src/com/jni/bitmap_operations/JniBitmapHolder.java

package com.jni.bitmap_operations;导入 java.nio.ByteBuffer;导入 android.graphics.Bitmap;导入 android.util.Log;公共类 JniBitmapHolder{ByteBuffer _handler =null;静止的{System.loadLibrary("JniBitmapOperations");}私有本机 ByteBuffer jniStoreBitmapData(位图位图);私有本机位图 jniGetBitmapFromStoredBitmapData(ByteBuffer 处理程序);私有本机无效 jniFreeBitmapData(字节缓冲区处理程序);私有本机 void jniRotateBitmapCcw90(ByteBuffer 处理程序);private native void jniCropBitmap(ByteBuffer handler,final int left,final int top,final int right,final int bottom);公共 JniBitmapHolder(){}public JniBitmapHolder(最终位图位图){存储位图(位图);}public void storeBitmap(最终位图位图){if(_handler!=null)自由位图();_handler=jniStoreBitmapData(位图);}public void rotateBitmapCcw90(){如果(_handler==null)返回;jniRotateBitmapCcw90(_handler);}public voidcropBitmap(final int left,final int top,final int right,final int bottom){如果(_handler==null)返回;jniCropBitmap(_handler,left,top,right,bottom);}公共位图 getBitmap(){如果(_handler==null)返回空;返回 jniGetBitmapFromStoredBitmapData(_handler);}公共位图 getBitmapAndFree(){最终位图 bitmap=getBitmap();自由位图();返回位图;}public void 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天全站免登陆