Linux图形三部曲(1):Linux 图形基础理论与图像表示核心原理

本系列三部曲每篇链接:

Linux图形三部曲(1):Linux 图形基础理论与图像表示核心原理 - 知乎

Linux图形三部曲(2):Linux 图形硬件架构与显示管线全解析 - 知乎

Linux图形三部曲(3):Linux 图形软件栈全架构:内核→用户态→渲染引擎 - 知乎


在整个 Linux 图形栈的学习路线里,绝大多数人一上来就扎进 DRM、Wayland、Mesa 这些名词里,结果越学越乱,遇到花屏、颜色偏差、画面错位、格式不兼容这类最常见问题时,依然完全找不到方向。根本原因只有一个:没有先把最底层的图像表示理论真正弄懂

像素、分辨率、色彩空间、采样、量化、内存排布、Stride、FourCC、Alpha 混合、卷积滤波......这些听起来基础的概念,不是可学可不学的"常识",而是整个 Linux 图形世界的第一性原理。不管是嵌入式开机 Logo、桌面窗口、3D 游戏、视频播放,还是车载仪表、无人机图传、机顶盒输出,所有图像行为最终都可以追溯到这一套理论。

本文参考Bootlin官方资料,将阐述图像从物理世界到数字世界、从数学描述到内存存储、从单个像素到完整画面的全部逻辑。

如有专业词汇翻译不当、内容理解有误的地方,还望指正。


一、先回到起点:图像到底是什么?

我们每天都在看屏幕,但很少有人认真想过:屏幕上的画面,本质到底是什么?

在现实世界里光是连续的,一片树叶、一面墙、一张纸,它们反射的光在空间上是不间断的。如果用胶片去记录,它可以保留无限精细的过渡,不存在"最小单元"。这种记录方式,叫做模拟图像。它的特点是空间连续、信息连续、没有颗粒感,但无法被计算机直接存储、计算、传输。

计算机和所有数字系统都无法处理无限连续的信息。我们必须做一件事:把连续的光,切成一份份有限、可计数、可存储的小单元 。这个"切割"过程,就叫量化(Quantization) ,也叫采样(Sampling) 。切割出来的最小单元,就是我们最熟悉的词:像素(Pixel)

所以,数字图像的本质可以用一句最精准的话概括: 数字图像 = 对连续光信号进行空间量化后形成的二维像素阵列。

一个像素,就是一个位置上的"光信息小包",它包含这个点的亮度、颜色信息。无数像素按照行列整齐排列,就成了我们看到的图。宽有多少像素、高有多少像素,就是分辨率 ,比如 1920×1080。宽和高的比例,就是宽高比,比如 16:9。

单位面积里有多少像素,决定画面细腻度,我们用 DPI(Dots Per Inch) 表示。DPI 越高,像素越小,人眼越看不出颗粒感。绝大多数系统默认像素是正方形,所以不用分别计算横向和纵向密度。这看起来是个小细节,但在图像计算、内存布局、硬件缩放时,是一切规则的基础。

把连续信号切成离散像素,必然带来一个无法避免的问题:信息损失 。这种损失不是 bug,而是物理规律。最直观的表现就是混叠(Aliasing) ,比如斜线出现锯齿、密集纹理产生莫尔条纹、小字边缘粗糙。这些问题不是软件写错了,而是采样本身带来的天然现象

这里必须提到**奈奎斯特采样定理**,它是所有数字信号处理的基石:想要正确还原一个信号,采样频率必须大于信号最高频率的两倍。放到图像里就是:如果图像里有非常细密的纹理、小字、网格这些"高频信息",而你的像素分辨率不够,就一定无法正确还原,最终呈现出锯齿、模糊、波纹。

这也是为什么高分辨率屏幕更清晰、为什么图像放大后会失真、为什么抗锯齿是图形系统必须做的事。所有这些现象,都能从"连续→离散"这个第一步里找到根源。

