计算机系统解密:从理解计算机到编写高效代码
上QQ阅读APP看书,第一时间看更新

1.12 表示颜色

数字的另一个常见用途是表示颜色。大家知道,数字可以用来表示图形中的坐标。计算机图形学通过在电子图纸上绘制色块的方式绘图。每一对坐标上的色块被称作像素

计算机显示器通过使用RGB颜色模型,将红、绿、蓝三色光混合在一起产生各种颜色,RGB模型的名字为红绿蓝三色首字母组成。颜色可以用一个颜色立方体来表示,其中每个轴代表一个原色,如图1-18所示。值为0表示该颜色的光亮度为0,值为1表示该颜色的亮度达到了极限。

图1-18 RGB颜色立方体

可以看到,如果没有任何一个色灯亮着,则产生黑色,如果所有色灯的亮度都处于最大,则产生白色。如果只有红灯亮起,就会产生红色的阴影。将红色和绿色光混合在一起会产生黄色。将三原色的色灯都设置为同一水平的亮度会产生灰色。由于添加不同的原色会产生不同的颜色,这种混合颜色的方式称为加色法系统。图1-19展示了颜色立方体中几种颜色的坐标。

图1-19 RGB颜色立方体示例

如果你曾经尝试过绘画,你可能对减色法系统比较熟悉,在这个系统中,三原色是蓝绿、品红和黄色。减色法系统是通过去除白光中的波长来产生不同颜色,而不是像加色法系统通过增加颜色来得到不同颜色。虽然这两种颜色系统都不能产生出所有人眼可见的颜色,但减色法系统能比加色法系统产生更丰富的颜色。一整套印前技术的存在,使那些在计算机显示器前操作的艺术家们的设计在印刷到杂志上时仍能保持颜色无偏差。如果你对色彩感兴趣,推荐你阅读Maureen Stone的A Field Guide to Digital Color

人眼是非常复杂的机器,它为了生存而进化,并不是为了计算而进化的。人眼可以分辨出大约1 000万种颜色,但人眼的分辨能力不是线性的;光照度增加一倍并不一定会导致人眼能感知到的亮度增加一倍。更糟糕的是,随着时间的推移,眼睛的反应会随着整体光照亮度的变化而缓慢改变。这就是所谓的暗适应。而且人眼对不同颜色的反应不同,眼睛对绿色的变化非常敏感,对蓝色的变化相对不敏感,国家电视系统委员会(National Television System Committee, NTSC)标准就利用了这一现象。现代计算机选择将1 000万四舍五入到最接近2的幂,并使用24位来表示颜色。这24位被分成3个8位的字段,每种原色占有一个字段。

可以看到,表1-10中没有给出24位的名称。这是因为现代计算机并不是设计为在24位的单元上运行的(尽管还是有些24位的机器,比如霍尼韦尔公司的DDP-224)。因此,表示颜色的位按照最接近的标准尺寸——32位(长字)——封装,如图1-20所示。

图1-20 RGB颜色封装

可以看到,这个封装标准下每种颜色都会留下8个未使用的位。8位一个很大的数量,考虑到现在的计算机显示器有超过800万的像素,我们不能浪费这些位,那么可以用这8个位做什么?答案是,我们可以用这8个位来表示一些上文中没提到的:透明度(也就是能“看穿”颜色的程度)。到目前为止,我们只讨论过不透明的颜色,可是这些不透明的颜色不能用于制作玫瑰色眼镜等物品。

1.12.1 增加透明度

在早期的动画电影中,每一帧都是手绘的。这不仅是一项非常繁重的工作,而且由于不可能在每一帧上精确地重现背景,还会有很多视觉“抖动”。美国动画师John Bray(1879—1978)和Earl Hurd(1880—1940)在1915年发明了“赛璐珞动画”,解决了这个问题。在“赛璐珞动画”中,移动的角色画在透明的赛璐珞片上,且可以在静态背景图像上移动。

虽然计算机动画的起源可以追溯到20世纪40年代,但它真正腾飞是在20世纪70年代和80年代。当时的计算机速度还不够快,无法实现电影导演想要的效果。而且需要一种机制将不同算法产生的对象组合起来。就像“赛璐珞动画”一样,透明度允许影像合成,或者将不同来源的图像组合在一起。如果你用过GIMP或Photoshop这样的图像编辑器,可能对图像组合这个概念很熟悉。

1984年,卢卡斯电影公司的Tom Duff和Thomas Porter发明了一种实现一定透明度和影像合成的方法,这种方法后来成了行业标准。他们给每一个像素添加了一个透明度值,称为αα是一个介于0和1之间的值,其中0表示颜色完全透明,1表示颜色完全不透明。一组合成代数方程定义了具有不同α的颜色如何结合产生新的颜色。

Duff和Porter的实现方式很巧妙。他们没有使用浮点系统,而是用数字255表示α的值为1,充分利用了图1-20所示的多出来的8位。Duff和Porter用各颜色的值乘以α存储颜色,而不是直接存储红色、绿色、蓝色值。例如,如果颜色为中等红色,那么它对应的红色值为200,绿色值和蓝色值为0。如果是不透明的红色,那么红色值为200,因为α为1(α值用255表示)。但是半透明的中等红色的α将是0.5,所以储存的红色的值为200×0.5=100,存储的α为127(255×0.5≈127)。图1-21展示的是带透明度值α的像素的存储安排。

图1-21 RGB α颜色封装

因此,合成图像需要将颜色值乘以α。以预乘形式存储颜色意味着我们不需要每次在使用像素点时都做一次这样的乘法。

1.12.2 编码颜色

因为网页主要是文本文档,这意味着它们往往是用UTF-8编码的人类可读字符序列,我们需要一种方法使文本可以表示颜色。

我们用类似于URL编码的方式来实现文本表示颜色,使用十六进制三联体指定颜色。十六进制三联体是一个#后面有6个十六进制的值。其格式为#rrggbb,其中rr是红色值,gg是绿色值,bb是蓝色值。例如,#ffff00为黄色,#000000为黑色,#ffffff则是白色。三个8位颜色值中的每一个都转换为两个字符表示的十六进制值。

虽然α在网页中也可用,但没有简明的格式可以表示它,它的表示完全使用了另一套方案。