use super::primaries::{Primaries, WhitePoint};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MasteringDisplayColorVolume {
pub display_primaries: Primaries,
pub white_point: WhitePoint,
pub max_luminance: f64,
pub min_luminance: f64,
}
impl MasteringDisplayColorVolume {
#[must_use]
pub fn new_bt2020(max_luminance: f64, min_luminance: f64) -> Self {
Self {
display_primaries: Primaries {
red: (0.708, 0.292),
green: (0.170, 0.797),
blue: (0.131, 0.046),
},
white_point: WhitePoint::D65,
max_luminance,
min_luminance,
}
}
#[must_use]
pub fn new_dci_p3(max_luminance: f64, min_luminance: f64) -> Self {
Self {
display_primaries: Primaries {
red: (0.680, 0.320),
green: (0.265, 0.690),
blue: (0.150, 0.060),
},
white_point: WhitePoint::D65,
max_luminance,
min_luminance,
}
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.max_luminance > self.min_luminance
&& self.min_luminance >= 0.0
&& self.max_luminance <= 10000.0
}
#[must_use]
pub fn dynamic_range_stops(&self) -> f64 {
if self.min_luminance > 0.0 {
(self.max_luminance / self.min_luminance).log2()
} else {
f64::INFINITY
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ContentLightLevel {
pub max_cll: u16,
pub max_fall: u16,
}
impl ContentLightLevel {
#[must_use]
pub const fn new(max_cll: u16, max_fall: u16) -> Self {
Self { max_cll, max_fall }
}
#[must_use]
pub const fn is_valid(&self) -> bool {
self.max_cll >= self.max_fall && self.max_cll > 0
}
#[must_use]
pub const fn is_hdr(&self) -> bool {
self.max_cll > 300
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Hdr10PlusMetadata {
pub application_version: u8,
pub num_windows: u8,
pub target_max_luminance: u16,
}
impl Hdr10PlusMetadata {
#[must_use]
pub const fn new(target_max_luminance: u16) -> Self {
Self {
application_version: 1,
num_windows: 1,
target_max_luminance,
}
}
#[must_use]
pub const fn is_valid(&self) -> bool {
self.num_windows > 0 && self.num_windows <= 3 && self.target_max_luminance > 0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DolbyVisionMetadata {
pub profile: u8,
pub level: u8,
pub rpu_present: bool,
pub el_present: bool,
pub bl_present: bool,
}
impl DolbyVisionMetadata {
#[must_use]
pub const fn new(profile: u8, level: u8) -> Self {
Self {
profile,
level,
rpu_present: true,
el_present: false,
bl_present: true,
}
}
#[must_use]
pub const fn is_profile_5(&self) -> bool {
self.profile == 5
}
#[must_use]
pub const fn is_profile_8(&self) -> bool {
self.profile == 8
}
#[must_use]
pub const fn is_hdr10_compatible(&self) -> bool {
self.profile == 7 || self.profile == 8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct HlgParameters {
pub application_version: u8,
}
impl HlgParameters {
#[must_use]
pub const fn new(application_version: u8) -> Self {
Self {
application_version,
}
}
#[must_use]
pub const fn is_sdr_compatible(&self) -> bool {
self.application_version == 0
}
}
impl Default for HlgParameters {
fn default() -> Self {
Self::new(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mdcv_creation() {
let mdcv = MasteringDisplayColorVolume::new_bt2020(1000.0, 0.005);
assert_eq!(mdcv.max_luminance, 1000.0);
assert_eq!(mdcv.min_luminance, 0.005);
assert!(mdcv.is_valid());
}
#[test]
fn test_mdcv_dci_p3() {
let mdcv = MasteringDisplayColorVolume::new_dci_p3(1000.0, 0.05);
assert_eq!(mdcv.max_luminance, 1000.0);
assert!(mdcv.is_valid());
}
#[test]
fn test_mdcv_validation() {
let valid = MasteringDisplayColorVolume::new_bt2020(1000.0, 0.005);
assert!(valid.is_valid());
let invalid = MasteringDisplayColorVolume::new_bt2020(0.001, 1000.0);
assert!(!invalid.is_valid());
let too_bright = MasteringDisplayColorVolume::new_bt2020(20000.0, 0.005);
assert!(!too_bright.is_valid());
}
#[test]
fn test_mdcv_dynamic_range() {
let mdcv = MasteringDisplayColorVolume::new_bt2020(1000.0, 0.005);
let stops = mdcv.dynamic_range_stops();
assert!((stops - 17.6).abs() < 0.1);
}
#[test]
fn test_cll_creation() {
let cll = ContentLightLevel::new(1000, 400);
assert_eq!(cll.max_cll, 1000);
assert_eq!(cll.max_fall, 400);
assert!(cll.is_valid());
}
#[test]
fn test_cll_validation() {
let valid = ContentLightLevel::new(1000, 400);
assert!(valid.is_valid());
let suspicious = ContentLightLevel::new(100, 500);
assert!(!suspicious.is_valid());
let zero = ContentLightLevel::new(0, 0);
assert!(!zero.is_valid());
}
#[test]
fn test_cll_is_hdr() {
let hdr = ContentLightLevel::new(1000, 400);
assert!(hdr.is_hdr());
let sdr = ContentLightLevel::new(200, 100);
assert!(!sdr.is_hdr());
}
#[test]
fn test_hdr10_plus_creation() {
let metadata = Hdr10PlusMetadata::new(1000);
assert_eq!(metadata.application_version, 1);
assert_eq!(metadata.num_windows, 1);
assert_eq!(metadata.target_max_luminance, 1000);
assert!(metadata.is_valid());
}
#[test]
fn test_dolby_vision_profiles() {
let dv5 = DolbyVisionMetadata::new(5, 6);
assert!(dv5.is_profile_5());
assert!(!dv5.is_profile_8());
assert!(!dv5.is_hdr10_compatible());
let dv8 = DolbyVisionMetadata::new(8, 6);
assert!(!dv8.is_profile_5());
assert!(dv8.is_profile_8());
assert!(dv8.is_hdr10_compatible());
let dv7 = DolbyVisionMetadata::new(7, 6);
assert!(dv7.is_hdr10_compatible());
}
#[test]
fn test_hlg_parameters() {
let hlg = HlgParameters::new(0);
assert_eq!(hlg.application_version, 0);
assert!(hlg.is_sdr_compatible());
let hlg_hdr = HlgParameters::new(1);
assert!(!hlg_hdr.is_sdr_compatible());
}
#[test]
fn test_hlg_default() {
let hlg = HlgParameters::default();
assert_eq!(hlg.application_version, 0);
assert!(hlg.is_sdr_compatible());
}
}