///|
/// 抽象 profile(`abst`)与 device link profile(`link`)的变换入口。
///
/// 这两类 profile 不走「设备 ↔ PCS」的常规链:
///
/// - **`abst`(抽象)**:输入输出**都在 PCS**(`color_space` 与 `pcs` 都是
/// `Lab ` 或 `XYZ `),典型用途是色彩偏好调整,串在 ICC 链的中间;
/// - **`link`(设备链接)**:`pcs` 字段此时记的是**目标设备空间**,
/// A2B 直接把源设备值映射成目标设备值,不经过 PCS。
///
/// 二者的共同点是都可以复用 `LutTable`,只是输入/输出的编码方式不同。
///|
/// 抽象 profile:PCS → PCS。
///
/// 输入按 profile 的 `color_space` 编码(`Lab ` 或 `XYZ `),
/// 输出按 `pcs` 编码;两者的 v2/v4 Lab 编码差异由 profile 版本决定。
pub fn Profile::apply_abstract(
self : Profile,
intent : Int32,
xyz : XyzNumber,
) -> XyzNumber raise IccError {
let table = self.a2b(intent)
let is_v2 = table.pcs_is_v2(self.is_v2())
let depth = table.bit_depth()
let encoded = match self.header.color_space {
"Lab " => lab_to_clut_input(xyz_to_lab(xyz), depth, is_v2)
"XYZ " => xyz_to_clut_input(xyz, depth)
other =>
raise BadTagData(
tag="A2B0",
reason="抽象 profile 的输入应为 PCS(Lab 或 XYZ),实际 \"\{other}\"",
)
}
let raw = table.evaluate(encoded)
match self.header.pcs {
"Lab " => lab_to_xyz(clut_output_to_lab(raw, depth, is_v2))
"XYZ " => clut_output_to_xyz(raw, depth)
other =>
raise BadTagData(
tag="A2B0",
reason="抽象 profile 的输出应为 PCS(Lab 或 XYZ),实际 \"\{other}\"",
)
}
}
///|
/// device link profile:源设备值 → 目标设备值。
///
/// 不经过 PCS,因此返回的数组就是目标设备的通道值;
/// 通道数由 A2B 表的输出通道数决定(如 RGB → CMYK 得 4 个值)。
pub fn Profile::apply_device_link(
self : Profile,
intent : Int32,
inputs : Array[Double],
) -> Array[Double] raise IccError {
self.a2b(intent).evaluate(inputs)
}
///|
/// 该 profile 是否是抽象 profile。
pub fn Profile::is_abstract(self : Profile) -> Bool {
self.header.device_class == "abst"
}
///|
/// 该 profile 是否是 device link profile。
pub fn Profile::is_device_link(self : Profile) -> Bool {
self.header.device_class == "link"
}