JNI:RGB565位图到灰度 - 如何将JNI更改的数据传递回Java?

时间:2015-01-31 12:54:05

标签: java android c++ android-ndk java-native-interface

在JNI中,我有一个指向8位整数数组(uint8_t *)的指针。我想将其数据传递给Java部分。

我的问题是,数组中的数据必须是uint_8格式,因为我是从格式为RGB565的位图中将其作为颜色信息实现的,我想将其转换为灰度:

Java部分:

    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 ++部分:

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;
}

错误与SetShortArrayRegion函数一致:

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

编辑&gt;&gt;上面的错误不再出现,它是由于通过fcvmemfree释放内存不足造成的,代码是重新编辑的。使用convertRGB565ToGrayScale中的数据填充jbyteArray arrayToJava时仍然存在问题,在SetByteArrayRegion调用后它始终为空。问题仍然是:

我应该如何将更改的数据传递给Java部分?

2 个答案:

答案 0 :(得分:0)

jshort相当于16位整数。 uint8_t是一个8位无符号字符类型:

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

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

因此,您不能仅仅将一个指针类型转换为另一个指针类型,尤其是不同大小类型。存在严格别名规则的问题:

What is the strict aliasing rule?

所以你应该尝试使用int16_t作为类型。根据上述链接中的JNI文档,这与jshort定义的匹配。

答案 1 :(得分:0)

您的问题不在于转换数据类型,而在于您过早释放缓冲区这一事实。 convertRGB565ToGrayScale不为输出分配任何新缓冲区,但将输出数据写入调用方提供的缓冲区。 convertRGB565ToGrayScale返回后,grayScaled指向与lum相同的缓冲区。由于grayScaled的使用时间较晚,因此请将fcvMemFree来电转移到SetShortArrayRegion之后。

其次,不要在C ++函数中创建一个新的arrayToJava对象,而是使用你传入的对象。(然后你需要在java端分配正确的大小,如{ {1}})。当您在一个函数中覆盖变量时,它不会在调用函数时更改相同的变量。