跳转至

屏幕色彩格式与色深(RGB565 / RGB888 等)

你可能在 microcontroller 的屏幕驱动、LCM 规格书或 GPU 文档里见过 RGB565RGB888 这类写法,括号里的数字看着很像,却决定了屏幕能显示多少种颜色、一帧画面占多大内存。本文把这一族「RGB 色彩格式」讲清楚。

一、先搞清楚几个基础概念

屏幕上的每张彩色图像,都是由一个个**像素(pixel)**拼出来的。每个像素的颜色,在数字世界里通常被拆成 R(红)、G(绿)、B(蓝) 三个分量来表示——因为人眼里的任意颜色,都可以用这三原色按不同亮度叠加出来。

关键在于:每个分量用多少位(bit)来表示它的「亮度等级」,也就是所谓的色深(bit depth)

  • 1 位只能表示 0 或 1(关 / 开),2 个等级;
  • 8 位可以表示从 0 到 255,共 256 个等级;
  • 一个像素所有分量的位数加在一起,叫做 bpp(bits per pixel,每像素位数)

三个分量各占 \(n\) 位时,单个分量的等级数是 \(2^n\),整像素能表达的颜色总数是:

\[ \text{颜色数}=2^{\text{R位}+\text{G位}+\text{B位}} \]

这就是「为什么 RGB888 比 RGB565 颜色多得多」的根本原因。

二、常见 RGB 格式速查表

格式 R / G / B 位宽 总位宽 (bpp) 颜色总数 俗称 典型用途
RGB444 4 / 4 / 4 12 \(2^{12}=4\,096\) 增强色 早期低端屏、伪彩
RGB555 5 / 5 / 5 15 \(2^{15}=32\,768\) 高彩(旧) 老式 Windows 16 位色(留 1 位空缺)
RGB565 5 / 6 / 5 16 \(2^{16}=65\,536\) 高彩色 (High Color) MCU / 嵌入式 LCD、RGB 并行接口
RGB666 6 / 6 / 6 18 \(2^{18}=262\,144\) 部分 LCD 屏原生 18-bit 接口
RGB888 8 / 8 / 8 24 \(2^{24}=16\,777\,216\) 真彩色 (True Color) 桌面显示器、GPU 帧缓冲、手机屏
RGBA8888 8 / 8 / 8 / 8(A) 32 同上+透明度 带 Alpha 图形合成、UI、游戏
RGB10 / 12-bit 10 / 10 / 10 等 30+ 10 亿+ HDR 深色 HDR 显示器、专业屏

注:你提到的「RGB556」大概率是 RGB565 的笔误——5/6/5 才是标准写法(绿色多一位)。真正存在的是 RGB555(三通道都为 5 位)。

三、重点格式详解

3.1 RGB565(16 位高彩色)

这是嵌入式里最常见的格式,因为 16 位刚好能塞进一个 uint16_t,处理和传输都很省:

位布局(一个 16 位值,从左到右为高位到低位):
[ R4 R3 R2 R1 R0 | G5 G4 G3 G2 G1 G0 | B4 B3 B2 B1 B0 ]
 15           11   10              5   4            0
  • R 占 5 位:0~31
  • G 占 6 位:0~63 ← 注意绿色比红蓝多一位
  • B 占 5 位:0~31

为什么绿色多给一位?因为人眼对绿色(其实是亮度)最敏感,多分一级绿色,观感上的「颜色阶梯感」比多分红色/蓝色小得多。这是经过感知优化后的经典折中。

3.2 RGB888(24 位真彩色)

每个分量各 8 位(0~255),直接对应我们最熟悉的「#RRGGBB」十六进制颜色,也是绝大多数显示器、相机、图像文件的原生表示:

[ R7..R0 | G7..G0 | B7..G0 ]   共 24 位,常拆成 3 个字节

它的 1677 万种颜色已经远超人眼能分辨的界限,所以叫「真彩色」。

3.3 字节序(Endianness)这个坑

RGB565 是 16 位值,但内存和接口总线一次传 8 位(1 字节)。把它拆成两个字节时,哪个字节在前,不同 MCU、不同屏驱 IC 的默认不一样:

  • 小端(Little-Endian):低字节在前 —— [低 8 位][高 8 位]
  • 大端(Big-Endian):高字节在前 —— [高 8 位][低 8 位]

很多「屏幕颜色发红/发蓝、整体错乱」的 bug,根源就是 MCU 写进去的字节序和屏幕期望的字节序反了。驱动初始化时通常有一个 RGB/BGR数据字节序 的配置位,调出来和你的代码一致即可。

3.4 32 位对齐与 Padding

