use derive_more::{Display, IsVariant};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
pub enum ColorMatrix {
Bt601,
#[default]
Bt709,
Bt2020Ncl,
Smpte240m,
Fcc,
YCgCo,
}
impl ColorMatrix {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Bt601 => "bt601",
Self::Bt709 => "bt709",
Self::Bt2020Ncl => "bt2020nc",
Self::Smpte240m => "smpte240m",
Self::Fcc => "fcc",
Self::YCgCo => "ycgco",
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
pub enum ColorPrimaries {
Bt709,
#[default]
Unspecified,
Bt470M,
Bt470Bg,
Smpte170M,
Smpte240M,
Film,
Bt2020,
SmpteSt428,
SmpteRp431,
SmpteEg432,
Ebu3213E,
}
impl ColorPrimaries {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Bt709 => "bt709",
Self::Unspecified => "unspecified",
Self::Bt470M => "bt470m",
Self::Bt470Bg => "bt470bg",
Self::Smpte170M => "smpte170m",
Self::Smpte240M => "smpte240m",
Self::Film => "film",
Self::Bt2020 => "bt2020",
Self::SmpteSt428 => "smpte428",
Self::SmpteRp431 => "smpte431",
Self::SmpteEg432 => "smpte432",
Self::Ebu3213E => "ebu3213",
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
pub enum ColorTransfer {
Bt709,
#[default]
Unspecified,
Bt470M,
Bt470Bg,
Smpte170M,
Smpte240M,
Linear,
Log100,
Log316,
Iec6196624,
Bt1361Ecg,
Iec6196621,
Bt2020_10Bit,
Bt2020_12Bit,
SmpteSt2084Pq,
SmpteSt428,
AribStdB67Hlg,
}
impl ColorTransfer {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Bt709 => "bt709",
Self::Unspecified => "unspecified",
Self::Bt470M => "gamma22",
Self::Bt470Bg => "gamma28",
Self::Smpte170M => "smpte170m",
Self::Smpte240M => "smpte240m",
Self::Linear => "linear",
Self::Log100 => "log100",
Self::Log316 => "log316",
Self::Iec6196624 => "iec61966-2-4",
Self::Bt1361Ecg => "bt1361e",
Self::Iec6196621 => "iec61966-2-1",
Self::Bt2020_10Bit => "bt2020-10",
Self::Bt2020_12Bit => "bt2020-12",
Self::SmpteSt2084Pq => "smpte2084",
Self::SmpteSt428 => "smpte428",
Self::AribStdB67Hlg => "arib-std-b67",
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
pub enum ColorRange {
#[default]
Unspecified,
Limited,
Full,
}
impl ColorRange {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Unspecified => "unspecified",
Self::Limited => "tv",
Self::Full => "pc",
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
pub enum ChromaLocation {
#[default]
Unspecified,
Left,
Center,
TopLeft,
Top,
BottomLeft,
Bottom,
}
impl ChromaLocation {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Unspecified => "unspecified",
Self::Left => "left",
Self::Center => "center",
Self::TopLeft => "topleft",
Self::Top => "top",
Self::BottomLeft => "bottomleft",
Self::Bottom => "bottom",
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ColorInfo {
primaries: ColorPrimaries,
transfer: ColorTransfer,
matrix: ColorMatrix,
range: ColorRange,
chroma_location: ChromaLocation,
}
impl ColorInfo {
pub const UNSPECIFIED: Self = Self {
primaries: ColorPrimaries::Unspecified,
transfer: ColorTransfer::Unspecified,
matrix: ColorMatrix::Bt709,
range: ColorRange::Unspecified,
chroma_location: ChromaLocation::Unspecified,
};
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(
primaries: ColorPrimaries,
transfer: ColorTransfer,
matrix: ColorMatrix,
range: ColorRange,
chroma_location: ChromaLocation,
) -> Self {
Self {
primaries,
transfer,
matrix,
range,
chroma_location,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn primaries(&self) -> ColorPrimaries {
self.primaries
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn transfer(&self) -> ColorTransfer {
self.transfer
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn matrix(&self) -> ColorMatrix {
self.matrix
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn range(&self) -> ColorRange {
self.range
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn chroma_location(&self) -> ChromaLocation {
self.chroma_location
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_primaries(mut self, v: ColorPrimaries) -> Self {
self.primaries = v;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_transfer(mut self, v: ColorTransfer) -> Self {
self.transfer = v;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_matrix(mut self, v: ColorMatrix) -> Self {
self.matrix = v;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_range(mut self, v: ColorRange) -> Self {
self.range = v;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_chroma_location(mut self, v: ChromaLocation) -> Self {
self.chroma_location = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_primaries(&mut self, v: ColorPrimaries) -> &mut Self {
self.primaries = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_transfer(&mut self, v: ColorTransfer) -> &mut Self {
self.transfer = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_matrix(&mut self, v: ColorMatrix) -> &mut Self {
self.matrix = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_range(&mut self, v: ColorRange) -> &mut Self {
self.range = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_chroma_location(&mut self, v: ChromaLocation) -> &mut Self {
self.chroma_location = v;
self
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, IsVariant)]
#[non_exhaustive]
pub enum DcpTargetGamut {
#[default]
DciP3,
Rec709,
Rec2020,
}
impl DcpTargetGamut {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn default_dcp() -> Self {
Self::DciP3
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_match_spec() {
assert!(matches!(ColorMatrix::default(), ColorMatrix::Bt709));
assert!(matches!(
ColorPrimaries::default(),
ColorPrimaries::Unspecified
));
assert!(matches!(
ColorTransfer::default(),
ColorTransfer::Unspecified
));
assert!(matches!(ColorRange::default(), ColorRange::Unspecified));
assert!(matches!(
ChromaLocation::default(),
ChromaLocation::Unspecified
));
}
#[test]
fn is_variant_helpers_compile_for_each_enum() {
assert!(ColorMatrix::Bt709.is_bt_709());
assert!(ColorPrimaries::Bt2020.is_bt_2020());
assert!(ColorTransfer::SmpteSt2084Pq.is_smpte_st_2084_pq());
assert!(ColorRange::Full.is_full());
assert!(ChromaLocation::Center.is_center());
}
#[test]
fn copy_and_eq() {
let m1 = ColorMatrix::Bt709;
let m2 = m1; assert_eq!(m1, m2);
}
#[test]
fn color_info_default_is_unspecified_with_bt709_matrix() {
let ci = ColorInfo::default();
assert_eq!(ci, ColorInfo::UNSPECIFIED);
assert!(ci.primaries().is_unspecified());
assert!(ci.matrix().is_bt_709());
}
#[test]
fn color_info_builders_chain() {
let ci = ColorInfo::UNSPECIFIED
.with_primaries(ColorPrimaries::Bt2020)
.with_transfer(ColorTransfer::SmpteSt2084Pq)
.with_matrix(ColorMatrix::Bt2020Ncl)
.with_range(ColorRange::Limited)
.with_chroma_location(ChromaLocation::Left);
assert!(ci.primaries().is_bt_2020());
assert!(ci.transfer().is_smpte_st_2084_pq());
assert!(ci.matrix().is_bt_2020_ncl());
assert!(ci.range().is_limited());
assert!(ci.chroma_location().is_left());
}
#[test]
fn color_info_setters_chain() {
let mut ci = ColorInfo::UNSPECIFIED;
ci.set_primaries(ColorPrimaries::Bt709)
.set_transfer(ColorTransfer::Bt709)
.set_matrix(ColorMatrix::Bt709)
.set_range(ColorRange::Limited)
.set_chroma_location(ChromaLocation::Left);
assert!(ci.primaries().is_bt_709());
assert!(ci.range().is_limited());
}
#[test]
fn color_info_const_construction() {
const CI: ColorInfo = ColorInfo::new(
ColorPrimaries::Bt709,
ColorTransfer::Bt709,
ColorMatrix::Bt709,
ColorRange::Limited,
ChromaLocation::Left,
);
assert!(CI.matrix().is_bt_709());
}
#[cfg(feature = "std")]
#[test]
fn as_str_matches_display() {
use std::format;
for (s, d) in [
(
ColorMatrix::Bt601.as_str(),
format!("{}", ColorMatrix::Bt601),
),
(
ColorMatrix::Bt2020Ncl.as_str(),
format!("{}", ColorMatrix::Bt2020Ncl),
),
(
ColorMatrix::YCgCo.as_str(),
format!("{}", ColorMatrix::YCgCo),
),
] {
assert_eq!(s, d, "ColorMatrix as_str/Display mismatch");
}
assert_eq!(ColorPrimaries::SmpteSt428.as_str(), "smpte428");
assert_eq!(ColorTransfer::SmpteSt2084Pq.as_str(), "smpte2084");
assert_eq!(ColorTransfer::Bt2020_10Bit.as_str(), "bt2020-10");
assert_eq!(ColorRange::Limited.as_str(), "tv");
assert_eq!(ColorRange::Full.as_str(), "pc");
assert_eq!(ChromaLocation::TopLeft.as_str(), "topleft");
}
}