二、颜色的本质:人眼、光、与色彩模型

像素解决了"位置"问题,下一个问题就是:颜色怎么用数字表示?

这不是一个纯技术问题,而是一个生物学问题

人类视网膜上有三种感知颜色的细胞,分别对应红、绿、蓝三个波段。自然界中所有颜色,进入人眼后都会被大脑翻译成这三种刺激的组合。这就是三色视觉 。几乎所有数字图像系统都以 RGB 为基础,不是因为工程师喜欢 RGB,而是因为RGB 最贴合人的眼睛

但 RGB 只是一个模型,不是一套标准。同样一组 RGB 数值,在不同屏幕上可能显示出完全不同的颜色。因为它们用的**色彩空间(Colorspace)**不同。

Color gamut of agiven colorspace

色彩空间可以理解成一套"翻译字典":

  • 它规定红、绿、蓝三原色的确切波长;
  • 它规定什么是"标准白光"(白点);
  • 它规定能表示的颜色范围,也就是色域(Gamut)

我们最常见的 sRGB,是为网页、图片、显示器设计的标准色域。而 Display P3、Adobe RGB 这类广色域,能表示更多绿色和红色,适合摄影、影视。超出色域的颜色,设备无法显示,只能被"切掉"或"映射"成最近的可表示颜色。这就是为什么同一张图在手机和电脑上看起来不一样------不是坏了,是颜色范围不一样。

在 Linux 图形栈、视频系统、嵌入式显示里,我们很少只使用 RGB。真正大量出现的是另外两套模型:HSV/HSBYUV/YCbCr

HSV 用色相、饱和度、明度来描述颜色,更贴近人类直觉,绘图软件里的调色盘基本都用它。但在渲染、传输、存储里,真正统治世界的是 YUV

HSV diagram

YUV 把图像拆成亮度(Y)色度(U/V) 两个独立部分。它的设计逻辑非常简单粗暴:人眼对亮度极度敏感,对颜色细节不敏感。你可以大幅度压缩颜色信息,人眼几乎看不出来,但不能压缩亮度。这就是视频能被压得很小、能流畅在线播放的根本原因。

从 RGB 转到 YUV 是固定的数学变换,以最经典的 BT.601 为例:

复制代码
Y = 0.299 × R + 0.587 × G + 0.114 × B
U = -0.147 × R - 0.289 × G + 0.436 × B
V = 0.615 × R - 0.515 × G - 0.100 × B

Translation between BT.601 in YUV and sRGB in RGB

反向变换也有对应公式:

复制代码
R = Y + 1.140 × V
G = Y - 0.395 × U − 0.581 × V
B = Y + 2.032 × U

Linux 内核 DRM、显示驱动、视频解码器、硬件加速器,底层全是这套计算。如果你不理解 YUV,几乎不可能搞定任何视频或图像格式问题。

接下来是色深(Color Depth),也就是用多少位二进制表示一个像素。

  • 8 bit:256 色,早期系统、简单界面
  • 16 bit:65536 色,很多嵌入式设备
  • 24 bit:1600 万色,真彩色,RGB 各 8 位
  • 32 bit:带 Alpha 透明通道,现代桌面标准

24Bit VS 16Bit每个像素

色深本质就是颜色的量化精度。位数越少,颜色过渡越生硬,容易出现色带;位数越高,颜色越平滑,但占用内存越大。Linux 驱动、缓冲区大小计算、硬件格式配置,第一步永远是色深。

很多人遇到偏色、色带、画面发灰、透明度异常,第一反应去查驱动,其实 90% 的情况都是色深、色域、色彩空间不匹配导致的。

三、图像大小、数据量与色度亚采样

我们来算一笔最现实的账:一张数字图像,到底占多大内存?

公式非常直白: 大小(字节)= 宽 × 高 × 每像素比特数 ÷ 8

比如一张 4000×3000、32 位的图片: 4000 × 3000 × 32 / 8 = 48,000,000 字节 ≈ 46 MB。