RGB888 严格说是 24 位(3 字节),但很多 GPU / 图形库为了内存访问效率,会用 RGBX8888 / RGBA8888(第 4 字节放透明度或留空)把每个像素补到 32 位(4 字节)。多出来的 1 字节不增加颜色,只是为了让 CPU 用 32 位整数一次读写一个像素更快。

四、为什么需要这么多格式?

核心矛盾就一句话:颜色越细腻,占的内存和带宽越大

以一帧 1920×1080(约 207 万像素)为例,不同格式下「单帧显存」和「60fps 所需带宽」大致是:

格式 每像素 单帧显存 60fps 带宽
RGB565 2 字节 ≈ 4.1 MB ≈ 248 MB/s
RGB888 3 字节 ≈ 6.2 MB ≈ 373 MB/s
RGBA8888 4 字节 ≈ 8.3 MB ≈ 497 MB/s

对一块 STM32 这类 MCU 来说,片上 SRAM 可能只有几百 KB,根本放不下一帧 RGB888,所以用 RGB565 是必然的节省;而手机、电脑的 GPU 显存以 GB 计,自然上 RGB888 / 10-bit 追求画质。

经验法则 - 资源紧张、带屏驱 IC 的小屏 → 优先 RGB565 - 需要 UI 透明度、图形合成 → RGBA8888 - 追求画质 / 对接显示器 → RGB888 或更高

五、量化误差与抖动(Dithering)

当你把一张 24 位的图(RGB888)硬塞进 16 位(RGB565)时,每个分量要「砍掉」低位:

  • 红色 8 位 → 5 位:直接丢掉低 3 位,误差最多 \(\pm 7\)
  • 蓝色同理
  • 绿色 8 位 → 6 位:丢掉低 2 位

结果就是色带(banding)——本该平滑过渡的渐变(如天空、肤色)出现一圈圈阶梯。解决方法是 抖动(Dithering):用「误差扩散」或有序噪点,把舍入误差打散成人眼不易察觉的细颗粒,肉眼看起来反而更平滑。很多屏驱 IC 和 GPU 都内置了 FRC(Frame Rate Control)抖动来「用 6-bit 面板假装 8-bit」。

六、实际处理代码示例

下面是一段在嵌入式里最常用的「24 位 ↔ 16 位」互转代码。

#include <stdint.h>

// RGB888 (各 8 位) -> RGB565 (16 位)
// 取 R 的高 5 位、G 的高 6 位、B 的高 5 位
uint16_t rgb888_to_565(uint8_t r, uint8_t g, uint8_t b) {
    return (uint16_t)((r & 0xF8) << 8)   // R: 取高5位,移到 bit15..11
         | (uint16_t)((g & 0xFC) << 3)   // G: 取高6位,移到 bit10..5
         | (uint16_t)( b        >> 3);    // B: 取高5位,移到 bit4..0
}

// RGB565 -> RGB888
// 关键:低位要用「复制高位」补满,而不是简单左移补 0(否则颜色会整体变暗)
void rgb565_to_888(uint16_t c, uint8_t *r, uint8_t *g, uint8_t *b) {
    uint8_t r5 = (c >> 11) & 0x1F;
    uint8_t g6 = (c >> 5)  & 0x3F;
    uint8_t b5 =  c        & 0x1F;
    *r = (r5 << 3) | (r5 >> 2);   // 5 位 -> 8 位:复制高 3 位到低 3 位
    *g = (g6 << 2) | (g6 >> 4);   // 6 位 -> 8 位
    *b = (b5 << 3) | (b5 >> 2);   // 5 位 -> 8 位
}

注意 rgb565_to_888不能写成 r5 << 3 就完事:5 位最大值 31 左移 3 位只到 248,且低 3 位永远是 0,颜色会偏暗、渐变出现断层。用「高位复制」才能还原出均匀的 0~255。

七、色深与显示面板本身

别以为「我给了 RGB888 数据,屏幕就一定显示 1677 万色」。面板本身也有原生色深:

  • 6-bit 面板:每个分量只有 64 级,靠 FRC 抖动「假装」8-bit;
  • 8-bit 面板:真 256 级,主流显示器;
  • 10-bit 面板:1024 级,配合 HDR 才能显现更广的明暗层次。

所以「内容色深、传输格式、面板原生位深」三者要配套,画质才不会被最短的那块木板限制。

八、小结

  • RGBxyz 里的数字 = 红 / 绿 / 蓝 各分配多少位,总和就是这个像素的 bpp;
  • RGB565(16 位 / 6.5 万色) 省内存,是嵌入式屏首选;RGB888(24 位 / 1677 万色) 是真彩色,桌面和手机主流;
  • 真正影响观感的还有字节序、量化误差、抖动、面板原生位深这几个工程细节;
  • 选格式的本质是在 画质 ↔ 内存 / 带宽 / 功耗 之间做取舍。