在 Java 上创建位图并使用 JNI 将其发送到 C++ 并通过 OpenGL 渲染它,黑色

Create bitmap on Java and send it to C++ using JNI and render it by OpenGL, black

在 Android 上,我使用 JNI 将字符串从 C++ 发送到 Java,然后将其绘制到位图,然后 return 将字节数组返回到 C++ 以将其绘制到 OpenGL ,我得到一个黑色矩形。

首先,我通过这种方式在 Java 中创建位图:

public static byte[] DrawString(String text) {
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(Color.rgb(110, 110, 110));
    float scale = ActivityGame.getCurrentActivity().getResources().getDisplayMetrics().density;
    paint.setTextSize((int) (24 * scale));
    Rect bounds = new Rect();
    paint.getTextBounds(text, 0, text.length(), bounds);

    Bitmap bitmap = Bitmap.createBitmap((int) (bounds.width() * scale), (int) (bounds.height() * scale), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    int x = (bitmap.getWidth() - bounds.width()) / 6;
    int y = (bitmap.getHeight() + bounds.height()) / 5;
    canvas.drawText(text, x * scale, y * scale, paint);

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
    byte[] byteArray = stream.toByteArray();
    bitmap.recycle();
    return byteArray;
}

然后在C++中我通过JNI这样调用前面的函数:

static unsigned char* DrawString(std::string text,int* length){
    JNIEnv *env= nullptr;
    JNIUtils::JVM->AttachCurrentThread(&env, NULL);
    jclass jniUtilsCls = env->FindClass("com/moussa/mightypolygons/JNIUtils");
    jmethodID drawStringMethodId = env->GetStaticMethodID(jniUtilsCls, "DrawString", "(Ljava/lang/String;)[B");
    jstring _text=env->NewStringUTF(text.c_str());
    jbyteArray bmpArr=(jbyteArray)env->CallStaticObjectMethod(jniUtilsCls,drawStringMethodId,_text);
    jsize num_bytes = env->GetArrayLength( bmpArr);
    jbyte* elements = env->GetByteArrayElements(bmpArr, NULL);
    unsigned char* bmp=new unsigned char[num_bytes];
    if (elements) {
        for(int i = 0; i < num_bytes; i++) {
            bmp[i] = elements[i];
        }
        *length=(int)num_bytes;
        return bmp;
    }
    return NULL;
}

我终于得到了字符数组和长度,现在我在 OpenGL 中创建了一个纹理:

Texture::Texture(unsigned char *bmp,int len, TextureTypes type) {
image = stbi_load_from_memory(bmp,len, &width, &height, &nrComponents, 4);
glGenTextures(1, &textureObj);
if (image) {
    GLenum format;
    if (nrComponents == 1)
        format = GL_RED;
    else if (nrComponents == 3)
        format = GL_RGB;
    else if (nrComponents == 4)
        format = GL_RGBA;
    else
        format = GL_RGBA;
    glBindTexture(GL_TEXTURE_2D, textureObj);
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                    format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                    format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    stbi_image_free(image);
} else {
    stbi_image_free(image);
}
}

我得到一个与位图宽度和高度相同的黑色矩形。

编辑:

我从 Java 端将位图保存为 PNG,然后从 C++ 重新加载它,它使用相同的代码完美呈现,所以问题出在字节数组或 GL 参数或其他方面吗?

编辑 2:

我在 Java 中注意到 ByteArray 包含带负值的带符号字节,当它在 C++ 中转换为 unsigned char* 时它的值发生了变化,例如字节数组中的第一个字节在 Java 中是 -119 如果转换为 unsigned char

则变为 137

我使用不同的方法发送来自 Java 的位图对象,并使用 android/bitmap.h

读取其像素
AndroidBitmapInfo androidBitmapInfo ;
void* pixels;
AndroidBitmap_getInfo(env, bitmap, &androidBitmapInfo);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
unsigned char* pixelsChar = (unsigned char*) pixels;
AndroidBitmap_unlockPixels(env, bitmap);

现在可以正常使用了。