use crate::cmm::cmm_render_intent::RenderIntent;
use crate::cmm::cmm_transform::ColorMatrix;
use crate::cmm::cmm_transform::Xyz;
use crate::utils::ordered_float::F64;
use jay_proc::StrFmt;
use jay_proc::jay_hash;
#[jay_hash]
#[derive(Copy, Clone, Debug, Eq, StrFmt)]
pub struct Luminance {
pub min: F64,
pub max: F64,
pub white: F64,
}
#[jay_hash]
#[derive(Copy, Clone, Debug, Eq, StrFmt)]
pub struct TargetLuminance {
pub min: F64,
pub max: F64,
}
impl Luminance {
pub const SRGB: Self = Self {
min: F64(0.2),
max: F64(80.0),
white: F64(80.0),
};
pub const BT1886: Self = Self {
min: F64(0.01),
max: F64(100.0),
white: F64(100.0),
};
pub const ST2084_PQ: Self = Self {
min: F64(0.0),
max: F64(10000.0),
white: F64(203.0),
};
#[expect(unused)]
pub const HLG: Self = Self {
min: F64(0.005),
max: F64(1000.0),
white: F64(203.0),
};
pub const WINDOWS_SCRGB: Self = Self {
min: F64(0.0),
max: F64(80.0),
white: F64(203.0),
};
}
impl Luminance {
pub fn to_target(&self) -> TargetLuminance {
TargetLuminance {
min: self.min,
max: self.max,
}
}
pub fn embeds_into(&self, other: &Self, intent: RenderIntent) -> bool {
const EPSILON: f64 = 0.001;
let scaler = scaler(self, other);
if (scaler - 1.0).abs() > EPSILON {
return false;
}
let offset = offset(self, other, intent);
if offset.abs() > EPSILON {
return false;
}
true
}
}
impl Default for Luminance {
fn default() -> Self {
Self::SRGB
}
}
#[allow(non_snake_case)]
pub fn white_balance(
from: &Luminance,
to: &Luminance,
w_to: (F64, F64),
intent: RenderIntent,
) -> ColorMatrix<Xyz, Xyz> {
let a = scaler(from, to);
let d = offset(from, to, intent);
let s = a - d;
let (F64(x_to), F64(y_to)) = w_to;
let X_to = x_to / y_to;
let Y_to = 1.0;
let Z_to = (1.0 - x_to - y_to) / y_to;
ColorMatrix::new([
[s, 0.0, 0.0, d * X_to],
[0.0, s, 0.0, d * Y_to],
[0.0, 0.0, s, d * Z_to],
])
}
fn scaler(from: &Luminance, to: &Luminance) -> f64 {
let a =
(from.max - from.min) / (to.max - to.min) * (to.white - to.min) / (from.white - from.min);
a.0
}
fn offset(from: &Luminance, to: &Luminance, intent: RenderIntent) -> f64 {
match intent.black_point_compensation() {
true => 0.0,
false => ((from.min - to.min) / (to.max - to.min)).0,
}
}