#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Primaries {
Bt709 = 1, Unspecified = 2,
Bt470M = 4, Bt470Bg = 5, Bt601 = 6, Smpte240 = 7, GenericFilm = 8,
Bt2020 = 9, Xyz = 10, Smpte431 = 11, Smpte432 = 12, Ebu3213 = 22,
}
impl Primaries {
pub fn from_u8(v: u8) -> Self {
match v {
1 => Self::Bt709,
4 => Self::Bt470M,
5 => Self::Bt470Bg,
6 => Self::Bt601,
7 => Self::Smpte240,
8 => Self::GenericFilm,
9 => Self::Bt2020,
10 => Self::Xyz,
11 => Self::Smpte431,
12 => Self::Smpte432,
22 => Self::Ebu3213,
_ => Self::Unspecified,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum TransferFunction {
Bt709 = 1, Unspecified = 2,
Bt470M = 4, Bt470Bg = 5, Bt601 = 6, Smpte240 = 7, Linear = 8,
Log100 = 9,
Log100Sqrt10 = 10,
Iec61966 = 11, Bt1361 = 12, Srgb = 13, Bt2020_10bit = 14, Bt2020_12bit = 15, Pq = 16, Smpte428 = 17, Hlg = 18, }
impl TransferFunction {
pub fn from_u8(v: u8) -> Self {
match v {
1 => Self::Bt709,
4 => Self::Bt470M,
5 => Self::Bt470Bg,
6 => Self::Bt601,
7 => Self::Smpte240,
8 => Self::Linear,
9 => Self::Log100,
10 => Self::Log100Sqrt10,
11 => Self::Iec61966,
12 => Self::Bt1361,
13 => Self::Srgb,
14 => Self::Bt2020_10bit,
15 => Self::Bt2020_12bit,
16 => Self::Pq,
17 => Self::Smpte428,
18 => Self::Hlg,
_ => Self::Unspecified,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum MatrixCoefficients {
Identity = 0, Bt709 = 1,
Unspecified = 2,
Fcc = 4,
Bt470Bg = 5, Smpte170m = 6, Smpte240m = 7,
YCgCo = 8,
Bt2020Ncl = 9, Bt2020Cl = 10, Smpte2085 = 11,
ChromaticityDerivedNcl = 12,
ChromaticityDerivedCl = 13,
ICtCp = 14,
IPtPc2 = 15,
YCgCoRe = 16,
YCgCoRo = 17,
}
impl MatrixCoefficients {
pub fn from_u8(v: u8) -> Self {
match v {
0 => Self::Identity,
1 => Self::Bt709,
4 => Self::Fcc,
5 => Self::Bt470Bg,
6 => Self::Smpte170m,
7 => Self::Smpte240m,
8 => Self::YCgCo,
9 => Self::Bt2020Ncl,
10 => Self::Bt2020Cl,
11 => Self::Smpte2085,
12 => Self::ChromaticityDerivedNcl,
13 => Self::ChromaticityDerivedCl,
14 => Self::ICtCp,
15 => Self::IPtPc2,
16 => Self::YCgCoRe,
17 => Self::YCgCoRo,
_ => Self::Unspecified,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cicp {
pub primaries: Primaries,
pub transfer: TransferFunction,
pub matrix: MatrixCoefficients,
pub full_range: bool,
}
impl Cicp {
pub const fn unspecified() -> Self {
Self {
primaries: Primaries::Unspecified,
transfer: TransferFunction::Unspecified,
matrix: MatrixCoefficients::Unspecified,
full_range: false,
}
}
pub const fn srgb() -> Self {
Self {
primaries: Primaries::Bt709,
transfer: TransferFunction::Srgb,
matrix: MatrixCoefficients::Bt709,
full_range: true,
}
}
pub const fn bt709() -> Self {
Self {
primaries: Primaries::Bt709,
transfer: TransferFunction::Bt709,
matrix: MatrixCoefficients::Bt709,
full_range: true,
}
}
pub const fn bt2020_pq() -> Self {
Self {
primaries: Primaries::Bt2020,
transfer: TransferFunction::Pq,
matrix: MatrixCoefficients::Bt2020Ncl,
full_range: true,
}
}
pub fn nclx_payload(&self) -> Vec<u8> {
let mut p = Vec::with_capacity(11);
p.extend_from_slice(b"nclx");
p.extend_from_slice(&(self.primaries as u16).to_be_bytes());
p.extend_from_slice(&(self.transfer as u16).to_be_bytes());
p.extend_from_slice(&(self.matrix as u16).to_be_bytes());
p.push(if self.full_range { 0x80 } else { 0x00 });
p
}
}
impl Default for Cicp {
fn default() -> Self {
Self::unspecified()
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ColorMetadata {
pub cicp: Option<Cicp>,
pub icc: Option<Vec<u8>>,
}
impl ColorMetadata {
pub fn from_cicp(enc: Cicp) -> Self {
Self {
cicp: Some(enc),
icc: None,
}
}
pub fn from_icc(bytes: Vec<u8>) -> Self {
Self {
cicp: None,
icc: Some(bytes),
}
}
pub fn color_encoding(&self) -> Cicp {
self.cicp.unwrap_or_else(Cicp::srgb)
}
pub fn is_empty(&self) -> bool {
self.cicp.is_none() && self.icc.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nclx_payload_layout() {
let p = Cicp::bt709().nclx_payload();
assert_eq!(&p[0..4], b"nclx");
assert_eq!(u16::from_be_bytes([p[4], p[5]]), 1);
assert_eq!(u16::from_be_bytes([p[6], p[7]]), 1);
assert_eq!(u16::from_be_bytes([p[8], p[9]]), 1);
assert_eq!(p[10] >> 7, 1);
}
#[test]
fn pq_encoding_values() {
let e = Cicp::bt2020_pq();
assert_eq!(e.primaries as u8, 9);
assert_eq!(e.transfer as u8, 16);
assert_eq!(e.matrix as u8, 9);
}
#[test]
fn default_cicp_is_not_falsely_signaled_as_bt709() {
assert_eq!(Cicp::default(), Cicp::unspecified());
}
#[test]
fn from_u8_roundtrips() {
assert_eq!(Primaries::from_u8(1), Primaries::Bt709);
assert_eq!(Primaries::from_u8(12), Primaries::Smpte432);
assert_eq!(Primaries::from_u8(99), Primaries::Unspecified);
assert_eq!(
MatrixCoefficients::from_u8(6),
MatrixCoefficients::Smpte170m
);
assert_eq!(TransferFunction::from_u8(16), TransferFunction::Pq);
}
}