use super::{LinearSrgb, Lms};
#[allow(unused_imports)]
use num_traits::Float;
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Oklab {
pub l: f32,
pub a: f32,
pub b: f32,
}
impl Oklab {
pub fn new(l: f32, a: f32, b: f32) -> Self {
Oklab { l, a, b }
}
pub fn from_linear_srgb(linear_srgb: LinearSrgb) -> Self {
let lms = Lms::from_linear_srgb(linear_srgb);
Self::from_lms(lms)
}
pub fn to_linear_srgb(self) -> LinearSrgb {
let lms = self.to_lms();
lms.to_linear_srgb()
}
pub fn from_lms(lms: Lms) -> Self {
const LMS_TO_OKLAB: [[f32; 3]; 3] = [
[0.210_454_26, 0.793_617_8, -0.004_072_047],
[1.977_998_5, -2.428_592_2, 0.450_593_7],
[0.025_904_037, 0.782_771_77, -0.808_675_77],
];
let Lms {
long,
medium,
short,
} = lms;
let l_cbrt = long.cbrt();
let m_cbrt = medium.cbrt();
let s_cbrt = short.cbrt();
Oklab {
l: LMS_TO_OKLAB[0][0] * l_cbrt
+ LMS_TO_OKLAB[0][1] * m_cbrt
+ LMS_TO_OKLAB[0][2] * s_cbrt,
a: LMS_TO_OKLAB[1][0] * l_cbrt
+ LMS_TO_OKLAB[1][1] * m_cbrt
+ LMS_TO_OKLAB[1][2] * s_cbrt,
b: LMS_TO_OKLAB[2][0] * l_cbrt
+ LMS_TO_OKLAB[2][1] * m_cbrt
+ LMS_TO_OKLAB[2][2] * s_cbrt,
}
}
pub fn to_lms(self) -> Lms {
const OKLAB_TO_LMS_CBRT: [[f32; 3]; 3] = [
[1.0, 0.396_337_78, 0.215_803_76],
[1.0, -0.105_561_346, -0.063_854_17],
[1.0, -0.089_484_18, -1.291_485_5],
];
let Oklab { l, a, b } = self;
let l_cbrt =
OKLAB_TO_LMS_CBRT[0][0] * l + OKLAB_TO_LMS_CBRT[0][1] * a + OKLAB_TO_LMS_CBRT[0][2] * b;
let m_cbrt =
OKLAB_TO_LMS_CBRT[1][0] * l + OKLAB_TO_LMS_CBRT[1][1] * a + OKLAB_TO_LMS_CBRT[1][2] * b;
let s_cbrt =
OKLAB_TO_LMS_CBRT[2][0] * l + OKLAB_TO_LMS_CBRT[2][1] * a + OKLAB_TO_LMS_CBRT[2][2] * b;
let long = l_cbrt * l_cbrt * l_cbrt;
let medium = m_cbrt * m_cbrt * m_cbrt;
let short = s_cbrt * s_cbrt * s_cbrt;
Lms::new(long, medium, short)
}
}