4. 像素格式与颜色深度:RGB565、RGB888、ARGB8888 格式解析、像素排列与内存对齐

像素格式(Pixel Format)与颜色深度(Color Depth)直接决定了 Framebuffer 中每个像素占用的内存大小、色彩表现能力以及数据吞吐量。在嵌入式 Linux 显示系统中,选择错误的像素格式会导致色彩失真、性能瓶颈甚至显示异常。本节将深入解析三种最常用的像素格式,并重点讨论像素在内存中的排列方式与对齐规则。

4.1 像素格式核心概念

像素格式定义了每个像素在内存中如何表示颜色分量。常见的颜色分量包括:

  • R(Red):红色分量
  • G(Green):绿色分量
  • B(Blue):蓝色分量
  • A(Alpha):透明度分量

颜色深度指每个像素占用的总位数(bpp,bits per pixel)。深度越大,可表示的颜色数量越多,但内存占用和带宽消耗也越大。

性能提示:在资源受限的嵌入式设备上,RGB565 是性能与色彩平衡的最佳选择;而在需要高色彩精度或透明叠加效果的场景中,ARGB8888 是标准配置。

4.2 三种主流像素格式详解

4.2.1 RGB565(16bpp)

RGB565 使用 16 位(2 字节)表示一个像素,红色占 5 位、绿色占 6 位、蓝色占 5 位。绿色多分配 1 位是因为人眼对绿色最敏感。

分量 位数 取值范围 内存偏移(小端)
R(Red) 5 0–31 高 5 位(Byte 0 的高 5 位)
G(Green) 6 0–63 Byte 0 低 3 位 + Byte 1 高 3 位
B(Blue) 5 0–31 低 5 位(Byte 1 的低 5 位)

内存布局(小端模式):

Byte 0: [R4 R3 R2 R1 R0 G5 G4 G3]
Byte 1: [G2 G1 G0 B4 B3 B2 B1 B0]

像素值 = (R << 11) | (G << 5) | B
优点:内存占用低,带宽需求小,适合低端 MCU 和低成本 LCD。
缺点:颜色精度有限,渐变场景可能出现色带(banding)。

4.2.2 RGB888(24bpp)

RGB888 使用 24 位(3 字节)表示一个像素,每个分量各占 8 位,可表示 16,777,216 种颜色(真彩色)。

分量 位数 取值范围 内存偏移(小端)
B(Blue) 8 0–255 Byte 0
G(Green) 8 0–255 Byte 1
R(Red) 8 0–255 Byte 2

内存布局(小端模式):

Byte 0: B7 B6 B5 B4 B3 B2 B1 B0
Byte 1: G7 G6 G5 G4 G3 G2 G1 G0
Byte 2: R7 R6 R5 R4 R3 R2 R1 R0
注意:RGB888 每个像素占用 3 字节,不是 2 的幂次。这会导致内存对齐问题,许多硬件加速器要求每行起始地址 4 字节对齐,因此实际行跨度(stride)可能大于 width × 3。

4.2.3 ARGB8888(32bpp)

ARGB8888 使用 32 位(4 字节)表示一个像素,在 RGB888 基础上增加 8 位 Alpha 透明度通道。这是现代图形系统(如 Linux DRM、Android、Qt)中最常用的格式。

分量 位数 取值范围 内存偏移(小端)
B(Blue) 8 0–255 Byte 0
G(Green) 8 0–255 Byte 1
R(Red) 8 0–255 Byte 2
A(Alpha) 8 0–255 Byte 3

内存布局(小端模式):

Byte 0: B7 B6 B5 B4 B3 B2 B1 B0
Byte 1: G7 G6 G5 G4 G3 G2 G1 G0
Byte 2: R7 R6 R5 R4 R3 R2 R1 R0
Byte 3: A7 A6 A5 A4 A3 A2 A1 A0
优点:支持透明叠加、硬件加速友好(4 字节对齐)、色彩精度最高。
缺点:内存占用大,带宽需求高,对总线压力大。

4.3 像素格式对比总表

格式 bpp 每像素字节 颜色数 Alpha 典型场景
RGB565 16 2 65,536 无 低端 MCU、低成本 LCD、GUI 控件
RGB888 24 3 16.7M 无 真彩色显示、照片浏览
ARGB8888 32 4 16.7M 有 窗口合成、透明叠加、GPU 渲染

