1. 引言
本节为非规范性内容。
CSS Color 4 为开放 Web 平台引入了广色域(WCG)色彩空间。 这些色彩空间本质上都是标准动态范围(SDR)色彩空间。 本规范定义了支持高动态范围(HDR)的扩展。

虽然 WCG 规范已基本稳定数十年, 但 HDR 标准则不那么成熟, 在过去十年中经历了多次修订。
1.1. 值定义
本规范遵循CSS 属性定义约定 (来自[CSS2]), 并使用值定义语法 (来自[CSS-VALUES-3])。 本规范未定义的值类型,请参考 CSS Values & Units [CSS-VALUES-3]。 与其他 CSS 模块组合时,可能会拓展这些值类型的定义。
除每个属性定义中列出的特定属性值外, 本规范定义的所有属性 还接受CSS 范围关键字 作为属性值。 为增强可读性,此处未重复列出。
2. HDR 术语
2.1. 动态范围的定义
动态范围指亮度在最亮与最暗颜色之间的差距。 动态范围以摄影的档(stop)度量。 每增长一档,亮度加倍。
function DynamicRange( high, low) { return Math. log2( high) - Math. log2( low); }
HDR 参考白 ,也称为媒体白, 是指普通白色背景, 或深色背景下的白色文本。 它可以在整个屏幕上舒适地被观看。
在标准动态范围(SDR)下, HDR 参考白也是 能达到的最亮颜色, 即红绿蓝全亮时产生的颜色。
通常会根据用户偏好或观看环境不同, 调整屏幕的整体亮度, 以适应非标准场景。
对于 SDR,提升屏幕亮度不会改变动态范围, 因为最暗的颜色也会变得更亮。
在高动态范围(HDR)下, 能够显示比HDR 参考白更亮的颜色。 例如,当 HDR 显示器上的 HDR 参考白设为 203 cd/m² 时, 可能可以呈现高达 1000 cd/m² 的小高光。 通常,最亮颜色只能在显示屏的一小部分区域、 并且持续时间有限地显示。 这主要是因为能耗和发热限制。
对于 HDR,提升屏幕亮度会增加动态范围, 而HDR 参考白的亮度保持不变。
在§ 9 SDR 与 HDR 内容合成过程中, 以及色彩空间转换时, SDR 内容的 HDR 参考白 应锚定为203 cd/m² [Rpt_BT.2408], 以确保输入合成时的 HDR 参考白 最终与合成信号的 HDR 参考白一致。
在显示 HDR(或混合 SDR 和 HDR)内容时, 如果显示设备能力不及母版参考显示器, 或观看条件不同于标准条件, 则会执行色彩重渲染步骤(OOTF)。 此时,HDR 参考白的显示值 可能低于或高于 203 cd/m²。
2.2. Headroom简介
HDR 显示设备所能达到的峰值亮度水平 差异极大。
峰值白色大于 HDR 参考白的幅度, 被称为HDR headroom, 其数值取决于 HDR 参考白亮度、 用户偏好 以及观看条件。
它通常以摄影档(stop)为单位表示。 因此,标准动态范围(SDR) 的HDR headroom 定义为0档, 因为最亮的白即为HDR参考白。
在较暗环境中, 将 HDR 参考白设定为 100 cd/m2, 则可获得四档(16倍)HDR headroom。
Web 平台目前 并不直接暴露显示器的 headroom 水平, 因为它依赖于观看条件, 并且实时、非量化的 headroom 信息 可能成为跟踪途径 (例如,侦测到用户出门且是晴天)。
并非总是希望 使用显示器全部可用的 HDR headroom。 CSS 提供了一种方式, 用来粗略地控制 期望使用多大 headroom; 这个数值可以按元素、 甚至随时间变化。
3. 动态范围控制
3.1. dynamic-range-limit 属性
| 名称: | dynamic-range-limit |
|---|---|
| 取值: | standard | no-limit | constrained | <dynamic-range-limit-mix()> |
| 初始值: | no-limit |
| 适用元素: | 所有元素 |
| 继承性: | yes |
| 百分比: | n/a |
| 计算值: | 见 dynamic-range-limit 的计算值 |
| 规范顺序: | 按语法 |
| 动画类型: | 按 dynamic-range-limit-mix() |
测试
- standard
- 显示的最高亮度颜色 与 HDR 参考白相同,即 CSS 颜色 white。
- no-limit
- 显示的峰值亮度 远高于 HDR 参考白,即 CSS 颜色 white; 具体亮度不做规定。
- constrained
- 显示的峰值亮度 略高于 HDR 参考白,即 CSS 颜色 white, 使得 SDR 与 HDR 内容可以舒适地共同呈现。
3.2. 动态范围限制混合:dynamic-range-limit-mix() 函数
该函数接受一个或多个dynamic-range-limit 值, 内部转换为相对于 HDR 参考白的停数值, 并根据指定权重混合后调整显示效果。 出于隐私原因,实际计算结果不会被暴露。
dynamic-range-limit-mix() = dynamic-range-limit-mix( [ <'dynamic-range-limit'> && <percentage [0,100]> ]#{2,} )
测试
3.3. dynamic-range-limit 的计算值
如果指定值为 standard、constrained 或 no-limit,则计算值为指定值。
如果指定值是 dynamic-range-limit-mix(),则计算值采用以下算法:
-
令 v1, ..., vN 为要混合的参数的计算值。
-
令 p1, ..., pN 为混合百分比,归一化后总和为 100%。
-
贡献百分比如下定义:
-
令 p1_standard,...,pN_standard 为 standard 在 v1,...,vN 中的百分比
-
令 p1_constrained_high,...,pN_constrained_high 为 constrained 在 v1,...,vN 中的百分比
-
令 p1_no_limit,...,pN_no_limit 为 no-limit 在 v1,...,vN 中的百分比
-
-
按下式计算加权和:
-
p_standard=(p1_standard*p1+...+pN_standard*pN)/100。
-
p_constrained_high=(p1_constrained_high*p1+...+pN_constrained_high*pN)/100。
-
p_no_limit=(p1_no_limit*p1+...+pN_no_limit*pN)/100。
-
-
如果 p_standard、p_constrained_high 或 p_no_limit 等于 100%,则计算值分别为 standard、constrained 或 no-limit。
-
否则,计算值为 dynamic-range-limit-mix(),其参数为 standard、constrained 和 no-limit,按该顺序,百分比分别为 p_standard、p_constrained_high 与 p_no_limit,百分比为 0% 的参数省略。如果所有参数均被省略,则计算值为初始值。
dynamic-range-limit-mix ( high10 % , dynamic-range-limit-mix ( standard25 % , constrained75 % ) 20 % , dynamic-range-limit-mix ( constrained10 % , no-limit30 % ) 20 % )
为
dynamic-range-limit-mix ( standard10 % , constrained40 % , no-limit50 % )
4. <color> 语法
本模块扩展了 <color-function> 类型:
<color-function> = <rgb()> | <rgba()> |
<hsl()> | <hsla()> | <hwb()> |
<lab()> | <lch()> | <oklab()> | <oklch()> |
<ictcp()> | <jzazbz()> | <jzczhz()> |
<alpha()> |
<color()>
ictcp() = ictcp([from <color>]?
[<percentage> | <number> | none]
[<percentage> | <number> | none]
[<percentage> | <number> | none]
[ / [<alpha-value> | none] ]? )
jzazbz() = jzazbz([from <color>]?
[<percentage> | <number> | none]
[<percentage> | <number> | none]
[<percentage> | <number> | none]
[ / [<alpha-value> | none] ]? )
jzczhz() = jzczhz([from <color>]?
[<percentage> | <number> | none]
[<percentage> | <number> | none]
[<hue> | none]
[ / [<alpha-value> | none] ]? )
5. 带 headroom 参数的 HDR 颜色:hdr-color() 函数
hdr-color() 函数允许指定一系列颜色, 其值会根据HDR headroom 自动计算。
具体方法是声明两个<color> 值, 每个都绑定一个 HDR headroom 水平。 (这两个headroom值不能相同)。 实际颜色值会基于当前的 HDR headroom, 按照§ 5.1 基于 headroom 的颜色插值所述, 在这两个颜色间插值获得。
出于隐私原因,实际计算的颜色和实际 HDR headroom 都不会暴露, 因为这属于浏览器指纹收集向量。
其语法如下:
hdr-color() = color-hdr([ <color> && <number [0,∞]>? ]#{2})
color-hdr ( color ( rec2100-linear0.9 1.0 0.8 ) 0 , color ( rec2100-linear1.8 2.0 1.5 ) 2 );
在 HDR headroom <= 0 时, 也即 SDR 显示器上, 此色显示为
color ( rec2100-linear0.9 1.0 0.8 )
当显示器 HDR headroom >= 2 时, 此色显示为
color ( rec2100-linear1.8 2.0 1.5 ) 2 );
对于 headroom 在 0 到 2 之间的显示器,颜色将进行插值。例如见示例, 如 headroom = 1 时:
X = 243.664, Y = 275.713, Z = 244.000
颜色在 绝对 D65 CIE XYZ 空间插值。
它是通过将xyz-d65的每个颜色分量乘以 203 [Rpt_BT.2408] 获得, 实现由相对单位(白色的 Y 分量为 1,无论实际亮度如何) 向绝对单位 cd/m² 转换,也称为“尼特”。 在示例代码中, 此常量记作 Yw, 即 HDR 参考白的亮度。
5.1. 基于 headroom 的颜色插值
在 headroom 为 H 时,颜色 c1 在 headroom H1 与颜色 c2 在 headroom H2 之间插值得到结果色 cxyz,方法如下:
-
令 c1xyz 为 c1 转换为绝对 D65 CIE XYZ
-
令 c2xyz 为 c2 转换为绝对 D65 CIE XYZ
-
令 w1 = clamp((H - H2) / (H1 - H2), 0, 1)
-
令 w2 = clamp((H - H1) / (H2 - H1), 0, 1) 注意 w2 = 1 - w1
-
令 eps = 0.001
-
令 cxyz = Array(3)
-
对 i = 0 到 2: 令 cxyz[i] = pow(c1xyz[i] + eps, w1 ) * pow(c2xyz[i] + eps, w2 ) - eps
注意: eps 用于防止除零, 尤其是在与接近黑色的 SDR 颜色插值时非常重要。
c1xyz =[ 173.156 , 195.260 , 178.003 ] c2xyz =[ 342.883 , 389.315 , 334.467 ] w1 =0.5 w2 =0.5 cxyz =[ 243.664 , 275.713 , 244.000 ]
c1xyz =[ 156.285 , 188.337 , 28.015 ] c2xyz =[ 3776.1434 , 4362.407 , 1577.913 ] w1 =0.571 w2 =0.429 cxyz =[ 611.911 , 724.180 , 157.650 ]
注意:headroom <= 0.5 时将始终使用 c1,headroom >= 4 时始终使用 c2。
6. 设备无关 HDR 颜色
正如 CSS Color 4 § 9 设备无关颜色:CIE Lab 和 LCH, Oklab 和 OKLCh 允许指定设备无关的 SDR 颜色一样, 本规范允许指定设备无关的 HDR 颜色。
6.1. ICtCp 介绍
本节为非规范性内容。
ICTCP 色彩空间 的感知均匀性优于 CIE Lab, 并被广泛用作高动态范围(HDR)与广色域(WCG)图像 的视频和数码摄影系统色彩成像流程的一部分。
T 和 P 下标指代人类视觉中的 Tritanope(黄蓝轴) 和 Protanope(红绿轴)对立颜色轴。 为避免下标繁琐,简写常用 ICtCp。
ICTCP 最初由杜比实验室 [What_is_ICtCp] 基于 Ebner 和 Fairchild 的 IPT 色彩空间 [Development_ITP] 发展而来, 并在 [Rec_BT.2100] 中定义为 Constant Intensity ICTCP 信号格式。 它的设计目的是取代 YCbCr [Perrin]。
它基于 LMS 锥体原色模型; 使用 Hunt-Pointer-Estevez (HPE) XYZ 到 LMS 的变换, 并以D65 白点归一化。 随后应用 4% 串扰矩阵 [What_is_ICtCp], 用于减少 BT.2020 RGB 的色域包壳凹陷, 从而降低插值误差。 同时提升了恒色调线条和 Just Noticeable Difference (JND) MacAdam 椭圆的均匀性。
与Lab 主要是针对 低强度反射色的实验不同, ICTCP 经过了 高饱和度(广色域)、 自发光、高强度(HDR)颜色的测试。
这使其非常适合 HDR 色差度量 (deltaE ITP) 和 SDR 与 HDR 混合色的色域映射。
下面分别为两个色域下极广色域主红:
color ( rec2100-pq0.58 0 0 ) /* ictcp(44.6% -0.129 0.399) */ color ( rec20201 0 0 ) /* ictcp(44.7% -0.130 0.399) */
两者在 ICTCP 空间内极为接近, deltaE ITP = 0.487, 表明视觉上基本无差异。
而和 sRGB“红色”差别很大:
color ( rec2100-pq0.58 0 0 ) /* ictcp(44.6% -0.129 0.399) */ red/* ictcp(42.8% -0.116 0.279) */
deltaE ITP = 87.7, 说明两者视觉差异极大。
6.2. Jzazbz 与 JzCzhz 介绍
本节为非规范性内容。
Jzazbz 色彩空间 [Safdar-PUCS], 及其极坐标形式 JzCzhz, 专为提升广色域如 BT.2020 的感知均匀性, 准确预测亮度(尤其涵盖 HDR), 并实现色彩属性(亮度、色度和色相)间的最小干扰而设计。 相比于 CAM16-UCS [Safdar-PUCS],色相均匀性更好。
CIE Lab 的亮度分布在 [0%, 100%] 区间, 以 HDR 参考白为基准; 类似地,Oklab 的亮度区间为 [0, 1.0], 其中 1.0 也代表 HDR 参考白。 不同的是,Jzazbz 里的 Jz 轴取值区间为 [0, 1.0], 编码方式类似于 Perceptual Quantizer, 可表示超 13 档的动态范围。
6.3. ICtCp
ICtCp 色彩空间接受三个数值参数, 其中 I 表示亮度 (类似于 Lab 色彩空间的 L,但覆盖亮度高达 10,000 cd/m2), CT 和 CP 分别表示 黄蓝(tritanope)和红绿(protanope)对立色轴(类似于 Lab 的 b 和 a 分量)。
其主要特性如下:
| x | y | |
| 白点色度 | D65 | |
|---|---|---|
| 传递函数 | 感知量化器 | |
| 白色亮度 | 203 cd/m² | |
| 峰值白亮度 | 10,000 cd/m² | |
| 黑色亮度 | 0.001 cd/m² | |
| 图像状态 | display-referred | |
| 百分比 | I、Ct 和 Cp 可用百分比 | |
| 百分比参考范围 | 对 I:0% = 0.0,100% = 1.0 对 Ct/Cp:-100% = -0.5,100% = 0.5 | |
虽然 [Rec_BT.2100] 定义了 ICTCP 从线性光 BT.2100 RGB [Rec_BT.2100] 转换方式, 但实际上,这一转换经过 LMS, 所以任何其他色彩空间也可以按 [What_is_ICtCp] 的 XYZ 到 LMS 变换来表达。 示例代码 采用该方法实现。
ictcp(0.58069 0 0)
在 相对颜色 语法的 ictcp() 函数内, 允许的分量关键词有:
-
i 为 <number>,表示原色的 I(亮度)分量, 经过如有需要的 转换后为 ictcp。1.0 相当于 100%。
-
ct 及 cp 皆为 <number>, 分别对应原色的 ct 和 cp 轴, 如有需要经过 转换。0.5 等价 100%,-0.5 等价 -100%。
6.4. Jzazbz
Jzazbz 色彩空间接受三个数值参数, 其中 Jz 表示亮度(类似于 Lab 的 L), az 和 bz 分别表示 红-绿与黄-蓝的对立色通道(类似于 Lab 的 a 和 b)。
Jzazbz 具有以下特性:
| x | y | |
| 白点色度 | D65 | |
|---|---|---|
| 转移函数 | 感知量化器 | |
| 峰值白亮度 | 10,000 cd/m² | |
| 黑色亮度 | 0.001 cd/m² | |
| 图像状态 | display-referred | |
| 百分比 | Jz、az、bz 可用百分比 | |
| 百分比参考范围 | Jz: 0% = 0.0, 100% = 1.0 az 和 bz: -100% = -0.21, 100% = 0.21 | |
注意,与 Lab 不同的是,这里采用 D65 白点。 因此多数 RGB 色域(也采用 D65 白点)无需执行色度自适应步骤。
已提供将 XYZ-D65 与 Jzazbz 互转的示例代码。
jzazbz(0.22207 -0.00016 -0.00012)
在 相对颜色 语法的 jzazbz() 函数中, 可用的 分量关键字有:
-
j 为 <number>,对应原色的 Jz(亮度)分量, 如有需要经转换,转换至 jzazbz。 1.0 等价于 100%。
-
a 和 b 为 <number>, 分别对应原色的 az 和 bz 分量, 如有需要经转换,转换至 jzazbz。 0.21 等价于 100%,-0.21 等价于 -100%。
6.5. JzCzhz
类似于 LCH 是 Lab 的极坐标表示,JzCzhz 是 Jzazbz 的极坐标形式。
Jz 与 Jzazbz 中的数值一致且表示亮度, Cz 表示色度(色彩度), hz 表示色相角, 从正 az 轴往正 bz 轴的方向度量。
其特性如下:
| x | y | |
| 白点色度 | D65 | |
|---|---|---|
| 转移函数 | 感知量化器 | |
| 峰值白亮度 | 10,000 cd/m² | |
| 黑色亮度 | 0.001 cd/m² | |
| 图像状态 | display-referred | |
| 百分比 | Jz 和 Cz 可用百分比 | |
| 百分比参考范围 | Jz: 0% = 0.0, 100% = 1.0 Cz: 0% = 0.0, 100% = 0.26 | |
| 无色相 ε | Cz <= 0.0000026 | |
jzazbz(0.17542 -0.1179 0.1092)
下面是其极坐标形式
jzczhz(0.17542 0.1614 132.50)
在 相对颜色 语法的 jzczhz() 函数中, 可用的 分量关键字有:
-
j 为 <number>,对应原色的 Jz(亮度)分量, 如有需要经转换,转换至 jzczhz。 100 等价于 100%。
-
c 为 <number>,对应原色的 Cz(色度), 如有需要经转换,转换至 jzczhz。 0.26 等价于 100%。
-
h 为 <number>,对应原色的 hz(色相),以度为单位, 如有需要经转换,转换至 jzczhz, 并归一到 [0, 360] 范围。90 即 90deg。
6.5.1. Jzazbz 颜色转 JzCzhz 颜色
转为 JzCzhz 的过程十分简单:
- hz = atan2(bz, az) // 但需转换为度数!
- Cz = sqrt(az^2 + bz^2)
- Jz 保持不变
此外,实际代码应在 Cz 小于 ε 时返回none。
6.5.2. JzCzhz 颜色转 Jzazbz 颜色
转为 Jzazbz 的过程也十分简单:
- az = Cz cos(H) // H 需先转为弧度
- bz = Cz sin(H) // H 需先转为弧度
- Jz 保持不变
此外,实际代码应检测 hz 是否为none。
7. 指定预定义与自定义色彩空间:color() 函数
color() 函数可以在指定的色彩空间中(而非大多数其他色彩函数默认的隐式 sRGB 色彩空间)指定颜色。
本规范扩展了 color() 函数, 使其不限于HDR 预定义色彩空间, 还可使用 CSS Color 4 § 10 预定义色彩空间中的 SDR 预定义空间及 CSS Color 5 § 4 相对颜色的相对色彩语法。
现有语法如下:
<predefined-rgb> = srgb | srgb-linear | display-p3 | display-p3-linear | a98-rgb | prophoto-rgb | rec2020 | rec2100-pq | rec2100-hlg | rec2100-linear
8. HDR 预定义色彩空间:
除了 CSS Color 4 定义的 SDR 色彩空间 外,以下 HDR 色彩空间也被定义,可在 color 函数中使用。
这些新色彩空间的序列化值与 CSS Color 4 § 15. 序列化 <color> 值 中的描述完全一致。
8.1. rec2100-pq
rec2100-pq [Rec_BT.2100] 色彩空间接受三个数值参数, 分别代表颜色的红、绿、蓝通道, 每个参数的有效范围为 [0, 1], 无论实际每分量的位深(10位或12位)。
采用感知量化器(PQ)电光转换函数 [SMPTE-ST-2084],[Rec_BT.2100]。 PQ 假定参考观看条件下 屏幕环境光为 5 cd/m²。
ITU 参考 2100 标准用于 4K 和 8K HDR 电视。
其主要特征如下: (显示器原色与 [Rec.2020] 相同):
| x | y | |
| 红色色度 | 0.708 | 0.292 |
|---|---|---|
| 绿色色度 | 0.170 | 0.797 |
| 蓝色色度 | 0.131 | 0.046 |
| 白点色度 | D65 | |
| 转移函数 | 感知量化器 | |
| 白色亮度 | 203 cd/m² | |
| 峰值白亮度 | 10,000 cd/m² | |
| 黑色亮度 | ≤ 0.005 cd/m² | |
| 图像状态 | display-referred | |
| 百分比 | R、G、B 可用百分比 | |
| 百分比参考范围 | R、G、B: 0% = 0.0, 100% = 1.0 | |
对于在非参考观看环境下的窄范围 PQ 视频, 或 HLG(在任意观看环境下), 黑阶应通过 PLUGE 测试信号及其设定流程进行调整,详见 [Rec_BT.814] 附录。 对于 CSS 的 PQ 值(宽范围), 黑色对应码值 0。
color(rec2100-pq 1.0 1.0 1.0);
color(rec2100-pq 0.58 0.58 0.58);
此色为中灰, 类似摄影师常用的“18% 反射灰板”, 亮度约 17 cd/m²。
color(rec2100-pq 0.34 0.34 0.34)
添加其他示例,包括 sRGB 红绿蓝和 P3 红绿蓝的编码。
线性光 RGB 信号按以下方式编码为 PQ。 PQ 可编码的最大亮度为 10,000 cd/m²(峰值小面积白)。 HDR 参考白为 203 cd/m² [Rpt_BT.2408]。
var Er; // 红、绿或蓝分量,SDR范围[0, 1],HDR可至70左右 var Yw= 203 ; // 漫反射白亮度,单位cd/m² var x= Er* Yw/ 10000 ; // 峰值白亮度为10,000cd/m² const n= 2610 / ( 2 ** 14 ); const m= 2523 / ( 2 ** 5 ); const c1= 3424 / ( 2 ** 12 ); const c2= 2413 / ( 2 ** 7 ); const c3= 2392 / ( 2 ** 7 ); xPQ= ((( c1+ ( c2* ( x** n))) / ( 1 + ( c3* ( x** n)))) ** m);
xPQ 是“伽玛校正”(OETF校正)信号,范围[0, 1]。
PQ 编码值还原为线性光过程如下:
var xPQ; // PQ编码的红绿蓝分量,[0, 1] const ninv= ( 2 ** 14 ) / 2610 ; const minv= ( 2 ** 5 ) / 2523 ; const c1= 3424 / ( 2 ** 12 ); const c2= 2413 / ( 2 ** 7 ); const c3= 2392 / ( 2 ** 7 ); var x= ( (Math. max((( xPQ** minv) - c1), 0 ) / ( c2- ( c3* ( xPQ** minv)))) ** ninv); var Yw= 203 ; // 漫反射白亮度,cd/m² var Ea= x* 10000 ; // 亮度, [0, 10000]。 var Er= x* 10000 / Yw; // 相对漫反射白亮度, [0, 70左右]。
8.2. rec2100-hlg
rec2100-hlg [Rec_BT.2100] 色彩空间接受三个数值参数, 代表颜色的红、绿、蓝通道, 每个参数有效范围为 [0, 1], 无论分量实际位深。
采用混合对数伽马(HLG)电光转换函数 [ARIB_STD-B67],[Rec_BT.2100]。 HLG 可适用于不同亮度的显示器, 并适配各种观看环境, 用户可调整体亮度。 0.75 代表“漫反射”或“媒体白”, “18% 反射灰板” 对应值为 0.38。[Rec_BT.2390]。
其主要特征如下:(显示器原色与 [Rec.2020] 相同)
| x | y | |
| 红色色度 | 0.708 | 0.292 |
|---|---|---|
| 绿色色度 | 0.170 | 0.797 |
| 蓝色色度 | 0.131 | 0.046 |
| 白点色度 | D65 | |
| 转移函数 | 混合对数伽马 | |
| 白色亮度 | 取决于观看环境 | |
| 峰值白亮度 | 为参考白的 12 倍 | |
| 黑色亮度 | 取决于参考白,见正文 | |
| 图像状态 | scene-referred | |
| 百分比 | R、G、B 可用百分比 | |
| 百分比参考范围 | R、G、B: 0% = 0.0, 100% = 1.0 | |
对于所有观看环境下的窄范围 HLG 视频, 黑阶应通过 PLUGE 测试信号和设定流程调整,详见 [Rec_BT.814] 附录 4。
对于 CSS 下的 HLG 值(宽范围), 黑色对应码值 0。
color(rec2100-hlg 0.75 0.75 0.75);
此色为中灰, 亮度处于 26~104 cd/m²。
color(rec2100-hlg 0.38 0.38 0.38)
线性光 RGB 信号到 HLG 编码的方式如下 [Rec_BT.2390]:
var E; // 红绿蓝分量, [0, 1] const a= 0.17883277 ; const b= 0.28466892 ; // 1 - (4 * a) const c= 0.55991073 ; // 0.5 - a * Math.log(4 *a) // 处理负数值 var sign= E< 0 ? - 1 : 1 ; var abs= Math. abs( E); if ( abs<= 1 / 12 ) { Edash= sign* Math. sqrt( 3 * abs); } else { Edash= a* Math. log( 12 * E- b) + c; }
Edash 为“伽玛校正”(OETF 校正)信号。
HLG 解码还原到线性光如下 [Rec_BT.2390]:
var Edash; // 编码红绿蓝分量, [0, 1] const a= 0.17883277 ; const b= 0.28466892 ; // 1 - (4 * a) const c= 0.55991073 ; // 0.5 - a * Math.log(4 *a) if ( Edash<= 0.5 ) { E= ( Edash** 2 ) / 3 ; } else { E= ( Math. exp(( Edash- c) / a) + b) / 12 ; }
8.3. rec2100-linear
rec2100-linear [Rec_BT.2100] 色彩空间接受三个数值参数, 代表红、绿、蓝三通道, 每个分量标称范围为[0, 1], 不受分量位深影响。
红、绿、蓝全为 1.0 表示HDR 参考白,亮度为 203 cd/m²。
color ( rec2100-linear1 1 1 )
color(rec2100-linear 1 1 1)
采用线性光电-光转换函数。
主要特征如下:(显示器原色与 [Rec.2020] 相同):
| x | y | |
| 红色色度 | 0.708 | 0.292 |
|---|---|---|
| 绿色色度 | 0.170 | 0.797 |
| 蓝色色度 | 0.131 | 0.046 |
| 白点色度 | D65 | |
| 转移函数 | 线性 | |
| 白色亮度 | 203 cd/m² | |
| 峰值白亮度 | 10,000 cd/m² | |
| 黑色亮度 | 0.001 cd/m² | |
| 图像状态 | display-referred | |
| 百分比 | R、G、B 可用百分比 | |
| 百分比参考范围 | R、G、B: 0% = 0.0, 100% = 1.0 | |
9. SDR 与 HDR 内容合成
内容合成应在 CIE XYZ 空间进行, 因其为线性光空间,无色域限制。 实现也可以选择在线性光 RGB 空间混合, 只要正确处理超色域值(负数或超过100%), 并确保直到最终转到设备色域时才裁剪或映射。
使用 HLG 转换的 HDR, 必须将 SDR HDR 参考白 映射到 HLG 0.75 的亮度。 [SMPTE-ST-2084]
更多细节见 ITU Rpt_BT.2408-0 第 3 与第 4 表格 [Rpt_BT.2408]
使用 PQ 转换的 HDR, 应将 SDR 的 HDR 参考白 映射到 203 cd/m²,即显示 PQ 0.58 的亮度。 [SMPTE-ST-2084]。
不过,实现可以选择引入 色彩重渲染, 以适应非参考观看条件。
10. 序列化 <color> 值
10.1. 序列化 color() 函数值
本节扩展了 CSS Color 4 § 15.5 color() 函数值序列化
color() 值的序列化形式 来源于计算值,采用 color() 语法, 函数名及色彩空间名均采用 ASCII 小写字母。
分量值采用十进制序列化, 格式为 <number>。 各分量间,以及色彩空间名和首个色值间, 均用单个 ASCII 空格分隔。
HDR 预定义色彩空间的往返最低精度如下:
| HDR 色彩空间 | 最小位数 |
|---|---|
| rec2100-pq, rec2100-hlg | 10 |
| rec2100-linear, jzazbz, jzczhz, ictcp | 16 |
(建议内部存储使用 16 位、half-float 或 float 每个分量)。 值必须向 +∞ 舍入,不得直接截断。
11. 颜色转换示例代码
本节为非规范性内容。
测试
本节为非规范性内容,不需要测试。
为清晰起见,使用了用于矩阵乘法的一个库。 (这比将所有乘法与加法内联更易读)。 矩阵采用列主序。
本代码还假设已提供自 CSS Color 4 § 18. 颜色转换示例代码 的全部转换代码。
Jzazbz 中使用的 LMS 与 ICtCp 中使用的不同, 请务必使用正确的版本!
11.1. rec2100-linear 示例代码
BT.2020 与 BT.2100 色彩空间使用相同的 RGB 原色与白点,且二者都将 HDR 参考白置于分量值 1.0。
// 这些函数使用了 CSS Color 4 中的颜色转换函数 function XYZ_to_lin_2100( XYZ) { // 将 D65 XYZ 数组转换为线性光 BT.2100 RGB // 使得 [0,0,0] 为黑色,[1,1,1] 为媒体白 // 大于 1 的分量值表示 HDR 颜色 return XYZ_to_lin_2020( XYZ); } function lin_2100_to_XYZ( RGB) { // 将线性光 BT.2100 RGB 数组 // 转换为 D65 XYZ return lin_2020_to_XYZ( RGB); }
11.2. rec2100-pq 示例代码
function XYZ_to_pq_2100( XYZ) { // 将 D65 XYZ 数组转换为 PQ 编码的 BT.2100 RGB // 使得 [0,0,0] 为黑色,[1,1,1] 为 10,000 cd/m^2 的白; // 媒体白约为 [0.5807,0.5807,0.5807](四位有效数字) let linRGB= XYZ_to_lin_2100( XYZ); return pq_encode( linRGB); } function pq_2100_to_XYZ( RGB) { // 将 PQ 编码的 BT.2100 RGB 数组 // 转换为 D65 XYZ let linRGB= pq_decode( RGB); return lin_2100_to_XYZ( linRGB); } function pq_encode( RGB) { const Yw= 203 ; // 媒体白的绝对亮度,cd/m² const n= 2610 / ( 2 ** 14 ); const m= 2523 / ( 2 ** 5 ); const c1= 3424 / ( 2 ** 12 ); const c2= 2413 / ( 2 ** 7 ); const c3= 2392 / ( 2 ** 7 ); // 给定范围 [0, 1] 的 PQ 编码分量 // 返回相对于媒体白的线性光值 return RGB. map( function ( val) { let x= Math. max( val* Yw/ 10000 , 0 ); // 峰值白的绝对亮度为 10,000 cd/m² let num= ( c1+ ( c2* ( x** n))); let denom= ( 1 + ( c3* ( x** n))); return (( num/ denom) ** m); }); } function pq_decode( RGB) { const Yw= 203 ; // 媒体白的绝对亮度,cd/m² const ninv= ( 2 ** 14 ) / 2610 ; const minv= ( 2 ** 5 ) / 2523 ; const c1= 3424 / ( 2 ** 12 ); const c2= 2413 / ( 2 ** 7 ); const c3= 2392 / ( 2 ** 7 ); // 给定范围 [0, 1] 的 PQ 编码分量 // 返回相对于媒体白的线性光值 return RGB. map( function ( val) { let x= (( Math. max((( val** minv) - c1), 0 ) / ( c2- ( c3* ( val** minv)))) ** ninv); return ( x* 10000 / Yw); // 相对于漫反射白的亮度,[0,约 70] }); }
11.3. rec2100-hlg 示例代码
function XYZ_to_hlg_2100( XYZ) { // 将 D65 XYZ 数组转换为 HLG 编码的 BT.2100 RGB // 使得 [0,0,0] 为黑色,[0.75,0.75,0.75] 为媒体白 let linRGB= XYZ_to_lin_2100( XYZ); return hlg_encode( linRGB); } function hlg_2100_to_XYZ( RGB) { // 将 PQ 编码的 BT.2100 RGB 数组 // 转换为 D65 XYZ let linRGB= hlg_decode( RGB); return lin_2100_to_XYZ( linRGB); } function hlg_encode( RGB) { const a= 0.17883277 ; const b= 0.28466892 ; // 1 - (4 * a) const c= 0.55991073 ; // 0.5 - a * Math.log(4 *a) const scale= 3.7743 ; // 将 18% 灰置于 HLG 0.38,从而媒体白为 0.75 return RGB. map( function ( val) { // 先缩放,使线性光媒体白位于 1/3 val/= scale; // 然后应用 HLG OETF // ITU-R BT.2390-10 第 23 页 // 6.1 HLG 光电传递函数(OETF) if ( val<= 1 / 12 ) { return spow( 3 * val, 0.5 ); } return a* Math. log( 12 * val- b) + c; }); } function hlg_decode( RGB) { const a= 0.17883277 ; const b= 0.28466892 ; // 1 - (4 * a) const c= 0.55991073 ; // 0.5 - a * Math.log(4 *a) const scale= 3.7743 ; // 将 18% 灰置于 HLG 0.38,从而媒体白为 0.75 return RGB. map( function ( val) { // 先应用 HLG EOTF // ITU-R BT.2390-10 第 30 页 // 6.3 HLG 电光传递函数(EOTF) // 然后乘以 scale,使媒体白为 1.0 if ( val<= 0.5 ) { return ( val** 2 ) / 3 * scale; } return (( Math. exp(( val- c) / a) + b) / 12 ) * scale; }); } function spow( base, exp) { let sign= base< 0 ? - 1 : 1 ; return sign* ( Math. abs( base) ** exp); }
11.4. jzazbz 示例代码
这里复用上文定义的带符号幂函数(spow)。
function XYZ_to_Jzazbz( XYZ) { // 将 D65 XYZ 数组转换为 JzAzBz // 使得 [0,0,0] 为黑色, // 媒体白为 [0.2220, -0.00016, -0.0001] const Yw= 203 ; // 媒体白的绝对亮度 const M= [ [ 0.41478972 , 0.579999 , 0.0146480 ], [ - 0.2015100 , 1.120649 , 0.0531008 ], [ - 0.0166008 , 0.264800 , 0.6684799 ], ]; const b= 1.15 ; const g= 0.66 ; // 先将 XYZ 转换为绝对值(非媒体白相对) // PQ 的最大亮度为 10,000 cd/m² // BT.2048 指定媒体白 Y=203 cd/m² let [ Xa, Ya, Za] = XYZ. map( v=> v* Yw); // 然后修正 X 与 Y,以减小蓝色曲率 let Xm= b* Xa- ( b- 1 ) * Za; let Ym= g* Ya- ( g- 1 ) * Xa; // 进入 LMS 锥体域 let LMS= multiplyMatrices( M, [ Xm, Ym, Za]); return LMStoJzazbz( LMS); } function LMStoJzazbz( LMS) { const M= [ [ 0.5 , 0.5 , 0 ], [ 3.524000 , - 4.066708 , 0.542708 ], [ 0.199076 , 1.096799 , - 1.295875 ], ]; const c1= 3424 / 2 ** 12 ; const c2= 2413 / 2 ** 7 ; const c3= 2392 / 2 ** 7 ; const n= 2610 / 2 ** 14 ; const p= ( 1.7 * 2523 ) / 2 ** 5 ; // 相比常规 PQ,使用 1.7 的缩放 const d= - 0.56 ; const d0= 1.6295499532821566e-11 ; // 极小偏移,将黑位回调至 0 // 对 LMS 进行 PQ 编码 let PQLMS= ( LMS. map( function ( val) { let num= c1+ c2* spow(( val/ 10000 ), n); let denom= 1 + c3* spow(( val/ 10000 ), n); return spow(( num/ denom), p); }) ); // 计算 Iz、az、bz let [ Iz, az, bz] = multiplyMatrices( M, PQLMS); // 由 Iz 求 Jz let Jz= (( 1 + d) * Iz) / ( 1 + d* Iz) - d0; return [ Jz, az, bz]; } function Jzazbz_to_XYZ( Jzazbz) { // 将 JzAzBz 数组转换为 D65 XYZ // 使得 [0,0,0] 为黑色, // 媒体白为 [0.2220, -0.00016, -0.0001] const b= 1.15 ; const g= 0.66 ; const M= [ [ 1.9242264357876067 , - 1.0047923125953657 , 0.037651404030618 ], [ 0.35031676209499907 , 0.7264811939316552 , - 0.06538442294808501 ], [ - 0.09098281098284752 , - 0.3127282905230739 , 1.5227665613052603 ], ]; let LMS= Jzazbz_to_LMS( Jzazbz); // 修正后的绝对 XYZ let [ Xm, Ym, Za] = multiplyMatrices( LMS, M); // 反向修正 X 与 Y,得到以媒体白为基准的 D65 XYZ let Xa= ( Xm+ ( b- 1 ) * Za) / b; let Ya= ( Ym+ ( g- 1 ) * Xa) / g; return [ Xa, Ya, Za]; } function Jzazbz_to_LMS( Jzazbz) { const d= - 0.56 ; const d0= 1.6295499532821566e-11 ; const c1= 3424 / 2 ** 12 ; const c2= 2413 / 2 ** 7 ; const c3= 2392 / 2 ** 7 ; const pinv= 2 ** 5 / ( 1.7 * 2523 ); const M= [ [ 1 , 0.13860504327153927 , 0.05804731615611883 ], [ 1 , - 0.1386050432715393 , - 0.058047316156118904 ], [ 1 , - 0.09601924202631895 , - 0.81189189605603900 ], ]; let [ Jz, az, bz] = Jzazbz; let Iz= ( Jz+ d0) / ( 1 + d- d* ( Jz+ d0)); // 转至 LMS 锥体域 let PQLMS= multiplyMatrices([ Iz, az, bz], M); // 从 PQ 编码转换为线性光 LMS let LMS= ( PQLMS. map( function ( val) { let num= c1- spow( val, pinv); let denom= c3* spow( val, pinv) - c2; let x= 10000 * spow( num/ denom, ninv); return x; // 相对于漫反射白的亮度,[0,约 70] }) ); return LMS; }
11.5. ICtCp 示例代码
与其先转换到 rec2100-linear (这是 [Rec_BT.2100] 中的定义方式), 本示例直接从绝对 CIE XYZ 出发, 以便与其他颜色转换代码保持一致。
4% 串扰矩阵与色相旋转也被合并进 XYZ 到 LMS 的步骤中, 而不是拆成三个独立步骤。
最终结果相同,但步骤更少。
function XYZ_to_ICtCp( XYZ) { // 将 D65 XYZ 数组转换为 ICtCp // 下述矩阵包含 4% 串扰分量 // 来自杜比《What is ICtCp》文档中的流程 const M= [ [ 0.3592832590121217 , 0.6976051147779502 , - 0.0358915932320290 ], [ - 0.1920808463704993 , 1.1004767970374321 , 0.0753748658519118 ], [ 0.0070797844607479 , 0.0748396662186362 , 0.8433265453898765 ], ]; let LMS= multiplyMatrices( M, XYZ. map( v=> v* Yw)); return LMStoICtCp( LMS); } function LMStoICtCp( LMS) { const c1= 3424 / 4096 ; const c2= 2413 / 128 ; const c3= 2392 / 128 ; const m1= 2610 / 16384 ; const m2= 2523 / 32 ; // 此矩阵包含 Ebner LMS 系数、 // 旋转以及到 [-0.5, 0.5] 范围的缩放 // 有理项参见 Fröhlich 第 97 页 // 以及 ITU-R BT.2124-0 第 2–3 页 const M= [ [ 2048 / 4096 , 2048 / 4096 , 0 ], [ 6610 / 4096 , - 13613 / 4096 , 7003 / 4096 ], [ 17933 / 4096 , - 17390 / 4096 , - 543 / 4096 ], ]; // 应用 PQ EOTF // 值被缩放到 [0, 10,000] → [0, 1] // 分母因带有 “1 +” 不会出现除零问题 let PQLMS= LMS. map( function ( val) { let num= c1+ ( c2* (( val/ 10000 ) ** m1)); let denom= 1 + ( c3* (( val/ 10000 ) ** m1)); return ( num/ denom) ** m2; }); // LMS → IPT,并包含与 Y'C'bC'r 兼容的旋转 return multiplyMatrices( M, PQLMS); } function ICtCp_to_XYZ( ICtCp) { // 将 ICtCp 转换为绝对 D65 XYZ 数组 const M= [ [ 2.0701522183894223 , - 1.3263473389671563 , 0.2066510476294053 ], [ 0.3647385209748072 , 0.6805660249472273 , - 0.0453045459220347 ], [ - 0.0497472075358123 , - 0.0492609666966131 , 1.1880659249923042 ], ]; let LMS= ICtCptoLMS( ICtCp); return multiplyMatrices( M, LMS); } function ICtCptoLMS( ICtCp) { const c1= 3424 / 4096 ; const c2= 2413 / 128 ; const c3= 2392 / 128 ; const im1= 16384 / 2610 ; const im2= 32 / 2523 ; const M= [ [ 0.9999999999999998 , 0.0086090370379328 , 0.1110296250030260 ], [ 0.9999999999999998 , - 0.0086090370379328 , - 0.1110296250030259 ], [ 0.9999999999999998 , 0.5600313357106791 , - 0.3206271749873188 ], ]; let PQLMS= multiplyMatrices( M, ICtCp); // 撤销 PQ 编码,来源:BT.2124-0 附录 2 转换 3 let LMS= PQLMS. map( function ( val) { let num= Math. max(( val** im2) - c1, 0 ); let denom= ( c2- ( c3* ( val** im2))); return 10000 * (( num/ denom) ** im1); }); return LMS; }
11.6. hdr-color() 示例代码
function hdrColor( col1, H1, col2, H2, H) { // col1、col2 是表示两种颜色的数组,使用绝对 XYZ // H1 与 H2 是各自颜色的 headroom(以档计,即对数尺度,0 = SDR) // H 是可用的 headroom // 首先检查 headroom 是否不同 if ( H1== H2) return 0 ; let w1= clamp(( H- H2) / ( H1- H2), 0 , 1 ); let w2= clamp(( H- H1) / ( H2- H1), 0 , 1 ); let eps= 0.001 ; let cxyz= Array( 3 ); for ( let i= 0 ; i< 3 ; i++ ) { cxyz[ i] = Math. pow( c1xyz[ i] + eps, w1) * Math. pow( c2xyz[ i] + eps, w2) - eps; } return cxyz; } const clamp= ( n, min, max) => Math. min( Math. max( n, min), max)
12. ΔEITP 颜色差异示例代码
本节为非规范性内容。
测试
本节为非规范性内容,不需要测试。
ΔEITP [Rec_BT.2124] 颜色差度量 可用于测量在 HDR 或混合 SDR/HDR 场景中 两个显示参照(display-referred)颜色之间的感知差异, 例如用于显示校准或色域/色调映射。
它是在 ICtCp 色彩空间中的欧氏距离, 并经过缩放,使 ΔEITP = 1.0 表示一次恰可感知差异(JND)。
// 计算 deltaE ITP // 缩放的平方和开根 // ITU-R BT.2124-0 附录 1 /** * @param {number[]} reference - ICtCp 数组:I 为 0..1,Ct 与 Cp 为 -1..1 * @param {number[]} sample - ICtCp 数组:I 为 0..1,Ct 与 Cp 为 -1..1 * @return {number} 样本颜色与参考颜色的差异程度 */ function deltaEITP( reference, sample) { let [ I1, Ct1, Cp1] = reference; let [ I2, Ct2, Cp2] = sample; let ΔI= I1- I2; let ΔT= 0.5 * ( Ct1- Ct2); let ΔP= Cp1- Cp2; return 720 * Math. sqrt( ΔI** 2 + ΔT** 2 + ΔP** 2 ); }
隐私注意事项
Web 平台之所以不直接暴露 HDR headroom 的数值, 是因为那样会暴露当前的观看条件, 构成隐私侵犯。
安全注意事项
本文档未提出任何安全方面的顾虑
可访问性注意事项
部分用户可能对非常明亮的颜色较为敏感, 因此用户代理应提供一种机制, 允许用户选择限制最大亮度。建议采用 [Rec_BT.2390] 第 5.4.1 节 将映射到亮度范围受限的显示器 中的 toe 与 knee 过程。
dynamic-range-limit 属性也可以在用户样式表中设置为 standard 或 constrained。
变更
自 2024 年 12 月 17 日 首次公开工作草案 以来的变更
- 移除了以 SMPTE-ST-2094-50 作为 eps 理由的相关表述 (#12873, #11788)
- 指出 hdr-color 与自适应增益曲线的相似性
- 在颜色语法中添加 alpha() RCS #10689
- 将 display-p3-linear 添加到用于插值的色彩空间 (#12596)
- 补充了若干 ΔE ITP 示例 (#11250)
- 添加了 display-p3-linear 色彩空间 (#12596)
- 添加了 hdr-color() 使用的 clamp 函数
- 添加了 hdr-color() 示例代码
- 说明了 eps 因子的用途 (#11788)
- 添加了第二个 color-hdr() 示例
- 定义了术语 “Absolute D65 CIE XYZ”
- 明确了 color-hdr() 的伪代码,表明各分量分别计算 (#11694)
- 在 dynamic-range-limit-mix() 中,按工作组决议改为 “一个或多个” (#11694)
- 阐明当 dynamic-range-limit-mix() 的所有参数都被省略时,其计算值为初始值 (#11678)
- 移除了对 “color()” 的重新定义,改为链接到 CSS Color 5 中的定义 (#11954)
- 依据工作组决议,将 “constrained-high” 改回 “constrained” (#11698)
- 为 JzCzhz 的色度定义了 ε,用于无色相情况 (#11706)
- 正确导出了术语 “required conversion”,并在全文一致使用
- 移除了 JzCzhz 中错误的负色度参考范围
- 为相对 ictcp、jzazbz 和 jzczhz 添加了 RCS 分量关键字 (#11713)
- 避免 color() 链接指向仅支持 SDR 的 CSS Color 5
- 新增 ICtCp 的介绍性章节,并将定义从 color() 中移出 (#11713)
- 移除了已在 CSS Color 5 中存在的重复产生式 (#11954)
- 参考范围涵盖 BT.2100 色域 (#11710)
- 依据 CSS 工作组决议,移除了 “若总和为零则无效” 的表述 (#11678)
- 统一采用对数量纲(stops)的 headroom 定义,而非线性(倍数)headroom (#11787)
- 统一使用推荐术语 “HDR reference white”,而非 “media white”
- 添加了颜色插值示例 (#11616)
- 添加了插值算法 (#11616)
- 添加了按 headroom 参数化的颜色插值章节 (#11616)
- 根据工作组决议添加了 color-hdr 函数。 添加 ISO 21496-1 的资料性参考 (#11616)
- 添加了 Jzazbz 示例代码 (#9934)
- 补充了示例代码所缺失的有符号幂函数
- 添加了 rec2100 色彩空间示例代码 (#9934)
- 添加了 ICtCp 示例代码 (#9934)
- 添加了 Perrin ICtCp 论文的资料性参考
- 新增“颜色转换示例代码”章节 (#9934)
- 添加了杜比 “What is ICtCp” 白皮书的资料性参考 (#9934)
- 明确了 dynamic-range-limit-mix() 的语法与规范文本 (#11672)
- 添加了来自 BT.2124 的 ΔEITP 颜色差度量 (#11250)
- 对于 dynamic-range-limit,将 high 更改为 no-limit (#11698)
- 修正文字,dynamic-range-limit-mix 接受 2 个或更多值 (#11694)
- 移除了绝对与相对 HDR 概念,改为强调场景参照(scene-referred)与显示参照(display-referred)。 锚定 media white,并允许色彩重渲染 (#10460)
- 更清晰地定义了 “media white”
- 将说明性材料汇集至导言部分