#[cfg(all(not(feature = "std"), not(feature = "libm")))]
compile_error!(
"color requires either the `std` or `libm` feature. \
Add color = {{ features = [\"std\"] }} to your Cargo.toml."
);
pub trait MathState: 'static {
fn powf(base: f32, exp: f32) -> f32;
fn cbrt(v: f32) -> f32;
fn round(v: f32) -> f32;
fn sqrt(v: f32) -> f32;
fn ln(v: f32) -> f32;
fn exp(v: f32) -> f32;
fn sin(v: f32) -> f32;
fn cos(v: f32) -> f32;
fn atan2(y: f32, x: f32) -> f32;
}
#[cfg(feature = "std")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct StdMath;
#[cfg(feature = "std")]
impl MathState for StdMath {
#[inline(always)]
fn powf(b: f32, e: f32) -> f32 {
b.powf(e)
}
#[inline(always)]
fn cbrt(v: f32) -> f32 {
v.cbrt()
}
#[inline(always)]
fn round(v: f32) -> f32 {
v.round()
}
#[inline(always)]
fn sqrt(v: f32) -> f32 {
v.sqrt()
}
#[inline(always)]
fn ln(v: f32) -> f32 {
v.ln()
}
#[inline(always)]
fn exp(v: f32) -> f32 {
v.exp()
}
#[inline(always)]
fn sin(v: f32) -> f32 {
v.sin()
}
#[inline(always)]
fn cos(v: f32) -> f32 {
v.cos()
}
#[inline(always)]
fn atan2(y: f32, x: f32) -> f32 {
y.atan2(x)
}
}
#[cfg(feature = "libm")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct LibmMath;
#[cfg(feature = "libm")]
impl MathState for LibmMath {
#[inline(always)]
fn powf(b: f32, e: f32) -> f32 {
libm::powf(b, e)
}
#[inline(always)]
fn cbrt(v: f32) -> f32 {
libm::cbrtf(v)
}
#[inline(always)]
fn round(v: f32) -> f32 {
libm::roundf(v)
}
#[inline(always)]
fn sqrt(v: f32) -> f32 {
libm::sqrtf(v)
}
#[inline(always)]
fn ln(v: f32) -> f32 {
libm::logf(v)
}
#[inline(always)]
fn exp(v: f32) -> f32 {
libm::expf(v)
}
#[inline(always)]
fn sin(v: f32) -> f32 {
libm::sinf(v)
}
#[inline(always)]
fn cos(v: f32) -> f32 {
libm::cosf(v)
}
#[inline(always)]
fn atan2(y: f32, x: f32) -> f32 {
libm::atan2f(y, x)
}
}
#[cfg(feature = "std")]
pub type DefaultMath = StdMath;
#[cfg(all(not(feature = "std"), feature = "libm"))]
pub type DefaultMath = LibmMath;
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Mat3 {
pub col0: [f32; 4],
pub col1: [f32; 4],
pub col2: [f32; 4],
}
impl Mat3 {
pub const fn mul(a: &Self, b: &Self) -> Self {
macro_rules! e {
($r:expr, $c:expr) => {
a.col0[$r] * $c[0] + a.col1[$r] * $c[1] + a.col2[$r] * $c[2]
};
}
Self {
col0: [e!(0, b.col0), e!(1, b.col0), e!(2, b.col0), 0.0],
col1: [e!(0, b.col1), e!(1, b.col1), e!(2, b.col1), 0.0],
col2: [e!(0, b.col2), e!(1, b.col2), e!(2, b.col2), 0.0],
}
}
pub const fn invert(m: &Self) -> Self {
let [a, b, c, _] = m.col0;
let [d, e, f, _] = m.col1;
let [g, h, i, _] = m.col2;
let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
Self {
col0: [
(e * i - f * h) / det,
-(b * i - c * h) / det,
(b * f - c * e) / det,
0.0,
],
col1: [
-(d * i - f * g) / det,
(a * i - c * g) / det,
-(a * f - c * d) / det,
0.0,
],
col2: [
(d * h - e * g) / det,
-(a * h - b * g) / det,
(a * e - b * d) / det,
0.0,
],
}
}
#[inline]
pub fn apply(&self, v: [f32; 3]) -> [f32; 3] {
[
self.col0[0] * v[0] + self.col1[0] * v[1] + self.col2[0] * v[2],
self.col0[1] * v[0] + self.col1[1] * v[1] + self.col2[1] * v[2],
self.col0[2] * v[0] + self.col1[2] * v[1] + self.col2[2] * v[2],
]
}
#[cfg(feature = "glam")]
#[inline]
pub fn apply_glam(&self, v: glam::Vec4) -> glam::Vec4 {
let m = glam::Mat3::from_cols(
glam::Vec3::from_array([self.col0[0], self.col0[1], self.col0[2]]),
glam::Vec3::from_array([self.col1[0], self.col1[1], self.col1[2]]),
glam::Vec3::from_array([self.col2[0], self.col2[1], self.col2[2]]),
);
(m * v.truncate()).extend(v.w)
}
#[inline]
pub const fn luminance_weights(&self) -> [f32; 3] {
[self.col0[1], self.col1[1], self.col2[1]]
}
}