#![cfg(feature = "lut")]
use crate::mlaf::fmla;
use crate::{Chromaticity, ColorProfile, DataColorSpace, ProfileVersion, RenderingIntent, Xyz};
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub(crate) enum TransformDirection {
DeviceToPcs,
PcsToDevice,
}
impl ColorProfile {
pub(crate) fn detect_black_point(
&self,
intent: RenderingIntent,
transform_direction: TransformDirection,
) -> Option<Xyz> {
static DEFAULT_V4_BLACK_POINT: Xyz = Xyz::new(0.003357, 0.003479, 0.002869);
if intent != RenderingIntent::Perceptual && intent != RenderingIntent::Saturation {
return None;
}
if self.color_space == DataColorSpace::Cmyk {
return None;
} else if self.color_space == DataColorSpace::Rgb {
if self.version_internal < ProfileVersion::V4_0 {
return None;
}
return match transform_direction {
TransformDirection::DeviceToPcs => {
if self.has_device_to_pcs_lut() {
return Some(DEFAULT_V4_BLACK_POINT);
}
Some(Xyz::default())
}
TransformDirection::PcsToDevice => {
if self.has_pcs_to_device_lut() {
return Some(DEFAULT_V4_BLACK_POINT);
}
Some(Xyz::default())
}
};
}
None
}
}
pub(crate) fn compensate_bpc_in_lut(lut_xyz: &mut [f32], src_bp: Xyz, dst_bp: Xyz) {
if src_bp.eq(&Xyz::default()) && dst_bp.eq(&Xyz::default()) {
return;
}
const WP: Xyz = Chromaticity::D50.to_xyz();
let compute = |src_bp_c: f32, dst_bp_c: f32, wp_c: f32| -> (f32, f32) {
let denom = wp_c - src_bp_c;
if denom.abs() < 1e-10 {
return (1.0, 0.0); }
let a = (wp_c - dst_bp_c) / denom;
let b = dst_bp_c - a * src_bp_c;
(a, b)
};
let (ax, mut bx) = compute(src_bp.x, dst_bp.x, WP.x);
let (ay, mut by) = compute(src_bp.y, dst_bp.y, WP.y);
let (az, mut bz) = compute(src_bp.z, dst_bp.z, WP.z);
const S: f32 = 1.0 / (1.0 + 32767.0 / 32768.0);
bx *= S;
by *= S;
bz *= S;
for dst in lut_xyz.as_chunks_mut::<3>().0.iter_mut() {
dst[0] = fmla(dst[0], ax, bx); dst[1] = fmla(dst[1], ay, by);
dst[2] = fmla(dst[2], az, bz);
}
}