Richard Bao 的显示图片
Richard Bao
La vie est belle !
日志 - 2007年12月
2007/12/27 23:50:15

关于色彩模型的最新成果

已经有一个多星期没有写开发日志了,不过这并不表示没有任何进展,相反,在这一周中,色彩模型已经经历了几次重大的变化。

首先,考虑下面几个基本的问题:

  1. 索引应当是一种存储格式,而不是颜色模式。
  2. 每个颜色数据还应带有一个 Alpha 值。
  3. 只有 Lab 颜色(甚至可以采用 32 位)是绝对的,其它的颜色都不能独立存在,而需要色彩管理文件。

下面来慢慢讨论:

之前混淆了颜色与像素的概念,导致一直将索引模式也作为颜色模式来考虑,后来才发现这完全错误了。Photoshop 的颜色模式只是它的工作模式,表示像素的存储方式,而真正的颜色模式并没有那么丰富——就算是索引模式,它最后还是需要指向具体的颜色信息。因此,索引模式只是使用索引方式来存储 RGB/8 颜色信息而已。这样一来,所有的颜色都将归结为“通道色”的概念,每个颜色由一个或多个通道组成,各通道采用相同的颜色位深度,具有各自的连续取值范围。

除了本身的通道之外,每个颜色数据还带有 Alpha 值。在存储上,Alpha 值与其它通道值(如 R、G、B)是完全平等的,有自己的取值范围,与其它通道使用相同的位深度。如果也将其视为一个通道,那么可以与其它通道使用相同的处理机制,看起来是一个很不错的选择。然而,很快这种想法就遇到了麻烦:事实上,当两个颜色进行混合时,Alpha 值和通道值并不平等:

R = R' * alpha + R * (1 - alpha)
G = G' * alpha + G * (1 - alpha)
B = B' * alpha + B * (1 - alpha)

也就是说,Alpha 值与其它颜色分量通道的实质并不相同,它是一个“附加值”。

不同的颜色模式之间的转换好像很容易,其实不然。虽然网上有很多转换公式,但几乎没有人提到色彩管理文件的概念。事实上,如果没有色彩管理文件,RGB 与 CMYK 颜色之间根本没有转换关系,因为它们都只是一个相对颜色。这样一来,我们的模型中必然要再引入色彩管理文件的概念,而不同颜色模式之间的转换是由相应的色彩管理文件进行的。于是,与颜色值本身相关的有两套类型:颜色模式(ColorMode)与色彩管理文件(ColorProfile)。ColorMode 用于定义混合颜色所用的通道信息,包括每个通道的名称、位深度、取值范围等等;而 ColorProfile 用于定义颜色模式与 Lab 绝对颜色之间的映射关系。

颜色模型之所以难以处理,其实并不在于 RGB、CMYK 等各种模式。当“通道色”概念出现以后,所有的不同模式都已经化归为一了。真正的麻烦在于颜色深度:除了位图是 1bpc(bit per channel)以外,还有 8bpc、16bpc 和 32bpc。当返回通道的值的时候,它们对应着完全不同的数据类型(Byte、Int16、Single)。返回类型是没法多态的,而这三种数据类型之间又存在的本质的差别(整数与浮点),无法在字节层面上统一运算。颜色概念本身的抽象、颜色类型的复杂度及处理数据的性能问题之间产生了巨大的矛盾。为了解决这个矛盾,我决定将浮点改为整型,将 32bpc 的颜色值使用 Int32 来存储和运算——事实上并不会丢失精度。于是,产生了一个特别的想法:将所有颜色的数据都视为连续的 Int32 数组,然后通过为每个精度设置掩码和偏移量,用位运算得到具体的通道分量。例如:ARGB 色 0xFF336699,R 通道的掩码就是 0x00FF0000,偏移量即 16,这样,就有:

r = (value & mask) >> offset

这个方式把位数长度也化归了,我曾一度认为这是一个完美的解决方案。然后很快就出现了新问题,像 CMYK 这样的颜色模型,加上 Alpha 通道就是 5 个分量,如果使用刚才的方式,一个颜色占用 8 个字节(2 个 Int32 长度),但却只有 5 个字节用来保存信息,3 个字节被严重浪费了。

