macro_rules! bmd_log_tf {
(
$a:literal,
$b:literal,
$c:literal,
$d:literal,
$e:literal,
$lin_cut:literal,
$nonlinear_black:literal,
$linear_min:literal,
$linear_max:literal
$(,)?
) => {
pub const NONLINEAR_BLACK: f32 = $nonlinear_black;
pub const LINEAR_MIN: f32 = $linear_min;
pub const LINEAR_MAX: f32 = $linear_max;
const A: f32 = $a;
const B: f32 = $b;
const C: f32 = $c;
const D: f32 = $d;
const E: f32 = $e;
const LIN_CUT: f32 = $lin_cut;
const LOG_CUT: f32 = LIN_CUT * A + B;
#[inline]
pub fn from_linear(x: f32) -> f32 {
if x < LIN_CUT {
x * A + B
} else {
(x + C).ln() * D + E
}
}
#[inline]
pub fn to_linear(x: f32) -> f32 {
if x < LOG_CUT {
(x - B) / A
} else {
((x - E) / D).exp() - C
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constants() {
assert_eq!(from_linear(0.0), NONLINEAR_BLACK);
assert_eq!(to_linear(0.0), LINEAR_MIN);
assert_eq!(to_linear(1.0), LINEAR_MAX);
}
#[test]
fn round_trip() {
for i in 0..1024 {
let n = i as f32 / 1023.0;
assert!((n - from_linear(to_linear(n))).abs() < 0.000_001);
}
}
}
};
}
pub mod film_gen5 {
pub const NONLINEAR_BLACK: f32 = 0.09246575;
pub const LINEAR_MIN: f32 = -0.011162501;
pub const LINEAR_MAX: f32 = 222.86098;
const A: f32 = 8.283605932402494;
const B: f32 = 0.09246575342465753;
const C: f32 = 0.005494072432257808;
const D: f32 = 0.08692876065491224;
const E: f32 = 0.5300133392291939;
const LIN_CUT: f32 = 0.005;
const LOG_CUT: f32 = LIN_CUT * A + B;
#[inline]
pub fn from_linear(x: f32) -> f32 {
if x < LIN_CUT {
x * A + B
} else {
(x + C).ln() * D + E
}
}
#[inline]
pub fn to_linear(x: f32) -> f32 {
if x < LOG_CUT {
(x - B) / A
} else {
((x - E) / D).exp() - C
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constants() {
assert_eq!(from_linear(0.0), NONLINEAR_BLACK);
assert_eq!(to_linear(0.0), LINEAR_MIN);
assert_eq!(to_linear(1.0), LINEAR_MAX);
}
#[test]
fn from_linear_test() {
assert!((from_linear(0.0) - 0.0924657534246575).abs() < 0.00001);
assert!((from_linear(0.18) - 0.3835616438356165).abs() < 0.00001);
assert!((from_linear(1.0) - 0.5304896249573048).abs() < 0.00001);
assert!((from_linear(10.0) - 0.7302219538415439).abs() < 0.00001);
assert!((from_linear(40.0) - 0.8506949973834717).abs() < 0.00001);
assert!((from_linear(100.0) - 0.9303398518999735).abs() < 0.00001);
assert!((from_linear(222.86) - 1.0).abs() < 0.00001);
}
#[test]
fn to_linear_test() {
assert!((to_linear(0.0924657534246575) - 0.0).abs() < 0.00001);
assert!((to_linear(0.3835616438356165) - 0.18).abs() < 0.00001);
assert!((to_linear(0.5304896249573048) - 1.0).abs() < 0.00001);
assert!((to_linear(0.7302219538415439) - 10.0).abs() < 0.00001);
assert!((to_linear(0.8506949973834717) - 40.0).abs() < 0.0001);
assert!((to_linear(0.9303398518999735) - 100.0).abs() < 0.0001);
assert!((to_linear(1.0) - 222.86).abs() < 0.001);
}
#[test]
fn round_trip() {
for i in 0..1024 {
let n = i as f32 / 1023.0;
assert!((n - from_linear(to_linear(n))).abs() < 0.000_001);
}
}
}
}
pub mod davinci_intermediate {
pub const NONLINEAR_BLACK: f32 = 0.0;
pub const LINEAR_MIN: f32 = 0.0;
pub const LINEAR_MAX: f32 = 100.00002;
const A: f32 = 0.0075;
const B: f32 = 7.0;
const C: f32 = 0.07329248;
const M: f32 = 10.44426855;
const LIN_CUT: f32 = 0.00262409;
const LOG_CUT: f32 = LIN_CUT * M;
#[inline]
pub fn from_linear(x: f32) -> f32 {
if x < LIN_CUT {
x * M
} else {
((x + A).log2() + B) * C
}
}
#[inline]
pub fn to_linear(x: f32) -> f32 {
if x < LOG_CUT {
x / M
} else {
2.0f32.powf((x / C) - B) - A
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constants() {
assert_eq!(from_linear(0.0), NONLINEAR_BLACK);
assert_eq!(to_linear(0.0), LINEAR_MIN);
assert_eq!(to_linear(1.0), LINEAR_MAX);
}
#[test]
fn from_linear_test() {
assert!((from_linear(-0.01) - -0.104443).abs() < 0.00001);
assert!((from_linear(0.0) - 0.0).abs() < 0.00001);
assert!((from_linear(0.18) - 0.336043).abs() < 0.00001);
assert!((from_linear(1.0) - 0.513837).abs() < 0.00001);
assert!((from_linear(10.0) - 0.756599).abs() < 0.00001);
assert!((from_linear(40.0) - 0.903125).abs() < 0.00001);
assert!((from_linear(100.0) - 1.0).abs() < 0.00001);
}
#[test]
fn to_linear_test() {
assert!((to_linear(-0.104443) - -0.01).abs() < 0.00001);
assert!((to_linear(0.0) - 0.0).abs() < 0.00001);
assert!((to_linear(0.336043) - 0.18).abs() < 0.00001);
assert!((to_linear(0.513837) - 1.0).abs() < 0.00001);
assert!((to_linear(0.756599) - 10.0).abs() < 0.0001);
assert!((to_linear(0.903125) - 40.0).abs() < 0.001);
assert!((to_linear(1.0) - 100.0).abs() < 0.001);
}
#[test]
fn round_trip() {
for i in 0..1024 {
let n = i as f32 / 1023.0;
assert!((n - from_linear(to_linear(n))).abs() < 0.000_001);
}
}
}
}
pub mod film_4k {
bmd_log_tf!(
3.4845696382315063,
0.035388150275256276,
0.0797443784368146,
0.2952978430809614,
0.781640290185019,
0.005000044472991669,
0.03538815,
-0.010155673,
2.0150511,
);
}
pub mod film_46k_gen3 {
bmd_log_tf!(
4.6708570973650385,
0.07305940817239664,
0.0287284246696045,
0.15754052970309015,
0.6303838233991069,
0.00499997387034723,
0.07305941,
-0.015641542,
10.416711,
);
}
pub mod broadcast_film_gen4 {
bmd_log_tf!(
5.2212906000378565,
-0.00007134598996420424,
0.03630411093543444,
0.21566456116952773,
0.7133134738229736,
0.00500072683168086,
-7.134599e-5,
1.3664436e-5,
3.7421572,
);
}
pub mod film {
bmd_log_tf!(
4.969340550061595,
0.03538815027497705,
0.03251848397268609,
0.1864420102390252,
0.6723093484094137,
0.004999977151237935,
0.03538815,
-0.007121297,
5.765991,
);
}
pub mod pocket_4k_film_gen4 {
bmd_log_tf!(
4.323288448370592,
0.07305940818036996,
0.03444835397444396,
0.1703663112023471,
0.6454296550413368,
0.004958295208669562,
0.07305941,
-0.016899036,
7.979818,
);
}
pub mod pocket_6k_film_gen4 {
bmd_log_tf!(
4.724515510884684,
0.07305940816299691,
0.027941380463157067,
0.15545874964938466,
0.6272665887366995,
0.004963316175308281,
0.07305941,
-0.015463895,
10.969201,
);
}