网瘾少年

优化Web上的颜色 | WebKit

原文链接: webkit.org

by Dean Jackson @grorgwork

最近几年,显示器技术有了令人意想不到的发展。开始是移动设备,然后是台式机和笔记本电脑,都升级到了高分辨率的屏幕。在这个大背景下,Web开发者需要了解high-DPI(高解析度),并且要知晓如何利用较之前更高的分辨率更好的进行前端工作。不难发现,显示器正在趋向于更好的色彩再现,这将是一项重大的显示器变革。本文将阐述这些变化意味着什么, 并向作为Web开发者的大家讲解如何在探索新一代的显示器的同时向用户提供更好的体验。

大家之前数十年使用的传统的电脑显示器,我们称其为sRGB 显示器。 苹果最近推出的包括late-2015 Retina iMac 和 early-2016 iPad Pro在内的硬件产品比sRGB显示器能够显示更多的颜色。 此类的显示器被称为广色域显示器(接下来我将解释sRGB和色域是什么)。

广色域有什么用呢? 一个广泛的色域系统往往能够对原本的颜色进行更为精确的展现。 例如, 我的同事 @hober 有一双非常时髦的鞋子。

hober's bright orange shoes

很可惜,上图所展示的图片无法真实的传达这双鞋子有多时髦!问题在于,sRGB显示器无法表现出这双鞋子的面料所使用的颜色。拍摄这张照片的相机(索尼 a6300)有一个能够精确捕捉颜色信息的传感器,传感器所捕捉到的颜色数据会被存储到原始照片文件中,然而sRGB显示器无法完整的显示这些颜色数据。在原始照片中,一部分像素的颜色超出了传统显示器的颜色显示范围,在下面这个版本的照片中,将这部分像素的颜色都被替换成了浅蓝色:

hober's bright orange shoes, but with all the out-of-gamut pixels replaced by blue

显而易见,这双鞋子的面料及大部分草的颜色超出了sRGB标准的显示范围。实际看来照片上只有不到一半像素能够被准确的显示出来。作为一个web开发者,这个问题应该引起你的注意。假如你是一个正在售卖这些鞋子的电商,而你的顾客却对他们订购鞋子的颜色了解不够,所以当他们收到商品时会非常的讶异——因为商品的颜色和他们看到的不一样。

而广色域显示器减少了此类问题。如果你拥有有上文中提到的其中一台设备,或者一台类似的设备,那么观看下面的这个版本的照片时将会显示更多的色彩:

hober's bright orange shoes, this time with a color profile embedded

当你使用广色域显示器时,你会看到这双鞋的颜色是亮橙色,而且还能看到草地上稍微多了一些颜色变化。可惜的是,如果你没有一台广色域显示器,那么你看到的很可能像上面的第一张图一样。在这种情况下,我所能提供的最好的方式就是将彩色照片中你无法看到的那部分图片突出显示出来。

无论怎样,这都是一个很不错的消息。广色域显示器更加的具有表现力,并且能对实际情况进行更准确的描绘。很明显,你一定更倾向于选择那些应用了这项技术的图片为你的用户提供服务。

下面是另一个例子,这一次展示的是一张合成的图片。对于使用sRGB显示器的用户来说,下图只是一张红色的规则正方形。但是这里存在一个小把戏。在这张图片上实际上存在着两种不同色调的红色,其中一种只能在广色域显示器上才能清楚的显示。所以在广色域显示器上,你能看到在红色的正方形上面有一个不明显的WebKit标志。

A red square with a faint WebKit logo

有的时候正常图片和广色域图片之间的区别是很微妙的,但有的时候却又令人印象深刻。

示例

在下面这个示例中,你可以通过切换不同色域的图片去比较这些图片,也可以看到图片中超出sRGB显示器显示范围的那些像素。这里还有一个轻交互式的示例,可以将不同色域的图片并排展示以供比较。

术语

下面是在讨论颜色时常常会提到的一些术语。

