旋转使用JNI和放大器的位图; NDK [英] Rotating a bitmap using JNI & NDK
问题描述
我已经决定,由于位图需要大量的内存,并且可以很容易造成内存不足的错误,我会把硬盘,内存占用工作C / C ++ code。
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 .
- 在读的位图信息(宽,高)
- 在店内位图的像素到一个数组中。
- 循环使用位图。
- 创建相对大小的新位图。
- 把像素到新位图。
- 释放像素,并返回该位图。
尽管一切都似乎没有任何错误运行,输出的图像就不是原来的旋转。事实上,它完全毁掉它。
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?
当然,这仅仅是一个例子。在code应该能正常运行的任何位图,只要设备有足够的内存来容纳它。另外,我可能要进行其他操作的位图比它旋转等。
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文件:
cpp file:
#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文件中:
java file:
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);
编辑:在我得到我的回答,我想分享这个code及注意事项给大家:
after I got my answer, I wish to share this code and notes about it to everyone:
-
为了为它工作,我把它换成了codeuint16_t与uint32_t的(这是我问了一下我的code中的错误)的每个实例。
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.
在code将图像旋转90度计数器的时钟明智的。当然,你可以根据自己的需要改变它。
the code rotates the image 90 degrees counter clock wise. Of course you can change it depending on your needs.
我做了有这个功能和其他人,<一个好贴href="http://stackoverflow.com/questions/18250951/jni-bitmap-operations-for-helping-to-avoid-oom">here
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和放大器的位图; NDK的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!