gpt4 book ai didi

android - 使用大图时如何使用JNI位图操作避免OOM?

转载 作者:IT王子 更新时间:2023-10-28 23:50:54 25 4
gpt4 key购买 nike

关闭。这个问题需要details or clarity .它目前不接受答案。












想改进这个问题?通过 editing this post 添加详细信息并澄清问题.


8年前关闭。







Improve this question




背景

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

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

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

  • How to cache bitmaps into native memory
  • image decoding and manipulation using JNI on android
  • JNI - how to use multiple Jni wrapper instances with different fields?
  • Rotating a bitmap using JNI & NDK

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

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

    使用示例代码:
    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();

    该项目在 github 上可用
  • 项目页面在 github 上可用 here .
  • 随时提供建议和贡献。

  • 重要笔记

    与所示相同的注释 here ,加上:
  • 此处编写的当前功能(在项目页面上进行了更多更新):
  • 店铺
  • 恢复
  • 逆时针旋转 90 度
  • 庄稼。
  • 我为这段代码采取的方法既是内存效率(仅使用我需要的内存,并在不需要时释放它),又是 CPU 效率(我尝试尽可能使用指针和 CPU 内存缓存优化)。
  • 为了获得最佳性能,我做了很少的验证,尤其是在 JNI 部分。最好管理java“世界”上的验证。
  • 还有很多缺少 我认为应该添加的功能,我希望我有时间添加它们。如果有人愿意贡献,我也很乐意添加他们的代码。以下是我认为可能有用的功能:
  • 获取当前位图信息
  • 缩放位图,包括选择使用哪种算法(最近邻和双线性插值就足够了)。
  • 使用不同的位图格式
  • 在 JNI 中进行解码,以避免从一开始就创建 java 位图(而不是使用 java 世界上的堆),只有在结束时,当你完成所有操作时。
  • 人脸检测
  • 任何角度的旋转,或者至少是明显的角度。目前我只添加了逆时针旋转 90 度。
  • 最佳答案

    注意:这是一个有点旧的代码。如需最新版本,请查看 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();
    }
    }

    关于android - 使用大图时如何使用JNI位图操作避免OOM?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18250951/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com