纯 MoonBit 实现的 ICC 色彩管理引擎:解析 ICC profile 并执行真实色空间变换

| 已有 | 缺什么 |
|---|---|
| 色彩空间数值转换(sRGB↔HSL↔XYZ) | 按 ICC profile 做设备相关变换 |
| 能解析 ICC profile 元数据的库 | 用 profile 做实际色变换 |
| 图像编解码 | 图像进入/离开色彩工作流时的色彩管理环节 |
moon add qqq66668/moon_iccimport {
"qqq66668/moon_icc",
}let data = @fs.read_file_to_bytes("sRGB Color Space Profile.icc")
let header = @moon_icc.parse_header(data)
println(header.device_class) // "mntr"
println(header.color_space) // "RGB "
println(header.pcs) // "XYZ "
println(header.date_time.to_iso_string()) // "1998-02-09T06:49:00"
let tags = @moon_icc.parse_tag_table(data, header)
for tag in tags {
println("\{tag.signature} 偏移=\{tag.offset} 长度=\{tag.size}")
}let data = @fs.read_file_to_bytes("sRGB Color Space Profile.icc")
let profile = @moon_icc.Profile::parse(data)
let transform = profile.matrix_trc()
// sRGB 中灰 (0.5, 0.5, 0.5) → PCS XYZ
let xyz = transform.to_pcs(0.5, 0.5, 0.5)
// xyz = (0.20640003241890215, 0.21403939749252843, 0.17660683523992873)
// 再转到 Lab
let lab = @moon_icc.xyz_to_lab(xyz)
println(lab.l) // 53.585...let src = @moon_icc.Profile::parse(@fs.read_file_to_bytes("sRGB Color Space Profile.icc"))
let dst = @moon_icc.Profile::parse(@fs.read_file_to_bytes("display.icc"))
// 媒体相对比色意图
let link = @moon_icc.ColorTransform::new(src, dst)
let (r, g, b) = link.transform(0.5, 0.5, 0.5)let rgb = @moon_icc.Profile::parse(@fs.read_file_to_bytes("sRGB Color Space Profile.icc"))
let cmyk = @moon_icc.Profile::parse(@fs.read_file_to_bytes("printer.icc"))
let link = @moon_icc.ColorTransform::new(rgb, cmyk)
// 源 3 通道 → 目标 4 通道,用通道数无关的 transform_values
let out = link.transform_values([ 0.5, 0.5, 0.5 ]) // 4 个 CMYK 分量moon run cmd/main --target wasm-gc -- link testdata/srgb.icc testdata/display.icc
moon run cmd/main --target wasm-gc -- link testdata/srgb.icc testdata/cmyk_printer.iccsrc_channels 3
dst_channels 3
link 0 0 0 0
link 1 9 9 9
...src_channels 3
dst_channels 4
link 0 0 0 0 255
link 1 1 2 1 255
...源设备值 → PCS → 打样设备值 → PCS → 显示设备值
A2B B2A A2B B2Alet chain = @moon_icc.ColorTransform::proofing(
@moon_icc.Profile::parse(@fs.read_file_to_bytes("sRGB Color Space Profile.icc")),
@moon_icc.Profile::parse(@fs.read_file_to_bytes("printer.icc")), // 打样:CMYK 印刷机
@moon_icc.Profile::parse(@fs.read_file_to_bytes("display.icc")), // 显示设备
1, // 源/显示侧意图:媒体相对比色
1, // 打样意图:也可用 3(绝对比色)
)
let (proof_values, shown) = chain.evaluate([ 0.5, 0.5, 0.5 ])
// proof_values = 印这张图会用到的 CMYK 四个分量
// shown = 屏幕上应显示的 RGBmoon run cmd/main --target wasm-gc -- proof testdata/srgb.icc testdata/cmyk_printer.icc testdata/display.iccproof_intent 1
proof_channels 4
display_channels 3
proof 0 | 0 0 0 255 | 41 41 40
proof 51 | 13 17 16 224 | 61 60 59let source = @moon_icc.Profile::parse(@fs.read_file_to_bytes("sRGB Color Space Profile.icc"))
let target = @moon_icc.Profile::parse(@fs.read_file_to_bytes("printer.icc"))
// 建边界:CMYK 用 9 层采样 = 6561 个设备值 → A2B → Lab
let boundary = target.gamut_boundary(1, 9)
println(boundary.samples()) // 6561
println(boundary.coverage()) // 0.83...(CMYK 色域不填满 Lab 的所有方向)
// 判一个颜色(sRGB 纯绿)
let lab = @moon_icc.xyz_to_lab(source.device_to_pcs(1, [ 0.0, 1.0, 0.0 ]))
println(boundary.contains(lab)) // false —— SWOP CMYK 覆盖不了
println(boundary.signed_distance(lab)) // 18.70...(越界了多少,Lab 单位)| 指标 | 含义 | 用途 |
|---|---|---|
| GamutBoundary::contains / signed_distance | 到设备色域边界的几何位置 | 色域判定 |
| Profile::round_trip_delta_e | A2B 与 B2A 的互逆程度 | 设备特性/打样质量的映射误差指标 |
moon run cmd/main --target wasm-gc -- gamut testdata/srgb.icc testdata/cmyk_printer.iccsectors 16
samples 6561
coverage 0.94140625
gamut 0 0 0 0 boundary=out dist=8.59 mapping_de=12.88 ← 纸到不了 L*=0
gamut 1 255 255 255 boundary=out dist=5.69 mapping_de=3.42 ← 超出纸白边界
gamut 3 0 255 0 boundary=out dist=18.70 mapping_de=55.83 ← SWOP 覆盖不了 sRGB 纯绿moon run cmd/main --target wasm-gc -- parse testdata/srgb.icc
moon run cmd/main --target wasm-gc -- transform testdata/srgb.icc
moon run cmd/main --target wasm-gc -- link testdata/srgb.icc testdata/display.icc
moon run cmd/main --target wasm-gc -- link testdata/srgb.icc testdata/cmyk_printer.icc
moon run cmd/main --target wasm-gc -- link testdata/srgb.icc testdata/cmyk_printer.icc saturation
moon run cmd/main --target wasm-gc -- link testdata/srgb.icc testdata/cmyk_printer.icc absolute
moon run cmd/main --target wasm-gc -- proof testdata/srgb.icc testdata/cmyk_printer.icc testdata/display.icc
moon run cmd/main --target wasm-gc -- gamut testdata/srgb.icc testdata/cmyk_printer.icc
moon run cmd/main --target wasm-gc -- lut testdata/cmyk_printer.icc
moon run cmd/main --target wasm-gc -- lut testdata/test4.icc
moon run cmd/main --target wasm-gc -- named testdata/named_lab.icc
moon run cmd/main --target wasm-gc -- conformancesize 3144
version 2.1
class mntr
colorspace RGB
pcs XYZ
datetime 1998-02-09T06:49:00
intent 0
illuminant 0.964202880859375 1 0.8249053955078125
tagcount 17
cprt 336 51
desc 388 108
wtpt 496 20
...sample 0.25 0.049063689759460294 0.050879655743278275 0.041981500061132944 ...
sample 0.5 0.20640003241890215 0.21403939749252843 0.17660683523992873 ...
sample 1 0.9642791748046875 0.999969482421875 0.8250885009765625 ...| 功能 | 状态 |
|---|---|
| ICC profile 二进制解析(头部 + tag 表,含边界校验) | ✅ 已完成并验证 |
| tag 数据解析(XYZ / curv / para / ncl2) | ✅ 已完成并验证 |
| matrix/TRC 类型 profile → PCS XYZ 变换 | ✅ 已完成并验证 |
| XYZ ↔ Lab 转换与 CIE76 色差 | ✅ 已完成并验证 |
| 逆变换(PCS XYZ → 设备 RGB)与矩阵求逆 | ✅ 已完成并验证 |
| 多 profile 链接(源设备 → PCS → 目标设备) | ✅ 已完成并验证 |
| 媒体相对比色意图 | ✅ 已完成并验证 |
| CLUT 型 profile 变换(mft1 / mft2,CMYK 打印机等) | ✅ 已完成并验证 |
| 跨类型链接(matrix/TRC ↔ LUT 任意组合) | ✅ 已完成并验证 |
| 渲染意图:感知 / 媒体相对比色 / 饱和度 | ✅ 已完成并验证 |
| 渲染意图:绝对比色(PCS 白点缩放) | ✅ 已完成并验证(对 lcms2 197/200 完全一致、最大 1 级) |
| mAB / mBA(ICC v4 LUT) | ✅ 已完成并验证 |
| PCS 为 XYZ 的 LUT 型 profile | ✅ 已完成并验证(v4 样本;v2 路径无样本,见「不作为目标」的诚实清单) |
| 软打样链(源 → 打样设备往返 → 显示器) | ✅ 已完成并验证(对 lcms2 proofing 最大偏差 1 级) |
| 色域判断(几何色域边界:球面直方图) | ✅ 已完成并验证(设备值样本 0 误判;被证伪的往返 ΔE 法在同类样本上误判 53.7%) |
| abst / link(抽象 profile 与设备链接) | ✅ 已完成并验证(合成样本 + 解析式判据) |
| 命名色(nmcl 类 / ncl2 tag:色名 → PCS 查表) | ✅ 已完成并验证(对 lcms2 逐条 u16 对拍 5/5) |
| 类型 | 名称 | 表值位深 |
|---|---|---|
| mft1 | lut8Type | 8 位(表固定 256 项) |
| mft2 | lut16Type | 16 位(表项数在文件里显式给出) |
let profile = @moon_icc.Profile::parse(@fs.read_file_to_bytes("printer.icc"))
// 设备值按 profile 输入色空间的通道顺序给出,0..1
let lab = profile.device_to_lab(1, [ 0.0, 0.0, 0.0, 0.5 ]) // CMYK 50% 黑moon run cmd/main --target wasm-gc -- lut testdata/cmyk_printer.icc| 版本 | 位深 | L 满量程 | a/b 满量程 |
|---|---|---|---|
| v2 | 16 | 0xFF00 = 65280 | 0xFF00 |
| v2 | 8 | 0xFF = 255 | 0xFF |
| v4 | 16 | 0xFFFF = 65535 | 0xFFFF |
| 意图 | 编号 | 设备→PCS | PCS→设备 |
|---|---|---|---|
| 感知 | 0 | A2B0 | B2A0 |
| 媒体相对比色 | 1 | A2B1 | B2A1 |
| 饱和度 | 2 | A2B2 | B2A2 |
| 类型 | 名称 | 方向 |
|---|---|---|
| mAB | lutAToBType | 设备 → PCS |
| mBA | lutBToAType | PCS → 设备 |
mAB:A 曲线 → CLUT → M 曲线 → 矩阵 → B 曲线
mBA:B 曲线 → 矩阵 → M 曲线 → CLUT → A 曲线| 类型 | 内容 | 长度 |
|---|---|---|
| curv 项数 0 | 恒等 | 8 + 4 |
| curv 项数 1 | 单个 gamma | 8 + 4 + 2 |
| curv 项数 n>1 | 查找表 | 8 + 4 + 2n |
| para | 函数类型 + 参数 | 8 + 4 + 4×(1/3/4/5/7) |
| 插值选择 | 与 lcms2 的一致率(40 样本) |
|---|---|
| mAB 四面体 + mBA 三线性(本库) | 见下表 |
| 两者对调 | 2 项测试变红,A2B0 差 3 级、B2A0 差 4 级 |
| tag | 方向 | 完全一致 | 差 1 级 | 最大偏差 |
|---|---|---|---|---|
| A2B0 | 设备 → PCS | 40/40 | 0 | 0 级 |
| A2B1 | 设备 → PCS | 40/40 | 0 | 0 级 |
| B2A0 | PCS → 设备 | 38/40 | 2 | 1 级 |
| B2A1 | PCS → 设备 | 39/40 | 1 | 1 级 |
| PCS | 编码 | 1.0 对应 | 上限 |
|---|---|---|---|
| Lab (v2) | 16 位线性 | — | 0xFF00(L=100) |
| Lab (v4) | 16 位线性 | — | 0xFFFF(L=100) |
| XYZ (v2 与 v4 相同) | u1Fixed15Number | 0x8000 | 0xFFFF = 1.99997 |
A2B0:XYZ = 设备值 × D50
B2A0:设备值 = XYZ / 1.99997…
组合:设备输出 = 设备输入 × D50 / 1.99997…| 方向 | 与 lcms2 完全一致 | 最大偏差 | 与解析期望的最大偏差 |
|---|---|---|---|
| 设备 → PCS Lab | 26/26 | 0 级 | — |
| 组合(设备 → PCS → 设备) | 26/26 | 0 级 | 1 级(量化) |
| lcms2 flags | 与本库完全一致 | 最大偏差 |
|---|---|---|
| NONE(默认) | 3942/4096 | 4 级 |
| NOOPTIMIZE | 4042/4096 | 3 级 |
| FORCE_CLUT | 3370/4096 | 11 级 |
| 偏差 | 例数 | 目标线性值范围 | lcms2 输出 |
|---|---|---|---|
| 1 级 | 39 | 1.9e-4 .. 9.5e-1(全范围) | 7 .. 248 |
| 2 级 | 9 | 6.7e-5 .. 1.8e-4(极暗) | 2 .. 6 |
| 3 级 | 6 | 8.7e-5 .. 1.2e-4(极暗) | 3 .. 4 |
python tools/gen_golden.py # 生成链接金标(lcms2 NOOPTIMIZE)
python tools/gen_clut_golden.py # 生成 CLUT 金标(网格节点 + lcms2)
python tools/gen_cross_link_golden.py # 生成跨类型链接金标
python tools/gen_intent_golden.py # 生成四种渲染意图金标
python tools/gen_v4lut_golden.py # 生成 ICC v4 LUT(mAB/mBA)金标
python tools/gen_xyz_lut_profile.py # 合成 PCS 为 XYZ 的 LUT profile(并用 lcms2 复核)
python tools/gen_xyz_golden.py # 生成合成 XYZ profile 的金标
python tools/lcms2_flags_check.py # 复现 flags 对比表
python tools/lcms2_deviation_report.py # 复现链接偏差分布表
python tools/lcms2_intent_check.py # 复现"哪些 profile 能区分意图"
python tools/diff_oracle.py wasm-gc # 独立 Python 规范实现差分
python tools/make_demo_video.py # 重新生成演示视频moon run cmd/main --target wasm-gc -- link testdata/srgb.icc testdata/cmyk_printer.icc saturation| 编号 | 名称 | 命令行 | 对 LUT 型 profile 的效果 |
|---|---|---|---|
| 0 | 感知 | perceptual | 用 A2B0/B2A0 |
| 1 | 媒体相对比色(默认) | relative | 用 A2B1/B2A1 |
| 2 | 饱和度 | saturation | 用 A2B2/B2A2 |
| 3 | 绝对比色 | absolute | 用 A2B1/B2A1,外加 PCS 白点缩放 |
linear = TRC(encoded) # 先线性化
XYZ = M · linear # 再乘矩阵| 字段 | 处理 |
|---|---|
| vendorFlag | 原样读出(NamedColor2::vendor_flag()) |
| count | 条目数;上界检查用除法,避免 count × 步长 在 Int32 上溢出后绕过检查 |
| nDeviceCoords | 每条附带的设备坐标个数,规范允许 0..8,超出报 BadTagData |
| prefix / suffix | 32 字节 ASCII,去尾部 0x00 |
| rootName | 每条 32 字节 ASCII |
| PCS | 3 × uInt16Number(不是 s15Fixed16Number):Lab 按 v4 0xFFFF / v2 0xFF00 解码,XYZ 按 u1Fixed15(1.0 = 0x8000) |
| 设备坐标 | nDeviceCoords × u16 |
class nmcl
pcs Lab
version 4
vendor_flag 0
n_device_coords 3
prefix PANTONE
suffix C
count 5
named 0 PANTONE Red C lab=54.0002 80.8016 69.8988 device=255 0 0
named 3 PANTONE Gray C lab=50.0008 0 0 device=128 128 128
lookup PANTONE Red C -> 54.0002 80.8016 69.8988
lookup Red -> 未命中moon run cmd/main --target wasm-gc -- conformance
moon run cmd/main --target wasm-gc -- conformance testdata/cmyk_printer.icc实测教训:用错误的字节序读 sRGB Color Space Profile.icm,会把 3144 字节读成 1208745984,且 acsp 签名读成乱码。
moon test --target wasm-gc # 104 项测试
moon test --target js # 同一套测试,js 后端
moon test --target native # 同一套测试,native 后端(本机 Windows 缺 MSVC,以 CI 为准)
python tools/diff_oracle.py wasm-gc # 外部 oracle 差分
python tools/diff_oracle.py js # 同上,js 后端
python tools/verify_publish.py # 在全新工程里实拉已发布包并跑真实变换
python tools/check_readme_examples.py # 把本文件的示例代码逐块实跑并核对其中公布的数值
python tools/gen_proof_golden.py # 重新生成软打样金标(需要 MSYS2 gcc 编译 lcms2)
python tools/gen_abst_link_profiles.py # 重新生成 abst / link 合成样本(生成时用 lcms2 复核)
python tools/compare_gamut.py # 色域边界的独立对拍(lcms2 取 Lab + Python 重写,一致率 91.7%)
# 判据有效性(刻意植入错误看测试是否变红)见 MUTATION.md
moon run cmd/main -- conformance # 打印设备类覆盖矩阵(标准版本与必需行为)check_readme_examples.py 与 verify_publish.py 不放进 CI 的 push 门禁: 它们从 mooncakes.io 拉取已发布版本,而 push 门禁跑的是当前源码—— 若改了示例但尚未发布,二者会不一致而产生误报。这两个脚本用于发布前后的人工验证。
| 核心功能 | 覆盖它的判据 |
|---|---|
| profile 解析(头部 + tag 表 + 边界校验) | header_wbtest.mbt(合成字节流,含畸形输入);tools/diff_oracle.py 对 5 个真实 profile 逐字段比对 |
| matrix/TRC 变换与逆变换 | transform_wbtest.mbt;link_golden_wbtest.mbt(4096 彩色样本对 lcms2);差分脚本逐位比对 |
| LUT 变换(mft1/mft2) | clut_wbtest.mbt(625 个网格节点 + 300 个 lcms2 CMYK 样本);tools/gen_clut_golden.py 反解输入表定位节点 |
| LUT 变换(mAB /mBA ,v4) | v4lut_wbtest.mbt(节点判据读原始字节 + 40 样本对 lcms2);tools/diff_oracle.py 独立结构对拍 |
| PCS 为 XYZ 的 LUT | xyz_lut_wbtest.mbt(u1Fixed15 编码常数判据 + 26 样本双向对拍 + 解析期望式) |
| 多 profile 链接(四种跨类型组合) | cross_link_wbtest.mbt + cross_link_golden_wbtest.mbt(sRGB ↔ CMYK 各 300 样本) |
| 渲染意图(感知 / 相对 / 饱和度 / 绝对) | intent_wbtest.mbt + intent_golden_wbtest.mbt(200 样本 × 四意图);绝对比色另断言「与相对比色必须不同」以防缩放失效 |
| 软打样链 | proofing_wbtest.mbt(自洽性 + 物理正确性)+ proof_golden_wbtest.mbt(lcms2 cmsCreateProofingTransform 金标,最大偏差 1 级) |
| 色域判断 | proofing_wbtest.mbt(设备值样本零误判、纸白与设备黑 in、PCS 黑与原色 out、覆盖率、映射误差独立性) |
| 抽象 profile(abst)与 device link(link) | abstract_link_wbtest.mbt(恒等抽象不改变 PCS;device link 等于闭式 C=1-R, M=1-G, Y=1-B, K=0) |
| 命令行示例与端到端可用性 | cmd/main 八子命令;tools/verify_publish.py 在仓库之外的空工程里 moon add 实拉并运行 |
| 植入的错误 | 单元测试反应 |
|---|---|
| renderingIntent 读取位置错位 | 3 项变红,外部差分 12 处不一致 |
| TRC 与矩阵顺序颠倒 | 5 项变红(白点偏离 D50 达 17.3 色差) |
| 矩阵求逆漏掉转置 | 5 项变红(lcms2 一致率跌到 0%) |
| CLUT 维度步长顺序写反 | 4 项变红(节点偏差 91 个 L 单位,lcms2 差 93 级) |
| LUT 侧 PCS 不做 Lab↔XYZ 转换 | 2 项变红(偏差 255 级,即完全错) |
| mAB/mBA 矩阵段按"8 字节类型头 + 矩阵"读 | 5 项变红(偏差 215–255 级) |
| 取消曲线段 4 字节对齐 | 1 项变红(合成 tag 判据报出 ..cu 非法曲线类型) |
| 交换两个方向的插值方式 | 2 项变红(A2B0 差 3 级、B2A0 差 4 级) |
| mBA 阶段顺序颠倒(矩阵与 M 曲线互换) | 1 项变红(偏差 255 级) |
| 忽略矩阵的偏移项 | 1 项变红(偏差 255 级) |
| XYZ PCS 解码误按 u1Fixed16(数字 32768→65535) | 3 项变红(节点判据报出 2 倍偏差,端到端差 61–64 级) |
| XYZ PCS 编码(逆方向)误按 u1Fixed16 | 1 项变红(组合变换差 127 级) |
| 缺省的曲线段返回空数组而非透传 | 3 项变红(合成 profile 的最大输入处整体输出 0) |
| 项目 | 语言 | 许可证 | 参考范围 |
|---|---|---|---|
| Little-CMS (lcms2) | C | MIT | ICC 规范的工程化解读、tag 处理策略 |
lcms2 采用 MIT 许可证,与本项目 Apache-2.0 兼容。本项目未链接、未编译、未复制其代码; 仓库内只包含它的测试用 profile testdata/test4.icc(用于建立 ICC v4 LUT 判据), 其来源、许可与 MIT 声明全文见 testdata/README.md。
pub suberror IccError {
Truncated(expected~ : Int, offset~ : Int, actual~ : Int)
BadSignature(found~ : String)
SizeMismatch(declared~ : Int, actual~ : Int)
TagOutOfBounds(tag~ : String, offset~ : Int, size~ : Int)
BadTagTable(count~ : Int, offset~ : Int)
BadTagData(tag~ : String, reason~ : String)
OutOfRange(what~ : String, value~ : Int)
}pub struct BigEndianReader {
data : Bytes
pos : Int
}pub struct ColorTransform {
source : SourceStage
destination : DestinationStage
intent : Int
pcs_scale : XyzNumber
}fn ColorTransform::proofing(source : Profile, proof : Profile, display : Profile, intent : Int, proof_intent : Int) -> ProofTransform raise IccError| 参数 | 含义 |
|---|---|
| source | 图像所在设备(如 sRGB 相机/显示器) |
| proof | 要模拟的目标设备(如 CMYK 印刷机) |
| display | 观察者实际使用的显示设备 |
| intent | 源与显示侧意图(通常 1 = 媒体相对比色) |
| proof_intent | 打样 profile 两个方向的意图(通常 1,或 3 = 绝对比色) |
fn ColorTransform::transform(self : ColorTransform, r : Double, g : Double, b : Double) -> (Double, Double, Double)fn ColorTransform::with_intent(source : Profile, destination : Profile, intent : Int) -> ColorTransform raise IccError| 编号 | 意图 | 对 LUT 型 profile 的效果 |
|---|---|---|
| 0 | 感知 | 用 A2B0/B2A0 |
| 1 | 媒体相对比色 | 用 A2B1/B2A1 |
| 2 | 饱和度 | 用 A2B2/B2A2 |
| 3 | 绝对比色 | 用 A2B1/B2A1,外加 PCS 白点缩放 |
scale = diag(源媒体白点 / 目标媒体白点)pub struct IccDateTime {
year : Int
month : Int
day : Int
hour : Int
minute : Int
second : Int
}pub struct IccHeader {
size : Int
cmm_type : Int
major_version : Int
minor_version : Int
device_class : String
color_space : String
pcs : String
date_time : IccDateTime
platform : String
flags : Int
manufacturer : Int
model : Int
attributes : Int
rendering_intent : Int
illuminant : XyzNumber
creator : Int
profile_id : Bytes
}type InverseCurvepub struct InverseMatrixTrc {
inverse_matrix : Matrix3
inv_r : InverseCurve
inv_g : InverseCurve
inv_b : InverseCurve
}pub struct Lab {
l : Double
a : Double
b : Double
}fn MatrixTrcTransform::to_pcs(self : MatrixTrcTransform, r : Double, g : Double, b : Double) -> XyzNumberpub struct NamedColor2 {
vendor_flag : Int
prefix : String
suffix : String
device_coords_count : Int
pcs_space : String
major_version : Int
colors : Array[NamedColorEntry]
}fn Profile::gamut_boundary(self : Profile, intent : Int, steps : Int) -> GamutBoundary raise IccErrorpub struct ProofTransform {
source : SourceStage
proof_dest : DestinationStage
proof_source : SourceStage
display : DestinationStage
source_scale : XyzNumber
display_scale : XyzNumber
intent : Int
proof_intent : Int
}源设备值 --A2B--> PCS --B2A--> 打样设备值 --A2B--> PCS --B2A--> 显示设备值fn ProofTransform::evaluate(self : ProofTransform, inputs : Array[Double]) -> (Array[Double], Array[Double])pub struct XyzNumber {
x : Double
y : Double
z : Double
}| 版本 | 编码 | L 满量程 | a/b 满量程 |
|---|---|---|---|
| v2 | 16 位 | 0xFF00 = 65280 | 0xFF00 |
| v2 | 8 位 | 0xFF = 255 | 0xFF |
| v4 | 16 位 | 0xFFFF = 65535 | 0xFFFF |
fn intent_to_a2b_tag(intent : Int) -> String| 意图 | 编号 | tag |
|---|---|---|
| 感知 | 0 | A2B0 |
| 媒体相对比色 | 1 | A2B1 |
| 饱和度 | 2 | A2B2 |
| 绝对比色 | 3 | 无独立 tag,用 A2B1 再加白点调整 |
fn parse_steps(text : String) -> Int?fn rendering_intent_name(intent : Int) -> Stringfn s15f16_to_double(raw : Int) -> Doublelet target_specification : StringInstall
Download zip纯 MoonBit 实现的 ICC 色彩管理引擎:解析 ICC profile 并执行真实色空间变换