Skip to main content

image/codecs/avif/
decoder.rs

1//! Decoding of AVIF images.
2use crate::error::{
3    DecodingError, ImageFormatHint, LimitError, LimitErrorKind, UnsupportedError,
4    UnsupportedErrorKind,
5};
6use crate::{ColorType, ImageDecoder, ImageError, ImageFormat, ImageResult};
7///
8/// The [AVIF] specification defines an image derivative of the AV1 bitstream, an open video codec.
9///
10/// [AVIF]: https://aomediacodec.github.io/av1-avif/
11use 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
29/// AVIF Decoder.
30///
31/// Reads one image into the chosen input.
32pub 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    /// Create a new decoder that reads its input from `r`.
78    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
124/// Reshaping incorrectly aligned or sized FFI data into Rust constraints
125fn 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.as_chunks::<2>().0) {
132            *dst = u16::from_ne_bytes(*src);
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
152/// This is correct to transmute FFI data for Y plane and Alpha plane
153fn 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
192/// This is correct to transmute FFI data for Y plane and Alpha plane
193fn 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
248/// Getting one of prebuilt matrix of fails
249fn 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        // This is arguable, some applications prefer to go with Bt.709 as default,
258        // and some applications prefer Bt.601 as default.
259        // For ex. `Chrome` always prefer Bt.709 even for SD content
260        // However, nowadays standard should be Bt.709 for HD+ size otherwise Bt.601
261        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            // This matrix significantly differs from others because linearize values is required
290            // to compute Y instead of Y'.
291            // Actually it is almost everywhere is not implemented.
292            // Libavif + libheif missing this also so actually AVIF images
293            // with CL BT.2020 might be made only by mistake
294            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        // Normally this should never happen,
352        // if this happens then there is an incorrect implementation somewhere else
353        assert!(bit_depth == 8 || bit_depth == 10 || bit_depth == 12);
354
355        let (width, height) = self.dimensions();
356        // This is suspicious if this happens, better fail early
357        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        // Identity matrix should be possible only on 4:4:4
371        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            // Squashing alpha plane into a picture
442            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.as_chunks_mut::<4>().0.iter_mut().zip(slice) {
458                        rgba[3] = *a_src;
459                    }
460                }
461            }
462        } else {
463            // // 8+ bit-depth case
464            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                // If buffer from Decoder is unaligned
469                let mut aligned_store = vec![0u16; buf.len() / 2];
470                self.process_16bit_picture(&mut aligned_store, yuv_range, matrix_strategy)?;
471                let buf_chunks = buf.as_chunks_mut::<2>().0.iter_mut();
472                for (dst, src) in buf_chunks.zip(aligned_store.iter()) {
473                    *dst = src.to_ne_bytes();
474                }
475            }
476        }
477
478        Ok(())
479    }
480
481    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
482        (*self).read_image(buf)
483    }
484}
485
486impl<R: Read> AvifDecoder<R> {
487    fn process_16bit_picture(
488        &self,
489        target: &mut [u16],
490        yuv_range: YuvIntensityRange,
491        matrix_strategy: YuvMatrixStrategy,
492    ) -> ImageResult<()> {
493        let y_dav1d_plane = self.picture.plane(PlanarImageComponent::Y);
494
495        let (width, height) = (self.picture.width(), self.picture.height());
496        let bit_depth = self.picture.bit_depth();
497
498        // dav1d may return not aligned and not correctly constrained data,
499        // or at least I can't find guarantees on that
500        // so if it is happened, instead casting we'll need to reshape it into a target slice
501        // required criteria: bytemuck allows this align of this data, and stride must be dividable by 2
502
503        let y_plane_view = transmute_y_plane16(
504            &y_dav1d_plane,
505            self.picture.stride(PlanarImageComponent::Y) as usize,
506            width as usize,
507            height as usize,
508        );
509
510        let u_dav1d_plane = self.picture.plane(PlanarImageComponent::U);
511        let v_dav1d_plane = self.picture.plane(PlanarImageComponent::V);
512        let mut u_plane_view = Plane16View::default();
513        let mut v_plane_view = Plane16View::default();
514
515        if self.picture.pixel_layout() != PixelLayout::I400 {
516            u_plane_view = transmute_chroma_plane16(
517                &u_dav1d_plane,
518                self.picture.pixel_layout(),
519                self.picture.stride(PlanarImageComponent::U) as usize,
520                width as usize,
521                height as usize,
522            );
523            v_plane_view = transmute_chroma_plane16(
524                &v_dav1d_plane,
525                self.picture.pixel_layout(),
526                self.picture.stride(PlanarImageComponent::V) as usize,
527                width as usize,
528                height as usize,
529            );
530        }
531
532        let image = YuvPlanarImage {
533            y_plane: y_plane_view.data.as_ref(),
534            y_stride: y_plane_view.stride,
535            u_plane: u_plane_view.data.as_ref(),
536            u_stride: u_plane_view.stride,
537            v_plane: v_plane_view.data.as_ref(),
538            v_stride: v_plane_view.stride,
539            width: width as usize,
540            height: height as usize,
541        };
542
543        match matrix_strategy {
544            YuvMatrixStrategy::KrKb(standard) => {
545                let worker = match self.picture.pixel_layout() {
546                    PixelLayout::I400 => {
547                        if bit_depth == 10 {
548                            yuv400_to_rgba10
549                        } else {
550                            yuv400_to_rgba12
551                        }
552                    }
553                    PixelLayout::I420 => {
554                        if bit_depth == 10 {
555                            yuv420_to_rgba10
556                        } else {
557                            yuv420_to_rgba12
558                        }
559                    }
560                    PixelLayout::I422 => {
561                        if bit_depth == 10 {
562                            yuv422_to_rgba10
563                        } else {
564                            yuv422_to_rgba12
565                        }
566                    }
567                    PixelLayout::I444 => {
568                        if bit_depth == 10 {
569                            yuv444_to_rgba10
570                        } else {
571                            yuv444_to_rgba12
572                        }
573                    }
574                };
575                worker(image, target, yuv_range, standard)?;
576            }
577            YuvMatrixStrategy::CgCo => {
578                let worker = match self.picture.pixel_layout() {
579                    PixelLayout::I400 => unreachable!(),
580                    PixelLayout::I420 => {
581                        if bit_depth == 10 {
582                            ycgco420_to_rgba10
583                        } else {
584                            ycgco420_to_rgba12
585                        }
586                    }
587                    PixelLayout::I422 => {
588                        if bit_depth == 10 {
589                            ycgco422_to_rgba10
590                        } else {
591                            ycgco422_to_rgba12
592                        }
593                    }
594                    PixelLayout::I444 => {
595                        if bit_depth == 10 {
596                            ycgco444_to_rgba10
597                        } else {
598                            ycgco444_to_rgba12
599                        }
600                    }
601                };
602                worker(image, target, yuv_range)?;
603            }
604            YuvMatrixStrategy::Identity => {
605                let worker = match self.picture.pixel_layout() {
606                    PixelLayout::I400 => unreachable!(),
607                    PixelLayout::I420 => unreachable!(),
608                    PixelLayout::I422 => unreachable!(),
609                    PixelLayout::I444 => {
610                        if bit_depth == 10 {
611                            gbr_to_rgba10
612                        } else {
613                            gbr_to_rgba12
614                        }
615                    }
616                };
617                worker(image, target, yuv_range)?;
618            }
619        }
620
621        // Squashing alpha plane into a picture
622        if let Some(picture) = &self.alpha_picture {
623            if picture.pixel_layout() != PixelLayout::I400 {
624                return Err(ImageError::Decoding(DecodingError::new(
625                    ImageFormat::Avif.into(),
626                    AvifDecoderError::AlphaPlaneFormat(picture.pixel_layout()),
627                )));
628            }
629
630            let a_dav1d_plane = picture.plane(PlanarImageComponent::Y);
631            let a_plane_view = transmute_y_plane16(
632                &a_dav1d_plane,
633                picture.stride(PlanarImageComponent::Y) as usize,
634                width as usize,
635                height as usize,
636            );
637
638            for (buf, slice) in Iterator::zip(
639                target.chunks_exact_mut(width as usize * 4),
640                a_plane_view.data.as_ref().chunks_exact(a_plane_view.stride),
641            ) {
642                for (rgba, a_src) in buf.as_chunks_mut::<4>().0.iter_mut().zip(slice) {
643                    rgba[3] = *a_src;
644                }
645            }
646        }
647
648        // Expand current bit depth to target 16
649        let target_expand_bits = 16u32 - self.picture.bit_depth() as u32;
650        for item in target.iter_mut() {
651            *item = (*item).rotate_left(target_expand_bits);
652        }
653
654        Ok(())
655    }
656}
657
658/// `get_picture` and `send_pending_data` yield `Again` as a non-fatal error requesting more data is sent to the decoder
659/// This ensures that in the case of `Again` all pending data is submitted
660/// This should be called after `send_data` (which does not yield `Again` when called the first time)
661fn read_until_ready(decoder: &mut dav1d::Decoder) -> ImageResult<dav1d::Picture> {
662    loop {
663        match decoder.get_picture() {
664            Err(dav1d::Error::Again) => match decoder.send_pending_data() {
665                Ok(()) => {}
666                Err(dav1d::Error::Again) => {}
667                Err(e) => return Err(error_map(e)),
668            },
669            r => return r.map_err(error_map),
670        }
671    }
672}