0

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)
Community
  • 1
  • 1
AndroidLearner
  • 4,500
  • 4
  • 31
  • 62

1 Answers1

0

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

Community
  • 1
  • 1
android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • Thanks for your quick response.I have import this code from your sample only but in your sample you have made the different methods for rotation like(90 degree,180 degree,etc) and in my case i want to make one generic method for that where i pass the rotation figure and image should rotate accordingly.so i have make one generic method but found some issue and i have no clue about where i am wrong.please see the method rotateBitmap() where i have added one argument rotation to rotate the image.can you please help me to find out the issue? – AndroidLearner Apr 07 '14 at 10:10
  • what is the difference between my function and what you need? do you do the rotation on any value of degrees (for example 45 degrees) ? – android developer Apr 07 '14 at 10:18
  • yes,this is exactly what i want...while in your function it rotate to only clock wise or counter clock wise.please correct me if m wrong.If i use any of ur function which provide the image rotation(either cw90 or CcW90) then where should i need to make change to make it generic? – AndroidLearner Apr 07 '14 at 10:24
  • Thanks for poinitng me to the right direction.I have make changes and called the method but now m getting the error related to `Unsatisfied Link Error: Native Method Not Found..`Please check my edit. – AndroidLearner Apr 07 '14 at 11:07
  • so you've tried to merge some of the functions I've written? Anyway, I think your problem is with the upper declaration of "Java_com_example_ndksampleproject_MainActivity_rotateBitmap" - you missed the last parameter. make sure it's exactly the same as the one you've implemented. I'd also suggest using the functions I've made inside your function, instead of putting all the code there, but that's just code style/design... – android developer Apr 07 '14 at 11:30
  • I am able to rotate the image by using your code.After applying the rotation to the image i am applying the grayscale effect to the image but that's not as per my requirement.Can you please help me out in this case?http://stackoverflow.com/questions/22915293/apply-grayscale-effect-to-image-using-ndkc-c-in-android – AndroidLearner Apr 08 '14 at 07:09
  • Well it seems you've solved the problem both here and on your link. Please mark answers with a "V" when the problem is solved and the answer helped you. – android developer Apr 08 '14 at 09:26