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.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
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.chunks_exact_mut(4).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                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        // dav1d may return not aligned and not correctly constrained data,
500        // or at least I can't find guarantees on that
501        // so if it is happened, instead casting we'll need to reshape it into a target slice
502        // required criteria: bytemuck allows this align of this data, and stride must be dividable by 2
503
504        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        // Squashing alpha plane into a picture
623        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        // Expand current bit depth to target 16
650        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
659/// `get_picture` and `send_pending_data` yield `Again` as a non-fatal error requesting more data is sent to the decoder
660/// This ensures that in the case of `Again` all pending data is submitted
661/// This should be called after `send_data` (which does not yield `Again` when called the first time)
662fn 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}