#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Primaries {
Reserved,
Bt709 = 1,
Unspecified = 2,
Bt470M = 4,
Bt470Bg = 5,
Bt601 = 6,
Smpte240 = 7,
GenericFilm = 8,
Bt2020 = 9,
Xyz = 10,
Smpte431 = 11,
Smpte432 = 12,
Ebu3213 = 22,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum TransferFunction {
Reserved,
Bt709 = 1,
Unspecified = 2,
Bt470M = 4,
Bt470Bg = 5,
Bt601 = 6,
Smpte240 = 7,
Linear = 8,
Log100 = 9,
Log100sqrt10 = 10,
Iec61966 = 11,
Bt1361 = 12,
Srgb = 13,
Bt202010bit = 14,
Bt202012bit = 15,
Smpte2084 = 16,
Smpte428 = 17,
Hlg = 18,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum MatrixCoefficients {
Identity = 0, Bt709 = 1, Unspecified = 2, Reserved = 3, Fcc = 4, Bt470Bg = 5, Smpte170m = 6, Smpte240m = 7, YCgCo = 8, Bt2020Ncl = 9, Bt2020Cl = 10, Smpte2085 = 11, ChromaticityDerivedNCL = 12, ChromaticityDerivedCL = 13, ICtCp = 14, IPtC2 = 15, YCgCoRe = 16, YCgCoRo = 17, }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ColorEncoding {
pub primaries: Primaries,
pub transfer: TransferFunction,
pub matrix: MatrixCoefficients,
pub full_range: bool,
}
impl ColorEncoding {
pub const fn srgb() -> Self {
ColorEncoding {
primaries: Primaries::Bt709,
transfer: TransferFunction::Srgb,
matrix: MatrixCoefficients::Bt709,
full_range: true,
}
}
pub const fn bt709() -> Self {
ColorEncoding {
primaries: Primaries::Bt709,
transfer: TransferFunction::Bt709,
matrix: MatrixCoefficients::Bt709,
full_range: true,
}
}
pub const fn bt2020_pq() -> Self {
ColorEncoding {
primaries: Primaries::Bt2020,
transfer: TransferFunction::Smpte2084,
matrix: MatrixCoefficients::Bt2020Ncl,
full_range: true,
}
}
pub const fn unspecified() -> Self {
ColorEncoding {
primaries: Primaries::Unspecified,
transfer: TransferFunction::Unspecified,
matrix: MatrixCoefficients::Unspecified,
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 ColorEncoding {
fn default() -> Self {
ColorEncoding::unspecified()
}
}
#[derive(Clone, Debug)]
pub struct ColorMetadata {
pub cicp: Option<ColorEncoding>,
pub icc: Option<Vec<u8>>,
}
impl ColorMetadata {
pub fn cicp(enc: ColorEncoding) -> Self {
ColorMetadata {
cicp: Some(enc),
icc: None,
}
}
pub fn icc(profile: Vec<u8>) -> Self {
ColorMetadata {
cicp: None,
icc: Some(profile),
}
}
pub fn cicp_and_icc(enc: ColorEncoding, profile: Vec<u8>) -> Self {
ColorMetadata {
cicp: Some(enc),
icc: Some(profile),
}
}
pub fn with_cicp(mut self, enc: ColorEncoding) -> Self {
self.cicp = Some(enc);
self
}
pub fn with_icc(mut self, profile: Vec<u8>) -> Self {
self.icc = Some(profile);
self
}
pub fn color_encoding(&self) -> ColorEncoding {
self.cicp.unwrap_or_else(ColorEncoding::srgb)
}
}
impl Default for ColorMetadata {
fn default() -> Self {
ColorMetadata::cicp(ColorEncoding::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nclx_payload_layout() {
let p = ColorEncoding::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); assert_eq!(p.len(), 11);
}
#[test]
fn pq_encoding_values() {
let e = ColorEncoding::bt2020_pq();
assert_eq!(e.primaries as u8, 9);
assert_eq!(e.transfer as u8, 16);
assert_eq!(e.matrix as u8, 9);
}
}