使用 JNI & 旋转位图NDK [英] Rotating a bitmap using JNI & NDK

查看:29
本文介绍了使用 JNI & 旋转位图NDK的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经决定,由于位图占用大量内存,这很容易导致内存不足错误,我将把困难的、消耗内存的工作放在 C/C++ 代码上.

I've decided that since bitmaps take a lot of memory which can cause out-of-memory errors easily, I will put the hard, memory consuming work on C/C++ code .

  1. 读取位图信息(宽度、高度)
  2. 将位图像素存储到数组中.
  3. 回收位图.
  4. 创建一个大小相反的新位图.
  5. 将像素放入新的位图中.
  6. 释放像素并返回位图.

问题:

即使一切似乎都没有出现任何错误,但输出图像并不是原始图像的旋转.事实上,它完全毁了它.

The problem:

Even though everything seems to run without any errors, the output image is not a rotation of the original. In fact, it ruins it completely.

旋转应逆时针旋转 90 度.

The rotation should be counter clock wise, 90 degrees.

如您所见,不仅颜色变得更奇怪,而且大小与我设置的不匹配.这里确实有些奇怪.

So as you can see, not only the colors became weirder, but the size doesn't match what I've set to it. Something is really weird here.

也许我没有正确读取/放置数据?

Maybe I don't read/put the data correctly?

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

Of course this is just an example. The code should work fine on any bitmap, as long as the device has enough memory to hold it. Also, I might want to do other operations on the bitmap other than rotating it.

Android.mk 文件:

Android.mk file:

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;
  }

java 文件:

  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);

<小时>

在我得到答案后,我想与大家分享这段代码和注释:


after I got my answer, I wish to share this code and notes about it to everyone:

  • 为了使其正常工作,我在代码中将uint16_t"的每个实例替换为uint32_t"(这是我询问过的代码中的错误).

  • in order for it to work, i've replaced in the code every instance of "uint16_t" with "uint32_t" (that's the bug on my code I've asked about).

输入和输出位图必须使用 8888 配置(即 ARGB)

input and output bitmap must be with 8888 config (which is ARGB )

输入位图将在此过程中被回收.

input bitmap will be recycled during the process.

代码将图像逆时针旋转 90 度.当然,您可以根据需要进行更改.

the code rotates the image 90 degrees counter clock wise. Of course you can change it depending on your needs.

我发表了一篇具有此功能和其他功能的好帖子,这里 .

i've made a nice post having this functionality and others, here .

推荐答案

由于您使用的是 ARGB_8888 格式,因此每个像素都是 uint32_t 而不是 uint16_t代码>.尝试更改您的旋转位图创建以将 uint32_t 用于源和目标数组,它应该会更好地工作.

Since you're using ARGB_8888 format every pixel is an uint32_t not uint16_t. Try changing your rotated Bitmap creation to use uint32_t for source and destination arrays and it should work better.

这篇关于使用 JNI &amp; 旋转位图NDK的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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