这只是一张图。视频每秒 30 帧,一分钟就是 46×30×60 ≈ 82 GB。显然不可能直接传输和存储。

在 Linux 图形与视频系统里,最核心、最通用、最无处不在的压缩手段,不是复杂算法,而是色度亚采样(Chroma Subsampling)

sub-sampling

它的原理依然是:人眼看亮度很敏锐,看颜色很迟钝。所以我们可以让多个像素共用一组颜色数据,只保留完整的亮度。

行业用三个数字表示:J:a:b

  • 4:4:4:无压缩,每个像素都有完整 YUV
  • 4:2:2:横向 2 个像素共用一组色度
  • 4:2:0:横向 2 个、纵向 2 个像素共用一组色度

4:2:0 是 H.264、H.265、VP9、所有在线视频、所有摄像头、所有电视面板、绝大多数嵌入式系统的默认格式。它把数据量几乎减半,而观感几乎不变。

在 DRM、V4L2、显示驱动、接口配置里,我们会反复看到这些格式。不理解 4:2:0,就不可能真正理解 Linux 图形系统。

很多工程师调试视频输出,画面绿色紫色错乱、花屏、分块,其实就是亚采样格式不匹配:发送端是 4:2:0,显示端按 4:2:2 解析,自然一塌糊涂。

四、像素在内存里的真实排布:格式、Stride、FourCC

到这里,我们终于进入最实用、最容易踩坑、最决定显示成败 的部分:像素在内存里到底怎么放?

同样的图、同样的像素、同样的分辨率,在内存里可以有几十种不同排布。一旦排布错了,屏幕立刻花屏、偏色、错位、撕裂、拉伸。这是图形开发者最常见、最头疼的问题。

首先要理解三种存储结构: Packed(打包) :所有通道混在一起,RGBRGBRGB...... Semi-Planar(半平面) :Y 单独一块,UV 合并一块。 Planar(平面):Y、U、V 各占一块完全独立的内存。

Linux 里的 DRM、帧缓冲、视频渲染,同时支持这三种结构,驱动必须明确告诉硬件用哪一种。

接下来是最容易被忽略的关键:Stride / Pitch

很多新手以为一行像素的字节数 = 宽 × 位深 / 8。这是错的。 绝大多数硬件 DMA 有强制要求:每一行的起始地址必须按 16/32/64 字节对齐。不足的地方要填充空白字节,这部分空白就是 Stride。

比如一行 100 个像素,32 位深,理论 400 字节。如果硬件要求 64 字节对齐,400 向上取 64 的倍数是 448,那么 Stride = 448。最后 48 字节是空的,不显示,但必须预留。

计算缓冲区大小如果不带 Stride,一定会内存越界、画面错位、内核崩溃。这是嵌入式图形调试里排名前三的坑。

为了统一标识各种格式,行业用 FourCC:4 个字符的编码,代表一种像素格式。

  • RGB888 → RGB3
  • ARGB8888 → ARGB
  • YUV420 → YU12、YV12
  • DRM 内部用 XR24 表示 XRGB8888

FourCC 不是完美标准,但它是 Linux 图形栈里最通用的格式语言。libdrm、DRM 驱动、应用、工具、编码器,全部依赖它。

我们遇到的绝大多数"花屏但背光正常"的问题,基本都可以归为三类:

  • FourCC 格式不匹配
  • Stride 计算错误
  • 平面/打包/半平面结构搞错

五、把数学形状变成像素:光栅化与基础绘制

有了像素、格式、内存排布,下一步就是画图

raster order

把数学上的点、线、矩形、圆、曲线,变成屏幕上的实际像素,这个过程叫光栅化(Rasterization)。它的本质就是:把连续的数学形状,映射到离散的像素网格上。

