#![allow(dead_code)]
pub const BT2020_RED_XY: [f32; 2] = [0.708, 0.292];
pub const BT2020_GREEN_XY: [f32; 2] = [0.170, 0.797];
pub const BT2020_BLUE_XY: [f32; 2] = [0.131, 0.046];
pub const BT2020_WHITE_XY: [f32; 2] = [0.3127, 0.3290];
pub const BT2020_ALPHA: f32 = 1.099_296_8;
pub const BT2020_BETA: f32 = 0.018_053_97;
#[derive(Debug, Clone)]
pub struct Bt2020Config {
pub bit_depth: u8,
pub is_constant_luminance: bool,
pub reference_peak_luminance: f32,
}
impl Default for Bt2020Config {
fn default() -> Self {
Self {
bit_depth: 10,
is_constant_luminance: false,
reference_peak_luminance: 1000.0,
}
}
}
pub fn bt2020_oetf(linear: f32) -> f32 {
let c = linear.clamp(0.0, 1.0);
if c < BT2020_BETA {
4.5 * c
} else {
BT2020_ALPHA * c.powf(0.45) - (BT2020_ALPHA - 1.0)
}
}
pub fn bt2020_eotf(encoded: f32) -> f32 {
let c = encoded.clamp(0.0, 1.0);
if c < 4.5 * BT2020_BETA {
c / 4.5
} else {
((c + BT2020_ALPHA - 1.0) / BT2020_ALPHA).powf(1.0 / 0.45)
}
}
pub fn export_bt2020_config(cfg: &Bt2020Config) -> String {
format!(
"ITU-R BT.2020\nBit depth: {}\nPeak: {:.0} nits\nConstant luminance: {}\n",
cfg.bit_depth, cfg.reference_peak_luminance, cfg.is_constant_luminance
)
}
pub fn validate_bt2020_config(cfg: &Bt2020Config) -> bool {
matches!(cfg.bit_depth, 10 | 12) && cfg.reference_peak_luminance > 0.0
}
pub fn bt2020_oetf_triplet(rgb: [f32; 3]) -> [f32; 3] {
[
bt2020_oetf(rgb[0]),
bt2020_oetf(rgb[1]),
bt2020_oetf(rgb[2]),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_oetf_zero() {
assert_eq!(bt2020_oetf(0.0), 0.0);
}
#[test]
fn test_oetf_one() {
assert!((bt2020_oetf(1.0) - 1.0).abs() < 0.01);
}
#[test]
fn test_eotf_zero() {
assert_eq!(bt2020_eotf(0.0), 0.0);
}
#[test]
fn test_eotf_one() {
assert!((bt2020_eotf(1.0) - 1.0).abs() < 0.01);
}
#[test]
fn test_roundtrip() {
let v = 0.4f32;
assert!((bt2020_eotf(bt2020_oetf(v)) - v).abs() < 0.01);
}
#[test]
fn test_export_contains_bt2020() {
assert!(export_bt2020_config(&Bt2020Config::default()).contains("BT.2020"));
}
#[test]
fn test_validate_default() {
assert!(validate_bt2020_config(&Bt2020Config::default()));
}
#[test]
fn test_validate_invalid_bit_depth() {
let cfg = Bt2020Config {
bit_depth: 8,
..Default::default()
};
assert!(!validate_bt2020_config(&cfg));
}
#[test]
fn test_oetf_triplet_length() {
assert_eq!(bt2020_oetf_triplet([0.5, 0.5, 0.5]).len(), 3);
}
#[test]
fn test_linear_below_beta_uses_linear_segment() {
let v = 0.001f32;
assert!((bt2020_oetf(v) - 4.5 * v).abs() < 0.0001);
}
}