4.4 像素排列与内存对齐

4.4.1 像素排列方式

像素在 Framebuffer 中按行优先(row-major)顺序排列。对于宽度为 W、高度为 H 的显示区域,像素地址计算如下:

// 通用公式
pixel_addr = base_addr + (y * stride) + (x * bytes_per_pixel)

// RGB565 示例(stride = W * 2,假设无额外对齐)
pixel_addr = base_addr + y * W * 2 + x * 2

// ARGB8888 示例(stride = W * 4,天然 4 字节对齐)
pixel_addr = base_addr + y * W * 4 + x * 4

4.4.2 行跨度(Stride)与对齐

行跨度(stride/pitch)是内存中一行像素占用的实际字节数,可能大于 width × bytes_per_pixel。硬件通常要求每行起始地址对齐到特定边界(如 4 字节、8 字节、16 字节或 32 字节)。

常见对齐陷阱:RGB888(3 字节/像素)在宽度为奇数时,行跨度可能不是 4 的倍数。例如 800×480 的 RGB888 帧缓冲:
理论行字节 = 800 × 3 = 2400 字节(2400 % 4 = 0,恰好对齐)。
但如果宽度为 801:理论行字节 = 801 × 3 = 2403 字节(2403 % 4 = 3),需要填充 1 字节到 2404 字节。

对齐计算公式:

// 对齐到 N 字节边界
aligned_stride = ((width * bytes_per_pixel) + (N - 1)) & ~(N - 1)

// 示例:RGB888 对齐到 4 字节
aligned_stride = ((width * 3) + 3) & ~3

4.4.3 像素排列与对齐的 SVG 示意图

像素排列与行跨度对齐示意图 像素数据 填充字节(Padding) 行跨度边界 RGB565 (W=6, stride=12) P0 P1 P2 P3 P4 P5 stride=12 RGB888 (W=5, stride=15, 未对齐) P0 P1 P2 P3 P4 stride=15 (非4的倍数) RGB888 (W=5, stride=16, 4字节对齐) P0 P1 P2 P3 P4 Pad stride=16 (4的倍数) ARGB8888 (W=4, stride=16, 天然对齐) P0 P1 P2 P3

4.5 像素格式选择对性能的影响

性能维度 RGB565 RGB888 ARGB8888
内存带宽(800×480@60fps) 800×480×2×60 ≈ 46 MB/s 800×480×3×60 ≈ 69 MB/s 800×480×4×60 ≈ 92 MB/s
CPU 逐像素操作开销 低(16 位操作) 中(24 位操作,需处理对齐) 高(32 位操作)
DMA 传输效率 高(2 字节对齐) 中(需填充对齐) 高(4 字节天然对齐)
硬件加速兼容性 部分 GPU 不支持 部分硬件不支持 几乎所有 GPU 支持
优化建议:在 Linux DRM 驱动中,通过 drm_fourcc.h 定义的格式宏(如 DRM_FORMAT_RGB565、DRM_FORMAT_XRGB8888)来配置像素格式。对于不需要透明度的场景,优先使用 XRGB8888(无 Alpha 的 32 位格式)而非 ARGB8888,可减少 Alpha 通道的读写开销。

4.6 常见故障与排查

  • 色彩完全错乱(红蓝颠倒):检查像素格式的字节序(RGB vs BGR)。许多 LCD 控制器使用 BGR 顺序,而 CPU 使用 RGB 顺序。
  • 图像出现斜纹或错位:行跨度(stride)设置错误。确认驱动中设置的 stride 与硬件实际期望的一致。
  • 渐变出现明显色带:颜色深度不足。RGB565 在平滑渐变场景中容易出现此问题,考虑升级到 RGB888 或 ARGB8888。
  • 透明叠加区域显示黑色:Alpha 通道未正确初始化。ARGB8888 中 Alpha=0 表示完全透明,Alpha=255 表示完全不透明。
  • 性能突然下降:检查是否使用了非对齐的 stride,导致硬件需要额外的内存访问周期。
调试命令:使用 fbset -i 查看当前 Framebuffer 的像素格式、行跨度、分辨率等信息。使用 dd if=/dev/fb0 bs=1 count=64 | hexdump -C 查看原始像素数据,验证字节序和排列方式。