You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android NDK中使用glReadPixels读取像素生成bitmap出错如何解决?

问题原因
  • 坐标系不匹配:OpenGL 帧缓冲的默认坐标系原点在左下角,glReadPixels 会从左下角开始逐行向上读取像素数据;而 Android Bitmap 的存储顺序是从左上角逐行向下存储,直接拷贝会导致图像上下倒置。
  • 通道顺序不匹配:你使用 GL_RGBA 读取的像素数据通道顺序为 R→G→B→A,而 Android Bitmap.Config.ARGB_8888 要求的存储顺序为 A→R→G→B,直接拷贝会导致颜色通道错位,出现偏色等异常。
  • 字节对齐问题(潜在):OpenGL 默认的 GL_PACK_ALIGNMENT 参数为4,若图像宽度不是4的倍数,每行像素末尾会填充额外的空字节,导致读取的像素数据和你申请的 width * height * 4 内存大小不匹配,出现条纹状异常。
修复方案

步骤1:调整OpenGL字节对齐参数

在调用 glReadPixels 前添加如下代码,取消行填充:

glPixelStorei(GL_PACK_ALIGNMENT, 1);

步骤2:处理坐标翻转和通道转换

推荐在JNI层完成转换,性能更好,修改后的JNI代码如下:

jint size = width * height * 4;
GLubyte *srcPixels = static_cast<GLubyte *>(malloc(size));
// 读取像素
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(
    0,
    0,
    width,
    height,
    GL_RGBA,
    GL_UNSIGNED_BYTE,
    srcPixels
);
// 申请转换后的内存
GLubyte *dstPixels = static_cast<GLubyte *>(malloc(size));
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        // 翻转Y轴坐标,读取OpenGL左下角对应Bitmap左上角的位置
        int srcPos = ((height - 1 - y) * width + x) * 4;
        int dstPos = (y * width + x) * 4;
        // RGBA 转 ARGB
        dstPixels[dstPos + 0] = srcPixels[srcPos + 3]; // A通道
        dstPixels[dstPos + 1] = srcPixels[srcPos + 0]; // R通道
        dstPixels[dstPos + 2] = srcPixels[srcPos + 1]; // G通道
        dstPixels[dstPos + 3] = srcPixels[srcPos + 2]; // B通道
    }
}
free(srcPixels);
// 将dstPixels返回给Kotlin层使用,使用完成后记得释放dstPixels内存避免泄漏

Kotlin层代码不需要大幅修改,直接使用转换后的像素数组生成Bitmap即可:

val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val dataBuf = ByteBuffer.wrap(convertedPixels) // convertedPixels为JNI返回的转换后的字节数组
dataBuf.rewind()
bitmap.copyPixelsFromBuffer(dataBuf)

内容的提问来源于stack exchange,提问作者user1419851

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 16:36:04