色彩空间:色彩空间提供了一个能够定义和比较颜色的环境。它包含几组颜色区域,每一组都使用了不同的参数,最终描绘出所有的颜色。举个例子,灰度色彩空间只使用了一个参数,这个参数能够通过控制亮度等级实现由黑到白的转化。大家可能对RGB类色彩空间比较熟悉,这类色彩空间将红绿蓝三种颜色作为基本色,通过将这三种颜色的灯在屏幕上叠加最终表现出目标颜色。而在印刷作业中通常使用CMYK类色彩空间,以青色、洋红色、黄色以及黑色作为基本色来表现油墨的各种颜色。

颜色配置文件: 1993年,一群厂商成立ICC 以规范描述色彩空间的标准。颜色配置文件是一组定义了设备色彩空间的数据,使用不同的颜色配置文件也能够变换不同的色彩空间。一般来说人们会为常见的色彩空间类型命名,比如 sRGB (也有正式的比如IEC 61966-2-1)。上文中提到的sRGB标准的应用看起来稍微多了一些意义:这类的sRGB显示器可以通过使用sRGB标准从而显示sRGB色彩空间中对应的颜色。颜色配置数据可以写入到文件中,也可以直接内嵌到图片中,从而使电脑理解图片颜色值的意义。

色域:色域是指一种设备能够处理的颜色范围,或者说是一种色彩空间能够定义的颜色范围。就电脑显示器来说,色域就是它能够精确显示的所有颜色。色域的可视化是比较复杂的,有点类似于设计软件中的取色器。也可以想象成在一个平面中,边缘是基本色,所有的颜色按层次平铺在上面。在这个平面如果向红色端移动时,颜色会变得越来越红。同样的,向蓝色端移动就会越来越蓝。色域是在这个想象中的平面中,能够包含设备颜色范围内颜色的区域。下图是一个色域示意图,它显示大家展示了色彩模型,白色三角形划出了sRGB色彩空间的范围(你可能注意到,这个范围比总范围要小的多)。

Diagram showing the sRGB gamut

图片来源: Wikipedia

这个图表展示了一些能被肉眼看到的颜色,然后指出哪些色域不包括哪些颜色,这样其实是有一点误导的。虽然这些图表有利于比较不同规格的色域,但是需要注意的是,我们看到的是二维形式的色域平面,实际上色域却是三维甚至四维的空间(这里面是非常的复杂,我们只能尽量的给出一个比较浅显的介绍)。

广色域: 这其实是一个非正式的称呼,行内通常把它用来形容一些色域比sRGB要范围大的设备或者色彩空间,sRGB正是上文中提到的在过去大约十年内几乎所有电脑显示器都使用的色域。广色域显示器时存在了一段时间的,但是几乎都是供专业人士使用。而现在普通的消费者也能够购买到,如此以来那些购买广色域显示器的人也能够通过显示器看到广色域的各种颜色。广色域有时也被称为扩展色。苹果显示器支持比sRGB范围大25%的Display P3色彩空间。

色彩深度:电脑使用不同的精度或者深度来描述颜色。这与使用色域不同,色域是描述一定范围的颜色。色彩深度确切的说是指已定义的色域里不同颜色的总和。Web开发人员应该都熟悉CSS的rgb() 语法,这种语法能表示出以红绿蓝为基本色的256种颜色值。每条通道的深度为8位,那么一共包含16777216种颜色。如果加上透明度、半透明度以及不透明度,那么可以用32位编码来储存颜色。如果你使用8位元,无论你使用哪种色彩空间,所能表现出的颜色数目是一样的——不同的只是产生了另一套颜色。如果你使用了16位元,你会得到一个更为广阔的色彩空间,能够在色域已知的情况下描述出更多的颜色。一个很易懂的例子,当你描绘相似颜色间的渐变时,会看到明显的色彩条带,就好像电脑和显示器可能没有足够的深度去展示两个颜色间平滑的颜色变化。

下面这个例子中,即使两个结束点颜色间的所有颜色都在色域中,但是如果色彩深度不够还是会产生色彩条带的(当然这只是一个人为的例子,目的是用来突出效果)。