最基础的图形都遵循简单逻辑: 矩形就是限定 xmin/xmax、ymin/ymax,把范围内所有像素填色。 直线是根据两点坐标计算斜率,逐点绘制。嵌入式系统最常用 Bresenham 算法,速度快、不用浮点运算。 圆和椭圆用距离判断:到中心点的距离小于等于半径,就是圆内像素。 渐变则是在起点色和终点色之间做线性插值,每一点算出对应的 RGB。

这些看似简单,却是 Linux 底层、无 GPU 环境、开机 Logo、串口终端、嵌入式菜单的唯一绘制方式。很多人以为画图全是 GPU 做的,其实在系统启动早期、无驱动环境下,全是 CPU 直接写像素。

这里再次回到采样问题:斜线、圆、小字,直接光栅化一定会出现锯齿。解决办法是亚像素渲染(Sub-pixel Rendering)抗锯齿(Anti-aliasing)。原理是给边缘像素一个半透明值,让硬边缘变柔和。Linux 桌面字体清晰、矢量图平滑,核心就是这套逻辑。

不理解光栅化和采样,我们就无法理解:

  • 为什么字体需要渲染引擎
  • 为什么线条会锯齿
  • 为什么小图标要做高分辨率预处理
  • 为什么缩放会模糊

这些都是最底层的理论决定的。

六、像素的操作:混合、拷贝、缩放、滤波、抖动

图像不是画完就结束,而是要不断处理、叠加、变换。Linux 图形栈里所有渲染、合成、窗口、动画,本质都是下面这几种像素操作。

第一个最基础的操作是区域拷贝(Bit Blit)。把一块矩形像素从一个地方复制到另一个地方。窗口移动、截图、图层叠加、界面刷新,本质全是 Blit。硬件 2D 加速器存在的意义,就是加速 Blit。

第二个是Alpha 混合(Alpha Blending)。每个像素多一个 Alpha 通道,表示透明度。当一个图层叠在另一个上面时,最终颜色 = 前景×透明度 + 背景×(1−透明度)。窗口半透明、菜单、弹窗、图标抗锯齿,全都靠它。这套规则叫 Porter-Duff 混合模型,是计算机图形学最经典的理论之一。

Alpha Blending

第三个是色度键控(Color Keying)。把指定颜色变成透明,也就是影视里的绿幕。在视频叠加、字幕、嵌入式 UI 里非常常用。

Color Keying

第四个是缩放与插值。放大缩小图像时,必须重新采样。最近邻采样最快但锯齿严重;双线性采样更平滑;双三次采样质量最高但计算量大。缩小图像时如果不移除高频信息,会出现严重混叠,所以通常先做模糊再缩小。

第五个是线性滤波与卷积。用一个小矩阵(卷积核)遍历图像,每个像素由自己和周围像素加权计算。高斯模糊就是低通滤波,去掉细节;锐化是高通滤波,增强边缘;边缘检测是提取轮廓。图像处理库、视频特效、图形引擎,底层全是卷积。

Linear filtering in application

第六个是抖动(Dithering)。当色深不够时,用噪点模拟更多颜色,避免色带。GIF 图片、低色深屏幕、硬件显示引擎,都会用 Floyd-Steinberg 抖动。

Dithering


七、总结

数字图像是连续光的量化采样 ,最小单位是像素。 颜色基于人眼三色视觉 ,用 RGB/YUV 表示,受色域、色深、量化规则约束。 图像大小由分辨率、色深、色度亚采样 决定,4:2:0 是视频的通用语言。 像素在内存里的格式、对齐、排布 ,直接决定画面是否正常显示。 画图靠光栅化 ,图像处理靠混合、缩放、滤波、拷贝。 所有失真、锯齿、花屏、偏色、错位,几乎都来自采样、格式、同步、内存规则。

只要和像素打交道,就离不开这套理论。

到这里,我们第一篇图形基础理论就完整收尾。当我们真正理解这些,再去看 DRM、Wayland、Mesa、驱动代码,就会发现:所有设计、所有结构体、所有参数、所有流程,全都能对应上。