带着这个问题,我又去研究了 Photoshop 的存储行为,想看看它会不会产生这种因字节对齐而导致的空间浪费。结果自然是没有,一个 100 像素的单层图像,RGB 模式下就是 400 字节(还要加上透明度),CMYK 模式下就是 500 字节,完全没有浪费。而更令我吃惊的是:如果图像并没有用到每个通道,或者是某个通道的内容完全是纯色的时候,这个通道根本不产生数据!也就是说,如果 RGB 模式下某图像的 R 通道内亮度没有任何变化的话,那就只有 300 字节的内存使用;如果连透明度也没有的话,就只有 200 字节的内存使用。这个现象证明 Photoshop 是以通道为单位来存储位图的,而不是像素。说得清楚一些就是:它将位图分成了若干个通道,每个通道是一个只包含亮度信息的灰度位图。

于是,颜色及位图的模型将再次经历一次洗礼。

最后更新: 2007/12/27 23:50:15 | Windows Live Space 网站中的链接 添加评论 此链接将在新窗口中打开 | 评论 评论 (1) | Windows Live Space 网站中的链接 原文地址 此链接将在新窗口中打开
2007/12/20 22:47:12

颜色类型的关系

既然我们打算未来要支持那么多种颜色模式,那么用于表示颜色的也不可能像 GDI+ 那样一个单单的存储 8 位 ARGB 颜色的 Color 类型。很自然会想到,以 Color 类型为基类,派生出 RGB、CMYK、Lab 等各种具体的颜色模式。Color 类自身只需要维持一个用于保存实际颜色数据的字节数组的引用即可——它具体可以由各派生类来创建,因为不同的颜色模式和位数所需要的数据块长度不同。然后各个派生类从这个数据块中取出自己需要的部分。比如:表示 8 位 A-RGB 颜色的类可以创建一个 4 字节的数据块(data),然后将 data[0]、data[1]、data[2] 和 data[3] 分别表示 B、G、R、A 四个分量;而表示 16 位 A-CMYK 颜色的类则可以创建一个长度为 10 个字节的数据块,data[0] ~ data[1]、data[2] ~ data[3]、data[4] ~ data[5]、data[6] ~ data[7] 和 data[8] ~ data[9] 分别表示 K、Y、M、C 及 A 五个分量值。

问题产生于不同的颜色模式既可以按照色彩空间来分类,也可以按照颜色深度来分类。同样是 RGB 颜色模式,有 8 位、16 位、32 位三种。而同样是 16 位颜色深度,有 RGB、CMYK、Lab 三种。我们总是希望所有的类型最后都可以抽象为单独的 Color 类型,在绘图时,并不需要关心具体的颜色模式,只要相互一致即可。然而,C# 并不支持多重继承,这使得各颜色类型的关系建立非常困难。此外,还遇到了这样几个问题:

首先,各种色彩的绘制是按通道进行的,如果不考虑 Alpha 通道,那么只要简单的将新的通道值替换旧的即可。如果考虑 Alpha,则进行一个简单的混合运算。麻烦即在于,Color 基类拥有的仅仅是一个连续的字节数组,它无法知道自己包含多少通道,每个通道的字长是多少。

其次,当外界在大多数情况下只考虑 Color 这个基类型时,如何进行任意色彩模式之间的转换?不同的颜色空间,以及不同的颜色深度,都将影响色彩的转换。

除了大部分通道色之外,索引模式是不以通道进行处理的,它的像素点记录的也不是颜色信息,而是调色板编号。这将如何统一?索引到底应当视为颜色模式还是图像格式?

最后,跳出这堆类型,从外部来考虑,绘图软件在处理具体一幅图片的时候,其实是知道这张图片使用什么像素格式的,相关功能是否可用、通道面板里显示哪几个通道、信息面板以哪种方式显示真实色彩等等都是与图片的像素格式直接相关的。那么外部在调用这些色彩类型时,究竟有多少情况下会使用基类型,而多少情况下会指定具体的类型?如果经常需要检查类型,依靠反射肯定不是解决问题的办法。这样一来,这套色彩类型模型又将变成什么样?

