Android NDK中使用glReadPixels读取像素生成bitmap出错如何解决?
问题原因
- 坐标系不匹配:OpenGL 帧缓冲的默认坐标系原点在左下角,
glReadPixels会从左下角开始逐行向上读取像素数据;而 AndroidBitmap的存储顺序是从左上角逐行向下存储,直接拷贝会导致图像上下倒置。 - 通道顺序不匹配:你使用
GL_RGBA读取的像素数据通道顺序为 R→G→B→A,而 AndroidBitmap.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
相关产品推荐
相关产品推荐

