summaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/avif/decoder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/image/src/codecs/avif/decoder.rs')
-rw-r--r--vendor/image/src/codecs/avif/decoder.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/vendor/image/src/codecs/avif/decoder.rs b/vendor/image/src/codecs/avif/decoder.rs
new file mode 100644
index 0000000..acba4f8
--- /dev/null
+++ b/vendor/image/src/codecs/avif/decoder.rs
@@ -0,0 +1,177 @@
+//! Decoding of AVIF images.
+///
+/// The [AVIF] specification defines an image derivative of the AV1 bitstream, an open video codec.
+///
+/// [AVIF]: https://aomediacodec.github.io/av1-avif/
+use std::convert::TryFrom;
+use std::error::Error;
+use std::io::{self, Cursor, Read};
+use std::marker::PhantomData;
+use std::mem;
+
+use crate::error::DecodingError;
+use crate::{ColorType, ImageDecoder, ImageError, ImageFormat, ImageResult};
+
+use dav1d::{PixelLayout, PlanarImageComponent};
+use dcv_color_primitives as dcp;
+use mp4parse::{read_avif, ParseStrictness};
+
+fn error_map<E: Into<Box<dyn Error + Send + Sync>>>(err: E) -> ImageError {
+ ImageError::Decoding(DecodingError::new(ImageFormat::Avif.into(), err))
+}
+
+/// AVIF Decoder.
+///
+/// Reads one image into the chosen input.
+pub struct AvifDecoder<R> {
+ inner: PhantomData<R>,
+ picture: dav1d::Picture,
+ alpha_picture: Option<dav1d::Picture>,
+ icc_profile: Option<Vec<u8>>,
+}
+
+impl<R: Read> AvifDecoder<R> {
+ /// Create a new decoder that reads its input from `r`.
+ pub fn new(mut r: R) -> ImageResult<Self> {
+ let ctx = read_avif(&mut r, ParseStrictness::Normal).map_err(error_map)?;
+ let coded = ctx.primary_item_coded_data().unwrap_or_default();
+
+ let mut primary_decoder = dav1d::Decoder::new();
+ primary_decoder
+ .send_data(coded, None, None, None)
+ .map_err(error_map)?;
+ let picture = primary_decoder.get_picture().map_err(error_map)?;
+ let alpha_item = ctx.alpha_item_coded_data().unwrap_or_default();
+ let alpha_picture = if !alpha_item.is_empty() {
+ let mut alpha_decoder = dav1d::Decoder::new();
+ alpha_decoder
+ .send_data(alpha_item, None, None, None)
+ .map_err(error_map)?;
+ Some(alpha_decoder.get_picture().map_err(error_map)?)
+ } else {
+ None
+ };
+ let icc_profile = ctx
+ .icc_colour_information()
+ .map(|x| x.ok().unwrap_or_default())
+ .map(|x| x.to_vec());
+
+ assert_eq!(picture.bit_depth(), 8);
+ Ok(AvifDecoder {
+ inner: PhantomData,
+ picture,
+ alpha_picture,
+ icc_profile,
+ })
+ }
+}
+
+/// Wrapper struct around a `Cursor<Vec<u8>>`
+pub struct AvifReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
+impl<R> Read for AvifReader<R> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+ fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ if self.0.position() == 0 && buf.is_empty() {
+ mem::swap(buf, self.0.get_mut());
+ Ok(buf.len())
+ } else {
+ self.0.read_to_end(buf)
+ }
+ }
+}
+
+impl<'a, R: 'a + Read> ImageDecoder<'a> for AvifDecoder<R> {
+ type Reader = AvifReader<R>;
+
+ fn dimensions(&self) -> (u32, u32) {
+ (self.picture.width(), self.picture.height())
+ }
+
+ fn color_type(&self) -> ColorType {
+ ColorType::Rgba8
+ }
+
+ fn icc_profile(&mut self) -> Option<Vec<u8>> {
+ self.icc_profile.clone()
+ }
+
+ fn into_reader(self) -> ImageResult<Self::Reader> {
+ let plane = self.picture.plane(PlanarImageComponent::Y);
+ Ok(AvifReader(
+ Cursor::new(plane.as_ref().to_vec()),
+ PhantomData,
+ ))
+ }
+
+ fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
+ assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
+
+ dcp::initialize();
+
+ if self.picture.pixel_layout() != PixelLayout::I400 {
+ let pixel_format = match self.picture.pixel_layout() {
+ PixelLayout::I400 => todo!(),
+ PixelLayout::I420 => dcp::PixelFormat::I420,
+ PixelLayout::I422 => dcp::PixelFormat::I422,
+ PixelLayout::I444 => dcp::PixelFormat::I444,
+ PixelLayout::Unknown => panic!("Unknown pixel layout"),
+ };
+ let src_format = dcp::ImageFormat {
+ pixel_format,
+ color_space: dcp::ColorSpace::Bt601,
+ num_planes: 3,
+ };
+ let dst_format = dcp::ImageFormat {
+ pixel_format: dcp::PixelFormat::Rgba,
+ color_space: dcp::ColorSpace::Lrgb,
+ num_planes: 1,
+ };
+ let (width, height) = self.dimensions();
+ let planes = &[
+ self.picture.plane(PlanarImageComponent::Y),
+ self.picture.plane(PlanarImageComponent::U),
+ self.picture.plane(PlanarImageComponent::V),
+ ];
+ let src_buffers = planes.iter().map(AsRef::as_ref).collect::<Vec<_>>();
+ let strides = &[
+ self.picture.stride(PlanarImageComponent::Y) as usize,
+ self.picture.stride(PlanarImageComponent::U) as usize,
+ self.picture.stride(PlanarImageComponent::V) as usize,
+ ];
+ let dst_buffers = &mut [&mut buf[..]];
+ dcp::convert_image(
+ width,
+ height,
+ &src_format,
+ Some(strides),
+ &src_buffers,
+ &dst_format,
+ None,
+ dst_buffers,
+ )
+ .map_err(error_map)?;
+ } else {
+ let plane = self.picture.plane(PlanarImageComponent::Y);
+ buf.copy_from_slice(plane.as_ref());
+ }
+
+ if let Some(picture) = self.alpha_picture {
+ assert_eq!(picture.pixel_layout(), PixelLayout::I400);
+ let stride = picture.stride(PlanarImageComponent::Y) as usize;
+ let plane = picture.plane(PlanarImageComponent::Y);
+ let width = picture.width();
+ for (buf, slice) in Iterator::zip(
+ buf.chunks_exact_mut(width as usize * 4),
+ plane.as_ref().chunks_exact(stride),
+ ) {
+ for i in 0..width as usize {
+ buf[3 + i * 4] = slice[i];
+ }
+ }
+ }
+
+ Ok(())
+ }
+}