编程实现应用视图截图调用glReadPixels得全黑图是否与GLES版本有关
GLES视图glReadPixels截图全黑问题排查
问题复现代码
实现的截图逻辑如下:
val v = this.view!! val w = v.width val h = v.height val bitmapBuffer = IntArray(w * h) val bitmapSource = IntArray(w * h) val intBuffer: IntBuffer = IntBuffer.wrap(bitmapBuffer) intBuffer.position(0) glReadPixels(0, 0, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, intBuffer) var offset1: Int var offset2: Int for (i in 0 until h) { offset1 = i * w offset2 = (h - i - 1) * w for (j in 0 until w) { val texturePixel = bitmapBuffer[offset1 + j] val blue = texturePixel shr 16 and 0xff val red = texturePixel shl 16 and 0x00ff0000 val pixel = texturePixel and -0xff0100 or red or blue bitmapSource[offset2 + j] = pixel } } val bit: Bitmap = Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888)
运行后得到完全全黑的截图,疑问该问题是否和当前使用的GLES版本有关。
原因说明
该问题不直接和GLES版本绑定,绝大多数全黑结果是调用逻辑错误导致的,常见诱因如下:
- 调用时机错误:
glReadPixels必须在持有当前GL上下文的渲染线程中、且在帧缓冲完成全部绘制指令后调用。如果在视图首帧渲染完成前(比如onCreate/onResume生命周期刚触发时)、或者在非GL线程调用,读取到的缓冲区初始值就是全0,对应ARGB格式下的纯黑透明像素。 - 帧缓冲绑定错误:如果使用自定义FBO做离屏渲染,调用
glReadPixels时未绑定对应渲染FBO,默认会读取系统窗口的默认帧缓冲;如果实际渲染内容都输出到自定义FBO,未被绘制的默认帧缓冲读出来就是全黑。若开启了多重采样抗锯齿,不能直接从多重采样渲染缓冲读像素,必须先将内容blit到普通颜色缓冲再读取。 - 格式不匹配:代码中使用
GL_RGBA+GL_UNSIGNED_BYTE的格式组合读取,需要保证当前GL表面的颜色缓冲格式为RGBA8,如果表面配置为RGB565等其他格式,读取的像素值会错乱,极端情况呈现全黑。如果在高版本GLES环境下GL10.GL_RGBA常量引用错误,也会导致读取参数非法、调用失败。 - 上下文状态异常:调用读取接口时如果GL上下文未被当前线程持有(比如应用切后台后上下文被回收、其他线程抢占了上下文),
glReadPixels会静默失败,不会抛出异常,缓冲区保留初始的0值,最终生成全黑位图。
修复建议
- 将截图触发逻辑放到GL渲染的
onDrawFrame回调中,确保所有绘制指令执行完成后再调用glReadPixels,不要在主线程的视图生命周期方法中直接调用截图逻辑。 - 调用读取接口前先校验当前绑定的帧缓冲:如果内容渲染到自定义FBO,先执行
glBindFramebuffer(GL_FRAMEBUFFER, 渲染用FBO_ID)再读取;如果开启了多重采样,先创建同尺寸的普通FBO作为中转,通过glBlitFramebuffer将渲染内容拷贝到中转FBO后再执行读取。 - 读取前后调用
glGetError检查错误码,如果返回GL_INVALID_OPERATION或GL_INVALID_ENUM,优先排查参数格式、上下文绑定问题。 - 使用GLES3.0及以上版本时,可以通过
glReadBuffer明确指定要读取的颜色附件,避免读错附件位置。
内容的提问来源于stack exchange,提问作者Mithun Wijethunga
相关产品推荐
相关产品推荐

