From dc8311fb99ba22b4de61b364179f5a16e8311506 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sat, 16 Apr 2022 16:04:04 -0400 Subject: [PATCH] Merge read_tiled_image and read_stripped_image --- src/decoder/image.rs | 44 ++++++--------- src/decoder/mod.rs | 124 ++++++++++++++++++----------------------- tests/decode_images.rs | 9 +-- 3 files changed, 75 insertions(+), 102 deletions(-) diff --git a/src/decoder/image.rs b/src/decoder/image.rs index 1b154a7..eff950c 100644 --- a/src/decoder/image.rs +++ b/src/decoder/image.rs @@ -21,7 +21,6 @@ pub(crate) struct StripDecodeState { pub(crate) struct TileAttributes { pub image_width: usize, pub image_height: usize, - pub samples_per_pixel: usize, pub tile_width: usize, pub tile_length: usize, @@ -40,24 +39,6 @@ impl TileAttributes { fn padding_down(&self) -> usize { self.tile_length - self.image_height % self.tile_length } - pub fn row_samples(&self) -> usize { - self.tile_width * self.samples_per_pixel - } - pub fn tile_samples(&self) -> usize { - self.tile_length * self.tile_width * self.samples_per_pixel - } - fn tile_strip_samples(&self) -> usize { - (self.tile_samples() * self.tiles_across()) - - (self.padding_right() * self.tile_length * self.samples_per_pixel) - } - - /// Returns the tile offset in the result buffer, counted in samples - pub fn get_offset(&self, tile: usize) -> usize { - let row = tile / self.tiles_across(); - let column = tile % self.tiles_across(); - - (row * self.tile_strip_samples()) + (column * self.row_samples()) - } pub fn get_padding(&self, tile: usize) -> (usize, usize) { let row = tile / self.tiles_across(); @@ -250,7 +231,6 @@ impl Image { tile_attributes = Some(TileAttributes { image_width: usize::try_from(width)?, image_height: usize::try_from(height)?, - samples_per_pixel: bits_per_sample.len(), tile_width, tile_length, }); @@ -388,6 +368,20 @@ impl Image { Ok((*file_offset, *compressed_bytes)) } + pub(crate) fn chunk_dimensions(&self) -> TiffResult<(usize, usize)> { + match self.chunk_type { + ChunkType::Strip => { + let width = usize::try_from(self.width)?; + let strip_attrs = self.strip_decoder.as_ref().unwrap(); + Ok((width, usize::try_from(strip_attrs.rows_per_strip)?)) + } + ChunkType::Tile => { + let tile_attrs = self.tile_attributes.as_ref().unwrap(); + Ok((tile_attrs.tile_width, tile_attrs.tile_length)) + } + } + } + pub(crate) fn expand_chunk( &self, reader: impl Read, @@ -453,13 +447,11 @@ impl Image { super::invert_colors(&mut buffer.subrange(0..total_samples), color_type); } } else { - for row in 0..(dbg!(chunk_dimensions.1) - dbg!(padding.1)) { - let row_start = dbg!(row) * dbg!(output_width) * dbg!(samples); - let row_end = - dbg!(row_start) + (dbg!(chunk_dimensions.0) - dbg!(padding.0)) * samples; + for row in 0..(chunk_dimensions.1 - padding.1) { + let row_start = row * output_width * samples; + let row_end = row_start + (chunk_dimensions.0 - padding.0) * samples; - let row = &mut buffer.as_bytes_mut() - [(dbg!(row_start) * dbg!(byte_len))..(dbg!(row_end) * byte_len)]; + let row = &mut buffer.as_bytes_mut()[(row_start * byte_len)..(row_end * byte_len)]; reader.read_exact(row)?; // Skip horizontal padding diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 7ac9ee7..cb95808 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -1030,29 +1030,6 @@ impl Decoder { Ok(()) } - fn read_tile_to_buffer( - &mut self, - result: &mut DecodingBuffer, - tile: usize, - output_width: usize, - ) -> TiffResult<()> { - self.check_chunk_type(ChunkType::Tile)?; - - let offset = self.image.chunk_byte_range(tile)?.0; - self.goto_offset_u64(offset)?; - - let byte_order = self.reader.byte_order; - self.image.expand_chunk( - &mut self.reader, - result.copy(), - output_width, - byte_order, - tile, - )?; - - Ok(()) - } - fn result_buffer(&self, width: usize, height: usize) -> TiffResult { let buffer_size = match width .checked_mul(height) @@ -1139,60 +1116,69 @@ impl Decoder { let mut result = self.result_buffer(tile_width, tile_length)?; - self.read_tile_to_buffer(&mut result.as_buffer(0), tile, tile_width)?; + let offset = self.image.chunk_byte_range(tile)?.0; + self.goto_offset_u64(offset)?; + + let byte_order = self.reader.byte_order; + self.image.expand_chunk( + &mut self.reader, + result.as_buffer(0), + tile_width, + byte_order, + tile, + )?; self.current_chunk += 1; Ok(result) } - fn read_tiled_image(&mut self) -> TiffResult { - let width = usize::try_from(self.image().width)?; - let mut result = self.result_buffer(width, usize::try_from(self.image().height)?)?; - - for tile in 0..self.image().chunk_offsets.len() { - let buffer_offset = self - .image() - .tile_attributes - .as_ref() - .unwrap() - .get_offset(tile); - - self.read_tile_to_buffer(&mut result.as_buffer(buffer_offset), tile, width)?; - } - - Ok(result) - } - - fn read_stripped_image(&mut self) -> TiffResult { - let rows_per_strip = - usize::try_from(self.image().strip_decoder.as_ref().unwrap().rows_per_strip)?; - - let samples_per_strip = match usize::try_from(self.image().width)? - .checked_mul(rows_per_strip) - .and_then(|x| x.checked_mul(self.image().bits_per_sample.len())) - { - Some(s) => s, - None => return Err(TiffError::LimitsExceeded), - }; - - let mut result = self.result_buffer( - usize::try_from(self.image().width)?, - usize::try_from(self.image().height)?, - )?; - - for i in 0..usize::try_from(self.strip_count()?)? { - let r = result.as_buffer(samples_per_strip * i); - self.read_strip_to_buffer(r)?; - } - Ok(result) - } - /// Decodes the entire image and return it as a Vector pub fn read_image(&mut self) -> TiffResult { - match self.image().chunk_type { - ChunkType::Strip => self.read_stripped_image(), - ChunkType::Tile => self.read_tiled_image(), + let width = usize::try_from(self.image().width)?; + let height = usize::try_from(self.image().height)?; + let mut result = self.result_buffer(width, height)?; + if width == 0 || height == 0 { + return Ok(result); } + + let chunk_dimensions = self.image().chunk_dimensions()?; + let chunk_dimensions = ( + chunk_dimensions.0.min(width), + chunk_dimensions.1.min(height), + ); + if chunk_dimensions.0 == 0 || chunk_dimensions.1 == 0 { + return Err(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + )); + } + + let samples = self.image().bits_per_sample.len(); + if samples == 0 { + return Err(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + )); + } + + let chunks_across = (width - 1) / chunk_dimensions.0 + 1; + let strip_samples = width * chunk_dimensions.1 * samples; + + for chunk in 0..self.image().chunk_offsets.len() { + self.goto_offset_u64(self.image().chunk_offsets[chunk])?; + + let x = chunk % chunks_across; + let y = chunk / chunks_across; + let buffer_offset = y * strip_samples + x * chunk_dimensions.0 * samples; + let byte_order = self.reader.byte_order; + self.image.expand_chunk( + &mut self.reader, + result.as_buffer(buffer_offset).copy(), + width, + byte_order, + chunk, + )?; + } + + Ok(result) } } diff --git a/tests/decode_images.rs b/tests/decode_images.rs index d2a4419..5d947dc 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -324,7 +324,7 @@ fn test_too_many_value_bytes() { } #[test] -fn test_bad_strip_count() { +fn fuzzer_testcase5() { let image = [ 73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 0, 1, 1, 4, 0, 1, 0, 0, 0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, @@ -335,12 +335,7 @@ fn test_bad_strip_count() { let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap(); - let err = decoder.read_image().unwrap_err(); - - match err { - tiff::TiffError::IntSizeError => {} - unexpected => panic!("Unexpected error {}", unexpected), - } + let _ = decoder.read_image().unwrap_err(); } #[test]