Question

i want to rotate the image as per passing the orientation to the function in my rotateBitmap() funcation.

i have taken the reference about this method from this awesome library.

What i want to do is ::

I want to rotate the image as per the passing the orientation in function after that i want to apply the gray scaling effect to the image and then if want to downnsample the image and return the thumbnail of the image to the calling activity.

Here below i have pasted the my native code ..

What i have tried so far is ::

#include <jni.h>
#include <android/log.h>
#include <android/bitmap.h>
#include <stdio.h>
#include <cstring>
#include <unistd.h>

#define  LOG_TAG    "libphotophun"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
typedef struct 
{
    uint8_t alpha;
    uint8_t red;
    uint8_t green;
    uint8_t blue;
} argb;

class JniBitmap
{
public:
    uint32_t* _storedBitmapPixels;
    AndroidBitmapInfo _bitmapInfo;
    JniBitmap()
    {
        _storedBitmapPixels = NULL;
    }
};

extern "C"
{
        //store
        JNIEXPORT jobject JNICALL Java_com_example_ndksampleproject_MainActivity_jniStoreBitmapData(
                JNIEnv * env, jobject obj, jobject bitmap);
        //get
        JNIEXPORT jobject JNICALL Java_com_example_ndksampleproject_MainActivity_jniGetBitmapFromStoredBitmapData(
                JNIEnv * env, jobject obj, jobject handle);
        //free
        JNIEXPORT void JNICALL Java_com_example_ndksampleproject_MainActivity_jniFreeBitmapData(
                JNIEnv * env, jobject obj, jobject handle);

        JNIEXPORT jobject JNICALL Java_com_example_ndksampleproject_MainActivity_rotateBitmap(
                JNIEnv * env, jobject obj, jobject bitmap,int rotation);

}

/**free bitmap*/  //
JNIEXPORT void JNICALL Java_com_example_ndksampleproject_MainActivity_jniFreeBitmapData(
        JNIEnv * env, jobject obj, jobject handle)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
        return;
    delete[] jniBitmap->_storedBitmapPixels;
    jniBitmap->_storedBitmapPixels = NULL;
    delete jniBitmap;
}

/**restore java bitmap (from JNI data)*/  //
JNIEXPORT jobject JNICALL Java_com_example_ndksampleproject_MainActivity_jniGetBitmapFromStoredBitmapData(
        JNIEnv * env, jobject obj, jobject handle)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
    {
        LOGD("no bitmap data was stored. returning null...");
        return NULL;
    }
    //
    //creating a new bitmap to put the pixels into it - using Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :
    //
    jclass bitmapCls = env->FindClass("android/graphics/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, jniBitmap->_bitmapInfo.width,
            jniBitmap->_bitmapInfo.height, bitmapConfig);
    //
    // putting the pixels into the new bitmap:
    //
    int ret;
    void* bitmapPixels;
    if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
    {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
        return NULL;
    }
    uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
    int pixelsCount = jniBitmap->_bitmapInfo.height
            * jniBitmap->_bitmapInfo.width;
    memcpy(newBitmapPixels, jniBitmap->_storedBitmapPixels,
            sizeof(uint32_t) * pixelsCount);
    AndroidBitmap_unlockPixels(env, newBitmap);
    //LOGD("returning the new bitmap");
    return newBitmap;
}