最后更新: 2007/12/20 22:47:12 | Windows Live Space 网站中的链接 添加评论 此链接将在新窗口中打开 | 评论 评论 (6) | Windows Live Space 网站中的链接 原文地址 此链接将在新窗口中打开
2007/12/19 19:43:53

关于颜色模型的考虑

在整理功能时,突然意识到了 GDI+ 的一个重大限制:它仅仅支持 RGB 颜色模型。而平面绘图软件,至少应当是支持 RGB 和 CMYK 色彩空间的,GDI+ 由于仅仅用于屏幕绘图,仅需要与显示设备打交道,此根本没有考虑过 RGB 之外的色彩模型。这对于我们的 OpenPainter 来说,是一个很大的限制。

Photoshop 支持位图、灰度、RGB、CMYK、HSB、Lab、多通道及索引模式这几种颜色模式,对于灰度、RGB、CMYK、HSB、Lab及多通道这几种模式来说,还分 8 位、16 位、32 位三种色彩深度。我总是希望能够尽可能地利用 GDI+ 的既有功能,因此开始分析这些不同的模式。虽然说 RGB、CMYK、HSB、Lab 这些颜色空间的运算法则完全不同,但有一点是相同的,即它们都是由多个互相独立的通道组成。事实上,如果不考虑将图片显示出来的话,仅仅是绘图操作与保存,不同的颜色模式之间没有任何区别——无非是将每个像素根据通道数拆分成几个子像素,每个子像素根据所属通道的不同具有自己的取值范围而已。基于这个结论,我们可以将任何一种以通道为基础的颜色空间视为 n 个大小相同的灰度图像的组合。例如 RGB-8bits 图像即是由三个同尺寸的 8 位灰度图像合成的,分别代表 R、G、B 三个通道的亮度;而 CMYK-16bits 图像即是由四个同尺寸的 16 位灰度图像合成的,分别代表 C、M、Y、K 四个通道的颜色深度等等。

这么做其实可以很好地解决任何一种通道色彩模型,但限制很快又出现了,GDI+ 并不支持 8 位的灰度图像,而 8 位颜色深度是目前最为常用的。这使得我们几乎无法用刚才的方式来模拟通道色彩模型,而且每个通道单独绘制,也需要反复进行子像素亮度提取与绘制操作。无论是光栅位置的计算还是色彩混合操作次数都成倍增长,使 GDI+ 原本就不好的性能损失更大。

由于无法解决色彩模型这一根本限制,我们不得抛弃 GDI+,考虑基于 GDI 开发自己的图形库。这将意味着我们必须自己实现形状绘制、文本绘制、反走样、色彩混合、图像变换等基本的图形处理功能。鉴于这将是一个巨大的工程,我们可能在第一个公开预览版本中只支持 RGB-8bits 模式,继续使用 GDI+ 作为图形库。并在软件成型的基础上,逐步将其替换出来。

最后更新: 2007/12/19 19:48:30 | Windows Live Space 网站中的链接 添加评论 此链接将在新窗口中打开 | 评论 评论 (3) | Windows Live Space 网站中的链接 原文地址 此链接将在新窗口中打开
2007/12/15 22:04:05

OpenPainter 进度(Dec. 15)

首先,OpenPainter 仍在按照预想的计划进行着。因为按照规划,还需要相当长的一段时间才会有阶段性的成果,所以先发一篇解释一下进度。

初步的时间点是这样的:首先是详细研究 Photoshop 的所有功能点,我们将会基于 Photoshop CS3 版进行详细的功能罗列与整理,以及一些基本功能的技术尝试,这个时段将从立项起一直持续约两个月。紧接着,我们会从所有的功能点中圈出将在第一版中实现的部分,以及在架构上需要预留的功能部分,这个工作大约在 2008 年春节前结束。年后,将进入正式的架构设计阶段,这一阶段所需要的时间目前无法确定,考虑到前期已经进行了一部分技术尝试,对架构设计有一定的积级作用,这段时间可能将持续一个半月至两个月左右(可能会在同时撰写技术文档,以便他人分析软件架构)。之后将进入编码与测试工作阶段,预计在 2008 年底或 2009 年初发布第一个预览版本。