小的色彩深度的变化也会在相近颜色之间凸显出明显的跳跃式变化。

A light red to dark red gradient with distinct bands

而大的色彩深度的变化反而变化不是很明显。

A light red to dark red gradient without distinct bands

了解了以上内容之后,让我们通过深入Web色彩细节和了解最近的WebKit优化,从而帮助你在开发网页时更注意色彩的细节。我们也将介绍一些新特性,这些新特性我们已经提交给W3C,这将会使你尽可能的应用好这些新的显示技术。

Web的色彩

我们经常纠结于如何适当的处理Web上的色彩 我猜有现在很多读者开始费力的回忆起了那篇Web-safe colors! 然而现在我们要剥离开那篇文章,我们一直被局限住,比如一直以来,HTML和CSS被认为只能在sRGB色彩空间里工作。就像上文中hober鞋子的那个例子中所说的那样,CSS、图片和canvas还有很多色彩无法表现出来。因此当你在春天向你家人展示花园里盛开的花朵,或者想买一辆亮红色跑车来缓解你的中年危机时,就会因为色彩显示而产生问题。

当我们在sRGB显示器上展示上文中提到的那张鞋子的图片时,它会把超出sRGB色域的颜色压制在它能够显示的颜色当中,但是如果是像Display P3这种色域更广显示器就不需要这样做了。下面这张图对sRGB显示器和Display P3显示器色彩空间的不同做了一个直观的展示。

sRGB P3 gamut comparison

上图中彩色的三角形就是sRGB色彩空间范围。白色的三角形是Display P3色彩空间范围,很明显要比sRGB范围大,而且在红色、黄色、紫色和绿色方面具有更高的饱和度。而黑色的范围就是人的肉眼所能分辨的颜色范围。

还记得上文中带有不明显的WebKit标志的红色正方形吗?那张图是一张使用Display P3色彩空间的图片,先是填充rgb(255, 0, 0)红色,然后用另一种不同的红色rgb(241, 0, 0)绘制出logo。由于Display P3色彩空间中红色241以上的所有值超过了sRGB色彩空间的红色最高值,所以241~255的红色在sRGB色彩空间内是同一种颜色,因此在sRGB显示器上是无法看到这个logo的。

注:我在Twitter上看到有人对这个问题有一些困惑,所以在这里再另外解释一下。大致来说,Display P3色彩空间中值为241~255的红色在sRGB色彩空间内是难以区分的。但是这并不意味着Display P3中值为241的红色与sRGB中值为255的红色是一样的,这里面没有想象中的那么简单。我并不想在这篇介绍性的文章里讲太深。如果有人对此感兴趣,在macOS上有一款叫做 Color Sync Utility 的app,可以通过这款app转变不同的色彩空间或者色域来观察颜色的具体变化。

所以读完上文,你应当理解为什么要重视颜色,也应该懂得要通过运用这项技术为用户提供更好的体验。接下来让我们讨论一下在这个背景下对WebKit意味着什么。

图片配色

在上文中,我们提到网络上目前限定为使用sRGB色彩空间。在Mac上,WebKit的Safari浏览器也使用sRGB色彩空间数年了,因此可以通过不同的浏览器看到一致的颜色显示。(在写作本文时,其他大多数浏览器引擎对于颜色的显示也只是在设备的色彩空间内运行,在将颜色传达到显示硬件之前,是不对颜色值做处理的。)

WebKit在iOS和macOS上都可以对图片进行配色。只要图片带有颜色配置文件,无论是正常色域还是广色域,都能保证在显示器上正确的展示图片的颜色。如今数码相机不在生成的原始文件中使用sRGB标准,所以如果显示器还是一味的使用红绿蓝配色是不可能显示正确的颜色的。但通常情况下没有必要特意去配色,因为几乎所有的图片处理软件都默认可以对图片进行颜色文件配置。

结合上文中提到的例子,你应当了解,这种Safari浏览器所执行图片配色技术,在OS X 10.11.3以上的版本和iOS 9.3(Retina设备)以上的版本都是支持的。我们需要做的只是确保图片配置好相应的颜色配置文件。

