From b385a6e75264eab3cf808ed590b2bab0b160d0b1 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Fri, 7 Jan 2022 16:03:07 -0500 Subject: [PATCH] Move some decoder fields into a new Image struct --- src/decoder/image.rs | 96 +++++++++++ src/decoder/mod.rs | 369 ++++++++++++++++++++----------------------- 2 files changed, 265 insertions(+), 200 deletions(-) create mode 100644 src/decoder/image.rs diff --git a/src/decoder/image.rs b/src/decoder/image.rs new file mode 100644 index 0000000..3c20736 --- /dev/null +++ b/src/decoder/image.rs @@ -0,0 +1,96 @@ +use super::ChunkType; +use super::ifd::Directory; +use crate::tags::{ + CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, +}; + +#[derive(Debug)] +pub(crate) struct StripDecodeState { + pub rows_per_strip: u32, +} + +#[derive(Debug)] +/// Computed values useful for tile decoding +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, +} + +impl TileAttributes { + pub fn tiles_across(&self) -> usize { + (self.image_width + self.tile_width - 1) / self.tile_width + } + pub fn tiles_down(&self) -> usize { + (self.image_height + self.tile_length - 1) / self.tile_length + } + fn padding_right(&self) -> usize { + self.tile_width - self.image_width % self.tile_width + } + 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(); + let column = tile % self.tiles_across(); + + let padding_right = if column == self.tiles_across() - 1 { + self.padding_right() + } else { + 0 + }; + + let padding_down = if row == self.tiles_down() - 1 { + self.padding_down() + } else { + 0 + }; + + (padding_right, padding_down) + } +} + +#[derive(Debug)] +pub(crate) struct Image { + pub ifd: Option, + pub width: u32, + pub height: u32, + pub bits_per_sample: Vec, + pub samples: u8, + pub sample_format: Vec, + pub photometric_interpretation: PhotometricInterpretation, + pub compression_method: CompressionMethod, + pub predictor: Predictor, + pub jpeg_tables: Option>, + pub chunk_type: ChunkType, + pub strip_decoder: Option, + pub tile_attributes: Option, + pub chunk_offsets: Vec, + pub chunk_bytes: Vec, +} + +impl Image { + +} \ No newline at end of file diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 915b2ea..a5adf0b 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -8,6 +8,7 @@ use crate::{ }; use self::ifd::Directory; +use self::image::{Image, StripDecodeState, TileAttributes}; use crate::tags::{ CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type, }; @@ -17,6 +18,7 @@ use self::stream::{ }; pub mod ifd; +mod image; mod stream; /// Result of a decoding process @@ -233,74 +235,6 @@ impl<'a> DecodingBuffer<'a> { } } -#[derive(Debug)] -struct StripDecodeState { - rows_per_strip: u32, -} - -#[derive(Debug)] -/// Computed values useful for tile decoding -struct TileAttributes { - image_width: usize, - image_height: usize, - samples_per_pixel: usize, - - tile_width: usize, - tile_length: usize, -} - -impl TileAttributes { - fn tiles_across(&self) -> usize { - (self.image_width + self.tile_width - 1) / self.tile_width - } - fn tiles_down(&self) -> usize { - (self.image_height + self.tile_length - 1) / self.tile_length - } - fn padding_right(&self) -> usize { - self.tile_width - self.image_width % self.tile_width - } - fn padding_down(&self) -> usize { - self.tile_length - self.image_height % self.tile_length - } - fn row_samples(&self) -> usize { - self.tile_width * self.samples_per_pixel - } - 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 - 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()) - } - - fn get_padding(&self, tile: usize) -> (usize, usize) { - let row = tile / self.tiles_across(); - let column = tile % self.tiles_across(); - - let padding_right = if column == self.tiles_across() - 1 { - self.padding_right() - } else { - 0 - }; - - let padding_down = if row == self.tiles_down() - 1 { - self.padding_down() - } else { - 0 - }; - - (padding_right, padding_down) - } -} - #[derive(Debug, Copy, Clone, PartialEq)] /// Chunk type of the internal representation pub enum ChunkType { @@ -369,23 +303,9 @@ where bigtiff: bool, limits: Limits, next_ifd: Option, - ifd: Option, - width: u32, - height: u32, - bits_per_sample: Vec, - samples: u8, - sample_format: Vec, - photometric_interpretation: PhotometricInterpretation, - compression_method: CompressionMethod, - predictor: Predictor, - jpeg_tables: Option>, - chunk_type: ChunkType, - strip_decoder: Option, - tile_attributes: Option, - chunk_offsets: Vec, - chunk_bytes: Vec, current_chunk: usize, seen_ifds: HashSet, + image: Image, } trait Wrapping { @@ -584,21 +504,23 @@ impl Decoder { bigtiff, limits: Default::default(), next_ifd, - ifd: None, - width: 0, - height: 0, - bits_per_sample: vec![1], - samples: 1, - sample_format: vec![SampleFormat::Uint], - photometric_interpretation: PhotometricInterpretation::BlackIsZero, - compression_method: CompressionMethod::None, - jpeg_tables: None, - predictor: Predictor::None, - chunk_type: ChunkType::Strip, - strip_decoder: None, - tile_attributes: None, - chunk_offsets: Vec::new(), - chunk_bytes: Vec::new(), + image: Image { + ifd: None, + width: 0, + height: 0, + bits_per_sample: vec![1], + samples: 1, + sample_format: vec![SampleFormat::Uint], + photometric_interpretation: PhotometricInterpretation::BlackIsZero, + compression_method: CompressionMethod::None, + jpeg_tables: None, + predictor: Predictor::None, + chunk_type: ChunkType::Strip, + strip_decoder: None, + tile_attributes: None, + chunk_offsets: Vec::new(), + chunk_bytes: Vec::new(), + }, current_chunk: 0, seen_ifds, }; @@ -612,12 +534,12 @@ impl Decoder { } pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> { - Ok((self.width, self.height)) + Ok((self.image().width, self.image().height)) } pub fn colortype(&mut self) -> TiffResult { - match self.photometric_interpretation { - PhotometricInterpretation::RGB => match self.bits_per_sample[..] { + match self.image().photometric_interpretation { + PhotometricInterpretation::RGB => match self.image().bits_per_sample[..] { [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)), [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)), // FIXME: We should _ignore_ other components. In particular: @@ -627,36 +549,40 @@ impl Decoder { // > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements. _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( - self.photometric_interpretation, - self.bits_per_sample.clone(), + self.image().photometric_interpretation, + self.image().bits_per_sample.clone(), ), )), }, - PhotometricInterpretation::CMYK => match self.bits_per_sample[..] { + PhotometricInterpretation::CMYK => match self.image().bits_per_sample[..] { [c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)), _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( - self.photometric_interpretation, - self.bits_per_sample.clone(), + self.image().photometric_interpretation, + self.image().bits_per_sample.clone(), ), )), }, PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero - if self.bits_per_sample.len() == 1 => + if self.image().bits_per_sample.len() == 1 => { - Ok(ColorType::Gray(self.bits_per_sample[0])) + Ok(ColorType::Gray(self.image().bits_per_sample[0])) } // TODO: this is bad we should not fail at this point _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( - self.photometric_interpretation, - self.bits_per_sample.clone(), + self.image().photometric_interpretation, + self.image().bits_per_sample.clone(), ), )), } } + fn image(&self) -> &Image { + &self.image + } + /// Reset the decoder. #[deprecated = "Never should have been public. Only use Decoder::new()"] pub fn init(self) -> TiffResult> { @@ -668,30 +594,32 @@ impl Decoder { /// If there is no further image in the TIFF file a format error is returned. /// To determine whether there are more images call `TIFFDecoder::more_images` instead. pub fn next_image(&mut self) -> TiffResult<()> { - self.ifd = Some(self.read_ifd()?); - self.width = self.get_tag_u32(Tag::ImageWidth)?; - self.height = self.get_tag_u32(Tag::ImageLength)?; - self.strip_decoder = None; - self.tile_attributes = None; - self.chunk_offsets.clear(); - self.chunk_bytes.clear(); self.current_chunk = 0; - self.photometric_interpretation = self + self.image.ifd = Some(self.read_ifd()?); + self.image.width = self.get_tag_u32(Tag::ImageWidth)?; + self.image.height = self.get_tag_u32(Tag::ImageLength)?; + self.image.strip_decoder = None; + self.image.tile_attributes = None; + self.image.chunk_offsets.clear(); + self.image.chunk_bytes.clear(); + self.image.jpeg_tables = None; + + self.image.photometric_interpretation = self .find_tag_unsigned(Tag::PhotometricInterpretation)? .and_then(PhotometricInterpretation::from_u16) .ok_or(TiffUnsupportedError::UnknownInterpretation)?; // Try to parse both the compression method and the number, format, and bits of the included samples. // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images. - self.compression_method = match self.find_tag_unsigned(Tag::Compression)? { + self.image.compression_method = match self.find_tag_unsigned(Tag::Compression)? { Some(val) => CompressionMethod::from_u16(val) .ok_or(TiffUnsupportedError::UnknownCompressionMethod)?, None => CompressionMethod::None, }; - if self.compression_method == CompressionMethod::ModernJPEG { - self.jpeg_tables = self + if self.image.compression_method == CompressionMethod::ModernJPEG { + self.image.jpeg_tables = self .find_tag(Tag::JPEGTables)? .map(|_| { let vec = self.get_tag_u8_vec(Tag::JPEGTables)?; @@ -705,9 +633,9 @@ impl Decoder { .transpose()?; } - self.samples = self.find_tag_unsigned(Tag::SamplesPerPixel)?.unwrap_or(1); + self.image.samples = self.find_tag_unsigned(Tag::SamplesPerPixel)?.unwrap_or(1); - self.sample_format = match self.find_tag_unsigned_vec(Tag::SampleFormat)? { + self.image.sample_format = match self.find_tag_unsigned_vec(Tag::SampleFormat)? { Some(vals) => { let sample_format: Vec<_> = vals .into_iter() @@ -717,7 +645,7 @@ impl Decoder { // TODO: for now, only homogenous formats across samples are supported. if !sample_format.windows(2).all(|s| s[0] == s[1]) { return Err(TiffUnsupportedError::UnsupportedSampleFormat( - self.sample_format.clone(), + self.image.sample_format.clone(), ) .into()); } @@ -727,14 +655,14 @@ impl Decoder { None => vec![SampleFormat::Uint], }; - self.bits_per_sample = match self.samples { + self.image.bits_per_sample = match self.image.samples { 1 | 3 | 4 => self .find_tag_unsigned_vec(Tag::BitsPerSample)? .unwrap_or_else(|| vec![1]), - _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()), + _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.image.samples).into()), }; - self.predictor = self + self.image.predictor = self .find_tag_unsigned(Tag::Predictor)? .map(|p| { Predictor::from_u16(p) @@ -743,7 +671,7 @@ impl Decoder { .transpose()? .unwrap_or(Predictor::None); - let ifd = self.ifd.as_ref().unwrap(); + let ifd = self.image.ifd.as_ref().unwrap(); match ( ifd.contains_key(&Tag::StripByteCounts), ifd.contains_key(&Tag::StripOffsets), @@ -751,18 +679,18 @@ impl Decoder { ifd.contains_key(&Tag::TileOffsets), ) { (true, true, false, false) => { - self.chunk_type = ChunkType::Strip; + self.image.chunk_type = ChunkType::Strip; - self.chunk_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?; - self.chunk_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?; + self.image.chunk_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?; + self.image.chunk_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?; let rows_per_strip = self .find_tag(Tag::RowsPerStrip)? - .unwrap_or(ifd::Value::Unsigned(self.height)) + .unwrap_or(ifd::Value::Unsigned(self.image.height)) .into_u32()?; - self.strip_decoder = Some(StripDecodeState { rows_per_strip }); + self.image.strip_decoder = Some(StripDecodeState { rows_per_strip }); } (false, false, true, true) => { - self.chunk_type = ChunkType::Tile; + self.image.chunk_type = ChunkType::Tile; let tile_width = usize::try_from(self.get_tag_u32(Tag::TileWidth)?)?; let tile_length = usize::try_from(self.get_tag_u32(Tag::TileLength)?)?; @@ -773,19 +701,19 @@ impl Decoder { return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into()); } - self.tile_attributes = Some(TileAttributes { - image_width: usize::try_from(self.width)?, - image_height: usize::try_from(self.height)?, - samples_per_pixel: self.bits_per_sample.len(), + self.image.tile_attributes = Some(TileAttributes { + image_width: usize::try_from(self.image.width)?, + image_height: usize::try_from(self.image.height)?, + samples_per_pixel: self.image.bits_per_sample.len(), tile_width, tile_length, }); - self.chunk_offsets = self.get_tag_u64_vec(Tag::TileOffsets)?; - self.chunk_bytes = self.get_tag_u64_vec(Tag::TileByteCounts)?; + self.image.chunk_offsets = self.get_tag_u64_vec(Tag::TileOffsets)?; + self.image.chunk_bytes = self.get_tag_u64_vec(Tag::TileByteCounts)?; - let tile = self.tile_attributes.as_ref().unwrap(); - if self.chunk_offsets.len() != self.chunk_bytes.len() - || self.chunk_offsets.len() != tile.tiles_down() * tile.tiles_across() + let tile = self.image.tile_attributes.as_ref().unwrap(); + if self.image.chunk_offsets.len() != self.image.chunk_bytes.len() + || self.image.chunk_offsets.len() != tile.tiles_down() * tile.tiles_across() { return Err(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, @@ -984,7 +912,7 @@ impl Decoder { /// Tries to retrieve a tag. /// Return `Ok(None)` if the tag is not present. pub fn find_tag(&mut self, tag: Tag) -> TiffResult> { - let entry = match self.ifd.as_ref().unwrap().get(&tag) { + let entry = match self.image().ifd.as_ref().unwrap().get(&tag) { None => return Ok(None), Some(entry) => entry.clone(), }; @@ -1199,10 +1127,11 @@ impl Decoder { // Construct necessary reader to perform decompression. self.goto_offset_u64(offset)?; let byte_order = self.reader.byte_order; + let compression_method = self.image().compression_method; let reader = Self::create_reader( &mut self.reader, - self.compression_method, + compression_method, length, buffer.len(), buffer.byte_len(), @@ -1235,7 +1164,7 @@ impl Decoder { Self::fix_endianness(&mut buffer, byte_order); - if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + if self.image().photometric_interpretation == PhotometricInterpretation::WhiteIsZero { Self::invert_colors(&mut buffer, color_type); } @@ -1255,20 +1184,22 @@ impl Decoder { let byte_len = buffer.byte_len(); let byte_order = self.reader.byte_order; - let tile_attrs = self.tile_attributes.as_mut().unwrap(); + let tile_attrs = self.image().tile_attributes.as_ref().unwrap(); let (padding_right, padding_down) = tile_attrs.get_padding(tile); let tile_samples = tile_attrs.tile_samples(); let tile_length = tile_attrs.tile_length; let row_samples = tile_attrs.row_samples(); - let padding_right_samples = padding_right * self.bits_per_sample.len(); + let padding_right_samples = padding_right * self.image().bits_per_sample.len(); self.goto_offset_u64(offset)?; - let line_samples = output_width * self.bits_per_sample.len(); + let line_samples = output_width * self.image().bits_per_sample.len(); + let compression_method = self.image().compression_method; + let photometric_interpretation = self.image().photometric_interpretation; let mut reader = Self::create_reader( &mut self.reader, - self.compression_method, + compression_method, compressed_length, tile_samples, byte_len, @@ -1303,7 +1234,7 @@ impl Decoder { Self::fix_endianness(&mut buffer.subrange(row_start..row_end), byte_order); - if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { Self::invert_colors(&mut buffer.subrange(row_start..row_end), color_type); } } @@ -1346,10 +1277,10 @@ impl Decoder { } fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> { - if expected != self.chunk_type { + if expected != self.image().chunk_type { return Err(TiffError::UsageError(UsageError::InvalidChunkType( expected, - self.chunk_type, + self.image().chunk_type, ))); } @@ -1358,20 +1289,20 @@ impl Decoder { /// The chunk type (Strips / Tiles) of the image pub fn get_chunk_type(&self) -> ChunkType { - self.chunk_type + self.image().chunk_type } /// Number of strips in image pub fn strip_count(&mut self) -> TiffResult { self.check_chunk_type(ChunkType::Strip)?; - let rows_per_strip = self.strip_decoder.as_ref().unwrap().rows_per_strip; + let rows_per_strip = self.image().strip_decoder.as_ref().unwrap().rows_per_strip; if rows_per_strip == 0 { return Ok(0); } // rows_per_strip - 1 can never fail since we know it's at least 1 - let height = match self.height.checked_add(rows_per_strip - 1) { + let height = match self.image().height.checked_add(rows_per_strip - 1) { Some(h) => h, None => return Err(TiffError::IntSizeError), }; @@ -1382,7 +1313,7 @@ impl Decoder { /// Number of tiles in image pub fn tile_count(&mut self) -> TiffResult { self.check_chunk_type(ChunkType::Tile)?; - Ok(u32::try_from(self.chunk_offsets.len())?) + Ok(u32::try_from(self.image().chunk_offsets.len())?) } pub fn read_jpeg(&mut self) -> TiffResult { @@ -1416,12 +1347,15 @@ impl Decoder { self.goto_offset(*offset)?; let length = bytes[idx]; - if self.jpeg_tables.is_some() && length < 2 { + if self.image().jpeg_tables.is_some() && length < 2 { return Err(TiffError::FormatError( TiffFormatError::InvalidTagValueType(Tag::JPEGTables), )); } - let jpeg_reader = JpegReader::new(&mut self.reader, length, &self.jpeg_tables)?; + // TODO: avoid this clone + let jpeg_tables = self.image().jpeg_tables.clone(); + + let jpeg_reader = JpegReader::new(&mut self.reader, length, &jpeg_tables)?; let mut decoder = jpeg::Decoder::new(jpeg_reader); match decoder.decode() { @@ -1449,16 +1383,25 @@ impl Decoder { pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> { self.check_chunk_type(ChunkType::Strip)?; let index = self.current_chunk; - let offset = *self.chunk_offsets.get(index).ok_or(TiffError::FormatError( - TiffFormatError::InconsistentSizesEncountered, - ))?; - let byte_count = *self.chunk_bytes.get(index).ok_or(TiffError::FormatError( - TiffFormatError::InconsistentSizesEncountered, - ))?; - let rows_per_strip = usize::try_from(self.strip_decoder.as_ref().unwrap().rows_per_strip)?; + let offset = *self + .image() + .chunk_offsets + .get(index) + .ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; + let byte_count = *self + .image() + .chunk_bytes + .get(index) + .ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; + let rows_per_strip = + usize::try_from(self.image().strip_decoder.as_ref().unwrap().rows_per_strip)?; - let sized_width = usize::try_from(self.width)?; - let sized_height = usize::try_from(self.height)?; + let sized_width = usize::try_from(self.image().width)?; + let sized_height = usize::try_from(self.image().height)?; let strip_height_without_padding = index .checked_mul(rows_per_strip) @@ -1470,7 +1413,7 @@ impl Decoder { let buffer_size = sized_width .checked_mul(strip_height) - .and_then(|x| x.checked_mul(self.bits_per_sample.len())) + .and_then(|x| x.checked_mul(self.image().bits_per_sample.len())) .ok_or(TiffError::LimitsExceeded)?; if buffer.len() < buffer_size { @@ -1482,16 +1425,13 @@ impl Decoder { self.expand_strip(buffer.subrange(0..buffer_size), offset, byte_count)?; self.current_chunk += 1; - if u32::try_from(index)? == self.strip_count()? { - self.strip_decoder = None; - } - match self.predictor { + match self.image().predictor { Predictor::None => {} Predictor::Horizontal => { rev_hpredict( buffer.copy(), - (self.width, u32::try_from(strip_height)?), - usize::try_from(self.width)?, + (self.image().width, u32::try_from(strip_height)?), + usize::try_from(self.image().width)?, self.colortype()?, )?; } @@ -1506,15 +1446,24 @@ impl Decoder { tile: usize, output_width: usize, ) -> TiffResult<()> { - let file_offset = *self.chunk_offsets.get(tile).ok_or(TiffError::FormatError( - TiffFormatError::InconsistentSizesEncountered, - ))?; + let file_offset = *self + .image() + .chunk_offsets + .get(tile) + .ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; - let compressed_bytes = *self.chunk_bytes.get(tile).ok_or(TiffError::FormatError( - TiffFormatError::InconsistentSizesEncountered, - ))?; + let compressed_bytes = + *self + .image() + .chunk_bytes + .get(tile) + .ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; - let tile_attrs = self.tile_attributes.as_ref().unwrap(); + let tile_attrs = self.image().tile_attributes.as_ref().unwrap(); let tile_width = tile_attrs.tile_width; let tile_length = tile_attrs.tile_length; @@ -1528,7 +1477,7 @@ impl Decoder { output_width, )?; - match self.predictor { + match self.image().predictor { Predictor::None => {} Predictor::Horizontal => { rev_hpredict( @@ -1549,14 +1498,25 @@ impl Decoder { fn result_buffer(&self, width: usize, height: usize) -> TiffResult { let buffer_size = match width .checked_mul(height) - .and_then(|x| x.checked_mul(self.bits_per_sample.len())) + .and_then(|x| x.checked_mul(self.image().bits_per_sample.len())) { Some(s) => s, None => return Err(TiffError::LimitsExceeded), }; - let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8); - match self.sample_format.first().unwrap_or(&SampleFormat::Uint) { + let max_sample_bits = self + .image() + .bits_per_sample + .iter() + .cloned() + .max() + .unwrap_or(8); + match self + .image() + .sample_format + .first() + .unwrap_or(&SampleFormat::Uint) + { SampleFormat::Uint => match max_sample_bits { n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits), n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits), @@ -1593,14 +1553,15 @@ impl Decoder { self.check_chunk_type(ChunkType::Strip)?; let index = self.current_chunk; - let rows_per_strip = usize::try_from(self.strip_decoder.as_ref().unwrap().rows_per_strip)?; + let rows_per_strip = + usize::try_from(self.image().strip_decoder.as_ref().unwrap().rows_per_strip)?; let strip_height = cmp::min( rows_per_strip, - usize::try_from(self.height)? - index * rows_per_strip, + usize::try_from(self.image().height)? - index * rows_per_strip, ); - let mut result = self.result_buffer(usize::try_from(self.width)?, strip_height)?; + let mut result = self.result_buffer(usize::try_from(self.image().width)?, strip_height)?; self.read_strip_to_buffer(result.as_buffer(0))?; Ok(result) @@ -1612,7 +1573,7 @@ impl Decoder { let tile = self.current_chunk; - let tile_attrs = self.tile_attributes.as_ref().unwrap(); + let tile_attrs = self.image().tile_attributes.as_ref().unwrap(); let (padding_right, padding_down) = tile_attrs.get_padding(tile); let tile_width = tile_attrs.tile_width - padding_right; @@ -1628,11 +1589,16 @@ impl Decoder { } fn read_tiled_image(&mut self) -> TiffResult { - let width = usize::try_from(self.width)?; - let mut result = self.result_buffer(width, usize::try_from(self.height)?)?; + 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.chunk_offsets.len() { - let buffer_offset = self.tile_attributes.as_ref().unwrap().get_offset(tile); + 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)?; } @@ -1640,18 +1606,21 @@ impl Decoder { } fn read_stripped_image(&mut self) -> TiffResult { - let rows_per_strip = usize::try_from(self.strip_decoder.as_ref().unwrap().rows_per_strip)?; + 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.width)? + let samples_per_strip = match usize::try_from(self.image().width)? .checked_mul(rows_per_strip) - .and_then(|x| x.checked_mul(self.bits_per_sample.len())) + .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.width)?, usize::try_from(self.height)?)?; + 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); @@ -1662,7 +1631,7 @@ impl Decoder { /// Decodes the entire image and return it as a Vector pub fn read_image(&mut self) -> TiffResult { - let result = match (self.chunk_type, self.compression_method) { + let result = match (self.image().chunk_type, self.image().compression_method) { (_, CompressionMethod::ModernJPEG) => self.read_jpeg()?, (ChunkType::Strip, _) => self.read_stripped_image()?, (ChunkType::Tile, _) => self.read_tiled_image()?,