时间点 阶段工作
立项后两个月 功能罗列、技术尝试
2008 年春节前 功能点版本定义
2008 年春节后两个月 架构设计及文档撰写
2008 年底或 2009 年初 发布第一个预览版本

功能的罗列文档已经可以在 SourceForge.NET 上 OpenPainter 的 SVN 上获取。

最后更新: 2007/12/15 22:04:05 | Windows Live Space 网站中的链接 添加评论 此链接将在新窗口中打开 | 评论 评论 (3) | Windows Live Space 网站中的链接 原文地址 此链接将在新窗口中打开
2007/12/8 13:06:11

SourceForge.NET 上已立项

昨天晚上看到在 SourceForge.NET 上的 OpenPainter 立项申请已经获批。因为公司一直是在用 CVS,自己对 Subversion 没什么了解,工具也不习惯。不过捣鼓了一会儿,现在已经基本明白了。

项目有了名字,也有了可共享同步的服务器,可以开工了。在 SourceForge 上做比较正规些,也容易和世界各地的人交流。麻烦就是,为了无障碍交流,回到家还得继续写 English Document...,中文还只能算是 Language Pack。

SourceForge.NET 地址:http://sourceforge.net/projects/openpainter

有人愿意加入么?嘿嘿~

最后更新: 2007/12/8 13:06:11 | Windows Live Space 网站中的链接 添加评论 此链接将在新窗口中打开 | 评论 评论 (4) | Windows Live Space 网站中的链接 原文地址 此链接将在新窗口中打开
2007/12/6 20:21:10

名字就叫 OpenPainter 吧

起名字确实是很麻烦的事情,因为它需要满足至少以下三点:

  1. 英文名字易认易记
  2. 英文单词对于中国人来说认知率比较高……怎么说也得属于四级词汇里的吧……
  3. 主要的域名未被注册

昨天想了一晚上头痛得要死,倒是今天早上突然想到 Open 系列,一查,居然所有叫 openpainter 的域名都是开放的。经 Steve 和 Jordan 首肯,决定就这么叫着了。

回来就去注册了 openpainter.com、openpainter.net 和 openpainter.org,.cn 的域名不要了,没什么意义。有了名字,SourceForge 上也可以立项了。

最后更新: 2007/12/6 20:21:10 | Windows Live Space 网站中的链接 添加评论 此链接将在新窗口中打开 | 评论 评论 (1) | Windows Live Space 网站中的链接 原文地址 此链接将在新窗口中打开
2007/12/5 21:30:54

启动生平一个开源软件

其实最初的动力来自于在公司没有 Photoshop 可用,导火索是 Microsoft 扶持的那个免费开源的 Paint.NET 可用性实在差到无法忍受。其实很早之前就想过自己做一个玩玩,但是一直只是个想法罢了,今天无意中和同事们一说,却得到了积极的回应。自己一个人做也许无聊,但有一帮热心的人一起动手就不同了。于是,从今天(2007/12/5)开始,一个类似 Photoshop 的开源象素绘图软件项目启动。除了我之外,Steve Gu 和 Jordan Zhou 也从第一天开始正式加入。所谓三人成伙,不过我相信随着项目的成长,参与的人会越来越多。我也相信,在没有任何外部压力的情况下,我们能够开发出具有商业软件的质量的产品——虽然这只是一个免费的开源项目。

而且遇到的第一个难题居然是:想不出来这个项目叫什么名字好。

最后更新: 2007/12/6 19:34:21 | Windows Live Space 网站中的链接 添加评论 此链接将在新窗口中打开 | 评论 评论 (4) | Windows Live Space 网站中的链接 原文地址 此链接将在新窗口中打开