diff --git a/src/codecs/bmp/decoder.rs b/src/codecs/bmp/decoder.rs index 3fea60c0c7..028eb64737 100644 --- a/src/codecs/bmp/decoder.rs +++ b/src/codecs/bmp/decoder.rs @@ -1450,7 +1450,15 @@ impl BmpDecoder { /// Read the actual data of the image. This function is deliberately not public because it /// cannot be called multiple times without seeking back the underlying reader in between. pub(crate) fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> { - let data = match self.image_type { + buf.copy_from_slice(&self.get_image_data()?); + Ok(()) + } + + /// Read the actual data of the image and return it as a Vec. + /// This function is deliberately not public because it + /// cannot be called multiple times without seeking back the underlying reader in between. + pub(crate) fn get_image_data(&mut self) -> ImageResult> { + match self.image_type { ImageType::Palette => self.read_palettized_pixel_data(), ImageType::RGB16 => self.read_16_bit_pixel_data(Some(&R5_G5_B5_COLOR_MASK)), ImageType::RGB24 => self.read_full_byte_pixel_data(&FormatFullBytes::RGB24), @@ -1472,10 +1480,7 @@ impl BmpDecoder { Some(_) => self.read_32_bit_pixel_data(), None => Err(DecoderError::BitfieldMasksMissing(32).into()), }, - }?; - - buf.copy_from_slice(&data); - Ok(()) + } } } @@ -1512,11 +1517,8 @@ impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for BmpDecoder { } } - fn into_reader(self) -> ImageResult { - Ok(BmpReader( - Cursor::new(image::decoder_to_vec(self)?), - PhantomData, - )) + fn into_reader(mut self) -> ImageResult { + Ok(BmpReader(Cursor::new(self.get_image_data()?), PhantomData)) } fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { diff --git a/src/codecs/gif.rs b/src/codecs/gif.rs index 78c09cf292..b3936187e9 100644 --- a/src/codecs/gif.rs +++ b/src/codecs/gif.rs @@ -108,10 +108,9 @@ impl<'a, R: 'a + Read> ImageDecoder<'a> for GifDecoder { } fn into_reader(self) -> ImageResult { - Ok(GifReader( - Cursor::new(image::decoder_to_vec(self)?), - PhantomData, - )) + let mut buf = vec![0; self.total_bytes() as usize]; + self.read_image(&mut buf)?; + Ok(GifReader(Cursor::new(buf), PhantomData)) } fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { diff --git a/src/codecs/hdr/decoder.rs b/src/codecs/hdr/decoder.rs index 6915f0d15f..f59164766c 100644 --- a/src/codecs/hdr/decoder.rs +++ b/src/codecs/hdr/decoder.rs @@ -199,10 +199,9 @@ impl<'a, R: 'a + BufRead> ImageDecoder<'a> for HdrAdapter { } fn into_reader(self) -> ImageResult { - Ok(HdrReader( - Cursor::new(image::decoder_to_vec(self)?), - PhantomData, - )) + let mut buf = vec![0; self.total_bytes() as usize]; + self.read_image(&mut buf)?; + Ok(HdrReader(Cursor::new(buf), PhantomData)) } fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { diff --git a/src/codecs/ico/decoder.rs b/src/codecs/ico/decoder.rs index 3bcbdf0ff2..d34dc74093 100644 --- a/src/codecs/ico/decoder.rs +++ b/src/codecs/ico/decoder.rs @@ -296,10 +296,9 @@ impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for IcoDecoder { } fn into_reader(self) -> ImageResult { - Ok(IcoReader( - Cursor::new(image::decoder_to_vec(self)?), - PhantomData, - )) + let mut buf = vec![0; self.total_bytes() as usize]; + self.read_image(&mut buf)?; + Ok(IcoReader(Cursor::new(buf), PhantomData)) } fn read_image(self, buf: &mut [u8]) -> ImageResult<()> { diff --git a/src/codecs/openexr.rs b/src/codecs/openexr.rs index 52d6ba9421..f828f0f1d3 100644 --- a/src/codecs/openexr.rs +++ b/src/codecs/openexr.rs @@ -23,7 +23,6 @@ use exr::prelude::*; use crate::error::{DecodingError, EncodingError, ImageFormatHint}; -use crate::image::decoder_to_vec; use crate::{ ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageFormat, ImageResult, Progress, @@ -138,7 +137,9 @@ impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for OpenExrDecoder { /// Use `read_image` instead if possible, /// as this method creates a whole new buffer just to contain the entire image. fn into_reader(self) -> ImageResult { - Ok(Cursor::new(decoder_to_vec(self)?)) + let mut buf = vec![0; self.total_bytes() as usize]; + self.read_image(&mut buf)?; + Ok(Cursor::new(buf)) } fn scanline_bytes(&self) -> u64 { @@ -385,6 +386,7 @@ mod test { use crate::buffer_::{Rgb32FImage, Rgba32FImage}; use crate::error::{LimitError, LimitErrorKind}; + use crate::image::decoder_to_vec; use crate::{ImageBuffer, Rgb, Rgba}; const BASE_PATH: &[&str] = &[".", "tests", "images", "exr"]; diff --git a/src/codecs/pnm/decoder.rs b/src/codecs/pnm/decoder.rs index b3ca0f8268..de9107c7d4 100644 --- a/src/codecs/pnm/decoder.rs +++ b/src/codecs/pnm/decoder.rs @@ -614,10 +614,9 @@ impl<'a, R: 'a + Read> ImageDecoder<'a> for PnmDecoder { } fn into_reader(self) -> ImageResult { - Ok(PnmReader( - Cursor::new(image::decoder_to_vec(self)?), - PhantomData, - )) + let mut buf = vec![0; self.total_bytes() as usize]; + self.read_image(&mut buf)?; + Ok(PnmReader(Cursor::new(buf), PhantomData)) } fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { diff --git a/src/image.rs b/src/image.rs index 58e90b135d..20cdbbb9e8 100644 --- a/src/image.rs +++ b/src/image.rs @@ -604,9 +604,26 @@ where ))); } - let mut buf = vec![num_traits::Zero::zero(); total_bytes.unwrap() / std::mem::size_of::()]; - decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?; - Ok(buf) + use std::any::TypeId; + if TypeId::of::() == TypeId::of::() { + // It's faster to read u8 without zeroing the buffer first. + // On top of that, some implementations such as JPEG produce a `Vec` up front, + // so going through the other codepath would cause a memcpy(). + // A single large allocation here is actually OK because the memory won't be provisioned until + // we actually write to it, so this doesn't increase actual memory usage + let mut buf: Vec = Vec::with_capacity(total_bytes.unwrap()); + decoder.into_reader()?.read_to_end(&mut buf)?; + Ok(bytemuck::allocation::cast_vec(buf)) + } else { + // The input and output types may not be layout-compatible (same size and alignment), + // and an allocation must be freed with the same size and alignment with which it was allocated, + // so we have to create a Vec up front and initialize it, + // so that we could slice it and then change the layout of the slice, which is valid. + let mut buf = + vec![num_traits::Zero::zero(); total_bytes.unwrap() / std::mem::size_of::()]; + decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?; + Ok(buf) + } } /// Represents the progress of an image operation.