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::yuv::*;
17use dav1d::{PixelLayout, PlanarImageComponent};
18use mp4parse::{read_avif, ParseStrictness};
19
20fn error_map<E: Into<Box<dyn Error + Send + Sync>>>(err: E) -> ImageError {
21 ImageError::Decoding(DecodingError::new(ImageFormat::Avif.into(), err))
22}
23
24pub struct AvifDecoder<R> {
28 inner: PhantomData<R>,
29 picture: dav1d::Picture,
30 alpha_picture: Option<dav1d::Picture>,
31 icc_profile: Option<Vec<u8>>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35enum AvifDecoderError {
36 AlphaPlaneFormat(PixelLayout),
37 YuvLayoutOnIdentityMatrix(PixelLayout),
38}
39
40impl Display for AvifDecoderError {
41 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42 match self {
43 AvifDecoderError::AlphaPlaneFormat(pixel_layout) => match pixel_layout {
44 PixelLayout::I400 => unreachable!("This option must be handled correctly"),
45 PixelLayout::I420 => f.write_str("Alpha layout must be 4:0:0 but it was 4:2:0"),
46 PixelLayout::I422 => f.write_str("Alpha layout must be 4:0:0 but it was 4:2:2"),
47 PixelLayout::I444 => f.write_str("Alpha layout must be 4:0:0 but it was 4:4:4"),
48 },
49 AvifDecoderError::YuvLayoutOnIdentityMatrix(pixel_layout) => match pixel_layout {
50 PixelLayout::I400 => {
51 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4 but it was 4:0:0")
52 }
53 PixelLayout::I420 => {
54 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4 but it was 4:2:0")
55 }
56 PixelLayout::I422 => {
57 f.write_str("YUV layout on 'Identity' matrix must be 4:4:4 but it was 4:2:2")
58 }
59 PixelLayout::I444 => unreachable!("This option must be handled correctly"),
60 },
61 }
62 }
63}
64
65impl Error for AvifDecoderError {}
66
67impl<R: Read> AvifDecoder<R> {
68 pub fn new(mut r: R) -> ImageResult<Self> {
70 let ctx = read_avif(&mut r, ParseStrictness::Normal).map_err(error_map)?;
71 let coded = ctx.primary_item_coded_data().unwrap_or_default();
72
73 let mut primary_decoder = dav1d::Decoder::new().map_err(error_map)?;
74 primary_decoder
75 .send_data(coded.to_vec(), None, None, None)
76 .map_err(error_map)?;
77 let picture = read_until_ready(&mut primary_decoder)?;
78 let alpha_item = ctx.alpha_item_coded_data().unwrap_or_default();
79 let alpha_picture = if !alpha_item.is_empty() {
80 let mut alpha_decoder = dav1d::Decoder::new().map_err(error_map)?;
81 alpha_decoder
82 .send_data(alpha_item.to_vec(), None, None, None)
83 .map_err(error_map)?;
84 Some(read_until_ready(&mut alpha_decoder)?)
85 } else {
86 None
87 };
88 let icc_profile = ctx
89 .icc_colour_information()
90 .map(|x| x.ok().unwrap_or_default())
91 .map(|x| x.to_vec());
92
93 match picture.bit_depth() {
94 8 => (),
95 10 | 12 => (),
96 _ => {
97 return ImageResult::Err(ImageError::Decoding(DecodingError::new(
98 ImageFormatHint::Exact(ImageFormat::Avif),
99 format!(
100 "Avif format does not support {} bit depth",
101 picture.bit_depth()
102 ),
103 )))
104 }
105 };
106 Ok(AvifDecoder {
107 inner: PhantomData,
108 picture,
109 alpha_picture,
110 icc_profile,
111 })
112 }
113}
114
115fn reshape_plane(source: &[u8], stride: usize, width: usize, height: usize) -> Vec<u16> {
117 let mut target_plane = vec![0u16; width * height];
118 for (shaped_row, src_row) in target_plane
119 .chunks_exact_mut(width)
120 .zip(source.chunks_exact(stride))
121 {
122 for (dst, src) in shaped_row.iter_mut().zip(src_row.chunks_exact(2)) {
123 *dst = u16::from_ne_bytes([src[0], src[1]]);
124 }
125 }
126 target_plane
127}
128
129struct Plane16View<'a> {
130 data: std::borrow::Cow<'a, [u16]>,
131 stride: usize,
132}
133
134impl Default for Plane16View<'_> {
135 fn default() -> Self {
136 Plane16View {
137 data: std::borrow::Cow::Owned(vec![]),
138 stride: 0,
139 }
140 }
141}
142
143fn transmute_y_plane16(
145 plane: &dav1d::Plane,
146 stride: usize,
147 width: usize,
148 height: usize,
149) -> Plane16View {
150 let mut y_plane_stride = stride >> 1;
151
152 let mut bind_y = vec![];
153 let plane_ref = plane.as_ref();
154
155 let mut shape_y_plane = || {
156 y_plane_stride = width;
157 bind_y = reshape_plane(plane_ref, stride, width, height);
158 };
159
160 if stride & 1 == 0 {
161 match bytemuck::try_cast_slice(plane_ref) {
162 Ok(slice) => Plane16View {
163 data: std::borrow::Cow::Borrowed(slice),
164 stride: y_plane_stride,
165 },
166 Err(_) => {
167 shape_y_plane();
168 Plane16View {
169 data: std::borrow::Cow::Owned(bind_y),
170 stride: y_plane_stride,
171 }
172 }
173 }
174 } else {
175 shape_y_plane();
176 Plane16View {
177 data: std::borrow::Cow::Owned(bind_y),
178 stride: y_plane_stride,
179 }
180 }
181}
182
183fn transmute_chroma_plane16(
185 plane: &dav1d::Plane,
186 pixel_layout: PixelLayout,
187 stride: usize,
188 width: usize,
189 height: usize,
190) -> Plane16View {
191 let plane_ref = plane.as_ref();
192 let mut chroma_plane_stride = stride >> 1;
193 let mut bind_chroma = vec![];
194
195 let mut shape_chroma_plane = || {
196 chroma_plane_stride = match pixel_layout {
197 PixelLayout::I400 => unreachable!(),
198 PixelLayout::I420 | PixelLayout::I422 => (width + 1) / 2,
199 PixelLayout::I444 => width,
200 };
201 let u_plane_height = match pixel_layout {
202 PixelLayout::I400 => unreachable!(),
203 PixelLayout::I420 => (height + 1) / 2,
204 PixelLayout::I422 | PixelLayout::I444 => height,
205 };
206 bind_chroma = reshape_plane(plane_ref, stride, chroma_plane_stride, u_plane_height);
207 };
208
209 if stride & 1 == 0 {
210 match bytemuck::try_cast_slice(plane_ref) {
211 Ok(slice) => Plane16View {
212 data: std::borrow::Cow::Borrowed(slice),
213 stride: chroma_plane_stride,
214 },
215 Err(_) => {
216 shape_chroma_plane();
217 Plane16View {
218 data: std::borrow::Cow::Owned(bind_chroma),
219 stride: chroma_plane_stride,
220 }
221 }
222 }
223 } else {
224 shape_chroma_plane();
225 Plane16View {
226 data: std::borrow::Cow::Owned(bind_chroma),
227 stride: chroma_plane_stride,
228 }
229 }
230}
231
232fn get_matrix(
234 david_matrix: dav1d::pixel::MatrixCoefficients,
235) -> Result<YuvStandardMatrix, ImageError> {
236 match david_matrix {
237 dav1d::pixel::MatrixCoefficients::Identity => Ok(YuvStandardMatrix::Identity),
238 dav1d::pixel::MatrixCoefficients::BT709 => Ok(YuvStandardMatrix::Bt709),
239 dav1d::pixel::MatrixCoefficients::Unspecified => Ok(YuvStandardMatrix::Bt709),
244 dav1d::pixel::MatrixCoefficients::Reserved => Err(ImageError::Unsupported(
245 UnsupportedError::from_format_and_kind(
246 ImageFormat::Avif.into(),
247 UnsupportedErrorKind::GenericFeature(
248 "Using 'Reserved' color matrix is not supported".to_string(),
249 ),
250 ),
251 )),
252 dav1d::pixel::MatrixCoefficients::BT470M => Ok(YuvStandardMatrix::Bt470_6),
253 dav1d::pixel::MatrixCoefficients::BT470BG => Ok(YuvStandardMatrix::Bt601),
254 dav1d::pixel::MatrixCoefficients::ST170M => Ok(YuvStandardMatrix::Smpte240),
255 dav1d::pixel::MatrixCoefficients::ST240M => Ok(YuvStandardMatrix::Smpte240),
256 dav1d::pixel::MatrixCoefficients::YCgCo => Err(ImageError::Unsupported(
258 UnsupportedError::from_format_and_kind(
259 ImageFormat::Avif.into(),
260 UnsupportedErrorKind::GenericFeature("YCgCo matrix is not supported".to_string()),
261 ),
262 )),
263 dav1d::pixel::MatrixCoefficients::BT2020NonConstantLuminance => {
264 Ok(YuvStandardMatrix::Bt2020)
265 }
266 dav1d::pixel::MatrixCoefficients::BT2020ConstantLuminance => {
267 Err(ImageError::Unsupported(
273 UnsupportedError::from_format_and_kind(
274 ImageFormat::Avif.into(),
275 UnsupportedErrorKind::GenericFeature(
276 "BT2020ConstantLuminance matrix is not supported".to_string(),
277 ),
278 ),
279 ))
280 }
281 dav1d::pixel::MatrixCoefficients::ST2085 => Err(ImageError::Unsupported(
282 UnsupportedError::from_format_and_kind(
283 ImageFormat::Avif.into(),
284 UnsupportedErrorKind::GenericFeature("ST2085 matrix is not supported".to_string()),
285 ),
286 )),
287 dav1d::pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance
288 | dav1d::pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance => Err(
289 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
290 ImageFormat::Avif.into(),
291 UnsupportedErrorKind::GenericFeature(
292 "Chromaticity Derived Luminance matrix is not supported".to_string(),
293 ),
294 )),
295 ),
296 dav1d::pixel::MatrixCoefficients::ICtCp => Err(ImageError::Unsupported(
297 UnsupportedError::from_format_and_kind(
298 ImageFormat::Avif.into(),
299 UnsupportedErrorKind::GenericFeature(
300 "ICtCp Derived Luminance matrix is not supported".to_string(),
301 ),
302 ),
303 )),
304 }
305}
306
307impl<R: Read> ImageDecoder for AvifDecoder<R> {
308 fn dimensions(&self) -> (u32, u32) {
309 (self.picture.width(), self.picture.height())
310 }
311
312 fn color_type(&self) -> ColorType {
313 if self.picture.bit_depth() == 8 {
314 ColorType::Rgba8
315 } else {
316 ColorType::Rgba16
317 }
318 }
319
320 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
321 Ok(self.icc_profile.clone())
322 }
323
324 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
325 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
326
327 let bit_depth = self.picture.bit_depth();
328
329 assert!(bit_depth == 8 || bit_depth == 10 || bit_depth == 12);
332
333 let (width, height) = self.dimensions();
334 if width == 0 || height == 0 {
336 return Err(ImageError::Limits(LimitError::from_kind(
337 LimitErrorKind::DimensionError,
338 )));
339 }
340
341 let yuv_range = match self.picture.color_range() {
342 dav1d::pixel::YUVRange::Limited => YuvIntensityRange::Tv,
343 dav1d::pixel::YUVRange::Full => YuvIntensityRange::Pc,
344 };
345
346 let color_matrix = get_matrix(self.picture.matrix_coefficients())?;
347
348 if color_matrix == YuvStandardMatrix::Identity
350 && self.picture.pixel_layout() != PixelLayout::I444
351 {
352 return Err(ImageError::Decoding(DecodingError::new(
353 ImageFormat::Avif.into(),
354 AvifDecoderError::YuvLayoutOnIdentityMatrix(self.picture.pixel_layout()),
355 )));
356 }
357
358 if bit_depth == 8 {
359 let ref_y = self.picture.plane(PlanarImageComponent::Y);
360 let ref_u = self.picture.plane(PlanarImageComponent::U);
361 let ref_v = self.picture.plane(PlanarImageComponent::V);
362
363 let image = YuvPlanarImage {
364 y_plane: ref_y.as_ref(),
365 y_stride: self.picture.stride(PlanarImageComponent::Y) as usize,
366 u_plane: ref_u.as_ref(),
367 u_stride: self.picture.stride(PlanarImageComponent::U) as usize,
368 v_plane: ref_v.as_ref(),
369 v_stride: self.picture.stride(PlanarImageComponent::V) as usize,
370 width: width as usize,
371 height: height as usize,
372 };
373
374 let worker = match self.picture.pixel_layout() {
375 PixelLayout::I400 => yuv400_to_rgba8,
376 PixelLayout::I420 => yuv420_to_rgba8,
377 PixelLayout::I422 => yuv422_to_rgba8,
378 PixelLayout::I444 => yuv444_to_rgba8,
379 };
380
381 worker(image, buf, yuv_range, color_matrix)?;
382
383 if let Some(picture) = self.alpha_picture {
385 if picture.pixel_layout() != PixelLayout::I400 {
386 return Err(ImageError::Decoding(DecodingError::new(
387 ImageFormat::Avif.into(),
388 AvifDecoderError::AlphaPlaneFormat(picture.pixel_layout()),
389 )));
390 }
391
392 let stride = picture.stride(PlanarImageComponent::Y) as usize;
393 let plane = picture.plane(PlanarImageComponent::Y);
394
395 for (buf, slice) in Iterator::zip(
396 buf.chunks_exact_mut(width as usize * 4),
397 plane.as_ref().chunks_exact(stride),
398 ) {
399 for (rgba, a_src) in buf.chunks_exact_mut(4).zip(slice) {
400 rgba[3] = *a_src;
401 }
402 }
403 }
404 } else {
405 if let Ok(buf) = bytemuck::try_cast_slice_mut(buf) {
407 let target_slice: &mut [u16] = buf;
408 self.process_16bit_picture(target_slice, yuv_range, color_matrix)?;
409 } else {
410 let mut aligned_store = vec![0u16; buf.len() / 2];
412 self.process_16bit_picture(&mut aligned_store, yuv_range, color_matrix)?;
413 for (dst, src) in buf.chunks_exact_mut(2).zip(aligned_store.iter()) {
414 let bytes = src.to_ne_bytes();
415 dst[0] = bytes[0];
416 dst[1] = bytes[1];
417 }
418 }
419 }
420
421 Ok(())
422 }
423
424 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
425 (*self).read_image(buf)
426 }
427}
428
429impl<R: Read> AvifDecoder<R> {
430 fn process_16bit_picture(
431 &self,
432 target: &mut [u16],
433 yuv_range: YuvIntensityRange,
434 color_matrix: YuvStandardMatrix,
435 ) -> ImageResult<()> {
436 let y_dav1d_plane = self.picture.plane(PlanarImageComponent::Y);
437
438 let (width, height) = (self.picture.width(), self.picture.height());
439 let bit_depth = self.picture.bit_depth();
440
441 let y_plane_view = transmute_y_plane16(
447 &y_dav1d_plane,
448 self.picture.stride(PlanarImageComponent::Y) as usize,
449 width as usize,
450 height as usize,
451 );
452
453 let u_dav1d_plane = self.picture.plane(PlanarImageComponent::U);
454 let v_dav1d_plane = self.picture.plane(PlanarImageComponent::V);
455 let mut u_plane_view = Plane16View::default();
456 let mut v_plane_view = Plane16View::default();
457
458 if self.picture.pixel_layout() != PixelLayout::I400 {
459 u_plane_view = transmute_chroma_plane16(
460 &u_dav1d_plane,
461 self.picture.pixel_layout(),
462 self.picture.stride(PlanarImageComponent::U) as usize,
463 width as usize,
464 height as usize,
465 );
466 v_plane_view = transmute_chroma_plane16(
467 &v_dav1d_plane,
468 self.picture.pixel_layout(),
469 self.picture.stride(PlanarImageComponent::V) as usize,
470 width as usize,
471 height as usize,
472 );
473 }
474
475 let image = YuvPlanarImage {
476 y_plane: y_plane_view.data.as_ref(),
477 y_stride: y_plane_view.stride,
478 u_plane: u_plane_view.data.as_ref(),
479 u_stride: u_plane_view.stride,
480 v_plane: v_plane_view.data.as_ref(),
481 v_stride: v_plane_view.stride,
482 width: width as usize,
483 height: height as usize,
484 };
485
486 let worker = match self.picture.pixel_layout() {
487 PixelLayout::I400 => {
488 if bit_depth == 10 {
489 yuv400_to_rgba10
490 } else {
491 yuv400_to_rgba12
492 }
493 }
494 PixelLayout::I420 => {
495 if bit_depth == 10 {
496 yuv420_to_rgba10
497 } else {
498 yuv420_to_rgba12
499 }
500 }
501 PixelLayout::I422 => {
502 if bit_depth == 10 {
503 yuv422_to_rgba10
504 } else {
505 yuv422_to_rgba12
506 }
507 }
508 PixelLayout::I444 => {
509 if bit_depth == 10 {
510 yuv444_to_rgba10
511 } else {
512 yuv444_to_rgba12
513 }
514 }
515 };
516 worker(image, target, yuv_range, color_matrix)?;
517
518 if let Some(picture) = &self.alpha_picture {
520 if picture.pixel_layout() != PixelLayout::I400 {
521 return Err(ImageError::Decoding(DecodingError::new(
522 ImageFormat::Avif.into(),
523 AvifDecoderError::AlphaPlaneFormat(picture.pixel_layout()),
524 )));
525 }
526
527 let a_dav1d_plane = picture.plane(PlanarImageComponent::Y);
528 let a_plane_view = transmute_y_plane16(
529 &a_dav1d_plane,
530 picture.stride(PlanarImageComponent::Y) as usize,
531 width as usize,
532 height as usize,
533 );
534
535 for (buf, slice) in Iterator::zip(
536 target.chunks_exact_mut(width as usize * 4),
537 a_plane_view.data.as_ref().chunks_exact(a_plane_view.stride),
538 ) {
539 for (rgba, a_src) in buf.chunks_exact_mut(4).zip(slice) {
540 rgba[3] = *a_src;
541 }
542 }
543 }
544
545 let target_expand_bits = 16u32 - self.picture.bit_depth() as u32;
547 for item in target.iter_mut() {
548 *item = (*item << target_expand_bits) | (*item >> (16 - target_expand_bits));
549 }
550
551 Ok(())
552 }
553}
554
555fn read_until_ready(decoder: &mut dav1d::Decoder) -> ImageResult<dav1d::Picture> {
559 loop {
560 match decoder.get_picture() {
561 Err(dav1d::Error::Again) => match decoder.send_pending_data() {
562 Ok(_) => {}
563 Err(dav1d::Error::Again) => {}
564 Err(e) => return Err(error_map(e)),
565 },
566 r => return r.map_err(error_map),
567 }
568 }
569}