西洋画

什么是色域媒体查询?

色域媒体查询(color-gamut media query)是 CSS Media Queries Level 4 中新增的媒体属性。媒体查询允许我们根据不同的输出设备进行内容定制,而无需修改内容本身。色域媒体查询允许我们根据输出设备的支持色域分类应用不同的样式。

用法介绍

下面演示如何根据媒体色域给首页的题图提供不同的背景图片

1
2
3
4
5
6
7
8
9
10
11

@media (color-gamut: srgb) {
.hero {
background-image: url(http://example.com/foobar.png);
}
}
@media (color-gamut: p3) {
.hero {
background-image: url(http://example.com/foobar-p3.png);
}
}

在上面的例子中,大部分设备都支持 sRGB 色域将会把 .hero 选择出来的结点的 background-image 属性设置成 foobar.png; 对于支持 DCI P3 色域的设备,后面的样式会对之前设置的 background-image 属性覆盖成foobar-p3.png.

上面的例子是一个典型的色域媒体查询的应用场景。需要注意的是,对于给定的色域,color-gamut 仅仅查询设备能否显示该色域大部分颜色。由于色域间可能存在的包含关系,如 DCI P3 色域包含 sRGB 色域,因此一个能显示 DCI P3 色域的设备也能够显示 sRGB 色域,从而表达式color-gamut: srgb在支持 DCI P3 色域的设备上将会返回真值。所以我们通常将媒体色域查询按照色域递增的顺序排列,使的宽色域的样式能够覆盖上一条窄色域的样式。

由于色域呈现的视觉效果对大部分人而言差别并不大,这里的 jsfiddle 的例子对色域查询显示以文字形式展示查询结果。

浏览器支持

截至发稿时,只有 Chrome 58 和 Safari 10 支持色域查询。

从 Canon RAW 到 DCI P3

上面介绍了如何对 DCI P3 色域的设备进行图片内容定制。内容要消费首先需要生产者,下面介绍如何在 macOS 下如何将 Canon Raw 格式转化成使用 DCI P3 色域的文件,可以分别使用图形化界面的 Photos 或者命令行 GraphicsMagick 来转化。

Photos

将 RAW 文件导入到 Photos 中,选择文件,导出该图片。在导出对话框中的色彩配置中选择 Display P3. 可以参考 Apple 帮助网站的说明

GraphicsMagick

  1. 安装前置工具
    1
    brew install graphicsmagick dcraw
  2. 转化 CR2 为 DCI P3 色域的 jpg 文件。假设待转换文件为~/Desktop/test.CR2
    1
    2
    gm convert -profile /System/Library/ColorSync/Profiles/Display\ P3.icc \
    ~/Desktop/test.CR2 ~/Desktop/test-p3.jpg
    作为对比,还可以生成 sRGB 色域的 jpg 文件:
1
gm convert ~/Desktop/test.CR2 ~/Desktop/test-srgb.jpg

通过这两个文件可以对比出,DCI P3 色域确实比 sRGB 色域饱和度更高、色彩更加鲜艳。

隐私考虑

媒体查询一方面带来定制呈现形式的便利,另一方面也带来了用户隐私泄漏的风险。在媒体查询的 Level 4 草案中提到

At minimum, the same information should be inferrable via scripting by examining the User Agent string.

一个 MacOS Chrome 58 的用户代理信息为

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.33 Safari/537.36

显然,我们无法通过用户代理信息去判断一个设备是否支持 DCI P3 色域。截至发稿时,在消费领域支持 DCI P3 色域的桌面和移动设备有

  • Apple iMac (Late 2015)
  • Apple iPad Pro 9.7 英寸
  • Samsung Galaxy Note 7
  • iPhone 7
  • Surface Studio (2016)
  • Macbook Pro (Late 2016)

因此,对于用户信息追踪器而言,使用以下 CSS 样式可以追踪到使用这些设备的用户,后台可以统计此背景图的访问量来对用户进行区分。

1
2
3
4
5
6
7
8
9
10
@media screen and (color-gamut: p3) {
div.track-p3-device {
position: absolute;
width: 1px;
height: 1px;
top: 0;
left: 0;
background-image: url(http://example.com/transparent-track-dci-p3.png);
}
}

在 JavaScript 中,使用

1
2
3
if (window.matchMedia("(color-gamut: p3)").matches) {
//do something
}

也可以检测 DCI P3 色域的设备。因此,色域媒体查询可以让内容方根据设备色域来区分用户。

TL;DR: Chromium/Safari 是如何实现色域查询的?

简单地说,Chromium 自己定义了一个色域宽广度指标,对每个屏幕判断色域宽广度;Safari 使用硬件上报的扩展色域是否支持的信息。

下面介绍 Chromium(Chrome) 和 Safari 的色域查询首次实现方案。浏览器总会有不断的重构,标准也有可能会有更新与修复,因此实现部分只针对首次实现的方案进行介绍。CSS 关键字的处理这里略过不提,重点介绍浏览器如何判断设备的色域。

首先介绍 Safari 的最初实现。 判断设备色域代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static bool color_gamutMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix)
{
if (!value)
return true;

switch (downcast<CSSPrimitiveValue>(*value).getValueID()) {
case CSSValueSrgb:
return true;
case CSSValueP3:
// FIXME: For the moment we'll just assume an "extended
// color" display is at least as good as P3.
return screenSupportsExtendedColor();
case CSSValueRec2020:
// FIXME: At some point we should start detecting displays that
// support more colors.
return false;
default:
ASSERT_NOT_REACHED();
return true;
}
}

系统调用了screenSupportsExtendedColor方法,并将支持扩展颜色都当作支持到了 DCI P3 色域。此方法最终转到了 MobileGestaltSPI.h 去获取硬件信息HasExtendedColorDisplay, 因此 Safari 是通过设备上报的属性来决定是否支持 DCI P3 色域。对于 Rec2020 色域,目前 Safari 还不支持 Rec2020 判断. 截至发稿时 Safari 的实现与最初实现没有本质区别。

下面介绍 Chromium 的最初实现。Chrome 会在每个屏幕获取屏幕上报的色彩空间,通过这个色彩空间来判断色域,色域判断代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
ColorSpaceGamut getColorSpaceGamut(SkColorSpace* colorSpace) {
sk_sp<SkColorSpace> scRGB(SkColorSpace::MakeSRGBLinear());
std::unique_ptr<SkColorSpaceXform> transform(
SkColorSpaceXform::New(colorSpace, scRGB.get()));
if (!transform)
return ColorSpaceGamut::Unknown;
unsigned char in[3][4];
float out[3][4];
memset(in, 0, sizeof(in));
in[0][0] = 255;
in[1][1] = 255;
in[2][2] = 255;
in[0][3] = 255;
in[1][3] = 255;
in[2][3] = 255;
transform->apply(SkColorSpaceXform::kRGBA_F32_ColorFormat, out,
SkColorSpaceXform::kRGBA_8888_ColorFormat, in, 3,
kOpaque_SkAlphaType);
float score = out[0][0] * out[1][1] * out[2][2];
if (score < 0.9)
return ColorSpaceGamut::LessThanNTSC;
if (score < 0.95)
return ColorSpaceGamut::NTSC; // actual score 0.912839
if (score < 1.1)
return ColorSpaceGamut::SRGB; // actual score 1.0
if (score < 1.3)
return ColorSpaceGamut::AlmostP3;
if (score < 1.425)
return ColorSpaceGamut::P3; // actual score 1.401899
if (score < 1.5)
return ColorSpaceGamut::AdobeRGB; // actual score 1.458385
if (score < 2.0)
return ColorSpaceGamut::Wide;
if (score < 2.2)
return ColorSpaceGamut::BT2020; // actual score 2.104520
if (score < 2.7)
return ColorSpaceGamut::ProPhoto; // actual score 2.913247
return ColorSpaceGamut::UltraWide;
}

在上面的代码中,对于给定的色彩空间,生成从这个色彩空间到 sRGB 色彩空间的变换。然后构造了指定色彩空间中的#ff0000, #00ff00, #0000ff三个像素,对这三个像素施加色彩空间变换产生从0到1以 32bit 浮点数表示的三个向量。
$$
\begin{pmatrix}
255 & 0 & 0 \\
0 & 255 & 0 \\
0 & 0 & 255 \\
\end{pmatrix}
\to
\begin{pmatrix}
1 + \epsilon_R & \cdots & \cdots \\
\cdots & 1 + \epsilon_G & \cdots \\
\cdots & \cdots & 1 + \epsilon_B \\
\end{pmatrix}
$$
对于正常的覆盖 RGB 三原色的色彩空间,sRGB中的三个基础颜色向量通过变换产生的三个新向量一定集中在对角线上,即上面的主对角线以外的元素应该都不大。因而如果一个色彩空间色域越宽,那么对角线的乘积一定越大。 因此可以使用对角线乘积作为色域宽广度的一个指标。

Chromium 团队对常见色彩空间做了指标计算,从而给定一个色彩空间,通过这个评分可以近似区分许多色彩空间。然而因为标准的草案只有 srgb, p3, rec2020 三个取值, Chromium 在后面的判断中将这个函数降级以符合标准草案的行为。可以预期如果标准草案增加其他色域作为关键字,Chromium 可以进行方便的拓展。

由于目前的标准草案中并没有给出计算色域宽广度的量化指标,因此 Chromium 使用了一个简单的指标来定量区分。正如开发者所言

While I agree that this is a hacky method, I do think it is good enough to work, but not good enough to be standardized.

上面的方法实际上假定了色彩空间一定主要覆盖对角线上的色彩(也就是上文提到的#ff0000等三个变量),然而,如果自定义的色彩空间因为特殊用途(如为红绿色盲用户定制屏幕)而偏离对角线,那么使用转换后对角线乘积的计算方式有可能会出现问题,即该色彩空间可能更多覆盖对角线外的颜色,而在基准对角线颜色上覆盖不足,会被认为是窄色域。这也是后面对色域宽广度计算进行标准化时需要考虑的问题。

参考资料

[1] 色域媒体查询标准:https://drafts.csswg.org/mediaqueries-4/#color-gamut
[2] 维基百科对 DCI P3 色域的介绍:https://en.wikipedia.org/wiki/DCI-P3
[3] Safari 对色域媒体查询的最初实现:https://trac.webkit.org/changeset/199024/webkit
[4] Chromium 对色域媒体查询的最初实现:https://codereview.chromium.org/2652313004