diff --git a/src/codecs/avif/encoder.rs b/src/codecs/avif/encoder.rs index 17cb77ceff..f05f6a52ff 100644 --- a/src/codecs/avif/encoder.rs +++ b/src/codecs/avif/encoder.rs @@ -106,13 +106,7 @@ impl ImageEncoder for AvifEncoder { height: u32, color: ExtendedColorType, ) -> ImageResult<()> { - let expected_buffer_len = color.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - data.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - data.len(), - ); + color.assert_buf_len(width, height, data); self.set_color(color); // `ravif` needs strongly typed data so let's convert. We can either use a temporarily diff --git a/src/codecs/bmp/encoder.rs b/src/codecs/bmp/encoder.rs index 31264a852d..cff7d6b728 100644 --- a/src/codecs/bmp/encoder.rs +++ b/src/codecs/bmp/encoder.rs @@ -57,6 +57,8 @@ impl BmpEncoder { color_type: ExtendedColorType, palette: Option<&[[u8; 3]]>, ) -> ImageResult<()> { + color_type.assert_buf_len(width, height, image); + if palette.is_some() && color_type != ExtendedColorType::L1 && color_type != ExtendedColorType::L8 @@ -77,14 +79,6 @@ impl BmpEncoder { ))); } - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - image.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - image.len(), - ); - let bmp_header_size = BITMAPFILEHEADER_SIZE; let (dib_header_size, bits_per_pixel, palette_color_count) = diff --git a/src/codecs/farbfeld.rs b/src/codecs/farbfeld.rs index 1954124353..c015fcc81c 100644 --- a/src/codecs/farbfeld.rs +++ b/src/codecs/farbfeld.rs @@ -159,6 +159,8 @@ impl ImageEncoder for FarbfeldEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { + color_type.assert_buf_len(width, height, buf); + if color_type != ExtendedColorType::Rgba16 { return Err(ImageError::Unsupported( UnsupportedError::from_format_and_kind( diff --git a/src/codecs/gif.rs b/src/codecs/gif.rs index dea103e67f..53436278a4 100644 --- a/src/codecs/gif.rs +++ b/src/codecs/gif.rs @@ -504,6 +504,7 @@ impl GifEncoder { } /// Encode a single image. + #[track_caller] pub fn encode( &mut self, data: &[u8], @@ -511,6 +512,8 @@ impl GifEncoder { height: u32, color: ExtendedColorType, ) -> ImageResult<()> { + color.assert_buf_len(width, height, data); + let (width, height) = self.gif_dimensions(width, height)?; match color { ExtendedColorType::Rgb8 => { @@ -633,6 +636,7 @@ impl GifEncoder { } } impl ImageEncoder for GifEncoder { + #[track_caller] fn write_image( mut self, buf: &[u8], diff --git a/src/codecs/hdr/encoder.rs b/src/codecs/hdr/encoder.rs index 6d7863d61a..e02342822b 100644 --- a/src/codecs/hdr/encoder.rs +++ b/src/codecs/hdr/encoder.rs @@ -12,6 +12,7 @@ pub struct HdrEncoder { } impl ImageEncoder for HdrEncoder { + #[track_caller] fn write_image( self, unaligned_bytes: &[u8], @@ -19,9 +20,12 @@ impl ImageEncoder for HdrEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { + color_type.assert_buf_len(width, height, unaligned_bytes); + match color_type { ExtendedColorType::Rgb32F => { let bytes_per_pixel = color_type.bits_per_pixel() as usize / 8; + let rgbe_pixels = unaligned_bytes .chunks_exact(bytes_per_pixel) .map(|bytes| to_rgbe8(Rgb::(bytemuck::pod_read_unaligned(bytes)))); diff --git a/src/codecs/ico/encoder.rs b/src/codecs/ico/encoder.rs index b4d9da3a58..4e5e8ca82f 100644 --- a/src/codecs/ico/encoder.rs +++ b/src/codecs/ico/encoder.rs @@ -139,13 +139,7 @@ impl ImageEncoder for IcoEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); + color_type.assert_buf_len(width, height, buf); let image = IcoFrame::as_png(buf, width, height, color_type)?; self.encode_images(&[image]) diff --git a/src/codecs/jpeg/encoder.rs b/src/codecs/jpeg/encoder.rs index 816fb2b66b..bb49c78b49 100644 --- a/src/codecs/jpeg/encoder.rs +++ b/src/codecs/jpeg/encoder.rs @@ -203,13 +203,7 @@ impl JpegEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - image.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - image.len(), - ); + color_type.assert_buf_len(width, height, image); let (width, height) = match (u16::try_from(width), u16::try_from(height)) { (Ok(w @ 1..), Ok(h @ 1..)) => (w, h), diff --git a/src/codecs/openexr.rs b/src/codecs/openexr.rs index 442a915341..bf7e1b665f 100644 --- a/src/codecs/openexr.rs +++ b/src/codecs/openexr.rs @@ -329,13 +329,7 @@ where height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); + color_type.assert_buf_len(width, height, buf); write_buffer(self.0, buf, width, height, color_type) } diff --git a/src/codecs/png.rs b/src/codecs/png.rs index b7cc944cdf..318c9f5b28 100644 --- a/src/codecs/png.rs +++ b/src/codecs/png.rs @@ -872,13 +872,7 @@ impl ImageEncoder for PngEncoder { ) -> ImageResult<()> { use ExtendedColorType::*; - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); + color_type.assert_buf_len(width, height, buf); // PNG images are big endian. For 16 bit per channel and larger types, // the buffer may need to be reordered to big endian per the diff --git a/src/codecs/pnm/encoder.rs b/src/codecs/pnm/encoder.rs index 1172ef0c60..7fc155ae21 100644 --- a/src/codecs/pnm/encoder.rs +++ b/src/codecs/pnm/encoder.rs @@ -343,13 +343,7 @@ impl ImageEncoder for PnmEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); + color_type.assert_buf_len(width, height, buf); self.encode(buf, width, height, color_type) } diff --git a/src/codecs/qoi.rs b/src/codecs/qoi.rs index e24eed6bc1..1e4c15db60 100644 --- a/src/codecs/qoi.rs +++ b/src/codecs/qoi.rs @@ -73,6 +73,8 @@ impl ImageEncoder for QoiEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { + color_type.assert_buf_len(width, height, buf); + if !matches!( color_type, ExtendedColorType::Rgba8 | ExtendedColorType::Rgb8 @@ -85,14 +87,6 @@ impl ImageEncoder for QoiEncoder { )); } - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); - // Encode data in QOI let data = qoi::encode_to_vec(buf, width, height).map_err(encoding_error)?; diff --git a/src/codecs/tga/encoder.rs b/src/codecs/tga/encoder.rs index 6a65d164cb..ed6244e3cb 100644 --- a/src/codecs/tga/encoder.rs +++ b/src/codecs/tga/encoder.rs @@ -170,13 +170,7 @@ impl TgaEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); + color_type.assert_buf_len(width, height, buf); // Validate dimensions. if width == 0 || height == 0 { diff --git a/src/codecs/tiff.rs b/src/codecs/tiff.rs index 0f020ac64e..2f01bed8b8 100644 --- a/src/codecs/tiff.rs +++ b/src/codecs/tiff.rs @@ -799,13 +799,9 @@ impl ImageEncoder for TiffEncoder { use tiff::encoder::colortype::{ Gray16, Gray8, RGB32Float, RGBA32Float, RGB16, RGB8, RGBA16, RGBA8, }; - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); + + color_type.assert_buf_len(width, height, buf); + match color_type { ExtendedColorType::L8 => self.write_tiff::(width, height, buf), ExtendedColorType::Rgb8 => self.write_tiff::(width, height, buf), diff --git a/src/codecs/webp/encoder.rs b/src/codecs/webp/encoder.rs index 70423d8406..cbad6bcdd2 100644 --- a/src/codecs/webp/encoder.rs +++ b/src/codecs/webp/encoder.rs @@ -54,13 +54,7 @@ impl WebPEncoder { height: u32, color_type: ExtendedColorType, ) -> ImageResult<()> { - let expected_buffer_len = color_type.buffer_size(width, height); - assert_eq!( - expected_buffer_len, - buf.len() as u64, - "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", - buf.len(), - ); + color_type.assert_buf_len(width, height, buf); let color_type = match color_type { ExtendedColorType::L8 => image_webp::ColorType::L8, diff --git a/src/color.rs b/src/color.rs index 28bbedc189..ee239d3936 100644 --- a/src/color.rs +++ b/src/color.rs @@ -304,6 +304,18 @@ impl ExtendedColorType { let row_pitch = (width as u64 * bpp).div_ceil(8); row_pitch.saturating_mul(height as u64) } + + /// Asserts that the buffer has the correct length for an image of the given dimensions and color. + #[track_caller] + pub(crate) fn assert_buf_len(self, width: u32, height: u32, buf: &[u8]) { + let expected_buffer_len = self.buffer_size(width, height); + assert_eq!( + expected_buffer_len, + buf.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} but got {} for {width}x{height} {self:?} image", + buf.len(), + ); + } } impl From for ExtendedColorType {