3. Gamma校正原理
在 DRM CRTC 驱动里配置 Gamma 校正,第一步不是写寄存器,而是理解“为什么需要 Gamma”。显示链路从帧缓冲中的线性光强数值,到人眼最终感知的亮度,中间存在两次非线性:一次来自人眼对亮度的对数式感知,一次来自显示器件(LCD 的电光转换、OLED 的驱动特性)本身的非线性响应。Gamma 校正的本质,就是在 CRTC 的扫描输出阶段插入一张查找表,把线性帧缓冲数据映射成符合感知与器件特性的驱动值,使整条链路“看起来”是线性的。
一、Gamma 曲线:幂函数描述的输入输出关系
Gamma 曲线最常用的数学形式是幂函数:输出 = 输入^γ。当 γ=1 时,输入输出是直线,称为线性响应;当 γ>1 时,曲线向下凹,暗部被压暗;当 γ<1 时,曲线向上凸,暗部被提亮。传统 CRT 的物理响应约为 γ≈2.2,即输入电压对应的发光强度是电压的 2.2 次方,因此图像在 CRT 上会显得偏暗。为了补偿,内容制作端通常预先做 1/2.2≈0.4545 的编码,这就是 sRGB 传递函数的由来。
在 DRM 中,Gamma 查找表(LUT)通常有 256、1024 或 4096 个条目,每个条目是若干位(如 8/10/12 位)的输出值。驱动把用户空间下发的 blob 数据写入 CRTC 的 gamma LUT 寄存器,硬件在扫描时对每个像素的 R/G/B 分量分别查表输出。因此 Gamma 曲线在工程上就是“一张离散化的幂函数采样表”。
二、亮度感知:人眼不是线性测量仪
人眼对亮度的感知近似遵循韦伯-费希纳定律,即感知强度与物理亮度的对数成正比。这意味着在暗部,很小的物理亮度变化就能被人眼察觉;而在亮部,需要很大的变化才能感知到同样的差异。8 位量化如果直接线性分配,暗部会出现明显的“色带”和“断层”,因为相邻码值对应的物理亮度差异在暗部远大于人眼的辨别阈值。
Gamma 编码正是利用这一特性:把更多的码值分配给暗部,把较少的码值分配给亮部,使量化误差在感知上均匀分布。这也是为什么 sRGB 使用约 2.2 的等效 Gamma,而不是线性编码。在 DRM 驱动中,如果 Gamma LUT 配置错误,最典型的用户可见现象就是暗部细节丢失、渐变区域出现色带,或者整体画面发灰、对比度不足。
三、校正数学基础:从幂函数到 LUT 采样
Gamma 校正的数学核心是幂函数及其反函数。设帧缓冲中的归一化线性值为 x∈[0,1],显示器件物理响应为 y=x^γ_d,则为了在屏幕上得到线性感知,需要预补偿 x' = x^(1/γ_d)。当 γ_d=2.2 时,x' = x^0.4545。实际驱动中,这个连续函数被离散化为 N 个采样点,每个采样点计算后量化到 LUT 位宽。
/* 以 256 项、10 位 LUT 为例的 Gamma 表生成伪代码 */
for (i = 0; i < 256; i++) {
double x = i / 255.0; /* 归一化输入 */
double y = pow(x, 1.0 / 2.2); /* Gamma 预补偿 */
lut[i] = (uint16_t)(y * 1023.0 + 0.5); /* 量化到 10 位 */
}
/* DRM 属性下发:GAMMA_LUT 是一个 blob 属性 */
drmModeCreatePropertyBlob(fd, lut, sizeof(lut), &blob_id);
drmModeObjectSetProperty(fd, crtc_id, CRTC_GAMMA_LUT, blob_id);
需要注意,DRM 的 GAMMA_LUT 属性在 atomic 模式下是 CRTC 级对象,颜色管线中还有 DEGAMMA_LUT 和 CTM(色彩变换矩阵)。Gamma 校正通常位于 DEGAMMA 之后、CTM 之前或之后,具体顺序由硬件管线决定。驱动开发者必须查阅对应 SoC 的显示控制器手册,确认 LUT 在管线中的位置,否则会出现“校正了但方向反了”的问题。
四、案例与场景:一次暗部色带问题的排查
某嵌入式设备使用 8 位面板,用户反馈播放暗场景视频时天空区域出现明显色带。排查发现驱动默认下发了线性 Gamma 表(即 lut[i]=i 映射),没有做任何预补偿。由于面板本身 Gamma 约 2.2,线性输入导致暗部码值对应的物理亮度跨度过大,量化台阶被人眼捕捉。修正方式是生成 γ=2.2 的预补偿表并下发,色带随即消失。
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 暗部色带、断层 | LUT 暗部分辨率不足或未做 Gamma 预补偿 | 检查 LUT 项数与曲线形状 |
| 整体发灰、对比度低 | 误下发线性表或 Gamma 方向反了 | 确认 γ 指数与管线顺序 |
| 高光过曝、细节丢失 | LUT 高位截断或量化位宽不足 | 检查 LUT 位宽与面板位深匹配 |
| 颜色偏色 | R/G/B 三通道 LUT 不一致 | 核对三通道表数据是否独立正确 |
五、复盘:Gamma 校正的三个关键检查点
- 曲线方向:确认是预补偿(γ<1 提亮暗部)还是器件补偿(γ>1 压暗),方向错误会让画面完全失真。
- 采样精度:LUT 项数应覆盖输入位深,输出位宽应匹配面板位深,避免量化损失。
- 管线位置:确认 GAMMA_LUT 与 DEGAMMA、CTM 的先后顺序,避免重复校正或校正被覆盖。
理解 Gamma 曲线、亮度感知和校正数学,是后续在 DRM CRTC 驱动中正确配置 GAMMA_LUT 属性的前提。只有把“人眼感知—编码传递函数—器件响应—硬件 LUT”这条链路对齐,Gamma 校正才能真正发挥作用,而不是变成一组看似正确却让画面更糟的寄存器值。