对所有人而言,像素这个词并不陌生。各大手机厂商新品发布会时高频出现的一个词就是xxx手机像素多少多少,分辨率多高多高。那么这里说的手机像素是什么含义呢?
以500万像素为例,其含义是在一英寸的CCD面积上有500万个象素点,500万像素的分辨率是2592*1944或者2560*1920。
一块积木就好比是一个像素点
针对像素格式,介绍和分析其在图显系统中的实现原理和细节。
像素格式基本概念
像素格式描述了像素数据存储所用的格式,定义了像素在内存中的编码方式。
像素表示方式
下图标识了不同应用场景下所看到的像素格式的不同表示方式,虽然说像素格式不同但他们都是为了同一个目的:呈现色彩空间。
色彩空间的不同表示方式
bpp
bpp这个参数在代码中经常可见。例如libdrm代码中申请创建framebuffer的函数,bpp作为创建framebuffer的一个入参。
- static struct bo *
- bo_create_dumb(int fd, unsigned int width, unsigned int height, unsigned int bpp)
- {
- ...
- arg.bpp = bpp;
- arg.width = width;
- arg.height = height;
- ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg);
- ...
- return bo;
- }
bpp:bits per pixel,简称像素深度。它的含义是每个像素占用的二进制bit位数,例如在fourcc定义的像素格式会标明其所占的bit数。
- /* 8 bpp RGB */
- #define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8')
- /* 16 bpp RGB */
- #define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2')
- /* 24 bpp RGB */
- #define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4')
- /* 32 bpp RGB */
- #define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4')
stride
在内核代码中也成为pitch,确切的说,stride是framebuffer的概念,它指定了每行像素所占的内存空间大小,出于内存对齐的考虑,通常stride>=bppN。假如使用libdrm,那么在应用层填充像素数据时是不考虑对齐的。stride==bppN。
但在驱动代码中会对stride做对齐处理,例如:
- int
- i915_gem_dumb_create(struct drm_file *file,
- struct drm_device *dev,
- struct drm_mode_create_dumb *args)
- {
- int cpp = DIV_ROUND_UP(args->bpp, 8);
- ...
- /* have to work out size/pitch and return them */
- args->pitch = ALIGN(args->width * cpp, 64);
- ...
- return i915_gem_create(file, to_i915(dev),
- &args->size, &args->handle);
- }
创建好的framebuffer如下图所示:
亮度
亮度在图显系统中称之为luminance。YUV格式的像素Y分量变和此相关。当亮度最低时,图片为黑色;亮度最高时,图片为白色。对于bpp小于32的LCD屏幕而言,亮度是通过PWM来调节背光来实现的。
最暗和最亮
RGB
RGB即是代表红、绿、蓝三个通道的颜色,通过三者的不同程度的叠加,构造出人类视力所能感知的所有颜色,是运用最广的颜色系统之一。RGB按照色深分为1位、8位、16位、24位、32位。
根据RGB的数据格式特点可以构造图像数据,构造好的数据填充到DRM framebuffer,由图显处理器取出发送到显示设备。代码实现如下。
构造RGB16图像代码:
- static void fill_tiles_rgb16(const struct util_format_info *info, void *mem,
- unsigned int width, unsigned int height,
- unsigned int stride)
- {
- const struct util_rgb_info *rgb = &info->rgb;
- void *mem_base = mem;
- unsigned int x, y;
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- div_t d = div(x+y, width);
- uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
- + 0x000a1120 * (d.rem >> 6);
- uint16_t color =
- MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff,
- (rgb32 >> 8) & 0xff, rgb32 & 0xff,
- 255);
- ((uint16_t *)mem)[x] = color;
- }
- mem += stride;
- }
- make_pwetty(mem_base, width, height, stride, info->format);
- }
构造RGB24图像代码:
- static void fill_tiles_rgb24(const struct util_format_info *info, void *mem,
- unsigned int width, unsigned int height,
- unsigned int stride)
- {
- const struct util_rgb_info *rgb = &info->rgb;
- unsigned int x, y;
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- div_t d = div(x+y, width);
- uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
- + 0x000a1120 * (d.rem >> 6);
- struct color_rgb24 color =
- MAKE_RGB24(rgb, (rgb32 >> 16) & 0xff,
- (rgb32 >> 8) & 0xff, rgb32 & 0xff);
- ((struct color_rgb24 *)mem)[x] = color;
- }
- mem += stride;
- }
- }
构造RGB32图像代码:
- static void fill_tiles_rgb32(const struct util_format_info *info, void *mem,
- unsigned int width, unsigned int height,
- unsigned int stride)
- {
- const struct util_rgb_info *rgb = &info->rgb;
- void *mem_base = mem;
- unsigned int x, y;
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- div_t d = div(x+y, width);
- uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
- + 0x000a1120 * (d.rem >> 6);
- uint32_t alpha = ((y < height/2) && (x < width/2)) ? 127 : 255;
- uint32_t color =
- MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff,
- (rgb32 >> 8) & 0xff, rgb32 & 0xff,
- alpha);
- ((uint32_t *)mem)[x] = color;
- }
- mem += stride;
- }
- make_pwetty(mem_base, width, height, stride, info->format);
- }
YUV
YUV表示了色彩空间中的亮度和色度,主要应用于视频传输、显示。其中Y表示亮度(Luminance、Luma),U和V表示色度(Chrominance、Chroma)。另外,在视频压缩、传输过程中还会看到YCbCr的概念。其实,二者从本质上基本是一样的。YUV的优点之一是色度通道比Y通道具有更低的采样率,而不会显著降低感知质量。
YUV采样
各个视频设备厂商或编码芯片厂商定义了多种A:B:C格式的YUV采用规则。
4:4:4
表示没有对色度通道进行降采样,其中圆圈代表Y,X代表UV,以下类同。
4:2:2
表示2:1水平下采样,没有垂直下采样。每两条扫描线包含四个Y样本,对应两个U或V样本。
4:2:0
表示水平下采样2:1,垂直下采样2:1。
YUV存储
YUV存储分为packed格式和planar格式。
- packed格式
将Y、U和V组件打包存储在一个阵列中。像素被组织成一组宏像素,其布局取决于格式。如YUYV,即1个plane。
- planar格式
平面格式是将Y、U、V三个分量分别存储为三个独立的平面。如I420、YV12,即3个plane。另外,还可以支持semi-planar。Y连续存储,U、V交叉存储,如NV21、NV12,即2个plane。
对于不同的YUV表示形式,它们在内存中的排布式不同的。
- YUV444存储格式
- YUV422存储格式
构造YUV图像
构造YUV packed图像代码如下:
- static void fill_tiles_yuv_packed(const struct util_format_info *info,
- void *mem, unsigned int width,
- unsigned int height, unsigned int stride)
- {
- const struct util_yuv_info *yuv = &info->yuv;
- unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1;
- unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1;
- unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0;
- unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0;
- unsigned int x;
- unsigned int y;
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; x += 2) {
- div_t d = div(x+y, width);
- uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
- + 0x000a1120 * (d.rem >> 6);
- struct color_yuv color =
- MAKE_YUV_601((rgb32 >> 16) & 0xff,
- (rgb32 >> 8) & 0xff, rgb32 & 0xff);
- y_mem[2*x] = color.y;
- c_mem[2*x+u] = color.u;
- y_mem[2*x+2] = color.y;
- c_mem[2*x+v] = color.v;
- }
- y_mem += stride;
- c_mem += stride;
- }
- }
构造YUV planar图像代码如下:
- static void fill_tiles_yuv_planar(const struct util_format_info *info,
- unsigned char *y_mem, unsigned char *u_mem,
- unsigned char *v_mem, unsigned int width,
- unsigned int height, unsigned int stride)
- {
- const struct util_yuv_info *yuv = &info->yuv;
- unsigned int cs = yuv->chroma_stride;
- unsigned int xsub = yuv->xsub;
- unsigned int ysub = yuv->ysub;
- unsigned int x;
- unsigned int y;
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- div_t d = div(x+y, width);
- uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
- + 0x000a1120 * (d.rem >> 6);
- struct color_yuv color =
- MAKE_YUV_601((rgb32 >> 16) & 0xff,
- (rgb32 >> 8) & 0xff, rgb32 & 0xff);
- y_mem[x] = color.y;
- u_mem[x/xsub*cs] = color.u;
- v_mem[x/xsub*cs] = color.v;
- }
- y_mem += stride;
- if ((y + 1) % ysub == 0) {
- u_mem += stride * cs / xsub;
- v_mem += stride * cs / xsub;
- }
- }
- }
本文转载自微信公众号「Linux与SoC」,可以通过以下二维码关注。转载本文请联系Linux与SoC公众号。