Rotacionando um bitmap usando JNI e NDK
Decidi que, como os bitmaps consomem muita memória, o que pode causar erros de falta de memória com facilidade, colocarei o trabalho pesado, que consome memória, no código C / C ++.
As etapas que uso para rotacionar um bitmap são:ler informações de bitmap (largura, altura)armazene pixels de bitmap em uma matriz.reciclar o bitmap.crie um novo bitmap de tamanho oposto.Coloque os pixels no novo bitmap.Libere os pixels e retorne o bitmap.O problema:Mesmo que tudo pareça funcionar sem erros, a imagem de saída não é uma rotação do original. Na verdade, isso estraga completamente.
A rotação deve ser em sentido anti-horário, 90 graus.
Exemplo (a captura de tela é ampliada) do que recebo:Então, como você pode ver, não apenas as cores ficaram mais estranhas, mas o tamanho não combina com o que eu defini. Algo é muito estranho aqui.
Talvez eu não leia / coloque os dados corretamente?
Claro que isso é apenas um exemplo. O código deve funcionar bem em qualquer bitmap, contanto que o dispositivo tenha memória suficiente para mantê-lo. Além disso, talvez eu queira fazer outras operações no bitmap além de girá-lo.
Código que criei:Arquivo 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
arquivo 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;
}
arquivo 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);
EDIT: depois que eu tenho a minha resposta, desejo compartilhar este código e notas sobre isso para todos:
Para que isso funcione, eu substituí no código todas as instâncias de "uint16_t" com "uint32_t" (esse é o bug no meu código que eu perguntei sobre).
bitmap de entrada e saída deve ser com 8888 config (que é ARGB)
bitmap de entrada será reciclado durante o processo.
o código gira a imagem 90 graus no sentido horário. Claro que você pode mudar dependendo das suas necessidades.
melhor soluçãoEu fiz um post legal com essa funcionalidade e outras,Aqui .