如果图片没有相应的颜色配置文件,那么WebKit会用sRGB标准处理图片。这样来说,对于你所创作的作品,在边框和背景图片等方面,自然而然的匹配到你在CSS中的所做的颜色定义。这样来说,在CSS中定义一个rgb(255, 0, 0)将会匹配到相应的sRGB中的纯红色。

注:这里也有一个引发争论的点,一些人表示没有颜色配置文件的图片不应该默认用sRGB标准去处理。我们对上述问题给出的解释是:这样做会确保图片颜色与页面中CSS定义的颜色相匹配。

基于过去十年的显示器技术来说,这类方法是可以接受的。但是现在我们有了支持更广色域的显示器,谁不想更好的掌控自己要展示的内容呢。

显示检测

上文中我解释了为什么在广域显示器上应该更倾向于用广域图片服务用户。如果你为非广域显示器用户提供广域图片服务,那么WebKit会为这些图片配色并在sRGB色彩空间内显示这些图片。然而只能在某种程度上实现面向sRGB的转换,还不能保证其他浏览器或者平台能够完成同样的转换。作为一个精益求精的Web开发者或者设计师,应该倾向于更好的控制为终端用户提供的离线图片。另外,为图像嵌入颜色配置文件会使文件变大,所以没必要的话没人愿意去发送那些额外的数据。

这样来说,最好的解决方法就是,当用户使用广域显示器时为用户提供广色域图片,而当用户不使用广域显示器时就为用户提供sRGB图片。这也是一种响应式图片,元素和媒体查询正是为解决这种问题而被设计出来的。

WebKit now supports the (面向第四代CSS颜色模块 ) 色域 的媒体查询。下面是使用方法:

<picture>
  <source media="(color-gamut: p3)" srcset="photo-wide.jpg">
  <img href="photo-srgb.jpg">
</picture> 

也可以在外部样式表里使用:

.main {
  background-image: url("photo-srgb.jpg");
}

@media (color-gamut: p3) {
  .main {
    background-image: url("photo-wide.jpg");
  }
} 

也可以在脚本中使用:

if (window.matchMedia("(color-gamut: p3)").matches) {
  // 这里是漂亮的界面
} 

这种广色域查询支持在浏览器引擎和显示器硬件中以p3rec2020 作为条件值,同时这种查询针对性的模糊了系统对颜色范围的具体规定。默认情况下,几乎所有的显示器都支持sRGB标准,所以没有必要对上述功能进行测试。但是标准的广色域显示器具有支持或接近DCI P3空间的颜色范围,而且会进行媒体查询。举个例子,上文提到的Display P3色彩空间是DCI P3色彩空间的一个变种。rec2020色域值表明系统支持更广色域的显示,这种显示的色彩空间近似于Rec. 2020色彩空间(目前,网上支持Rec.2020的硬件是很少见的,所以不需要太多的担心这个问题)。

当媒体查询姗姗离去时,是时候在兼顾没有兼容的浏览器和硬件的用户的同时,尽快使用色域技术给广色域设备用户提供更好的颜色体验了。

未来发展方向

虽然提供和渲染广色域图片是比较容易实现的,但是当与其他页面元素(如背景色)结合起来,或者在canvas元素上绘图时,是不是就增加难度了呢?这也是标准机构努力克服的一些挑战。

CSS中的广色域颜色

我在上文中提到,Display P3中的rgb(241, 0, 0)相当于sRGB中的rgb(255, 0, 0)。那么当你想用CSS中定义的颜色去匹配广色域图片中的相应信息时应该这么做呢?无法实现,因为我们至今还无法再CSS中定义这些颜色。

下面是WebKit项目组成员对CSS做的解决方案。目前流行的思路是添加一个新函数color(),这个函数既可以读取颜色配置文件又可以用参数定义颜色。

/* 注: 语法预想,还未实现 。 */
strong {
  color: color(p3, 1.0, 0, 0); /* P3色彩空间的100%红 */
} 

在实际操作中,可能用到的是 @supports语法。

