1mod boxes;
12pub mod constants;
13mod writer;
14
15use crate::boxes::*;
16use arrayvec::ArrayVec;
17use std::io;
18
19pub struct Aviffy {
23 premultiplied_alpha: bool,
24 colr: ColrBox,
25}
26
27pub fn serialize<W: io::Write>(into_output: W, color_av1_data: &[u8], alpha_av1_data: Option<&[u8]>, width: u32, height: u32, depth_bits: u8) -> io::Result<()> {
43 Aviffy::new().write(into_output, color_av1_data, alpha_av1_data, width, height, depth_bits)
44}
45
46impl Aviffy {
47 #[must_use]
48 pub fn new() -> Self {
49 Self {
50 premultiplied_alpha: false,
51 colr: Default::default(),
52 }
53 }
54
55 pub fn premultiplied_alpha(&mut self, is_premultiplied: bool) -> &mut Self {
63 self.premultiplied_alpha = is_premultiplied;
64 self
65 }
66
67 pub fn matrix_coefficients(&mut self, matrix_coefficients: constants::MatrixCoefficients) -> &mut Self {
71 self.colr.matrix_coefficients = matrix_coefficients;
72 self
73 }
74
75 pub fn transfer_characteristics(&mut self, transfer_characteristics: constants::TransferCharacteristics) -> &mut Self {
78 self.colr.transfer_characteristics = transfer_characteristics;
79 self
80 }
81
82 pub fn color_primaries(&mut self, color_primaries: constants::ColorPrimaries) -> &mut Self {
85 self.colr.color_primaries = color_primaries;
86 self
87 }
88
89 pub fn full_color_range(&mut self, full_range: bool) -> &mut Self {
92 self.colr.full_range_flag = full_range;
93 self
94 }
95
96 pub fn write<W: io::Write>(&self, into_output: W, color_av1_data: &[u8], alpha_av1_data: Option<&[u8]>, width: u32, height: u32, depth_bits: u8) -> io::Result<()> {
112 self.make_boxes(color_av1_data, alpha_av1_data, width, height, depth_bits).write(into_output)
113 }
114
115 fn make_boxes<'data>(&self, color_av1_data: &'data [u8], alpha_av1_data: Option<&'data [u8]>, width: u32, height: u32, depth_bits: u8) -> AvifFile<'data> {
116 let mut image_items = ArrayVec::new();
117 let mut iloc_items = ArrayVec::new();
118 let mut compatible_brands = ArrayVec::new();
119 let mut ipma_entries = ArrayVec::new();
120 let mut data_chunks = ArrayVec::new();
121 let mut irefs = ArrayVec::new();
122 let mut ipco = IpcoBox::new();
123 let color_image_id = 1;
124 let alpha_image_id = 2;
125 const ESSENTIAL_BIT: u8 = 0x80;
126 let color_depth_bits = depth_bits;
127 let alpha_depth_bits = depth_bits; image_items.push(InfeBox {
130 id: color_image_id,
131 typ: FourCC(*b"av01"),
132 name: "",
133 });
134 let ispe_prop = ipco.push(IpcoProp::Ispe(IspeBox { width, height }));
135 let av1c_color_prop = ipco.push(IpcoProp::Av1C(Av1CBox {
137 seq_profile: if color_depth_bits >= 12 { 2 } else { 1 },
138 seq_level_idx_0: 31,
139 seq_tier_0: false,
140 high_bitdepth: color_depth_bits >= 10,
141 twelve_bit: color_depth_bits >= 12,
142 monochrome: false,
143 chroma_subsampling_x: false,
144 chroma_subsampling_y: false,
145 chroma_sample_position: 0,
146 }));
147 let pixi_3 = ipco.push(IpcoProp::Pixi(PixiBox {
149 channels: 3,
150 depth: color_depth_bits,
151 }));
152 let mut prop_ids: ArrayVec<u8, 5> = [ispe_prop, av1c_color_prop | ESSENTIAL_BIT, pixi_3].into_iter().collect();
153 if self.colr != Default::default() {
155 let colr_color_prop = ipco.push(IpcoProp::Colr(self.colr));
156 prop_ids.push(colr_color_prop);
157 }
158 ipma_entries.push(IpmaEntry {
159 item_id: color_image_id,
160 prop_ids,
161 });
162
163 if let Some(alpha_data) = alpha_av1_data {
164 image_items.push(InfeBox {
165 id: alpha_image_id,
166 typ: FourCC(*b"av01"),
167 name: "",
168 });
169 let av1c_alpha_prop = ipco.push(boxes::IpcoProp::Av1C(Av1CBox {
170 seq_profile: if alpha_depth_bits >= 12 { 2 } else { 0 },
171 seq_level_idx_0: 31,
172 seq_tier_0: false,
173 high_bitdepth: alpha_depth_bits >= 10,
174 twelve_bit: alpha_depth_bits >= 12,
175 monochrome: true,
176 chroma_subsampling_x: true,
177 chroma_subsampling_y: true,
178 chroma_sample_position: 0,
179 }));
180 let pixi_1 = ipco.push(IpcoProp::Pixi(PixiBox {
182 channels: 1,
183 depth: alpha_depth_bits,
184 }));
185
186 let auxc_prop = ipco.push(IpcoProp::AuxC(AuxCBox {
188 urn: "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha",
189 }));
190 irefs.push(IrefBox {
191 entry: IrefEntryBox {
192 from_id: alpha_image_id,
193 to_id: color_image_id,
194 typ: FourCC(*b"auxl"),
195 },
196 });
197 if self.premultiplied_alpha {
198 irefs.push(IrefBox {
199 entry: IrefEntryBox {
200 from_id: color_image_id,
201 to_id: alpha_image_id,
202 typ: FourCC(*b"prem"),
203 },
204 });
205 }
206 ipma_entries.push(IpmaEntry {
207 item_id: alpha_image_id,
208 prop_ids: [ispe_prop, av1c_alpha_prop | ESSENTIAL_BIT, auxc_prop, pixi_1].into_iter().collect(),
209 });
210
211 iloc_items.push(IlocItem {
214 id: color_image_id,
215 extents: [
216 IlocExtent {
217 offset: IlocOffset::Relative(alpha_data.len()),
218 len: color_av1_data.len(),
219 },
220 ].into(),
221 });
222 iloc_items.push(IlocItem {
223 id: alpha_image_id,
224 extents: [
225 IlocExtent {
226 offset: IlocOffset::Relative(0),
227 len: alpha_data.len(),
228 },
229 ].into(),
230 });
231 data_chunks.push(alpha_data);
232 data_chunks.push(color_av1_data);
233 } else {
234 iloc_items.push(IlocItem {
235 id: color_image_id,
236 extents: [
237 IlocExtent {
238 offset: IlocOffset::Relative(0),
239 len: color_av1_data.len(),
240 },
241 ].into(),
242 });
243 data_chunks.push(color_av1_data);
244 };
245
246 compatible_brands.push(FourCC(*b"mif1"));
247 compatible_brands.push(FourCC(*b"miaf"));
248 AvifFile {
249 ftyp: FtypBox {
250 major_brand: FourCC(*b"avif"),
251 minor_version: 0,
252 compatible_brands,
253 },
254 meta: MetaBox {
255 hdlr: HdlrBox {},
256 iinf: IinfBox { items: image_items },
257 pitm: PitmBox(color_image_id),
258 iloc: IlocBox { items: iloc_items },
259 iprp: IprpBox {
260 ipco,
261 ipma: IpmaBox {
264 entries: ipma_entries,
265 },
266 },
267 iref: irefs,
268 },
269 mdat: MdatBox {
272 data_chunks,
273 },
274 }
275 }
276
277 #[must_use] pub fn to_vec(&self, color_av1_data: &[u8], alpha_av1_data: Option<&[u8]>, width: u32, height: u32, depth_bits: u8) -> Vec<u8> {
278 let mut out = Vec::with_capacity(color_av1_data.len() + alpha_av1_data.map_or(0, |a| a.len()) + 410);
279 self.write(&mut out, color_av1_data, alpha_av1_data, width, height, depth_bits).unwrap(); out
281 }
282}
283
284#[must_use] pub fn serialize_to_vec(color_av1_data: &[u8], alpha_av1_data: Option<&[u8]>, width: u32, height: u32, depth_bits: u8) -> Vec<u8> {
286 Aviffy::new().to_vec(color_av1_data, alpha_av1_data, width, height, depth_bits)
287}
288
289#[test]
290fn test_roundtrip_parse_mp4() {
291 let test_img = b"av12356abc";
292 let avif = serialize_to_vec(test_img, None, 10, 20, 8);
293
294 let ctx = mp4parse::read_avif(&mut avif.as_slice(), mp4parse::ParseStrictness::Normal).unwrap();
295
296 assert_eq!(&test_img[..], ctx.primary_item_coded_data());
297}
298
299#[test]
300fn test_roundtrip_parse_mp4_alpha() {
301 let test_img = b"av12356abc";
302 let test_a = b"alpha";
303 let avif = serialize_to_vec(test_img, Some(test_a), 10, 20, 8);
304
305 let ctx = mp4parse::read_avif(&mut avif.as_slice(), mp4parse::ParseStrictness::Normal).unwrap();
306
307 assert_eq!(&test_img[..], ctx.primary_item_coded_data());
308 assert_eq!(&test_a[..], ctx.alpha_item_coded_data());
309}
310
311#[test]
312fn test_roundtrip_parse_avif() {
313 let test_img = [1,2,3,4,5,6];
314 let test_alpha = [77,88,99];
315 let avif = serialize_to_vec(&test_img, Some(&test_alpha), 10, 20, 8);
316
317 let ctx = avif_parse::read_avif(&mut avif.as_slice()).unwrap();
318
319 assert_eq!(&test_img[..], ctx.primary_item.as_slice());
320 assert_eq!(&test_alpha[..], ctx.alpha_item.as_deref().unwrap());
321}
322
323#[test]
324fn test_roundtrip_parse_avif_colr() {
325 let test_img = [1,2,3,4,5,6];
326 let test_alpha = [77,88,99];
327 let avif = Aviffy::new()
328 .matrix_coefficients(constants::MatrixCoefficients::Bt709)
329 .to_vec(&test_img, Some(&test_alpha), 10, 20, 8);
330
331 let ctx = avif_parse::read_avif(&mut avif.as_slice()).unwrap();
332
333 assert_eq!(&test_img[..], ctx.primary_item.as_slice());
334 assert_eq!(&test_alpha[..], ctx.alpha_item.as_deref().unwrap());
335}
336
337#[test]
338fn premultiplied_flag() {
339 let test_img = [1,2,3,4];
340 let test_alpha = [55,66,77,88,99];
341 let avif = Aviffy::new().premultiplied_alpha(true).to_vec(&test_img, Some(&test_alpha), 5, 5, 8);
342
343 let ctx = avif_parse::read_avif(&mut avif.as_slice()).unwrap();
344
345 assert!(ctx.premultiplied_alpha);
346 assert_eq!(&test_img[..], ctx.primary_item.as_slice());
347 assert_eq!(&test_alpha[..], ctx.alpha_item.as_deref().unwrap());
348}