#![allow(clippy::match_same_arms)]
use super::metadata::{ContentLightLevel, MasteringDisplayColorVolume};
use super::primaries::{ColorPrimaries, Primaries, WhitePoint};
use super::transfer::TransferCharacteristic;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Vp9ColorConfig {
pub bit_depth: u8,
pub color_space: u8,
pub color_range: bool,
pub subsampling_x: bool,
pub subsampling_y: bool,
}
impl Vp9ColorConfig {
#[must_use]
pub const fn new(
bit_depth: u8,
color_space: u8,
color_range: bool,
subsampling_x: bool,
subsampling_y: bool,
) -> Self {
Self {
bit_depth,
color_space,
color_range,
subsampling_x,
subsampling_y,
}
}
#[must_use]
pub const fn to_color_primaries(&self) -> ColorPrimaries {
match self.color_space {
1 | 3 => ColorPrimaries::Smpte170M, 2 => ColorPrimaries::BT709,
4 => ColorPrimaries::Smpte240M,
5 | 9 => ColorPrimaries::BT2020,
7 => ColorPrimaries::BT709, _ => ColorPrimaries::BT709, }
}
#[must_use]
pub const fn is_420(&self) -> bool {
self.subsampling_x && self.subsampling_y
}
#[must_use]
pub const fn is_422(&self) -> bool {
self.subsampling_x && !self.subsampling_y
}
#[must_use]
pub const fn is_444(&self) -> bool {
!self.subsampling_x && !self.subsampling_y
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[allow(clippy::struct_excessive_bools)]
pub struct Av1ColorConfig {
pub bit_depth: u8,
pub mono_chrome: bool,
pub color_primaries: u8,
pub transfer_characteristics: u8,
pub matrix_coefficients: u8,
pub color_range: bool,
pub subsampling_x: bool,
pub subsampling_y: bool,
pub chroma_sample_position: u8,
pub separate_uv_delta_q: bool,
}
impl Av1ColorConfig {
#[must_use]
#[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)]
pub const fn new(
bit_depth: u8,
mono_chrome: bool,
color_primaries: u8,
transfer_characteristics: u8,
matrix_coefficients: u8,
color_range: bool,
subsampling_x: bool,
subsampling_y: bool,
) -> Self {
Self {
bit_depth,
mono_chrome,
color_primaries,
transfer_characteristics,
matrix_coefficients,
color_range,
subsampling_x,
subsampling_y,
chroma_sample_position: 0,
separate_uv_delta_q: false,
}
}
#[must_use]
pub const fn to_color_primaries(&self) -> ColorPrimaries {
match self.color_primaries {
1 => ColorPrimaries::BT709,
4 => ColorPrimaries::Bt470M,
5 => ColorPrimaries::Bt470Bg,
6 => ColorPrimaries::Smpte170M,
7 => ColorPrimaries::Smpte240M,
8 => ColorPrimaries::Film,
9 => ColorPrimaries::BT2020,
10 => ColorPrimaries::BT709, 11 => ColorPrimaries::DciP3,
12 => ColorPrimaries::DisplayP3,
_ => ColorPrimaries::BT709, }
}
#[must_use]
pub const fn to_transfer_characteristic(&self) -> TransferCharacteristic {
match self.transfer_characteristics {
1 | 6 | 14 | 15 => TransferCharacteristic::Bt709, 8 => TransferCharacteristic::Linear,
13 => TransferCharacteristic::Srgb,
16 => TransferCharacteristic::Pq, 18 => TransferCharacteristic::Hlg, _ => TransferCharacteristic::Bt709, }
}
#[must_use]
pub const fn is_hdr(&self) -> bool {
matches!(self.transfer_characteristics, 16 | 18) }
#[must_use]
pub const fn is_420(&self) -> bool {
self.subsampling_x && self.subsampling_y
}
#[must_use]
pub const fn is_422(&self) -> bool {
self.subsampling_x && !self.subsampling_y
}
#[must_use]
pub const fn is_444(&self) -> bool {
!self.subsampling_x && !self.subsampling_y
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct MatroskaColorElements {
pub matrix_coefficients: Option<u8>,
pub bits_per_channel: Option<u8>,
pub chroma_subsampling_horz: Option<u8>,
pub chroma_subsampling_vert: Option<u8>,
pub cb_subsampling_horz: Option<u8>,
pub cb_subsampling_vert: Option<u8>,
pub chroma_siting_horz: Option<u8>,
pub chroma_siting_vert: Option<u8>,
pub range: Option<u8>,
pub transfer_characteristics: Option<u8>,
pub primaries: Option<u8>,
pub max_cll: Option<u16>,
pub max_fall: Option<u16>,
pub primary_r_chromaticity_x: Option<f64>,
pub primary_r_chromaticity_y: Option<f64>,
pub primary_g_chromaticity_x: Option<f64>,
pub primary_g_chromaticity_y: Option<f64>,
pub primary_b_chromaticity_x: Option<f64>,
pub primary_b_chromaticity_y: Option<f64>,
pub white_point_chromaticity_x: Option<f64>,
pub white_point_chromaticity_y: Option<f64>,
pub luminance_max: Option<f64>,
pub luminance_min: Option<f64>,
}
impl MatroskaColorElements {
#[must_use]
pub const fn new() -> Self {
Self {
matrix_coefficients: None,
bits_per_channel: None,
chroma_subsampling_horz: None,
chroma_subsampling_vert: None,
cb_subsampling_horz: None,
cb_subsampling_vert: None,
chroma_siting_horz: None,
chroma_siting_vert: None,
range: None,
transfer_characteristics: None,
primaries: None,
max_cll: None,
max_fall: None,
primary_r_chromaticity_x: None,
primary_r_chromaticity_y: None,
primary_g_chromaticity_x: None,
primary_g_chromaticity_y: None,
primary_b_chromaticity_x: None,
primary_b_chromaticity_y: None,
white_point_chromaticity_x: None,
white_point_chromaticity_y: None,
luminance_max: None,
luminance_min: None,
}
}
#[must_use]
pub fn to_color_primaries(&self) -> ColorPrimaries {
if let Some(primaries) = self.primaries {
match primaries {
1 => ColorPrimaries::BT709,
4 => ColorPrimaries::Bt470M,
5 => ColorPrimaries::Bt470Bg,
6 => ColorPrimaries::Smpte170M,
7 => ColorPrimaries::Smpte240M,
8 => ColorPrimaries::Film,
9 => ColorPrimaries::BT2020,
10 => ColorPrimaries::BT709,
11 => ColorPrimaries::DciP3,
12 => ColorPrimaries::DisplayP3,
_ => ColorPrimaries::BT709,
}
} else {
ColorPrimaries::BT709
}
}
#[must_use]
pub fn to_transfer_characteristic(&self) -> TransferCharacteristic {
if let Some(transfer) = self.transfer_characteristics {
match transfer {
1 | 6 | 14 | 15 => TransferCharacteristic::Bt709,
8 => TransferCharacteristic::Linear,
13 => TransferCharacteristic::Srgb,
16 => TransferCharacteristic::Pq,
18 => TransferCharacteristic::Hlg,
_ => TransferCharacteristic::Bt709,
}
} else {
TransferCharacteristic::Bt709
}
}
#[must_use]
pub fn to_mdcv(&self) -> Option<MasteringDisplayColorVolume> {
let primaries = Primaries {
red: (
self.primary_r_chromaticity_x?,
self.primary_r_chromaticity_y?,
),
green: (
self.primary_g_chromaticity_x?,
self.primary_g_chromaticity_y?,
),
blue: (
self.primary_b_chromaticity_x?,
self.primary_b_chromaticity_y?,
),
};
let white_point = WhitePoint::Custom(
self.white_point_chromaticity_x?,
self.white_point_chromaticity_y?,
);
Some(MasteringDisplayColorVolume {
display_primaries: primaries,
white_point,
max_luminance: self.luminance_max?,
min_luminance: self.luminance_min?,
})
}
#[must_use]
pub fn to_cll(&self) -> Option<ContentLightLevel> {
Some(ContentLightLevel {
max_cll: self.max_cll?,
max_fall: self.max_fall?,
})
}
#[must_use]
pub fn is_hdr(&self) -> bool {
matches!(self.transfer_characteristics, Some(16 | 18))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct HevcSeiParser {
_private: (),
}
impl HevcSeiParser {
pub fn parse_sei_message(_data: &[u8]) -> Result<(), &'static str> {
Err("HEVC/H.265 is not supported (patent-encumbered codec)")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vp9_color_config() {
let config = Vp9ColorConfig::new(10, 9, false, true, true);
assert_eq!(config.bit_depth, 10);
assert_eq!(config.color_space, 9);
assert!(!config.color_range);
assert!(config.is_420());
assert!(!config.is_422());
assert!(!config.is_444());
}
#[test]
fn test_vp9_to_color_primaries() {
let config = Vp9ColorConfig::new(10, 9, false, true, true);
assert_eq!(config.to_color_primaries(), ColorPrimaries::BT2020);
let config_709 = Vp9ColorConfig::new(8, 2, false, true, true);
assert_eq!(config_709.to_color_primaries(), ColorPrimaries::BT709);
}
#[test]
fn test_av1_color_config() {
let config = Av1ColorConfig::new(10, false, 9, 16, 9, false, true, true);
assert_eq!(config.bit_depth, 10);
assert!(!config.mono_chrome);
assert!(config.is_hdr());
assert!(config.is_420());
}
#[test]
fn test_av1_to_color_primaries() {
let config = Av1ColorConfig::new(10, false, 9, 16, 9, false, true, true);
assert_eq!(config.to_color_primaries(), ColorPrimaries::BT2020);
}
#[test]
fn test_av1_to_transfer_characteristic() {
let config = Av1ColorConfig::new(10, false, 9, 16, 9, false, true, true);
assert_eq!(
config.to_transfer_characteristic(),
TransferCharacteristic::Pq
);
let hlg_config = Av1ColorConfig::new(10, false, 9, 18, 9, false, true, true);
assert_eq!(
hlg_config.to_transfer_characteristic(),
TransferCharacteristic::Hlg
);
}
#[test]
fn test_matroska_color_elements() {
let mut color = MatroskaColorElements::new();
color.primaries = Some(9);
color.transfer_characteristics = Some(16);
color.max_cll = Some(1000);
color.max_fall = Some(400);
assert_eq!(color.to_color_primaries(), ColorPrimaries::BT2020);
assert_eq!(
color.to_transfer_characteristic(),
TransferCharacteristic::Pq
);
assert!(color.is_hdr());
let cll = color.to_cll().expect("CLL extraction should succeed");
assert_eq!(cll.max_cll, 1000);
assert_eq!(cll.max_fall, 400);
}
#[test]
fn test_matroska_mdcv_extraction() {
let mut color = MatroskaColorElements::new();
color.primary_r_chromaticity_x = Some(0.708);
color.primary_r_chromaticity_y = Some(0.292);
color.primary_g_chromaticity_x = Some(0.170);
color.primary_g_chromaticity_y = Some(0.797);
color.primary_b_chromaticity_x = Some(0.131);
color.primary_b_chromaticity_y = Some(0.046);
color.white_point_chromaticity_x = Some(0.3127);
color.white_point_chromaticity_y = Some(0.3290);
color.luminance_max = Some(1000.0);
color.luminance_min = Some(0.005);
let mdcv = color.to_mdcv().expect("MDCV extraction should succeed");
assert_eq!(mdcv.display_primaries.red, (0.708, 0.292));
assert_eq!(mdcv.max_luminance, 1000.0);
assert_eq!(mdcv.min_luminance, 0.005);
}
#[test]
fn test_hevc_sei_parser_unsupported() {
let result = HevcSeiParser::parse_sei_message(&[]);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"HEVC/H.265 is not supported (patent-encumbered codec)"
);
}
}