strong {
  color: rgb(255, 0, 0); /* sRGB空间的100%红 */
}

@supports (color: color(p3, 0, 0, 0)) {
  strong {
    color: color(p3, 1.0, 0, 0); /* P3空间的100%红 */
  }
} 

注:我曾经将这种语法写成color(p3, 255, 0, 0),这是一个严重的错误。这是现有rgb()函数中很伤脑筋的地方。新的函数color()放弃了8位参数作为作为参数,改为使用浮点数。

CSS会定义一些已知的颜色配置文件的名称,所以可以很容易找到想找的颜色值。因此对于是否允许作者引用外链配置文件,或者引用一些嵌有配置文件的图片都存在争议。

另外,CSS可能会决定允许在现有的rgb()函数中设置超出0-255(或则 0-100%)的参数。例如,rgb(102.34%, -0.1%, 4%)表示这种颜色:深红,淡绿,一丁点蓝。这种设置参数的方式会产生一些理解上的误导(比如一个参数为负的绿色是什么意思呢?)。

另一个提议是实现为整个文档定义一个色彩空间,这意味着所有的常规CSS颜色值都能在这个空间里演绎出来。这样嵌入配置文件的外部图像也能够匹配好颜色。

W3C的CSS工作小组目前正在进行这类问题的讨论。你的力量不容小觑——这个小组需要广大Web开发者的支持。如果你有兴趣参加,请以“[css-color]”为主题开头发邮件到 www-style email list.

WebKit将会在确认这些新特性稳定的前提下尽快去实施它们。

HTML中的广色域颜色

虽然CSS能够处理HTML文档中的大多数样式,但是在canvas元素这个重要的领域却超出了它的能力范围。2D和WebGL技术都是只支持在sRGB色彩空间内工作。这样来说即使在广色域浏览器上,也无法一个创建一个使用全范围颜色的画布。

目前提出的解决方案是对getContext函数加一个可选的flag,来确定画布应该选择哪一个色彩空间来进行配色。例如:

//注: 语法预想,还未实现。
canvas.getContext("2d", { colorSpace: "p3" }); 

这里还有一些其他的思考:如何创建一个有更大的色彩深度的画布呢。例如,在WebGL中你可以使用16位的半精度浮点纹理来创建画布。但是,即使你在WebGL规定内使用更深层次的纹理,你向文件中写入WebGL时也会被局限在8位精度。

因此开发者需要一种规定画布元素色彩深度的缓冲区。

但这样的话,当结合 getImageData/putImageData函数(或者WebGL中的readPixels )时,就会变得复杂起来。虽然8位元缓冲区的应用使画布的输入输出都不会造成精度的缺失,画布和程序方数据都是相同的类型,无论在性能还是存储上,这类转换也将会很有效率,但是一旦使用了可变的色彩深度,这一切将很难实现。例如,WebGL的半精度缓冲区在JavaScript上是没有对应的类型的,这意味着当进行数据的读写时,有一方必须做出转换,用更多的空间去存储数据,或者去操作ArrayBuffer并做繁琐的位掩码运算。

这些讨论每时每刻都在WhatWG github 中进行,很快将蔓延到W3C。再次热烈欢迎大家加入到讨论中来。

总结

广色域显示器的使用是计算机设备的未来趋势,现在正在逐步应用。随着越来越多的用户开始使用这种精致的显示器,选择使用这项拥有前所未有的颜色范围的显示技术无疑会为你的用户提供一个更美好的Web体验。

WebKit非常高兴通过介绍颜色匹配和色域检测为开发者描述了色彩改进后的新特性,这些新特性在目前的Safari Technology Preview以及macOS Sierra和iOS 10 betas都可以体验到。我们也希望包括在CSS内指定广色域颜色、用配置文件标记canvas元素以及扩展色彩深度在内的更多的先进颜色特性能够开始应用。

如果你有任何建议、疑问或者有趣的想法,都可以通过dino@apple.com@grorgwork联系我。当然也可以通过web-evangelist@apple.com@jonathandavis联系Jonathan Davis,或者向 @webkit发一些日常的碎碎念。