1

In JNI I have a pointer to an 8-bit integer array (uint8_t*). I want to pass its data to Java part.

My problem is, that data in array must be in uint_8 format, because I achieved it as colour information from a bitmap of format RGB565, which I want to change into grayscale:

Java part:

    private void loadJPEG(String fileName) {

    Bitmap old = BitmapFactory.decodeFile(fileName);
    Bitmap bmp = old.copy(Config.RGB_565, false);
    byte[] grayScaledBitmap = new byte[]{};

    if (bmp != null && bmp.getConfig() == Bitmap.Config.RGB_565) {

        ShortBuffer rgbBuf = ShortBuffer.allocate(bmp.getWidth() * bmp.getHeight()); 

        bmp.copyPixelsToBuffer(rgbBuf); 

        grayScaledBitmap = convertToLum(rgbBuf.array(), bmp.getWidth(), bmp.getHeight());
    }
}

private native byte[] convertToLum(short[] data, int w, int h);

C++ part:

void convertRGB565ToGrayScale(JNIEnv* env, uint8_t* src, unsigned int srcWidth, unsigned int srcHeight, uint8_t* dst) {
    unsigned int size = srcWidth * srcHeight;
    uint16_t rgb;
    uint16_t r;
    uint16_t g;
    uint16_t b;

    for (unsigned int i = 0; i < size; i++) {
        rgb = ((uint16_t*) src)[i]; 
        uint16_t tmp = (uint16_t) (rgb & 0xF800);
        tmp = tmp >> 8;

        r = (uint16_t) ((rgb & 0xF800) >> 8); //to rgb888
        g = (uint16_t) ((rgb & 0x07E0) >> 3);
        b = (uint16_t) ((rgb & 0x001F) << 3);

        dst[i] = (uint8_t) (((r << 1) + (g << 2) + g + b) >> 3); //to grayscale
    }
}


JNIEXPORT jbyteArray JNICALL Java_com_qualcomm_loadjpeg_LoadJpeg_convertToLum(JNIEnv* env, jobject obj, jshortArray img, jint w, jint h) {

    jshort* jimgData = NULL;
    jboolean isCopy = 0;
    jbyte* grayScaled;
    jbyteArray arrayToJava

    DPRINTF("%s %d\n", __FILE__, __LINE__);

    if (img != NULL) {
        // Get data from JNI
        jimgData = env->GetShortArrayElements(img, &isCopy);

        uint8_t* lum = (uint8_t*) fcvMemAlloc(w * h, 16);
        convertRGB565ToGrayScale(env, (uint8_t*) jimgData, w, h, lum);
        grayScaled = (jbyte*) lum;
        arrayToJava = env->NewByteArray(w*h);
        env->SetByteArrayRegion(arrayToJava, 0, w*h, grayScaled);

        env->ReleaseShortArrayElements(img, jimgData, JNI_ABORT);
        fcvMemFree(lum);


        DPRINTF("%s %d Done\n", __FILE__, __LINE__);
    }
return arrayToJava;
}

The error comes on line with SetShortArrayRegion function:

No source available for memcpy() at [hexadecimal adress]
No source available for ioctl() at ...

EDIT >> the error above no longer appears, it was caused because of bad memory-freeing via fcvmemfree, code is re-editet. I have still a problem with filling jbyteArray arrayToJava with data from convertRGB565ToGrayScale, it is always empty after SetByteArrayRegion call. The question is still:

How am I supposed to pass changed data to Java part?

witcher
  • 135
  • 2
  • 13

2 Answers2

0

A jshort is equivalent to a 16-bit integer. A uint8_t is an 8-bit unsigned character type:

http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html

http://en.cppreference.com/w/c/types/integer

So you can't just merely cast one pointer type to another, and especially of differing size types. There is the issue of the strict aliasing rule that can come into play:

What is the strict aliasing rule?

So what you should try is to use int16_t as the type. This matches what a jshort is defined as, according to the JNI documentation in the above link.

Community
  • 1
  • 1
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • Thank you for your respond but actually I need to use uint8_t. You can see the reason in editted question. – witcher Jan 31 '15 at 14:03
  • Why does your Java code use `short`? Why isn't it `byte`? Second, regardless of what you want to achieve, you can't cast one pointer type to another just like that, without knowing the consequences of doing so. That is what the strict aliasing rule is all about, and why I linked to it in my answer. If the error is a runtime error where memcpy fails, be lucky that the runtime caught it. Usually, strict aliasing errors are some of the toughest to diagnose. – PaulMcKenzie Jan 31 '15 at 15:33
  • Java uses short because whole this is actually sample app and I did not make changes in bitmap buffer data type. However, format RGB565 means that each colour takes 5bits (green 6), that is 5 + 6 + 5 = 16, which is size of a short. Why should I use byte? With the pointers you are right, I am not conscious of all the consequences. Actually I do not get a memcpy error, question is re-edited again. – witcher Jan 31 '15 at 22:51
0

Your problem isn't with converting data types, but with the fact that you free a buffer too soon. convertRGB565ToGrayScale doesn't allocate any new buffer for the output, but writes the output data into a buffer provided by the caller. After convertRGB565ToGrayScale returns, grayScaled points to the same buffer as lum. Since grayScaled is used much later, move the fcvMemFree call to after SetShortArrayRegion.

Secondly, don't create a new arrayToJava object within the C++ function, but use the one you passed in. (Then you need to allocate it for the right size on the java side, like grayScaledBitmap = new byte[bmp.getWidth() * bmp.getHeight()]). When you overwrite a variable in one function, it doesn't change the same variable in calling functions.

mstorsjo
  • 12,983
  • 2
  • 39
  • 62
  • Thank you and yes, you was right. Question is re-edited, because now I get another error. Not on some exact line of code, but after whole code is executed. It says: No source available for [hexadecimal address]. I can see that my arrayToJava is still empty after calling SetShortArrayRegion. Do you see the problem anywhere? – witcher Jan 31 '15 at 23:00
  • FWIW, I later edited the answer and added another part of the solution which should fix that issue as well. – mstorsjo Feb 04 '15 at 07:45