原创文章 · 图片与色彩

CMYK图片在浏览器里怎么准确转成RGB

Jinlab · 2026年10月1日 · 基于真实开发与真机测试整理

设计师交来的印刷稿、从印刷厂拿到的文件,常常是CMYK模式的JPEG。很多网页工具遇到它要么直接拒绝,要么交给浏览器去转换,转出来的颜色却不太对。这篇文章记录我们做图片工具pixforge时的实测:浏览器自带的转换错在哪里,我们换成什么办法,以及在手机上能撑到多大的图。

为什么CMYK是个麻烦

屏幕用红、绿、蓝三色光叠加发光,所以网页和手机图片基本都是RGB。印刷用青(C)、品红(M)、黄(Y)、黑(K)四种油墨叠在纸上,所以印刷文件常用CMYK,每个像素有四个数字,而不是三个。

CMYK不能直接显示在屏幕上,必须换算成RGB。难点在于:同样的四个数字,印在铜版纸上和印在报纸上,实际看到的颜色并不一样。所以CMYK文件通常会附带一份ICC色彩配置文件,说明"这些数字对应哪种纸、哪种油墨的效果"。换算时要拿这份配置当依据,才能算出屏幕上应该显示的颜色。

浏览器对CMYK的支持一向不太统一。于是网页图片工具遇到CMYK,最省事的办法就是拒绝。我们最早也是这样做的:先拦住,在提示里写明"这是CMYK图片",免得用户以为是文件坏了。

浏览器自带的转换:纯黑墨区域偏棕

后来我们想放开CMYK,最初的设想是交给浏览器自己转,因为浏览器本来就有色彩管理能力。动手之前,我们先做了一次对比实测。

测试文件是一张带FOGRA39L_coated色彩配置的CMYK JPEG(FOGRA39是欧洲常用的铜版纸印刷标准),我们把它交给桌面版Chromium转换,再和开源色彩引擎LittleCMS的结果逐区域比较:

  • 三块彩色区域,浏览器与LittleCMS的结果相差0到3(满分255),可以认为一致。
  • 只有黑墨、没有其他三种油墨的区域(四个通道的数值是0、0、0、120),浏览器转出来是(155, 138, 133),一个偏棕的灰色;LittleCMS转出来是(163, 164, 164),是中性的灰。
  • 我们把渲染意图的四种取值都试了一遍,浏览器的结果始终对不上,最大偏差在25到32之间。

也就是说,问题不在"颜色超出了RGB的范围",而是转换本身在中性灰和纯黑墨这类区域不准。这类区域恰恰是文字、线条、黑白照片里最常见的颜色,偏棕会让一张黑白印刷稿整体发黄,一眼就能看出来。

教训:我们原本给自己的方案写的是"交给浏览器准确转换,只有极少数超出sRGB的颜色会被轻微压缩",这句话是凭印象写的。实测才发现并不成立,好在是在上线之前查出来的。以后凡是要写"准确""无损",都先用参考实现做全图对比,并且专门看中性灰和纯黑墨这些最容易出问题的区域。

我们的做法:自己解码,自己换算

既然浏览器靠不住,就绕开它,自己完成两步:

  1. 取出原始的四通道数据。我们用PDF阅读器pdf.js里的JPEG解码器(Apache-2.0许可),它能直接给出CMYK的原始数字,而不是浏览器换算后的RGB。Adobe写法的CMYK和YCCK两种常见编码都支持。
  2. 用LittleCMS做颜色换算。我们使用的是编译成WebAssembly的lcms-wasm(MIT许可),在浏览器里按图片自带的ICC配置换算成sRGB。换算用相对比色,并开启黑场补偿,这与Photoshop的默认设置一致。

预览和导出用的是同一份换算结果,所以你在屏幕上看到什么,导出的就是什么。我们拿LittleCMS的参考结果和我们的输出做了全图比对:最大差不超过3,平均差小于0.04,纯黑墨区域保持中性灰,不再偏棕。

有一种情况我们仍然拒绝:没有Adobe标记的四通道JPEG。这种写法很少见,我们没法可靠判断它的数值含义,猜错了颜色就全错。我们只读文件头就判断出来,不会白白解码整张图再失败,并且会在提示里写明原因。

没带色彩配置的CMYK怎么办

并不是每份CMYK文件都附带配置。没有配置,就没有"这些数字对应什么纸和油墨"的依据,只能选一个默认标准。我们选了FOGRA39,这是欧洲铜版纸印刷的通用标准,对应的配置文件来自开源项目colord(CC0-1.0许可),只有遇到这类图片时才会下载,带了配置的图片不会多下载任何东西。

这个默认选择准不准,我们同样先测了。用色差ΔE2000来衡量(数字越小越接近,通常认为2左右以内普通人很难察觉):

  • 如果原稿确实是按铜版纸印刷准备的,平均色差只有1.6到2.7。
  • 如果原稿是为胶版纸或报纸准备的,平均色差约10到15,会有明显偏差。
  • 作为对照,浏览器原本的做法平均色差约17,但这个数字用的是更老的ΔE76公式,和上面ΔE2000的数字不能直接相比。

所以这个默认选择不是完美的:对铜版纸稿,它已经相当接近;对胶版纸或报纸稿,偏差仍然明显。对这类图片,我们在界面上给出一条温和的提示,说明"没有色彩配置,按铜版纸标准估算",不隐瞒这是一次估算。在你同意统计的前提下,我们还记录一个匿名标志,只记"这次打开的CMYK图有没有带配置"这一件事,用来了解这种情况到底有多常见。

手机上能撑到多大

CMYK每个像素有四个数字,换算又要经过解码、色彩换算、显示三步,内存和耗时都比普通图片大得多。我们在一台4GB内存的华为手机(浏览器内核为Chromium 132)上,用带配置的CMYK JPEG实测了三种尺寸:

图片大小打开用时内存峰值导出PNG
1200万像素11秒223MB2.8秒
2000万像素13秒350MB4.1秒
2400万像素15秒397MB4.7秒

三档都没有崩溃,不过打开确实要等十几秒。根据这组数据,我们把CMYK图片的上限定为2400万像素:超过上限,或者读不出尺寸的CMYK图,在解码之前就拦下并说明原因,而不是让手机卡死。没带配置的1200万像素图片复测同样是11秒、内存峰值224MB。

这些数字只对这一台设备成立。内存更小的手机(2到3GB)我们没有测过,如果以后CMYK解码的代码有改动,也需要重测。

失败时,说出真实原因

转换组件是后加载的,可能因为网络问题加载失败;也可能因为手机内存不够、文件损坏或者写法少见而失败。我们给每种失败各写了一条具体的提示,比如"转换组件没加载成功,可以重试"和"内存不够",不会笼统地说"出错了"。用户知道原因,才知道该重试、换文件还是换设备。

还没测到的地方

  • iPhone上的Safari我们没有测过浏览器自带转换的表现,上面的偏棕结论来自桌面版Chromium。
  • 浏览器的色彩管理以后可能会改进,结论只对我们测试时的版本成立。
  • 上面的色差数字来自我们自己的测试图,不同来源的印刷稿可能有出入。

如果你也在做网页上的图片处理,我们的建议很简单:遇到"浏览器应该能搞定"的色彩问题,先拿参考实现做一次全图对比,再决定要不要信它。

这套CMYK处理已用在Jinlab的图片工具pixforge里,打开CMYK图片即可使用。相关文章:手机拍的广色域(Display P3)照片,为什么在网页里处理后颜色变淡?怎么保住?