1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Tx3g {
10 pub data_reference_index: u16,
11 pub display_flags: u32,
12 pub horizontal_justification: i8,
13 pub vertical_justification: i8,
14 pub bg_color_rgba: RgbaColor,
15 pub box_record: [i16; 4],
16 pub style_record: [u8; 12],
17 pub ftab: Option<Ftab>,
19 pub disp: Option<Disp>,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Disp {
29 pub disparity_shift_in_16th_pel: i16,
32}
33
34impl Atom for Disp {
35 const KIND: FourCC = FourCC::new(b"disp");
36
37 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
38 Ok(Self {
39 disparity_shift_in_16th_pel: i16::decode(buf)?,
40 })
41 }
42
43 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
44 self.disparity_shift_in_16th_pel.encode(buf)
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Default)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct RgbaColor {
51 pub red: u8,
52 pub green: u8,
53 pub blue: u8,
54 pub alpha: u8,
55}
56
57impl Decode for RgbaColor {
58 fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
59 Ok(Self {
60 red: u8::decode(buf)?,
61 green: u8::decode(buf)?,
62 blue: u8::decode(buf)?,
63 alpha: u8::decode(buf)?,
64 })
65 }
66}
67
68impl Encode for RgbaColor {
69 fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
70 self.red.encode(buf)?;
71 self.green.encode(buf)?;
72 self.blue.encode(buf)?;
73 self.alpha.encode(buf)
74 }
75}
76
77impl Default for Tx3g {
78 fn default() -> Self {
79 Tx3g {
80 data_reference_index: 0,
81 display_flags: 0,
82 horizontal_justification: 1,
83 vertical_justification: -1,
84 bg_color_rgba: RgbaColor {
85 red: 0,
86 green: 0,
87 blue: 0,
88 alpha: 255,
89 },
90 box_record: [0, 0, 0, 0],
91 style_record: [0, 0, 0, 0, 0, 1, 0, 16, 255, 255, 255, 255],
92 ftab: None, disp: None,
94 }
95 }
96}
97
98impl Atom for Tx3g {
99 const KIND: FourCC = FourCC::new(b"tx3g");
100
101 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
102 u32::decode(buf)?; u16::decode(buf)?; let data_reference_index = u16::decode(buf)?;
105
106 let display_flags = u32::decode(buf)?;
107 let horizontal_justification = i8::decode(buf)?;
108 let vertical_justification = i8::decode(buf)?;
109 let bg_color_rgba = RgbaColor::decode(buf)?;
110 let box_record: [i16; 4] = [
111 i16::decode(buf)?,
112 i16::decode(buf)?,
113 i16::decode(buf)?,
114 i16::decode(buf)?,
115 ];
116 let style_record = <[u8; 12]>::decode(buf)?;
117 let mut ftab = None;
118 let mut disp = None;
119 while let Some(atom) = Any::decode_maybe(buf)? {
120 match atom {
121 Any::Ftab(atom) => ftab = atom.into(),
122 Any::Disp(atom) => disp = atom.into(),
123 unknown => Self::decode_unknown(&unknown)?,
124 }
125 }
126 skip_trailing_padding(buf);
127
128 Ok(Tx3g {
129 data_reference_index,
130 display_flags,
131 horizontal_justification,
132 vertical_justification,
133 bg_color_rgba,
134 box_record,
135 style_record,
136 ftab,
137 disp,
138 })
139 }
140
141 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
142 0u32.encode(buf)?; 0u16.encode(buf)?; self.data_reference_index.encode(buf)?;
145 self.display_flags.encode(buf)?;
146 self.horizontal_justification.encode(buf)?;
147 self.vertical_justification.encode(buf)?;
148 self.bg_color_rgba.encode(buf)?;
149 for n in 0..4 {
150 (self.box_record[n]).encode(buf)?;
151 }
152 for n in 0..12 {
153 (self.style_record[n]).encode(buf)?;
154 }
155 self.ftab.encode(buf)?;
156 self.disp.encode(buf)?;
157
158 Ok(())
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_tx3g() {
168 let expected = Tx3g {
169 data_reference_index: 1,
170 display_flags: 0,
171 horizontal_justification: 1,
172 vertical_justification: -1,
173 bg_color_rgba: RgbaColor {
174 red: 0,
175 green: 0,
176 blue: 0,
177 alpha: 255,
178 },
179 box_record: [0, 0, 0, 0],
180 style_record: [0, 0, 0, 0, 0, 1, 0, 16, 255, 255, 255, 255],
181 ftab: None,
182 disp: None,
183 };
184 let mut buf = Vec::new();
185 expected.encode(&mut buf).unwrap();
186
187 let mut buf = buf.as_ref();
188 let decoded = Tx3g::decode(&mut buf).unwrap();
189 assert_eq!(decoded, expected);
190 }
191
192 const ENCODED_TX3G_09: &[u8] = &[
194 0x00, 0x00, 0x00, 0x40, 0x74, 0x78, 0x33, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
196 0x00, 0x3c, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x12, 0xff, 0xff, 0xff,
197 0xff, 0x00, 0x00, 0x00, 0x12, 0x66, 0x74, 0x61, 0x62, 0x00, 0x01, 0x00, 0x01, 0x05, 0x53,
198 0x65, 0x72, 0x69, 0x66,
199 ];
200
201 fn get_reference_mpeg_09_tx3g() -> Tx3g {
253 Tx3g {
254 data_reference_index: 1,
255 display_flags: 0x00040000,
256 horizontal_justification: 1,
257 vertical_justification: -1,
258 bg_color_rgba: RgbaColor {
259 red: 0xFF,
260 green: 0x00,
261 blue: 0x00,
262 alpha: 0xFF,
263 },
264 box_record: [0, 0, 60, 400],
265 style_record: [0, 0, 0, 0, 0, 1, 0, 18, 255, 255, 255, 255],
266 ftab: Some(Ftab {
267 font_entries: vec![FontEntry {
268 font_id: 1,
269 font: "Serif".into(),
270 }],
271 }),
272 disp: None,
273 }
274 }
275
276 #[test]
277 fn test_tx3g_with_default_disparity() {
278 let expected = Tx3g {
279 disp: Some(Disp {
280 disparity_shift_in_16th_pel: 48,
281 }),
282 ..get_reference_mpeg_09_tx3g()
283 };
284 let mut buf = Vec::new();
285 expected.encode(&mut buf).unwrap();
286 let decoded = Tx3g::decode(&mut buf.as_slice()).expect("failed to decode tx3g");
287 assert_eq!(decoded, expected);
288 }
289
290 #[test]
291 fn test_mpeg_09_text_decode() {
292 let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_TX3G_09);
293 let tx3g = Tx3g::decode(buf).expect("failed to decode tx3g");
294 assert_eq!(tx3g, get_reference_mpeg_09_tx3g());
295 }
296
297 #[test]
298 fn test_mpeg_09_text_encode() {
299 let tx3g = get_reference_mpeg_09_tx3g();
300 let mut buf = Vec::new();
301 tx3g.encode(&mut buf).unwrap();
302 assert_eq!(buf.as_slice(), ENCODED_TX3G_09);
303 }
304}