use std::fmt;
use std::str::FromStr;
use crate::Error;
#[doc(hidden)]
#[macro_export]
macro_rules! named_enum {
($(#[$m:meta])* $name:ident { $($(#[$vm:meta])* $v:ident = $s:literal),+ $(,)? }) => {
$(#[$m])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum $name { $($(#[$vm])* $v),+ }
impl $name {
pub const fn as_str(self) -> &'static str {
match self { $(Self::$v => $s),+ }
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for $name {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
match s {
$($s => Ok(Self::$v),)+
_ => Err(Error::Invalid { what: stringify!($name), value: s.to_owned() }),
}
}
}
};
}
#[doc(hidden)]
pub use crate::named_enum;
named_enum!(
Primaries {
Bt709 = "bt709",
Bt2020 = "bt2020",
DisplayP3 = "smpte432",
DciP3 = "smpte431",
Bt601 = "smpte170m",
Unknown = "unknown",
}
);
named_enum!(
Transfer {
Bt709 = "bt709",
Srgb = "iec61966-2-1",
Pq = "smpte2084",
Hlg = "arib-std-b67",
Linear = "linear",
Unknown = "unknown",
}
);
named_enum!(
Matrix {
Bt709 = "bt709",
Bt2020Ncl = "bt2020nc",
Bt601 = "smpte170m",
Rgb = "gbr",
Unknown = "unknown",
}
);
named_enum!(
Range {
Limited = "tv",
Full = "pc",
Unknown = "unknown",
}
);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ColorSpace {
pub primaries: Primaries,
pub transfer: Transfer,
pub matrix: Matrix,
pub range: Range,
}
impl ColorSpace {
pub const BT709_TV: Self = Self {
primaries: Primaries::Bt709,
transfer: Transfer::Bt709,
matrix: Matrix::Bt709,
range: Range::Limited,
};
pub const BT709_RGB: Self = Self {
primaries: Primaries::Bt709,
transfer: Transfer::Bt709,
matrix: Matrix::Rgb,
range: Range::Full,
};
pub const UNKNOWN: Self = Self {
primaries: Primaries::Unknown,
transfer: Transfer::Unknown,
matrix: Matrix::Unknown,
range: Range::Unknown,
};
pub fn is_hdr(&self) -> bool {
matches!(self.transfer, Transfer::Pq | Transfer::Hlg)
}
}
impl Default for ColorSpace {
fn default() -> Self {
Self::BT709_TV
}
}
impl fmt::Display for ColorSpace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}/{}/{}/{}",
self.matrix, self.primaries, self.transfer, self.range
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn names_round_trip() {
assert_eq!("arib-std-b67".parse::<Transfer>().unwrap(), Transfer::Hlg);
assert_eq!(Range::Full.to_string(), "pc");
assert!("nope".parse::<Primaries>().is_err());
assert_eq!(ColorSpace::BT709_TV.to_string(), "bt709/bt709/bt709/tv");
}
#[test]
fn hdr() {
let hlg = ColorSpace {
transfer: Transfer::Hlg,
..ColorSpace::BT709_TV
};
assert!(hlg.is_hdr());
assert!(!ColorSpace::BT709_RGB.is_hdr());
}
}