Android中 glTexImage2D 的位图像素格式

4
我是一位有用的助手,可以翻译文本。
我正在尝试使用Bitmap类从Java加载纹理以在NDK OpenGL中使用。虽然它可以工作,但是我遇到了像素格式的问题。
首先,在Java中,我像这样从assets文件夹加载位图:
Bitmap bitmap = BitmapFactory.decodeStream(amgr.open(path));
return bitmap.copy(Bitmap.Config.ARGB_8888, false);

位图配置不支持RGBA通道顺序选项。

[JNI事情在这里发生]

使用GLES 1,我像这样缓冲纹理:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// `pixels` is the pixel buffer I produced earlier.

正如您所见,像素格式存在问题。 glTexImage2D 没有 ARGB 选项,但 Java 的 Bitmap 类没有创建 RGBA 缓冲区的选项。因此,我得到了混乱的颜色通道。顺便说一下,我确实需要 alpha 通道。
问题是:如何最有效地从 Java bitmap 类生成 RGBA8888 格式的像素缓冲区,或者如何以 ARGB8888 格式加载 GL 纹理?
肯定有一种方法,而不是逐个像素手动交换字节吧?
我目前正在这样做:
void pxl::swap_channels_ARGB_to_RGBA(void *pixBuf, const int len)
{
    jint *pixels = (jint *)pixBuf;

    for(int i = 0; i < len; i++)
    {
        jint pixel = pixels[i];

        jint a = (pixel >> 24) & 0xFF;
        jint r = (pixel >> 16) & 0xFF;
        jint g = (pixel >>  8) & 0xFF;
        jint b = (pixel >>  0) & 0xFF;

        pixels[i] = (jint)(a | (r << 24 ) | (g << 16) | (b << 8));
    }
}

或许还有其他错误?说实话,我并不确定关于glTexImage2D选项的内容。
谢谢!

你需要在JNI端执行glTexImage2D调用吗?或者你可以在Java端使用GLUtils.texImage2D上传它,然后只需通过JNI传递纹理ID作为int。 - samgak
谢谢,但是我正在尝试将所有的GL事物保留在C++中。手动交换对我来说可以工作,只是好奇是否有更优雅的解决方案。 - spinalwrap
我也很好奇,因为GLUtils的源代码并没有进行任何交换,但它确实可以工作。它通过将Bitmap对象作为jobject传递给JNI并调用getPixels()来工作。请检查util_texImage2D函数。 - samgak
你能展示一下如何在传递给JNI之前从位图中提取数据吗?你使用getPixels()还是copyPixelsToBuffer()? - samgak
2个回答

3

这个问题无法在OpenGL ES 1.1中解决,但可以通过OpenGL扩展EXT_texture_swizzle或者OpenGL ES 3.0来解决:

自OpenGL ES 3.0以来,您可以使用纹理交换参数来交换颜色通道。请参见glTexParameter

GL_TEXTURE_SWIZZLE_R

设置将应用于纹理像素的 r 分量之前返回给着色器的 swizzle。参数的有效值为GL_REDGL_GREENGL_BLUEGL_ALPHAGL_ZEROGL_ONE。如果 GL_TEXTURE_SWIZZLE_RGL_RED,则 r 的值将从获取的纹理像素的第一个通道中取出。如果 GL_TEXTURE_SWIZZLE_RGL_GREEN,则 r 的值将从获取的纹理像素的第二个通道中取出。...

这意味着当您将以下纹理参数设置为纹理对象时,颜色通道将在查找纹理时进行交换:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_GREEN);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);


OpenGL ES 3.0.5 规范; 3.8.14 纹理状态; 第162页 中可以找到相关部分。

要检查一个 OpenGL 扩展是否有效,可以使用 glGetString(GL_EXTENSIONS),它会返回一个以空格分隔的支持扩展列表。



完全不同的解决方案是使用 Canvas 进行转换。在画布上绘制 Bitmap,然后使用由画布保存的目标位图。
我在 GitHub 上找到了这个解决方案:修复 Android 2.3 无法解码 RGBA8888 格式位图
public static Bitmap convert(Bitmap bitmap, Bitmap.Config config) {
    Bitmap convertedBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), config);
    Canvas canvas          = new Canvas(convertedBitmap);
    Paint  paint           = new Paint();
    paint.setColor(Color.BLACK);
    canvas.drawBitmap(bitmap, 0, 0, paint);
    return convertedBitmap;
}

2

我认为没有直接的方法可以做到这一点,但是你可以通过以下方式优化你的算法

void pxl::swap_channels_ARGB_to_RGBA(void *pixBuf, const int len) {
    jint *pixels = (jint *)pixBuf;

    for(int i = 0; i < len; i++) {
        unsigned int aux = (int)pixels[i];
        pixels[i] = (aux << 8) | (aux >> 24);
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接