使用JNI和NDK旋转位图
背景:
我已经决定,由于位图占用大量内存,容易造成内存不足的错误,所以我将在C / C ++代码上花费大量的内存。
我用来旋转位图的步骤是:
- 读取位图信息(宽度,高度)
- 将位图像素存储到数组中。
- 回收位图。
- 创build一个相反大小的新位图。
- 把像素放入新的位图。
- 释放像素并返回位图。
问题:
即使一切似乎运行没有任何错误,输出图像不是原始的旋转。 事实上,它完全毁了它。
旋转应该是逆时针方向,90度。
示例(截图放大)我得到:
所以,正如你所看到的,不仅颜色变得诡异,而且尺寸与我所设定的不符。 这里真的很奇怪
也许我不读/正确地把数据?
当然这只是一个例子。 只要设备有足够的内存来保存它,代码就可以在任何位图上正常工作。 另外,我可能想对位图执行其他操作,而不是旋转它。
我创build的代码:
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; }
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);
编辑:我得到我的答案后,我希望分享这个代码,并注意到它给大家:
-
为了工作,我已经在代码中将“uint16_t”的每个实例replace为“uint32_t”(这是我询问的代码上的错误)。
-
input和输出位图必须与8888configuration(这是ARGB)
-
input位图将在这个过程中被回收。
-
代码将图像按时钟顺时针旋转90度。 当然,你可以根据你的需要来改变它。
解决scheme更好
我已经做了一个不错的职位,有这个function和其他人, 在这里 。
由于您使用ARGB_8888
格式,因此每个像素都是uint32_t
而不是uint16_t
。 尝试更改您的旋转位图创build使用uint32_t
源和目标数组,它应该更好地工作。