Skip to main content

ai_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};
7use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec};
8///
9/// The [AVIF] specification defines an image derivative of the AV1 bitstream, an open video codec.
10///
11/// [AVIF]: https://aomediacodec.github.io/av1-avif/
12use 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
30/// AVIF Decoder.
31///
32/// Reads one image into the chosen input.
33pub 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    /// Create a new decoder that reads its input from `r`.
79    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
125/// Reshaping incorrectly aligned or sized FFI data into Rust constraints
126fn 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
153/// This is correct to transmute FFI data for Y plane and Alpha plane
154fn 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
193/// This is correct to transmute FFI data for Y plane and Alpha plane
194fn 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
249/// Getting one of prebuilt matrix of fails
250fn 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        // This is arguable, some applications prefer to go with Bt.709 as default,
259        // and some applications prefer Bt.601 as default.
260        // For ex. `Chrome` always prefer Bt.709 even for SD content
261        // However, nowadays standard should be Bt.709 for HD+ size otherwise Bt.601
262        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            // This matrix significantly differs from others because linearize values is required
291            // to compute Y instead of Y'.
292            // Actually it is almost everywhere is not implemented.
293            // Libavif + libheif missing this also so actually AVIF images
294            // with CL BT.2020 might be made only by mistake
295            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        // Normally this should never happen,
353        // if this happens then there is an incorrect implementation somewhere else
354        assert!(bit_depth == 8 || bit_depth == 10 || bit_depth == 12);
355
356        let (width, height) = self.dimensions();
357        // This is suspicious if this happens, better fail early
358        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        // Identity matrix should be possible only on 4:4:4
372        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            // Squashing alpha plane into a picture
443            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            // // 8+ bit-depth case
465            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                // If buffer from Decoder is unaligned
470                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        // 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.as_chunks_mut::<4>().0.iter_mut().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}