/**store java bitmap as JNI data*/  //
JNIEXPORT jobject JNICALL Java_com_example_ndksampleproject_MainActivity_jniStoreBitmapData(
        JNIEnv * env, jobject obj, jobject bitmap)
{
    AndroidBitmapInfo bitmapInfo;
    uint32_t* storedBitmapPixels = NULL;
    //LOGD("reading bitmap info...");
    int ret;
    if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0)
    {
        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
        return NULL;
    }
    //LOGD("width:%d height:%d stride:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);
    if (bitmapInfo.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;
    storedBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
    int pixelsCount = bitmapInfo.height * bitmapInfo.width;
    memcpy(storedBitmapPixels, src, sizeof(uint32_t) * pixelsCount);
    AndroidBitmap_unlockPixels(env, bitmap);
    JniBitmap *jniBitmap = new JniBitmap();
    jniBitmap->_bitmapInfo = bitmapInfo;
    jniBitmap->_storedBitmapPixels = storedBitmapPixels;
    return env->NewDirectByteBuffer(jniBitmap, 0);
}
JNIEXPORT jobject JNICALL Java_com_example_ndksampleproject_MainActivity_rotateBitmap(JNIEnv * env, jobject obj, jobject bitmap,int rotation,jobject handle)
{
    JniBitmap* jniBitmap =jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if(rotation==90)
    {
        //jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
        if (jniBitmap->_storedBitmapPixels == NULL)
            return;
        uint32_t* previousData = jniBitmap->_storedBitmapPixels;
        uint32_t newWidth = jniBitmap->_bitmapInfo.height;
        uint32_t newHeight = jniBitmap->_bitmapInfo.width;
        jniBitmap->_bitmapInfo.width = newWidth;
        jniBitmap->_bitmapInfo.height = newHeight;
        uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
        int whereToGet = 0;
        // XY. ... ... ..X
        // ...>Y..>...>..Y
        // ... X.. .YX ...
        for (int x = 0; x < newWidth; ++x)
            for (int y = newHeight - 1; y >= 0; --y)
            {
                //take from each row (up to bottom), from left to right
                uint32_t pixel = previousData[whereToGet++];
                newBitmapPixels[newWidth * y + x] = pixel;
            }
        //delete[] previousData;
        jniBitmap->_storedBitmapPixels = newBitmapPixels;
    }
    else if(rotation==180)
    {
        //jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
        if (jniBitmap->_storedBitmapPixels == NULL)
            return;
        uint32_t* previousData = jniBitmap->_storedBitmapPixels;
        uint32_t width = jniBitmap->_bitmapInfo.width;
        uint32_t height = jniBitmap->_bitmapInfo.height;
        uint32_t* newBitmapPixels = new uint32_t[width * height];
        int whereToGet = 0;
        // XY. ...
        // ...>...
        // ... .YX
        jniBitmap->_storedBitmapPixels = newBitmapPixels;
        for (int y = height - 1; y >= 0; --y)
            for (int x = width - 1; x >= 0; --x)
            {
                //take from each row (up to bottom), from left to right
                uint32_t pixel = previousData[whereToGet++];
                newBitmapPixels[width * y + x] = pixel;
            }
        //delete[] previousData;
        jniBitmap->_storedBitmapPixels = newBitmapPixels;
    }

    else if(rotation==270)
    {
        //jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
        if (jniBitmap->_storedBitmapPixels == NULL)
            return;
        uint32_t* previousData = jniBitmap->_storedBitmapPixels;
        uint32_t newWidth = jniBitmap->_bitmapInfo.height;
        uint32_t newHeight = jniBitmap->_bitmapInfo.width;
        jniBitmap->_bitmapInfo.width = newWidth;
        jniBitmap->_bitmapInfo.height = newHeight;
        uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
        int whereToGet = 0;
        // XY. ..X ... ...
        // ...>..Y>...>Y..
        // ... ... .YX X..
        jniBitmap->_storedBitmapPixels = newBitmapPixels;
        for (int x = newWidth - 1; x >= 0; --x)
            for (int y = 0; y < newHeight; ++y)
            {
                //take from each row (up to bottom), from left to right
                uint32_t pixel = previousData[whereToGet++];
                newBitmapPixels[newWidth * y + x] = pixel;
            }
        //delete[] previousData;
        jniBitmap->_storedBitmapPixels = newBitmapPixels;
    }
    return jniBitmap;
}

Log cat ::

