mp4_atom/moov/trak/mdia/minf/stbl/stsd/
colr.rs

1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub enum Colr {
6    Nclx {
7        colour_primaries: u16,
8        transfer_characteristics: u16,
9        matrix_coefficients: u16,
10        full_range_flag: bool,
11    },
12    Ricc {
13        profile: Vec<u8>,
14    },
15    Prof {
16        profile: Vec<u8>,
17    },
18}
19
20impl Colr {
21    pub fn new(
22        colour_primaries: u16,
23        transfer_characteristics: u16,
24        matrix_coefficients: u16,
25        full_range_flag: bool,
26    ) -> Result<Self> {
27        Ok(Colr::Nclx {
28            colour_primaries,
29            transfer_characteristics,
30            matrix_coefficients,
31            full_range_flag,
32        })
33    }
34}
35
36impl Atom for Colr {
37    const KIND: FourCC = FourCC::new(b"colr");
38
39    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
40        let colour_type = FourCC::decode(buf)?;
41        if colour_type == FourCC::new(b"nclx") {
42            let colour_primaries = u16::decode(buf)?;
43            let transfer_characteristics = u16::decode(buf)?;
44            let matrix_coefficients = u16::decode(buf)?;
45            let full_range_flag = u8::decode(buf)? == 0x80;
46            Ok(Colr::Nclx {
47                colour_primaries,
48                transfer_characteristics,
49                matrix_coefficients,
50                full_range_flag,
51            })
52        } else if colour_type == FourCC::new(b"prof") {
53            let profile_len = buf.remaining();
54            let profile = buf.slice(profile_len).to_vec();
55            Ok(Colr::Prof { profile })
56        } else if colour_type == FourCC::new(b"rICC") {
57            let profile_len = buf.remaining();
58            let profile = buf.slice(profile_len).to_vec();
59            Ok(Colr::Ricc { profile })
60        } else {
61            Err(Error::UnexpectedBox(colour_type))
62        }
63    }
64
65    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
66        match self {
67            Colr::Nclx {
68                colour_primaries,
69                transfer_characteristics,
70                matrix_coefficients,
71                full_range_flag,
72            } => {
73                b"nclx".encode(buf)?;
74                colour_primaries.encode(buf)?;
75                transfer_characteristics.encode(buf)?;
76                matrix_coefficients.encode(buf)?;
77                if *full_range_flag {
78                    0x80u8.encode(buf)?;
79                } else {
80                    0x00u8.encode(buf)?;
81                }
82            }
83            Colr::Ricc { profile } => {
84                b"rICC".encode(buf)?;
85                profile.encode(buf)?;
86            }
87            Colr::Prof { profile } => {
88                b"prof".encode(buf)?;
89                profile.encode(buf)?;
90            }
91        }
92        Ok(())
93    }
94}
95
96impl Default for Colr {
97    fn default() -> Self {
98        Colr::Nclx {
99            // These match MIAF defaults (ISO/IEC 23000-22:2025 7.3.6.4), probably a reasonable set
100            colour_primaries: 1,
101            transfer_characteristics: 13,
102            matrix_coefficients: 5,
103            full_range_flag: true,
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_nclx_decode() {
114        const ENCODED: &[u8] = &[
115            0x00, 0x00, 0x00, 0x13, 0x63, 0x6f, 0x6c, 0x72, 0x6e, 0x63, 0x6c, 0x78, 0x00, 0x01,
116            0x00, 0x01, 0x00, 0x01, 0x00,
117        ];
118
119        let buf = &mut std::io::Cursor::new(&ENCODED);
120
121        let colr = Colr::decode(buf).expect("failed to decode colr");
122
123        assert_eq!(
124            colr,
125            Colr::Nclx {
126                colour_primaries: 1,
127                transfer_characteristics: 1,
128                matrix_coefficients: 1,
129                full_range_flag: false
130            }
131        );
132    }
133
134    #[test]
135    fn test_nclx_encode() {
136        const ENCODED: &[u8] = &[
137            0x00, 0x00, 0x00, 0x13, 0x63, 0x6f, 0x6c, 0x72, 0x6e, 0x63, 0x6c, 0x78, 0x00, 0x01,
138            0x00, 0x0d, 0x00, 0x06, 0x80,
139        ];
140
141        let colr = Colr::Nclx {
142            colour_primaries: 1,
143            transfer_characteristics: 13,
144            matrix_coefficients: 6,
145            full_range_flag: true,
146        };
147
148        let mut buf = Vec::new();
149        colr.encode(&mut buf).unwrap();
150
151        assert_eq!(buf.as_slice(), ENCODED);
152    }
153}