1use crate::error::{
3 DecodingError, ImageFormatHint, LimitError, LimitErrorKind, UnsupportedError,
4 UnsupportedErrorKind,
5};
6use crate::{ColorType, ImageDecoder, ImageError, ImageFormat, ImageResult};
7use std::error::Error;
12use std::fmt::{Display, Formatter};
13use std::io::Read;
14use std::marker::PhantomData;
15
16use crate::codecs::avif::ycgco::{
17 ycgco420_to_rgba10, ycgco420_to_rgba12, ycgco420_to_rgba8, ycgco422_to_rgba10,
18 ycgco422_to_rgba12, ycgco422_to_rgba8, ycgco444_to_rgba10, ycgco444_to_rgba12,
19 ycgco444_to_rgba8,
20};
21use crate::codecs::avif::yuv::*;
22use dav1d::{PixelLayout, PlanarImageComponent};
23use mp4parse::{read_avif, ParseStrictness};
24
25fn error_map<E: Into<Box<dyn Error + Send + Sync>>>(err: E) -> ImageError {
26 ImageError::Decoding(DecodingError::new(ImageFormat::Avif.into(), err))
27}
28
29pub struct AvifDecoder<R> {
33 inner: PhantomData<R>,
34 picture: dav1d::Picture,
35 alpha_picture: Option<dav1d::Picture>,
36 icc_profile: Option<Vec<u8>>,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40enum AvifDecoderError {
41 AlphaPlaneFormat(PixelLayout),
42 YuvLayoutOnIdentityMatrix(PixelLayout),
43 UnsupportedLayoutAndMatrix(PixelLayout, YuvMatrixStrategy),
44}
45
46impl Display for AvifDecoderError {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 match self {
49 AvifDecoderError::AlphaPlaneFormat(pixel_layout) => match pixel_layout {
50 PixelLayout::I400 => unreachable!("This option must be handled correctly"),
51 PixelLayout::I420 => f.write_str("Alpha layout must be 4:0:0, but it was 4:2:0"),
52 PixelLayout::I422 => f.write_str("Alpha layout must be 4:0:0, but it was 4:2:2"),
53 PixelLayout::I444 => f.write_str("Alpha layout must be 4:0:0, but it was 4:4:4"),
54 },
55 AvifDecoderError::YuvLayoutOnIdentityMatrix(pixel_layout) => match pixel_layout {
56 PixelLayout::I400 => {
57 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4, but it was 4:0:0")
58 }
59 PixelLayout::I420 => {
60 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4, but it was 4:2:0")
61 }
62 PixelLayout::I422 => {
63 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4, but it was 4:2:2")
64 }
65 PixelLayout::I444 => unreachable!("This option must be handled correctly"),
66 },
67 AvifDecoderError::UnsupportedLayoutAndMatrix(layout, matrix) => f.write_fmt(
68 format_args!("YUV layout {layout:?} on matrix {matrix:?} is not supported",),
69 ),
70 }
71 }
72}
73
74impl Error for AvifDecoderError {}
75
76impl<R: Read> AvifDecoder<R> {
77 pub fn new(mut r: R) -> ImageResult<Self> {
79 let ctx = read_avif(&mut r, ParseStrictness::Normal).map_err(error_map)?;
80 let coded = ctx.primary_item_coded_data().unwrap_or_default();
81
82 let mut primary_decoder = dav1d::Decoder::new().map_err(error_map)?;
83 primary_decoder
84 .send_data(coded.to_vec(), None, None, None)
85 .map_err(error_map)?;
86 let picture = read_until_ready(&mut primary_decoder)?;
87 let alpha_item = ctx.alpha_item_coded_data().unwrap_or_default();
88 let alpha_picture = if !alpha_item.is_empty() {
89 let mut alpha_decoder = dav1d::Decoder::new().map_err(error_map)?;
90 alpha_decoder
91 .send_data(alpha_item.to_vec(), None, None, None)
92 .map_err(error_map)?;
93 Some(read_until_ready(&mut alpha_decoder)?)
94 } else {
95 None
96 };
97 let icc_profile = ctx
98 .icc_colour_information()
99 .map(|x| x.ok().unwrap_or_default())
100 .map(|x| x.to_vec());
101
102 match picture.bit_depth() {
103 8 => (),
104 10 | 12 => (),
105 _ => {
106 return ImageResult::Err(ImageError::Decoding(DecodingError::new(
107 ImageFormatHint::Exact(ImageFormat::Avif),
108 format!(
109 "Avif format does not support {} bit depth",
110 picture.bit_depth()
111 ),
112 )))
113 }
114 };
115 Ok(AvifDecoder {
116 inner: PhantomData,
117 picture,
118 alpha_picture,
119 icc_profile,
120 })
121 }
122}
123
124fn reshape_plane(source: &[u8], stride: usize, width: usize, height: usize) -> Vec<u16> {
126 let mut target_plane = vec![0u16; width * height];
127 for (shaped_row, src_row) in target_plane
128 .chunks_exact_mut(width)
129 .zip(source.chunks_exact(stride))
130 {
131 for (dst, src) in shaped_row.iter_mut().zip(src_row.chunks_exact(2)) {
132 *dst = u16::from_ne_bytes([src[0], src[1]]);
133 }
134 }
135 target_plane
136}
137
138struct Plane16View<'a> {
139 data: std::borrow::Cow<'a, [u16]>,
140 stride: usize,
141}
142
143impl Default for Plane16View<'_> {
144 fn default() -> Self {
145 Plane16View {
146 data: std::borrow::Cow::Owned(vec![]),
147 stride: 0,
148 }
149 }
150}
151
152fn transmute_y_plane16(
154 plane: &dav1d::Plane,
155 stride: usize,
156 width: usize,
157 height: usize,
158) -> Plane16View<'_> {
159 let mut y_plane_stride = stride >> 1;
160
161 let mut bind_y = vec![];
162 let plane_ref = plane.as_ref();
163
164 let mut shape_y_plane = || {
165 y_plane_stride = width;
166 bind_y = reshape_plane(plane_ref, stride, width, height);
167 };
168
169 if stride & 1 == 0 {
170 match bytemuck::try_cast_slice(plane_ref) {
171 Ok(slice) => Plane16View {
172 data: std::borrow::Cow::Borrowed(slice),
173 stride: y_plane_stride,
174 },
175 Err(_) => {
176 shape_y_plane();
177 Plane16View {
178 data: std::borrow::Cow::Owned(bind_y),
179 stride: y_plane_stride,
180 }
181 }
182 }
183 } else {
184 shape_y_plane();
185 Plane16View {
186 data: std::borrow::Cow::Owned(bind_y),
187 stride: y_plane_stride,
188 }
189 }
190}
191
192fn transmute_chroma_plane16(
194 plane: &dav1d::Plane,
195 pixel_layout: PixelLayout,
196 stride: usize,
197 width: usize,
198 height: usize,
199) -> Plane16View<'_> {
200 let plane_ref = plane.as_ref();
201 let mut chroma_plane_stride = stride >> 1;
202 let mut bind_chroma = vec![];
203
204 let mut shape_chroma_plane = || {
205 chroma_plane_stride = match pixel_layout {
206 PixelLayout::I400 => unreachable!(),
207 PixelLayout::I420 | PixelLayout::I422 => width.div_ceil(2),
208 PixelLayout::I444 => width,
209 };
210 let u_plane_height = match pixel_layout {
211 PixelLayout::I400 => unreachable!(),
212 PixelLayout::I420 => height.div_ceil(2),
213 PixelLayout::I422 | PixelLayout::I444 => height,
214 };
215 bind_chroma = reshape_plane(plane_ref, stride, chroma_plane_stride, u_plane_height);
216 };
217
218 if stride & 1 == 0 {
219 match bytemuck::try_cast_slice(plane_ref) {
220 Ok(slice) => Plane16View {
221 data: std::borrow::Cow::Borrowed(slice),
222 stride: chroma_plane_stride,
223 },
224 Err(_) => {
225 shape_chroma_plane();
226 Plane16View {
227 data: std::borrow::Cow::Owned(bind_chroma),
228 stride: chroma_plane_stride,
229 }
230 }
231 }
232 } else {
233 shape_chroma_plane();
234 Plane16View {
235 data: std::borrow::Cow::Owned(bind_chroma),
236 stride: chroma_plane_stride,
237 }
238 }
239}
240
241#[derive(Copy, Clone, Debug, PartialOrd, Eq, PartialEq)]
242enum YuvMatrixStrategy {
243 KrKb(YuvStandardMatrix),
244 CgCo,
245 Identity,
246}
247
248fn get_matrix(
250 david_matrix: dav1d::pixel::MatrixCoefficients,
251) -> Result<YuvMatrixStrategy, ImageError> {
252 match david_matrix {
253 dav1d::pixel::MatrixCoefficients::Identity => Ok(YuvMatrixStrategy::Identity),
254 dav1d::pixel::MatrixCoefficients::BT709 => {
255 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709))
256 }
257 dav1d::pixel::MatrixCoefficients::Unspecified => {
262 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709))
263 }
264 dav1d::pixel::MatrixCoefficients::Reserved => Err(ImageError::Unsupported(
265 UnsupportedError::from_format_and_kind(
266 ImageFormat::Avif.into(),
267 UnsupportedErrorKind::GenericFeature(
268 "Using 'Reserved' color matrix is not supported".to_string(),
269 ),
270 ),
271 )),
272 dav1d::pixel::MatrixCoefficients::BT470M => {
273 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt470_6))
274 }
275 dav1d::pixel::MatrixCoefficients::BT470BG => {
276 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt601))
277 }
278 dav1d::pixel::MatrixCoefficients::ST170M => {
279 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240))
280 }
281 dav1d::pixel::MatrixCoefficients::ST240M => {
282 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240))
283 }
284 dav1d::pixel::MatrixCoefficients::YCgCo => Ok(YuvMatrixStrategy::CgCo),
285 dav1d::pixel::MatrixCoefficients::BT2020NonConstantLuminance => {
286 Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt2020))
287 }
288 dav1d::pixel::MatrixCoefficients::BT2020ConstantLuminance => {
289 Err(ImageError::Unsupported(
295 UnsupportedError::from_format_and_kind(
296 ImageFormat::Avif.into(),
297 UnsupportedErrorKind::GenericFeature(
298 "BT2020ConstantLuminance matrix is not supported".to_string(),
299 ),
300 ),
301 ))
302 }
303 dav1d::pixel::MatrixCoefficients::ST2085 => Err(ImageError::Unsupported(
304 UnsupportedError::from_format_and_kind(
305 ImageFormat::Avif.into(),
306 UnsupportedErrorKind::GenericFeature("ST2085 matrix is not supported".to_string()),
307 ),
308 )),
309 dav1d::pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance
310 | dav1d::pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance => Err(
311 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
312 ImageFormat::Avif.into(),
313 UnsupportedErrorKind::GenericFeature(
314 "Chromaticity Derived Luminance matrix is not supported".to_string(),
315 ),
316 )),
317 ),
318 dav1d::pixel::MatrixCoefficients::ICtCp => Err(ImageError::Unsupported(
319 UnsupportedError::from_format_and_kind(
320 ImageFormat::Avif.into(),
321 UnsupportedErrorKind::GenericFeature(
322 "ICtCp Derived Luminance matrix is not supported".to_string(),
323 ),
324 ),
325 )),
326 }
327}
328
329impl<R: Read> ImageDecoder for AvifDecoder<R> {
330 fn dimensions(&self) -> (u32, u32) {
331 (self.picture.width(), self.picture.height())
332 }
333
334 fn color_type(&self) -> ColorType {
335 if self.picture.bit_depth() == 8 {
336 ColorType::Rgba8
337 } else {
338 ColorType::Rgba16
339 }
340 }
341
342 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
343 Ok(self.icc_profile.clone())
344 }
345
346 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
347 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
348
349 let bit_depth = self.picture.bit_depth();
350
351 assert!(bit_depth == 8 || bit_depth == 10 || bit_depth == 12);
354
355 let (width, height) = self.dimensions();
356 if width == 0 || height == 0 {
358 return Err(ImageError::Limits(LimitError::from_kind(
359 LimitErrorKind::DimensionError,
360 )));
361 }
362
363 let yuv_range = match self.picture.color_range() {
364 dav1d::pixel::YUVRange::Limited => YuvIntensityRange::Tv,
365 dav1d::pixel::YUVRange::Full => YuvIntensityRange::Pc,
366 };
367
368 let matrix_strategy = get_matrix(self.picture.matrix_coefficients())?;
369
370 if matrix_strategy == YuvMatrixStrategy::Identity
372 && self.picture.pixel_layout() != PixelLayout::I444
373 {
374 return Err(ImageError::Decoding(DecodingError::new(
375 ImageFormat::Avif.into(),
376 AvifDecoderError::YuvLayoutOnIdentityMatrix(self.picture.pixel_layout()),
377 )));
378 }
379
380 if matrix_strategy == YuvMatrixStrategy::CgCo
381 && self.picture.pixel_layout() == PixelLayout::I400
382 {
383 return Err(ImageError::Decoding(DecodingError::new(
384 ImageFormat::Avif.into(),
385 AvifDecoderError::UnsupportedLayoutAndMatrix(
386 self.picture.pixel_layout(),
387 matrix_strategy,
388 ),
389 )));
390 }
391
392 if bit_depth == 8 {
393 let ref_y = self.picture.plane(PlanarImageComponent::Y);
394 let ref_u = self.picture.plane(PlanarImageComponent::U);
395 let ref_v = self.picture.plane(PlanarImageComponent::V);
396
397 let image = YuvPlanarImage {
398 y_plane: ref_y.as_ref(),
399 y_stride: self.picture.stride(PlanarImageComponent::Y) as usize,
400 u_plane: ref_u.as_ref(),
401 u_stride: self.picture.stride(PlanarImageComponent::U) as usize,
402 v_plane: ref_v.as_ref(),
403 v_stride: self.picture.stride(PlanarImageComponent::V) as usize,
404 width: width as usize,
405 height: height as usize,
406 };
407
408 match matrix_strategy {
409 YuvMatrixStrategy::KrKb(standard) => {
410 let worker = match self.picture.pixel_layout() {
411 PixelLayout::I400 => yuv400_to_rgba8,
412 PixelLayout::I420 => yuv420_to_rgba8,
413 PixelLayout::I422 => yuv422_to_rgba8,
414 PixelLayout::I444 => yuv444_to_rgba8,
415 };
416
417 worker(image, buf, yuv_range, standard)?;
418 }
419 YuvMatrixStrategy::CgCo => {
420 let worker = match self.picture.pixel_layout() {
421 PixelLayout::I400 => unreachable!(),
422 PixelLayout::I420 => ycgco420_to_rgba8,
423 PixelLayout::I422 => ycgco422_to_rgba8,
424 PixelLayout::I444 => ycgco444_to_rgba8,
425 };
426
427 worker(image, buf, yuv_range)?;
428 }
429 YuvMatrixStrategy::Identity => {
430 let worker = match self.picture.pixel_layout() {
431 PixelLayout::I400 => unreachable!(),
432 PixelLayout::I420 => unreachable!(),
433 PixelLayout::I422 => unreachable!(),
434 PixelLayout::I444 => gbr_to_rgba8,
435 };
436
437 worker(image, buf, yuv_range)?;
438 }
439 }
440
441 if let Some(picture) = self.alpha_picture {
443 if picture.pixel_layout() != PixelLayout::I400 {
444 return Err(ImageError::Decoding(DecodingError::new(
445 ImageFormat::Avif.into(),
446 AvifDecoderError::AlphaPlaneFormat(picture.pixel_layout()),
447 )));
448 }
449
450 let stride = picture.stride(PlanarImageComponent::Y) as usize;
451 let plane = picture.plane(PlanarImageComponent::Y);
452
453 for (buf, slice) in Iterator::zip(
454 buf.chunks_exact_mut(width as usize * 4),
455 plane.as_ref().chunks_exact(stride),
456 ) {
457 for (rgba, a_src) in buf.chunks_exact_mut(4).zip(slice) {
458 rgba[3] = *a_src;
459 }
460 }
461 }
462 } else {
463 if let Ok(buf) = bytemuck::try_cast_slice_mut(buf) {
465 let target_slice: &mut [u16] = buf;
466 self.process_16bit_picture(target_slice, yuv_range, matrix_strategy)?;
467 } else {
468 let mut aligned_store = vec![0u16; buf.len() / 2];
470 self.process_16bit_picture(&mut aligned_store, yuv_range, matrix_strategy)?;
471 for (dst, src) in buf.chunks_exact_mut(2).zip(aligned_store.iter()) {
472 let bytes = src.to_ne_bytes();
473 dst[0] = bytes[0];
474 dst[1] = bytes[1];
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.chunks_exact_mut(4).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}