frameworks/native/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp 是Android提供的yuv格式纹理贴图的例子。
前面先申请存放纹理数据的buffer
yuvTexBuffer = new GraphicBuffer(yuvTexWidth, yuvTexHeight, yuvTexFormat, yuvTexUsage);
其中 yuvTexFormat = HAL_PIXEL_FORMAT_YV12
class GraphicBuffer在 frameworks/native/include/ui/GraphicBuffer.h 中定义,其初始化函数通过initSize调用allocator.alloc申请空间。allocator是GraphicBufferAllocator的引用,这个class包含了alloc_device_t变量,是grallocHAL模块中alloc设备使用的结构体,完成对图形缓冲区的管理,实现从硬件内存到应用地址的映射。
后续会通过vendor driver提供的eglCreateImageKHR调用dpy->winsys->get_native_buffer,到winsysp_native_buffer_validate,get_buffer_egl_format,再到hal_format_to_egl_format,完成从HAL format到egl format的映射,例如 HAL_PIXEL_FORMAT_YV12 <-> EGL_COLOR_BUFFER_FORMAT_YV12_BT601_NARROW
配置在 arm/xxx/android/config/mali_config_interface_reference.cpp
在eglCreateImageKHR通过get_native_buffer到egl_color_buffer_wrap_external_planar生成EGLImageKHR后,调用glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image),从EGLImageKHR产生纹理。
这个过程利用了OpenGL的extension - GL_OES_EGL_image_external
https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
这个extension定义了一个新的texture target - TEXTURE_EXTERNAL_OES;相应地在shader中增加了一个新的sampler类型 - samplerExternalOES,是a handle for accessing an external texture。
注意:这个extension已经过时,应该使用GL_OES_EGL_image_external_essl3
https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external_essl3.txt
Vulkan则用VkSamplerYcbcrModelConversion定义了从source color model到shader color model的转换。
typedef enum VkSamplerYcbcrModelConversion {
VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY=0, // input values unmodified
VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY=1, // no conversion but range expanded
VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709=2, // YCbCr to RGB as defined in BT.709
VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601=3, // YCbCr to RGB as defined in BT.601
VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020=4, // YCbCr to RGB as defined in BT.2020
} VkSamplerYcbcrModelConversion;
原程序的YUV值共有2^3=8种组合,所以输出色块共有8种颜色。为了调试方便可以改动赋值以输出纯色,以红色(255, 0, 0)为例,
Y = 0.257 * R + 0.504 * G + 0.098 * B + 16 = 0.257 * 255 + 16 = 81
U = -0.148 * R - 0.291 * G + 0.439 * B + 128 = -0.148 * 255 + 128 = 90
V = 0.439 * R - 0.368 * G - 0.071 * B + 128 = 0.439 * 255 + 128 = 240
在修改yuvTexWidth = yuvTexHeight = 128的情况下,
yuvTexStrideY = 128
yuvTexOffsetV = 16384 yuvTexStrideV = 64
yuvTexOffsetU = 20480 yuvTexStrideU = 64
为了检查输出,可以在renderFrame和eglSwapBuffers之间插入glReadPixels如下:
GLubyte *pixels = (GLubyte *) calloc(4, sizeof(GLubyte));
glReadPixels(w/2, h/2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
printf("RGBA values: %d %d %d %d\n", pixels[0], pixels[1], pixels[2], pixels[3]);
free(pixels);
另外ISP信号通常使用YUV420SP也就是NV21格式。
HAL_PIXEL_FORMAT_YCrCb_420_SP <-> EGL_COLOR_BUFFER_FORMAT_NV21_BT601_NARROW
http://blog.sina.com.cn/s/blog_5423c45a0102xk7h.html