Android 使用JNI旋转位图&;NDK 背景:

Android 使用JNI旋转位图&;NDK 背景:,android,bitmap,android-ndk,java-native-interface,image-rotation,Android,Bitmap,Android Ndk,Java Native Interface,Image Rotation,我决定,由于位图占用大量内存,很容易导致内存不足错误,因此我将在C/C++代码上进行艰苦的、消耗内存的工作 我用于旋转位图的步骤有: 读取位图信息(宽度、高度) 将位图像素存储到数组中 回收位图 创建一个大小相反的新位图 将像素放入新位图中 释放像素并返回位图 问题是: 即使一切运行似乎没有任何错误,输出图像也不是原始图像的旋转。事实上,它完全毁掉了它 旋转应为逆时针90度 我得到的示例(屏幕截图放大): 正如你所看到的,不仅颜色变得奇怪,而且尺寸与我设定的不匹配。这里真的有点奇怪 可能我没有

我决定,由于位图占用大量内存,很容易导致内存不足错误,因此我将在C/C++代码上进行艰苦的、消耗内存的工作

我用于旋转位图的步骤有:
  • 读取位图信息(宽度、高度)
  • 将位图像素存储到数组中
  • 回收位图
  • 创建一个大小相反的新位图
  • 将像素放入新位图中
  • 释放像素并返回位图
  • 问题是: 即使一切运行似乎没有任何错误,输出图像也不是原始图像的旋转。事实上,它完全毁掉了它

    旋转应为逆时针90度

    我得到的示例(屏幕截图放大):

    正如你所看到的,不仅颜色变得奇怪,而且尺寸与我设定的不匹配。这里真的有点奇怪

    可能我没有正确读取/放置数据

    当然,这只是一个例子。只要设备有足够的内存来保存位图,代码就可以在任何位图上正常工作。此外,除了旋转位图之外,我可能还想对位图执行其他操作

    我创建的代码: Android.mk文件:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := JniTest
    LOCAL_SRC_FILES := JniTest.cpp
    LOCAL_LDLIBS := -llog
    LOCAL_LDFLAGS += -ljnigraphics
    include $(BUILD_SHARED_LIBRARY)
    APP_OPTIM := debug
    LOCAL_CFLAGS := -g
    
    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_example_jnitest_MainActivity_rotateBitmapCcw90(JNIEnv * env, jobject obj, jobject bitmap);
      }
    
    JNIEXPORT jobject JNICALL Java_com_example_jnitest_MainActivity_rotateBitmapCcw90(JNIEnv * env, jobject obj, jobject bitmap)
      {
      //
      //getting bitmap info:
      //
      LOGD("reading bitmap info...");
      AndroidBitmapInfo info;
      int ret;
      if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0)
        {
        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
        return NULL;
        }
      LOGD("width:%d height:%d stride:%d", info.width, info.height, info.stride);
      if (info.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;
      uint32_t* tempPixels = new uint32_t[info.height * info.width];
      int stride = info.stride;
      int pixelsCount = info.height * info.width;
      memcpy(tempPixels, src, sizeof(uint32_t) * pixelsCount);
      AndroidBitmap_unlockPixels(env, bitmap);
      //
      //recycle bitmap - using bitmap.recycle()
      //
      LOGD("recycling bitmap...");
      jclass bitmapCls = env->GetObjectClass(bitmap);
      jmethodID recycleFunction = env->GetMethodID(bitmapCls, "recycle", "()V");
      if (recycleFunction == 0)
        {
        LOGE("error recycling!");
        return NULL;
        }
      env->CallVoidMethod(bitmap, recycleFunction);
      //
      //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...");
      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, info.height, info.width, bitmapConfig);
      //
      // putting the pixels into the new bitmap:
      //
      if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
        {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
        return NULL;
        }
      uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
      int whereToPut = 0;    
      for (int x = info.width - 1; x >= 0; --x)
        for (int y = 0; y < info.height; ++y)
          {
          uint32_t pixel = tempPixels[info.width * y + x];
          newBitmapPixels[whereToPut++] = pixel;
          }
      AndroidBitmap_unlockPixels(env, newBitmap);
      //
      // freeing the native memory used to store the pixels
      //
      delete[] tempPixels;
      return newBitmap;
      }
    
    #包括
    #包括
    
    #include.

    因为您使用的是
    ARGB\u 8888
    格式,所以每个像素都是
    uint32\u t
    而不是
    uint16\u t
    。尝试将旋转位图创建更改为对源数组和目标数组使用
    uint32\u t
    ,它应该工作得更好。

    既然您使用的是
    ARGB\u 8888
    格式,您是否应该使用
    uint32\u t
    创建旋转位图?是什么让您认为在C中保存位图比在Java中占用的内存更少,改变了它,它成功了!!!非常感谢。请给我一个答案,这样我就可以打勾了@里德卡利:没有。它消除了最大堆大小的限制,在某些情况下,最大堆大小可能太小。例如,8MP摄像头图像可能需要30MB或RAM,使用正常旋转技术将使其使用双倍的内存。@harism,你知道我还可以如何使用其他位图配置吗,如果我需要为它们使用uint16_t呢?@androiddeveloper不幸的是,我不太确定例如RGB_565是如何打包到内存中的。我想他们会把它们打包成
    uint16\u t
    以节省内存,但最好先验证一下。谢谢。效果很好。我不敢相信我做了这么多的工作,但我没有把这件事做好。你有没有关于这段代码的速度优化技巧?
      static
        {
        System.loadLibrary("JniTest");
        }
    
      /**
       * rotates a bitmap by 90 degrees counter-clockwise . <br/>
       * notes:<br/>
       * -the input bitmap will be recycled and shouldn't be used anymore <br/>
       * -returns the rotated bitmap . <br/>
       * -could take some time , so do the operation in a new thread
       */
      public native Bitmap rotateBitmapCcw90(Bitmap bitmap);
    
    ...
      Bitmap rotatedImage=rotateBitmapCcw90(bitmapToRotate);