1use crate::error::{
3 DecodingError, ImageFormatHint, LimitError, LimitErrorKind, UnsupportedError,
4 UnsupportedErrorKind,
5};
6use crate::{ColorType, ImageDecoder, ImageError, ImageFormat, ImageResult};
7use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec};
8use core::error::Error;
13use core::fmt::{Display, Formatter};
14use core::marker::PhantomData;
15use no_std_io::io::Read;
16
17use crate::codecs::avif::ycgco::{
18 ycgco420_to_rgba10, ycgco420_to_rgba12, ycgco420_to_rgba8, ycgco422_to_rgba10,
19 ycgco422_to_rgba12, ycgco422_to_rgba8, ycgco444_to_rgba10, ycgco444_to_rgba12,
20 ycgco444_to_rgba8,
21};
22use crate::codecs::avif::yuv::*;
23use dav1d::{PixelLayout, PlanarImageComponent};
24use mp4parse::{read_avif, ParseStrictness};
25
26fn error_map<E: Into<Box<dyn Error + Send + Sync>>>(err: E) -> ImageError {
27 ImageError::Decoding(DecodingError::new(ImageFormat::Avif.into(), err))
28}
29
30pub struct AvifDecoder<R> {
34 inner: PhantomData<R>,
35 picture: dav1d::Picture,
36 alpha_picture: Option<dav1d::Picture>,
37 icc_profile: Option<Vec<u8>>,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41enum AvifDecoderError {
42 AlphaPlaneFormat(PixelLayout),
43 YuvLayoutOnIdentityMatrix(PixelLayout),
44 UnsupportedLayoutAndMatrix(PixelLayout, YuvMatrixStrategy),
45}
46
47impl Display for AvifDecoderError {
48 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
49 match self {
50 AvifDecoderError::AlphaPlaneFormat(pixel_layout) => match pixel_layout {
51 PixelLayout::I400 => unreachable!("This option must be handled correctly"),
52 PixelLayout::I420 => f.write_str("Alpha layout must be 4:0:0, but it was 4:2:0"),
53 PixelLayout::I422 => f.write_str("Alpha layout must be 4:0:0, but it was 4:2:2"),
54 PixelLayout::I444 => f.write_str("Alpha layout must be 4:0:0, but it was 4:4:4"),
55 },
56 AvifDecoderError::YuvLayoutOnIdentityMatrix(pixel_layout) => match pixel_layout {
57 PixelLayout::I400 => {
58 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4, but it was 4:0:0")
59 }
60 PixelLayout::I420 => {
61 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4, but it was 4:2:0")
62 }
63 PixelLayout::I422 => {
64 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4, but it was 4:2:2")
65 }
66 PixelLayout::I444 => unreachable!("This option must be handled correctly"),
67 },
68 AvifDecoderError::UnsupportedLayoutAndMatrix(layout, matrix) => f.write_fmt(
69 format_args!("YUV layout {layout:?} on matrix {matrix:?} is not supported",),
70 ),
71 }
72 }
73}
74
75impl Error for AvifDecoderError {}
76
77impl<R: Read> AvifDecoder<R> {
78 pub fn new(mut r: R) -> ImageResult<Self> {
80 let ctx = read_avif(&mut r, ParseStrictness::Normal).map_err(error_map)?;
81 let coded = ctx.primary_item_coded_data().unwrap_or_default();
82
83 let mut primary_decoder = dav1d::Decoder::new().map_err(error_map)?;
84 primary_decoder
85 .send_data(coded.to_vec(), None, None, None)
86 .map_err(error_map)?;
87 let picture = read_until_ready(&mut primary_decoder)?;
88 let alpha_item = ctx.alpha_item_coded_data().unwrap_or_default();
89 let alpha_picture = if !alpha_item.is_empty() {
90 let mut alpha_decoder = dav1d::Decoder::new().map_err(error_map)?;
91 alpha_decoder
92 .send_data(alpha_item.to_vec(), None, None, None)
93 .map_err(error_map)?;
94 Some(read_until_ready(&mut alpha_decoder)?)
95 } else {
96 None
97 };
98 let icc_profile = ctx
99 .icc_colour_information()
100 .map(|x| x.ok().unwrap_or_default())
101 .map(|x| x.to_vec());
102
103 match picture.bit_depth() {
104 8 => (),
105 10 | 12 => (),
106 _ => {
107 return ImageResult::Err(ImageError::Decoding(DecodingError::new(
108 ImageFormatHint::Exact(ImageFormat::Avif),
109 format!(
110 "Avif format does not support {} bit depth",
111 picture.bit_depth()
112 ),
113 )))
114 }
115 };
116 Ok(AvifDecoder {
117 inner: PhantomData,
118 picture,
119 alpha_picture,
120 icc_profile,
121 })
122 }
123}
124
125fn reshape_plane(source: &[u8], stride: usize, width: usize, height: usize) -> Vec<u16> {
127 let mut target_plane = vec![0u16; width * height];
128 for (shaped_row, src_row) in target_plane
129 .chunks_exact_mut(width)
130 .zip(source.chunks_exact(stride))
131 {
132 for (dst, src) in shaped_row.iter_mut().zip(src_row.as_chunks::<2>().0) {
133 *dst = u16::from_ne_bytes(*src);
134 }
135 }
136 target_plane
137}
138
139struct Plane16View<'a> {
140 data: alloc::borrow::Cow<'a, [u16]>,
141 stride: usize,
142}
143
144impl Default for Plane16View<'_> {
145 fn default() -> Self {
146 Plane16View {
147 data: alloc::borrow::Cow::Owned(vec![]),
148 stride: 0,
149 }
150 }
151}
152
153fn transmute_y_plane16(
155 plane: &dav1d::Plane,
156 stride: usize,
157 width: usize,
158 height: usize,
159) -> Plane16View<'_> {
160 let mut y_plane_stride = stride >> 1;
161
162 let mut bind_y = vec![];
163 let plane_ref = plane.as_ref();
164
165 let mut shape_y_plane = || {
166 y_plane_stride = width;
167 bind_y = reshape_plane(plane_ref, stride, width, height);
168 };
169
170 if stride & 1 == 0 {
171 match bytemuck::try_cast_slice(plane_ref) {
172 Ok(slice) => Plane16View {
173 data: alloc::borrow::Cow::Borrowed(slice),
174 stride: y_plane_stride,
175 },
176 Err(_) => {
177 shape_y_plane();
178 Plane16View {
179 data: alloc::borrow::Cow::Owned(bind_y),
180 stride: y_plane_stride,
181 }
182 }
183 }
184 } else {
185 shape_y_plane();
186 Plane16View {
187 data: alloc::borrow::Cow::Owned(bind_y),
188 stride: y_plane_stride,
189 }
190 }
191}
192
193fn transmute_chroma_plane16(
195 plane: &dav1d::Plane,
196 pixel_layout: PixelLayout,
197 stride: usize,
198 width: usize,
199 height: usize,
200) -> Plane16View<'_> {
201 let plane_ref = plane.as_ref();
202 let mut chroma_plane_stride = stride >> 1;
203 let mut bind_chroma = vec![];
204
205 let mut shape_chroma_plane = || {
206 chroma_plane_stride = match pixel_layout {
207 PixelLayout::I400 => unreachable!(),
208 PixelLayout::I420 | PixelLayout::I422 => width.div_ceil(2),
209 PixelLayout::I444 => width,
210 };
211 let u_plane_height = match pixel_layout {
212 PixelLayout::I400 => unreachable!(),
213 PixelLayout::I420 => height.div_ceil(2),
214 PixelLayout::I422 | PixelLayout::I444 => height,
215 };
216 bind_chroma = reshape_plane(plane_ref, stride, chroma_plane_stride, u_plane_height);
217 };
218
219 if stride & 1 == 0 {
220 match bytemuck::try_cast_slice(plane_ref) {
221 Ok(slice) => Plane16View {
222 data: alloc::borrow::Cow::Borrowed(slice),
223 stride: chroma_plane_stride,
224 },
225 Err(_) => {
226 shape_chroma_plane();
227 Plane16View {
228 data: alloc::borrow::Cow::Owned(bind_chroma),
229 stride: chroma_plane_stride,
230 }
231 }
232 }
233 } else {
234 shape_chroma_plane();
235 Plane16View {
236 data: alloc::borrow::Cow::Owned(bind_chroma),
237 stride: chroma_plane_stride,
238 }
239 }
240}
241
242#[derive(Copy, Clone, Debug, PartialOrd, Eq, PartialEq)]
243enum YuvMatrixStrategy {
244 KrKb(YuvStandardMatrix),
245 CgCo,
246 Identity,
247}
248
249fn get_matrix(
251 david_matrix: dav1d::pixel::MatrixCoefficients,
252) -> Result<YuvMatrixStrategy, ImageError> {
253 match david_matrix {
254 dav1d::pixel::MatrixCoefficients::Identity => Ok(YuvMatrixStrategy::Identity),
255 dav1d::pixel::MatrixCoefficients::BT709 => {
256 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709))
257 }
258 dav1d::pixel::MatrixCoefficients::Unspecified => {
263 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709))
264 }
265 dav1d::pixel::MatrixCoefficients::Reserved => Err(ImageError::Unsupported(
266 UnsupportedError::from_format_and_kind(
267 ImageFormat::Avif.into(),
268 UnsupportedErrorKind::GenericFeature(
269 "Using 'Reserved' color matrix is not supported".to_string(),
270 ),
271 ),
272 )),
273 dav1d::pixel::MatrixCoefficients::BT470M => {
274 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt470_6))
275 }
276 dav1d::pixel::MatrixCoefficients::BT470BG => {
277 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt601))
278 }
279 dav1d::pixel::MatrixCoefficients::ST170M => {
280 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240))
281 }
282 dav1d::pixel::MatrixCoefficients::ST240M => {
283 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240))
284 }
285 dav1d::pixel::MatrixCoefficients::YCgCo => Ok(YuvMatrixStrategy::CgCo),
286 dav1d::pixel::MatrixCoefficients::BT2020NonConstantLuminance => {
287 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt2020))
288 }
289 dav1d::pixel::MatrixCoefficients::BT2020ConstantLuminance => {
290 Err(ImageError::Unsupported(
296 UnsupportedError::from_format_and_kind(
297 ImageFormat::Avif.into(),
298 UnsupportedErrorKind::GenericFeature(
299 "BT2020ConstantLuminance matrix is not supported".to_string(),
300 ),
301 ),
302 ))
303 }
304 dav1d::pixel::MatrixCoefficients::ST2085 => Err(ImageError::Unsupported(
305 UnsupportedError::from_format_and_kind(
306 ImageFormat::Avif.into(),
307 UnsupportedErrorKind::GenericFeature("ST2085 matrix is not supported".to_string()),
308 ),
309 )),
310 dav1d::pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance
311 | dav1d::pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance => Err(
312 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
313 ImageFormat::Avif.into(),
314 UnsupportedErrorKind::GenericFeature(
315 "Chromaticity Derived Luminance matrix is not supported".to_string(),
316 ),
317 )),
318 ),
319 dav1d::pixel::MatrixCoefficients::ICtCp => Err(ImageError::Unsupported(
320 UnsupportedError::from_format_and_kind(
321 ImageFormat::Avif.into(),
322 UnsupportedErrorKind::GenericFeature(
323 "ICtCp Derived Luminance matrix is not supported".to_string(),
324 ),
325 ),
326 )),
327 }
328}
329
330impl<R: Read> ImageDecoder for AvifDecoder<R> {
331 fn dimensions(&self) -> (u32, u32) {
332 (self.picture.width(), self.picture.height())
333 }
334
335 fn color_type(&self) -> ColorType {
336 if self.picture.bit_depth() == 8 {
337 ColorType::Rgba8
338 } else {
339 ColorType::Rgba16
340 }
341 }
342
343 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
344 Ok(self.icc_profile.clone())
345 }
346
347 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
348 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
349
350 let bit_depth = self.picture.bit_depth();
351
352 assert!(bit_depth == 8 || bit_depth == 10 || bit_depth == 12);
355
356 let (width, height) = self.dimensions();
357 if width == 0 || height == 0 {
359 return Err(ImageError::Limits(LimitError::from_kind(
360 LimitErrorKind::DimensionError,
361 )));
362 }
363
364 let yuv_range = match self.picture.color_range() {
365 dav1d::pixel::YUVRange::Limited => YuvIntensityRange::Tv,
366 dav1d::pixel::YUVRange::Full => YuvIntensityRange::Pc,
367 };
368
369 let matrix_strategy = get_matrix(self.picture.matrix_coefficients())?;
370
371 if matrix_strategy == YuvMatrixStrategy::Identity
373 && self.picture.pixel_layout() != PixelLayout::I444
374 {
375 return Err(ImageError::Decoding(DecodingError::new(
376 ImageFormat::Avif.into(),
377 AvifDecoderError::YuvLayoutOnIdentityMatrix(self.picture.pixel_layout()),
378 )));
379 }
380
381 if matrix_strategy == YuvMatrixStrategy::CgCo
382 && self.picture.pixel_layout() == PixelLayout::I400
383 {
384 return Err(ImageError::Decoding(DecodingError::new(
385 ImageFormat::Avif.into(),
386 AvifDecoderError::UnsupportedLayoutAndMatrix(
387 self.picture.pixel_layout(),
388 matrix_strategy,
389 ),
390 )));
391 }
392
393 if bit_depth == 8 {
394 let ref_y = self.picture.plane(PlanarImageComponent::Y);
395 let ref_u = self.picture.plane(PlanarImageComponent::U);
396 let ref_v = self.picture.plane(PlanarImageComponent::V);
397
398 let image = YuvPlanarImage {
399 y_plane: ref_y.as_ref(),
400 y_stride: self.picture.stride(PlanarImageComponent::Y) as usize,
401 u_plane: ref_u.as_ref(),
402 u_stride: self.picture.stride(PlanarImageComponent::U) as usize,
403 v_plane: ref_v.as_ref(),
404 v_stride: self.picture.stride(PlanarImageComponent::V) as usize,
405 width: width as usize,
406 height: height as usize,
407 };
408
409 match matrix_strategy {
410 YuvMatrixStrategy::KrKb(standard) => {
411 let worker = match self.picture.pixel_layout() {
412 PixelLayout::I400 => yuv400_to_rgba8,
413 PixelLayout::I420 => yuv420_to_rgba8,
414 PixelLayout::I422 => yuv422_to_rgba8,
415 PixelLayout::I444 => yuv444_to_rgba8,
416 };
417
418 worker(image, buf, yuv_range, standard)?;
419 }
420 YuvMatrixStrategy::CgCo => {
421 let worker = match self.picture.pixel_layout() {
422 PixelLayout::I400 => unreachable!(),
423 PixelLayout::I420 => ycgco420_to_rgba8,
424 PixelLayout::I422 => ycgco422_to_rgba8,
425 PixelLayout::I444 => ycgco444_to_rgba8,
426 };
427
428 worker(image, buf, yuv_range)?;
429 }
430 YuvMatrixStrategy::Identity => {
431 let worker = match self.picture.pixel_layout() {
432 PixelLayout::I400 => unreachable!(),
433 PixelLayout::I420 => unreachable!(),
434 PixelLayout::I422 => unreachable!(),
435 PixelLayout::I444 => gbr_to_rgba8,
436 };
437
438 worker(image, buf, yuv_range)?;
439 }
440 }
441
442 if let Some(picture) = self.alpha_picture {
444 if picture.pixel_layout() != PixelLayout::I400 {
445 return Err(ImageError::Decoding(DecodingError::new(
446 ImageFormat::Avif.into(),
447 AvifDecoderError::AlphaPlaneFormat(picture.pixel_layout()),
448 )));
449 }
450
451 let stride = picture.stride(PlanarImageComponent::Y) as usize;
452 let plane = picture.plane(PlanarImageComponent::Y);
453
454 for (buf, slice) in Iterator::zip(
455 buf.chunks_exact_mut(width as usize * 4),
456 plane.as_ref().chunks_exact(stride),
457 ) {
458 for (rgba, a_src) in buf.as_chunks_mut::<4>().0.iter_mut().zip(slice) {
459 rgba[3] = *a_src;
460 }
461 }
462 }
463 } else {
464 if let Ok(buf) = bytemuck::try_cast_slice_mut(buf) {
466 let target_slice: &mut [u16] = buf;
467 self.process_16bit_picture(target_slice, yuv_range, matrix_strategy)?;
468 } else {
469 let mut aligned_store = vec![0u16; buf.len() / 2];
471 self.process_16bit_picture(&mut aligned_store, yuv_range, matrix_strategy)?;
472 let buf_chunks = buf.as_chunks_mut::<2>().0.iter_mut();
473 for (dst, src) in buf_chunks.zip(aligned_store.iter()) {
474 *dst = src.to_ne_bytes();
475 }
476 }
477 }
478
479 Ok(())
480 }
481
482 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
483 (*self).read_image(buf)
484 }
485}
486
487impl<R: Read> AvifDecoder<R> {
488 fn process_16bit_picture(
489 &self,
490 target: &mut [u16],
491 yuv_range: YuvIntensityRange,
492 matrix_strategy: YuvMatrixStrategy,
493 ) -> ImageResult<()> {
494 let y_dav1d_plane = self.picture.plane(PlanarImageComponent::Y);
495
496 let (width, height) = (self.picture.width(), self.picture.height());
497 let bit_depth = self.picture.bit_depth();
498
499 let y_plane_view = transmute_y_plane16(
505 &y_dav1d_plane,
506 self.picture.stride(PlanarImageComponent::Y) as usize,
507 width as usize,
508 height as usize,
509 );
510
511 let u_dav1d_plane = self.picture.plane(PlanarImageComponent::U);
512 let v_dav1d_plane = self.picture.plane(PlanarImageComponent::V);
513 let mut u_plane_view = Plane16View::default();
514 let mut v_plane_view = Plane16View::default();
515
516 if self.picture.pixel_layout() != PixelLayout::I400 {
517 u_plane_view = transmute_chroma_plane16(
518 &u_dav1d_plane,
519 self.picture.pixel_layout(),
520 self.picture.stride(PlanarImageComponent::U) as usize,
521 width as usize,
522 height as usize,
523 );
524 v_plane_view = transmute_chroma_plane16(
525 &v_dav1d_plane,
526 self.picture.pixel_layout(),
527 self.picture.stride(PlanarImageComponent::V) as usize,
528 width as usize,
529 height as usize,
530 );
531 }
532
533 let image = YuvPlanarImage {
534 y_plane: y_plane_view.data.as_ref(),
535 y_stride: y_plane_view.stride,
536 u_plane: u_plane_view.data.as_ref(),
537 u_stride: u_plane_view.stride,
538 v_plane: v_plane_view.data.as_ref(),
539 v_stride: v_plane_view.stride,
540 width: width as usize,
541 height: height as usize,
542 };
543
544 match matrix_strategy {
545 YuvMatrixStrategy::KrKb(standard) => {
546 let worker = match self.picture.pixel_layout() {
547 PixelLayout::I400 => {
548 if bit_depth == 10 {
549 yuv400_to_rgba10
550 } else {
551 yuv400_to_rgba12
552 }
553 }
554 PixelLayout::I420 => {
555 if bit_depth == 10 {
556 yuv420_to_rgba10
557 } else {
558 yuv420_to_rgba12
559 }
560 }
561 PixelLayout::I422 => {
562 if bit_depth == 10 {
563 yuv422_to_rgba10
564 } else {
565 yuv422_to_rgba12
566 }
567 }
568 PixelLayout::I444 => {
569 if bit_depth == 10 {
570 yuv444_to_rgba10
571 } else {
572 yuv444_to_rgba12
573 }
574 }
575 };
576 worker(image, target, yuv_range, standard)?;
577 }
578 YuvMatrixStrategy::CgCo => {
579 let worker = match self.picture.pixel_layout() {
580 PixelLayout::I400 => unreachable!(),
581 PixelLayout::I420 => {
582 if bit_depth == 10 {
583 ycgco420_to_rgba10
584 } else {
585 ycgco420_to_rgba12
586 }
587 }
588 PixelLayout::I422 => {
589 if bit_depth == 10 {
590 ycgco422_to_rgba10
591 } else {
592 ycgco422_to_rgba12
593 }
594 }
595 PixelLayout::I444 => {
596 if bit_depth == 10 {
597 ycgco444_to_rgba10
598 } else {
599 ycgco444_to_rgba12
600 }
601 }
602 };
603 worker(image, target, yuv_range)?;
604 }
605 YuvMatrixStrategy::Identity => {
606 let worker = match self.picture.pixel_layout() {
607 PixelLayout::I400 => unreachable!(),
608 PixelLayout::I420 => unreachable!(),
609 PixelLayout::I422 => unreachable!(),
610 PixelLayout::I444 => {
611 if bit_depth == 10 {
612 gbr_to_rgba10
613 } else {
614 gbr_to_rgba12
615 }
616 }
617 };
618 worker(image, target, yuv_range)?;
619 }
620 }
621
622 if let Some(picture) = &self.alpha_picture {
624 if picture.pixel_layout() != PixelLayout::I400 {
625 return Err(ImageError::Decoding(DecodingError::new(
626 ImageFormat::Avif.into(),
627 AvifDecoderError::AlphaPlaneFormat(picture.pixel_layout()),
628 )));
629 }
630
631 let a_dav1d_plane = picture.plane(PlanarImageComponent::Y);
632 let a_plane_view = transmute_y_plane16(
633 &a_dav1d_plane,
634 picture.stride(PlanarImageComponent::Y) as usize,
635 width as usize,
636 height as usize,
637 );
638
639 for (buf, slice) in Iterator::zip(
640 target.chunks_exact_mut(width as usize * 4),
641 a_plane_view.data.as_ref().chunks_exact(a_plane_view.stride),
642 ) {
643 for (rgba, a_src) in buf.as_chunks_mut::<4>().0.iter_mut().zip(slice) {
644 rgba[3] = *a_src;
645 }
646 }
647 }
648
649 let target_expand_bits = 16u32 - self.picture.bit_depth() as u32;
651 for item in target.iter_mut() {
652 *item = (*item).rotate_left(target_expand_bits);
653 }
654
655 Ok(())
656 }
657}
658
659fn read_until_ready(decoder: &mut dav1d::Decoder) -> ImageResult<dav1d::Picture> {
663 loop {
664 match decoder.get_picture() {
665 Err(dav1d::Error::Again) => match decoder.send_pending_data() {
666 Ok(()) => {}
667 Err(dav1d::Error::Again) => {}
668 Err(e) => return Err(error_map(e)),
669 },
670 r => return r.map_err(error_map),
671 }
672 }
673}