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

JNI: RGB565 Bitmap to Grayscale - how to pass in JNI changed data back to Java?

本文关键字:JNI 数据 Java 位图 RGB565 灰度      更新时间:2023-10-16

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

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

爪哇部分:

    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 %dn", __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 Donen", __FILE__, __LINE__);
    }
return arrayToJava;
}

该错误与 SetShortArrayRegion 函数有关:

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

编辑>>上面的错误不再出现,它是由于通过 fcvmemfree 释放内存错误引起的,代码被重新编辑。我仍然有一个问题,用convertRGB565ToGrayScale的数据填充jbyteArray arrayToJava,在SetByteArrayRegion调用后它总是空的。问题仍然是:

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

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

因此,您不能只将一种指针类型转换为另一种指针类型,尤其是不同大小类型的指针类型。 有一个严格的混叠规则的问题,可能会发挥作用:

什么是严格的混叠规则?

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

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

其次,不要在 C++ 函数中创建新的 arrayToJava 对象,而是使用您传入的对象。(然后你需要在java端为正确的大小分配它,如grayScaledBitmap = new byte[bmp.getWidth() * bmp.getHeight()])。当您覆盖一个函数中的变量时,它不会在调用函数时更改同一变量。