4. 色彩管理基础
在 DRM CRTC 驱动中做 Gamma 校正与色彩管理,第一步不是写寄存器,而是把“颜色到底被怎样描述”这件事讲清楚。显示管线里每一个环节——GPU 合成、CRTC 扫描输出、编码器、面板——都在用某种数值表示颜色,如果这些数值背后的色彩空间、色域、白点和传递函数不一致,那么即使 Gamma LUT 数值算得再精确,最终画面依然会偏色、发灰或过曝。本节把这四个基础概念串成一条完整的“颜色描述链”,为后续 Gamma LUT、CTM(Color Transformation Matrix)、3D LUT 的配置打地基。
一、色彩空间:给数值一个“坐标系”
色彩空间(Color Space)本质上是“数值 → 颜色”的映射规则。同一个三元组 (1.0, 0.0, 0.0),在 sRGB 里是标准红,在 BT.2020 里却是更饱和的红。因此色彩空间必须包含三件事:基色坐标(红绿蓝三原色在色度图上的位置)、白点、传递函数。三者缺一,颜色就无法被唯一还原。
在 DRM 中,色彩空间通常通过 connector 的属性暴露,例如 Colorspace 或 max bpc 配合 EDID 中的色彩特性。常见的有 sRGB、BT.709、BT.2020、DCI-P3、Adobe RGB 等。驱动需要根据用户空间(如 compositor)下发的属性,把 CRTC 的输出格式与下游编码器、面板能力对齐。
二、色域:色彩空间的“面积”
色域(Gamut)是色彩空间在色度图上覆盖的范围,用三角形面积直观表示。sRGB 与 BT.709 基色几乎重合,覆盖约 35% 的 CIE 1931 可见色;DCI-P3 覆盖约 45%;BT.2020 覆盖约 75%。色域越大,能显示的颜色越鲜艳,但对面板和背光要求也越高。
工程上最容易踩的坑是“大色域面板直接显示 sRGB 内容”:面板原生是 P3,输入却是 sRGB 数值,如果不做色域映射,红色会被拉得过分饱和,肤色发橙。正确做法是在 CRTC 的 CTM 或 3D LUT 中做 gamut mapping,把 sRGB 基色映射到 P3 基色,而不是简单地把数值原样送出去。
三、白点:颜色的“锚”
白点是色彩空间中最关键的参考点,决定了“什么叫做白”。常见白点有 D65(6504K,sRGB/BT.709/BT.2020 使用)、DCI-P3 的 DCI 白点(约 6300K)、D50(印刷)。白点偏移会直接导致整幅画面偏冷或偏暖。
在 DRM 驱动里,白点通常不单独作为属性,而是隐含在色彩空间和 CTM 中。如果面板白点与内容白点不一致,需要通过 CTM 做白点适配(White Point Adaptation),常用 Bradford 或 CAT02 色适应矩阵。忽略这一步,画面会出现“整体偏蓝”或“整体偏黄”的典型症状。
四、传递函数:数值与亮度的非线性关系
传递函数(Transfer Function)描述“编码数值”与“实际光亮度”之间的关系,分为 OETF(编码端)和 EOTF(显示端)。sRGB 使用近似 2.2 的 gamma 曲线,PQ(SMPTE ST 2084)用于 HDR,HLG 用于广播 HDR。Gamma 校正的本质,就是在 CRTC 中插入一条反向曲线,抵消面板的 EOTF,使最终输出符合目标传递函数。
下面这张图把“色彩空间 → 色域 → 白点 → 传递函数”四要素与 DRM 管线中的对应配置点串起来,帮助建立整体认知:
五、四要素对照与常见配置
下表把四个概念在 DRM 驱动中的典型表现、常见取值和错配症状整理成清单,便于排障时快速定位:
| 概念 | DRM 中的体现 | 常见取值 | 错配典型症状 |
|---|---|---|---|
| 色彩空间 | Connector Colorspace 属性、EDID 色彩特性 | sRGB、BT.709、BT.2020、DCI-P3 | 整体色调偏移、饱和度异常 |
| 色域 | CTM / 3D LUT 中的基色映射矩阵 | sRGB→P3、P3→BT.2020 | 红色过饱和、肤色发橙 |
| 白点 | CTM 中的色适应矩阵(Bradford/CAT02) | D65、DCI、D50 | 画面整体偏蓝或偏黄 |
| 传递函数 | CRTC Gamma LUT、PQ/HLG 元数据 | sRGB gamma 2.2、PQ、HLG | 暗部死黑、高光过曝、灰阶断层 |
六、案例:一次“偏蓝”的排障复盘
某项目使用 P3 面板,播放 sRGB 内容时用户反馈“整体偏蓝、暗部发灰”。排查过程如下:
- 确认 Framebuffer 格式为
DRM_FORMAT_XRGB8888,内容标记为 sRGB。 - 读取 Connector 的
Colorspace属性,发现被设置为Default,未显式声明 sRGB。 - 检查 CRTC 的 CTM,发现矩阵为单位矩阵,未做 sRGB→P3 的基色映射,也未做 D65→DCI 白点适配。
- 检查 Gamma LUT,发现仍使用面板出厂默认曲线,未按 sRGB EOTF 反向补偿。
修复方案:在用户空间下发 Colorspace=sRGB,驱动侧在 CTM 中写入 sRGB→P3 基色映射矩阵并叠加 D65→DCI 白点适配,同时按 sRGB gamma 2.2 生成反向 Gamma LUT。修复后偏蓝消失,暗部层次恢复。
七、配置片段示例
以下是一个典型的属性设置与 Gamma LUT 生成片段,展示四要素如何落到代码层面:
/* 1. 声明内容色彩空间 */
drmModeObjectSetProperty(fd, connector_id,
DRM_MODE_OBJECT_CONNECTOR,
prop_colorspace, DRM_MODE_COLORIMETRY_sRGB);
/* 2. 配置 CTM:sRGB -> P3 基色映射 + D65 -> DCI 白点适配 */
struct drm_color_ctm ctm = {
.matrix = {
/* 3x3 矩阵,定点数格式 */
...
}
};
drmModeObjectSetProperty(fd, crtc_id,
DRM_MODE_OBJECT_CRTC, prop_ctm, &ctm);
/* 3. 生成 sRGB gamma 2.2 反向 LUT */
for (int i = 0; i < lut_size; i++) {
double x = (double)i / (lut_size - 1);
double y = pow(x, 1.0 / 2.2); /* 反向补偿 */
gamma_lut[i].red = (uint16_t)(y * 0xFFFF);
gamma_lut[i].green = (uint16_t)(y * 0xFFFF);
gamma_lut[i].blue = (uint16_t)(y * 0xFFFF);
}
drmModeObjectSetProperty(fd, crtc_id,
DRM_MODE_OBJECT_CRTC, prop_gamma_lut, gamma_lut);
理解色彩空间、色域、白点和传递函数,是后续所有 Gamma 与色彩管理配置的前提。它们不是四个孤立名词,而是一条从内容到面板的完整描述链。只有这条链上每一环都对齐,CRTC 中的 Gamma LUT 和 CTM 才能真正发挥作用,画面才能既准确又好看。