Compile++ thumb  : photophun <= photophun.cpp
In file included from D:/Workspace/NDKSampleProject/jni/photophun.cpp:2:0:
D:/android-ndk-r8b/platforms/android-14/arch-arm/usr/include/jni.h:592:13: note: the mangling of 'va_list' has changed in GCC 4.4
D:/Workspace/NDKSampleProject/jni/photophun.cpp: In function '_jobject* Java_com_example_ndksampleproject_MainActivity_rotateBitmap(JNIEnv*, jobject, jobject, int, jobject)':
D:/Workspace/NDKSampleProject/jni/photophun.cpp:154:4: error: return-statement with no value, in function returning 'jobject {aka _jobject*}' [-fpermissive]
D:/Workspace/NDKSampleProject/jni/photophun.cpp:179:4: error: return-statement with no value, in function returning 'jobject {aka _jobject*}' [-fpermissive]
D:/Workspace/NDKSampleProject/jni/photophun.cpp:204:4: error: return-statement with no value, in function returning 'jobject {aka _jobject*}' [-fpermissive]
D:/Workspace/NDKSampleProject/jni/photophun.cpp:226:9: error: cannot convert 'JniBitmap*' to 'jobject {aka _jobject*}' in return
/cygdrive/d/android-ndk-r8b/build/core/build-binary.mk:255: recipe for target `/cygdrive/d/Workspace/NDKSampleProject/obj/local/armeabi/objs/photophun/photophun.o' failed
make: *** [/cygdrive/d/Workspace/NDKSampleProject/obj/local/armeabi/objs/photophun/photophun.o] Error 1

Please let me know if you need anything from my side. Please help me as i am in great need to complete this thing to resolve the OOM issue. Many thanks in advance....

EDIT ::

extern "C"
{
        
        JNIEXPORT void JNICALL com_example_ndksampleproject_MainActivity_rotateBitmap(
                JNIEnv * env, jobject obj, jobject bitmap,int rotation);

}


    JNIEXPORT void JNICALL com_example_ndksampleproject_MainActivity_rotateBitmap(JNIEnv * env, jobject obj, jobject bitmap,uint32_t rotation,jobject handle)
    {
        JniBitmap* jniBitmap =jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
        if(rotation==90)
        {
            jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    
            if (jniBitmap->_storedBitmapPixels == NULL)
                return;
            uint32_t* previousData = jniBitmap->_storedBitmapPixels;
            uint32_t newWidth = jniBitmap->_bitmapInfo.height;
            uint32_t newHeight = jniBitmap->_bitmapInfo.width;
            jniBitmap->_bitmapInfo.width = newWidth;
            jniBitmap->_bitmapInfo.height = newHeight;
            uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
            int whereToGet = 0;
            // XY. ... ... ..X
            // ...>Y..>...>..Y
            // ... X.. .YX ...
            for (int x = 0; x < newWidth; ++x)
                for (int y = newHeight - 1; y >= 0; --y)
                {
                    //take from each row (up to bottom), from left to right
                    uint32_t pixel = previousData[whereToGet++];
                    newBitmapPixels[newWidth * y + x] = pixel;
                }
            delete[] previousData;
            //jniBitmap->_storedBitmapPixels = newBitmapPixels;
        }
        else if(rotation==180)
        {
            jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
            if (jniBitmap->_storedBitmapPixels == NULL)
                return;
            uint32_t* previousData = jniBitmap->_storedBitmapPixels;
            uint32_t width = jniBitmap->_bitmapInfo.width;
            uint32_t height = jniBitmap->_bitmapInfo.height;
            uint32_t* newBitmapPixels = new uint32_t[width * height];
            int whereToGet = 0;
            // XY. ...
            // ...>...
            // ... .YX
            jniBitmap->_storedBitmapPixels = newBitmapPixels;
            for (int y = height - 1; y >= 0; --y)
                for (int x = width - 1; x >= 0; --x)
                {
                    //take from each row (up to bottom), from left to right
                    uint32_t pixel = previousData[whereToGet++];
                    newBitmapPixels[width * y + x] = pixel;
                }
            delete[] previousData;
            //jniBitmap->_storedBitmapPixels = newBitmapPixels;
        }
    
        else if(rotation==270)
        {
            jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
            if (jniBitmap->_storedBitmapPixels == NULL)
                return;
            uint32_t* previousData = jniBitmap->_storedBitmapPixels;
            uint32_t newWidth = jniBitmap->_bitmapInfo.height;
            uint32_t newHeight = jniBitmap->_bitmapInfo.width;
            jniBitmap->_bitmapInfo.width = newWidth;
            jniBitmap->_bitmapInfo.height = newHeight;
            uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
            int whereToGet = 0;
            // XY. ..X ... ...
            // ...>..Y>...>Y..
            // ... ... .YX X..
            jniBitmap->_storedBitmapPixels = newBitmapPixels;
            for (int x = newWidth - 1; x >= 0; --x)
                for (int y = 0; y < newHeight; ++y)
                {
                    //take from each row (up to bottom), from left to right
                    uint32_t pixel = previousData[whereToGet++];
                    newBitmapPixels[newWidth * y + x] = pixel;
                }
            delete[] previousData;
            //jniBitmap->_storedBitmapPixels = newBitmapPixels;
        }
        //return jniBitmap;
    }

I called the native method this Way ::

BitmapFactory.Options options = new BitmapFactory.Options();
    Bitmap tempBmp=null;
    ByteBuffer _handler =null;
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId())
        {
        case R.id.item_rotate_90:
            options.inPreferredConfig = Config.ARGB_8888;
            bitmapOrig = BitmapFactory.decodeResource(this.getResources(), R.drawable.sampleimage,options);
            rotateBitmap(bitmapOrig,90,_handler);
            tempBmp=getBitmapAndFree();
            if(tempBmp!=null)
            {
                ivDisplay.setImageBitmap(tempBmp);
            }
            break;
    

LogCat ::

FATAL EXCEPTION: main
java.lang.UnsatisfiedLinkError: Native method not found: com.example.ndksampleproject.MainActivity.rotateBitmap:(Landroid/graphics/Bitmap;ILjava/nio/ByteBuffer;)V
at com.example.ndksampleproject.MainActivity.rotateBitmap(Native Method)
at com.example.ndksampleproject.MainActivity.onOptionsItemSelected(MainActivity.java:113)
at android.app.Activity.onMenuItemSelected(Activity.java:2612)
at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected(PhoneWindow.java:1061)
at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735)
Was it helpful?

Solution

The code I've made already supports rotation. just look at the sample project to see how to call it.

Also, gray scaling shouldn't be a too hard operation to implement on the C++ code I've made. I'm sure there are examples of it on the Internet.

About downsampling, I didn't make it as it's a lot of work, but I have added scaling of images that are already decoded (meaning they are stored as bitmap objects). However, you can always downscale using the normal Google's way , or my way (the bottom code).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top