From 0e0a7b4c203a5cf4930d470f6e003e1cb41b805b Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Sat, 23 May 2026 21:57:32 -0700 Subject: [PATCH 1/2] docs(encoder): correct write_image panic condition for ExtendedColorType --- src/codecs/bmp/encoder.rs | 8 ++++++-- src/codecs/jpeg/encoder.rs | 4 +++- src/codecs/tga/encoder.rs | 4 +++- src/codecs/tiff.rs | 4 +++- src/codecs/webp/encoder.rs | 4 +++- src/io/encoder.rs | 6 +++++- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/codecs/bmp/encoder.rs b/src/codecs/bmp/encoder.rs index 5ea4a63871..31264a852d 100644 --- a/src/codecs/bmp/encoder.rs +++ b/src/codecs/bmp/encoder.rs @@ -26,7 +26,9 @@ impl BmpEncoder { /// /// # Panics /// - /// Panics if `width * height * c.bytes_per_pixel() != image.len()`. + /// Panics if the buffer does not hold exactly the number of bytes required for the given + /// `width`, `height`, and color type, accounting for rows padded to whole bytes for + /// sub-byte color types: `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. #[track_caller] pub fn encode( &mut self, @@ -43,7 +45,9 @@ impl BmpEncoder { /// /// # Panics /// - /// Panics if `width * height * c.bytes_per_pixel() != image.len()`. + /// Panics if the buffer does not hold exactly the number of bytes required for the given + /// `width`, `height`, and color type, accounting for rows padded to whole bytes for + /// sub-byte color types: `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. #[track_caller] pub fn encode_with_palette( &mut self, diff --git a/src/codecs/jpeg/encoder.rs b/src/codecs/jpeg/encoder.rs index 4135d5ebff..816fb2b66b 100644 --- a/src/codecs/jpeg/encoder.rs +++ b/src/codecs/jpeg/encoder.rs @@ -192,7 +192,9 @@ impl JpegEncoder { /// /// # Panics /// - /// Panics if `width * height * color_type.bytes_per_pixel() != image.len()`. + /// Panics if the buffer does not hold exactly the number of bytes required for the given + /// `width`, `height`, and `color_type`, accounting for rows padded to whole bytes for + /// sub-byte color types: `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. #[track_caller] fn encode( self, diff --git a/src/codecs/tga/encoder.rs b/src/codecs/tga/encoder.rs index 5c526a6312..6a65d164cb 100644 --- a/src/codecs/tga/encoder.rs +++ b/src/codecs/tga/encoder.rs @@ -159,7 +159,9 @@ impl TgaEncoder { /// /// # Panics /// - /// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`. + /// Panics if the buffer does not hold exactly the number of bytes required for the given + /// `width`, `height`, and `color_type`, accounting for rows padded to whole bytes for + /// sub-byte color types: `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. #[track_caller] pub fn encode( mut self, diff --git a/src/codecs/tiff.rs b/src/codecs/tiff.rs index 2adf5f9b43..0f020ac64e 100644 --- a/src/codecs/tiff.rs +++ b/src/codecs/tiff.rs @@ -785,7 +785,9 @@ impl ImageEncoder for TiffEncoder { /// /// # Panics /// - /// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`. + /// Panics if the buffer does not hold exactly the number of bytes required for the given + /// `width`, `height`, and `color_type`, accounting for rows padded to whole bytes for + /// sub-byte color types: `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. #[track_caller] fn write_image( self, diff --git a/src/codecs/webp/encoder.rs b/src/codecs/webp/encoder.rs index b25ca5e258..70423d8406 100644 --- a/src/codecs/webp/encoder.rs +++ b/src/codecs/webp/encoder.rs @@ -43,7 +43,9 @@ impl WebPEncoder { /// /// # Panics /// - /// Panics if `width * height * color.bytes_per_pixel() != data.len()`. + /// Panics if the buffer does not hold exactly the number of bytes required for the given + /// `width`, `height`, and color type, accounting for rows padded to whole bytes for + /// sub-byte color types: `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. #[track_caller] pub fn encode( self, diff --git a/src/io/encoder.rs b/src/io/encoder.rs index b58ea84234..b20b5420a5 100644 --- a/src/io/encoder.rs +++ b/src/io/encoder.rs @@ -32,7 +32,11 @@ pub trait ImageEncoder { /// /// # Panics /// - /// Panics if `width * height * color_type.bytes_per_pixel() != buf.len()`. + /// Panics if `buf` does not hold exactly the number of bytes required for the given `width`, + /// `height`, and `color_type`. Pixel rows are laid out in row-major order. For color types + /// whose `bits_per_pixel()` is not a multiple of 8 (such as `L1`), each row is padded to a + /// whole number of bytes, so the expected length is + /// `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. fn write_image( self, buf: &[u8], From 5635527040309a8ac1c153404601984efd3bd587 Mon Sep 17 00:00:00 2001 From: say Date: Mon, 25 May 2026 14:31:47 -0700 Subject: [PATCH 2/2] docs: make buffer_size pub with byte-rounding doc, simplify write_image panic Signed-off-by: say --- src/color.rs | 8 ++++++-- src/io/encoder.rs | 6 +----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/color.rs b/src/color.rs index e1c705c92d..28bbedc189 100644 --- a/src/color.rs +++ b/src/color.rs @@ -294,8 +294,12 @@ impl ExtendedColorType { } } - /// Returns the number of bytes required to hold a width x height image of this color type. - pub(crate) fn buffer_size(self, width: u32, height: u32) -> u64 { + /// Returns the number of bytes required to hold a `width x height` image of this color type. + /// + /// Each pixel row occupies exactly `width * bits_per_pixel()` bits, rounded **up to the + /// nearest byte** (no additional row padding is added). The total is `row_bytes * height`, + /// saturating at [`u64::MAX`] for astronomically large inputs. + pub fn buffer_size(self, width: u32, height: u32) -> u64 { let bpp = self.bits_per_pixel() as u64; let row_pitch = (width as u64 * bpp).div_ceil(8); row_pitch.saturating_mul(height as u64) diff --git a/src/io/encoder.rs b/src/io/encoder.rs index b20b5420a5..eb9ae816a4 100644 --- a/src/io/encoder.rs +++ b/src/io/encoder.rs @@ -32,11 +32,7 @@ pub trait ImageEncoder { /// /// # Panics /// - /// Panics if `buf` does not hold exactly the number of bytes required for the given `width`, - /// `height`, and `color_type`. Pixel rows are laid out in row-major order. For color types - /// whose `bits_per_pixel()` is not a multiple of 8 (such as `L1`), each row is padded to a - /// whole number of bytes, so the expected length is - /// `height * ((width * color_type.bits_per_pixel() as u32 + 7) / 8)`. + /// Panics if `buf.len() as u64 != color_type.buffer_size(width, height)`. fn write_image( self, buf: &[u8],