From 98cd3dc2d296c95edd61385cfea0e9deec081b90 Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+197g@users.noreply.github.com> Date: Mon, 7 Jul 2025 22:07:16 +0200 Subject: [PATCH] Refactor directory reading This allows us to recombine the reader and other file data in a Decoder with any IFD, whether that of an image or an extension IFD as indicated by an IFD pointer such as Exif and GPS. --- src/decoder/image.rs | 31 ++- src/decoder/mod.rs | 631 +++++++++++++++++++++++++----------------- src/decoder/stream.rs | 7 +- 3 files changed, 398 insertions(+), 271 deletions(-) diff --git a/src/decoder/image.rs b/src/decoder/image.rs index 777d5eb..cebfaf1 100644 --- a/src/decoder/image.rs +++ b/src/decoder/image.rs @@ -1,8 +1,8 @@ use super::ifd::Value; -use super::stream::{ByteOrder, PackBitsReader}; +use super::stream::PackBitsReader; use super::tag_reader::TagReader; -use super::{predict_f16, predict_f32, predict_f64, Limits}; -use super::{stream::EndianReader, ChunkType}; +use super::ChunkType; +use super::{predict_f16, predict_f32, predict_f64, ValueReader}; use crate::tags::{ CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag, }; @@ -82,16 +82,20 @@ pub(crate) struct Image { impl Image { pub fn from_reader( - reader: &mut EndianReader, + reader: &mut ValueReader, ifd: Directory, - limits: &Limits, - bigtiff: bool, ) -> TiffResult { + let ValueReader { + reader, + bigtiff, + limits, + } = reader; + let mut tag_reader = TagReader { reader, limits, ifd: &ifd, - bigtiff, + bigtiff: *bigtiff, }; let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; @@ -540,13 +544,20 @@ impl Image { pub(crate) fn expand_chunk( &self, - reader: impl Read, + reader: &mut ValueReader, buf: &mut [u8], output_row_stride: usize, - byte_order: ByteOrder, chunk_index: u32, - limits: &Limits, ) -> TiffResult<()> { + let ValueReader { + reader, + bigtiff: _, + limits, + } = reader; + + let byte_order = reader.byte_order; + let reader = reader.inner(); + // Validate that the color type is supported. let color_type = self.colortype()?; match color_type { diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 66f4b9a..af09f1f 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -10,6 +10,7 @@ use crate::{ UsageError, }; use half::f16; +use ifd::Entry; use self::image::Image; use self::stream::{ByteOrder, EndianReader}; @@ -261,9 +262,9 @@ pub struct Decoder where R: Read + Seek, { - reader: EndianReader, - bigtiff: bool, - limits: Limits, + /// There are grouped for borrow checker reasons. This allows us to implement methods that + /// borrow the stream access and the other fields mutably at the same time. + value_reader: ValueReader, current_ifd: Option, next_ifd: Option, /// The IFDs we visited already in this chain of IFDs. @@ -273,6 +274,22 @@ where image: Image, } +/// All the information needed to read and interpret byte slices from the underlying file, i.e. to +/// turn an entry of a tag into an `ifd::Value` or otherwise fetch arrays of similar types. Used +/// only as the type of the field [`Decoder::value_reader`] and passed to submodules. +#[derive(Debug)] +struct ValueReader { + reader: EndianReader, + bigtiff: bool, + limits: Limits, +} + +/// Reads a directory's tag values from an underlying stream. +pub struct IfdDecoder<'lt> { + decoder: &'lt mut dyn EntryDecoder, + ifd: &'lt Directory, +} + fn rev_hpredict_nsamp(buf: &mut [u8], bit_depth: u8, samples: usize) { match bit_depth { 0..=8 => { @@ -503,9 +520,11 @@ impl Decoder { let ifd_offsets = vec![current_ifd]; let mut decoder = Decoder { - reader, - bigtiff, - limits: Default::default(), + value_reader: ValueReader { + reader, + bigtiff, + limits: Default::default(), + }, next_ifd, ifd_offsets, current_ifd: None, @@ -534,7 +553,7 @@ impl Decoder { } pub fn with_limits(mut self, limits: Limits) -> Decoder { - self.limits = limits; + self.value_reader.limits = limits; self } @@ -584,10 +603,10 @@ impl Decoder { // If the index is within the list of ifds then we can load the selected image/IFD if let Some(ifd_offset) = self.ifd_offsets.get(ifd_index) { - let ifd = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?; + let ifd = self.value_reader.read_directory(*ifd_offset)?; self.next_ifd = ifd.next(); self.current_ifd = Some(*ifd_offset); - self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?; + self.image = Image::from_reader(&mut self.value_reader, ifd)?; Ok(()) } else { @@ -598,24 +617,23 @@ impl Decoder { } fn next_ifd(&mut self) -> TiffResult { - if self.next_ifd.is_none() { + let Some(next_ifd) = self.next_ifd.take() else { return Err(TiffError::FormatError( TiffFormatError::ImageFileDirectoryNotFound, )); - } + }; - let ifd_offset = self.next_ifd.take().unwrap(); - let ifd = Self::read_ifd(&mut self.reader, self.bigtiff, ifd_offset)?; + let ifd = self.value_reader.read_directory(next_ifd)?; // Ensure this walk does not get us into a cycle. - self.seen_ifds.insert_next(ifd_offset, ifd.next())?; + self.seen_ifds.insert_next(next_ifd, ifd.next())?; // Extend the list of known IFD offsets in this chain, if needed. if self.ifd_offsets.last().copied() == self.current_ifd { - self.ifd_offsets.push(ifd_offset); + self.ifd_offsets.push(next_ifd); } - self.current_ifd = Some(ifd_offset); + self.current_ifd = Some(next_ifd); self.next_ifd = ifd.next(); Ok(ifd) @@ -626,7 +644,7 @@ impl Decoder { /// To determine whether there are more images call `TIFFDecoder::more_images` instead. pub fn next_image(&mut self) -> TiffResult<()> { let ifd = self.next_ifd()?; - self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?; + self.image = Image::from_reader(&mut self.value_reader, ifd)?; Ok(()) } @@ -637,12 +655,12 @@ impl Decoder { /// Returns the byte_order of the file. pub fn byte_order(&self) -> ByteOrder { - self.reader.byte_order + self.value_reader.reader.byte_order } #[inline] pub fn read_ifd_offset(&mut self) -> Result { - if self.bigtiff { + if self.value_reader.bigtiff { self.read_long8() } else { self.read_long().map(u64::from) @@ -653,61 +671,61 @@ impl Decoder { #[inline] pub fn read_byte(&mut self) -> Result { let mut buf = [0; 1]; - self.reader.inner().read_exact(&mut buf)?; + self.value_reader.reader.inner().read_exact(&mut buf)?; Ok(buf[0]) } /// Reads a TIFF short value #[inline] pub fn read_short(&mut self) -> Result { - self.reader.read_u16() + self.value_reader.reader.read_u16() } /// Reads a TIFF sshort value #[inline] pub fn read_sshort(&mut self) -> Result { - self.reader.read_i16() + self.value_reader.reader.read_i16() } /// Reads a TIFF long value #[inline] pub fn read_long(&mut self) -> Result { - self.reader.read_u32() + self.value_reader.reader.read_u32() } /// Reads a TIFF slong value #[inline] pub fn read_slong(&mut self) -> Result { - self.reader.read_i32() + self.value_reader.reader.read_i32() } /// Reads a TIFF float value #[inline] pub fn read_float(&mut self) -> Result { - self.reader.read_f32() + self.value_reader.reader.read_f32() } /// Reads a TIFF double value #[inline] pub fn read_double(&mut self) -> Result { - self.reader.read_f64() + self.value_reader.reader.read_f64() } #[inline] pub fn read_long8(&mut self) -> Result { - self.reader.read_u64() + self.value_reader.reader.read_u64() } #[inline] pub fn read_slong8(&mut self) -> Result { - self.reader.read_i64() + self.value_reader.reader.read_i64() } /// Reads a string #[inline] pub fn read_string(&mut self, length: usize) -> TiffResult { let mut out = vec![0; length]; - self.reader.inner().read_exact(&mut out)?; + self.value_reader.reader.inner().read_exact(&mut out)?; // Strings may be null-terminated, so we trim anything downstream of the null byte if let Some(first) = out.iter().position(|&b| b == 0) { out.truncate(first); @@ -718,13 +736,13 @@ impl Decoder { /// Reads a TIFF IFA offset/value field #[inline] pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> { - if self.bigtiff { + if self.value_reader.bigtiff { return Err(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, )); } let mut val = [0; 4]; - self.reader.inner().read_exact(&mut val)?; + self.value_reader.reader.inner().read_exact(&mut val)?; Ok(val) } @@ -732,7 +750,7 @@ impl Decoder { #[inline] pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> { let mut val = [0; 8]; - self.reader.inner().read_exact(&mut val)?; + self.value_reader.reader.inner().read_exact(&mut val)?; Ok(val) } @@ -744,211 +762,7 @@ impl Decoder { #[inline] pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> { - self.reader.goto_offset(offset) - } - - /// Read a directory from a known offset. - /// - /// This may always modify the position of the reader. - pub fn read_directory(&mut self, ptr: IfdPointer) -> TiffResult { - Self::read_ifd(&mut self.reader, self.bigtiff, ptr) - } - - /// Reads a IFD entry. - // An IFD entry has four fields: - // - // Tag 2 bytes - // Type 2 bytes - // Count 4 bytes - // Value 4 bytes either a pointer the value itself - fn read_entry( - reader: &mut EndianReader, - bigtiff: bool, - ) -> TiffResult> { - let tag = Tag::from_u16_exhaustive(reader.read_u16()?); - let type_ = match Type::from_u16(reader.read_u16()?) { - Some(t) => t, - None => { - // Unknown type. Skip this entry according to spec. - reader.read_u32()?; - reader.read_u32()?; - return Ok(None); - } - }; - let entry = if bigtiff { - let mut offset = [0; 8]; - - let count = reader.read_u64()?; - reader.inner().read_exact(&mut offset)?; - ifd::Entry::new_u64(type_, count, offset) - } else { - let mut offset = [0; 4]; - - let count = reader.read_u32()?; - reader.inner().read_exact(&mut offset)?; - ifd::Entry::new(type_, count, offset) - }; - Ok(Some((tag, entry))) - } - - /// Reads the IFD starting at the indicated location. - fn read_ifd( - reader: &mut EndianReader, - bigtiff: bool, - ifd_location: IfdPointer, - ) -> TiffResult { - reader.goto_offset(ifd_location.0)?; - - let mut entries: BTreeMap<_, _> = BTreeMap::new(); - - let num_tags = if bigtiff { - reader.read_u64()? - } else { - reader.read_u16()?.into() - }; - - for _ in 0..num_tags { - let (tag, entry) = match Self::read_entry(reader, bigtiff)? { - Some(val) => val, - None => { - continue; - } // Unknown data type in tag, skip - }; - - entries.insert(tag.to_u16(), entry); - } - - let next_ifd = if bigtiff { - reader.read_u64()? - } else { - reader.read_u32()?.into() - }; - - let next_ifd = core::num::NonZeroU64::new(next_ifd); - - Ok(Directory { entries, next_ifd }) - } - - /// 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.image().ifd.as_ref().unwrap().get(tag) { - None => return Ok(None), - Some(entry) => entry.clone(), - }; - - Ok(Some(entry.val( - &self.limits, - self.bigtiff, - &mut self.reader, - )?)) - } - - /// Tries to retrieve a tag and convert it to the desired unsigned type. - pub fn find_tag_unsigned>(&mut self, tag: Tag) -> TiffResult> { - self.find_tag(tag)? - .map(|v| v.into_u64()) - .transpose()? - .map(|value| { - T::try_from(value).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()) - }) - .transpose() - } - - /// Tries to retrieve a vector of all a tag's values and convert them to - /// the desired unsigned type. - pub fn find_tag_unsigned_vec>( - &mut self, - tag: Tag, - ) -> TiffResult>> { - self.find_tag(tag)? - .map(|v| v.into_u64_vec()) - .transpose()? - .map(|v| { - v.into_iter() - .map(|u| { - T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()) - }) - .collect() - }) - .transpose() - } - - /// Tries to retrieve a tag and convert it to the desired unsigned type. - /// Returns an error if the tag is not present. - pub fn get_tag_unsigned>(&mut self, tag: Tag) -> TiffResult { - self.find_tag_unsigned(tag)? - .ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into()) - } - - /// Tries to retrieve a tag. - /// Returns an error if the tag is not present - pub fn get_tag(&mut self, tag: Tag) -> TiffResult { - match self.find_tag(tag)? { - Some(val) => Ok(val), - None => Err(TiffError::FormatError( - TiffFormatError::RequiredTagNotFound(tag), - )), - } - } - - /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult { - self.get_tag(tag)?.into_u32() - } - pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult { - self.get_tag(tag)?.into_u64() - } - - /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_f32(&mut self, tag: Tag) -> TiffResult { - self.get_tag(tag)?.into_f32() - } - - /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_f64(&mut self, tag: Tag) -> TiffResult { - self.get_tag(tag)?.into_f64() - } - - /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult> { - self.get_tag(tag)?.into_u32_vec() - } - - pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult> { - self.get_tag(tag)?.into_u16_vec() - } - pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult> { - self.get_tag(tag)?.into_u64_vec() - } - - /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_f32_vec(&mut self, tag: Tag) -> TiffResult> { - self.get_tag(tag)?.into_f32_vec() - } - - /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_f64_vec(&mut self, tag: Tag) -> TiffResult> { - self.get_tag(tag)?.into_f64_vec() - } - - /// Tries to retrieve a tag and convert it to a 8bit vector. - pub fn get_tag_u8_vec(&mut self, tag: Tag) -> TiffResult> { - self.get_tag(tag)?.into_u8_vec() - } - - /// Tries to retrieve a tag and convert it to a ascii vector. - pub fn get_tag_ascii_string(&mut self, tag: Tag) -> TiffResult { - self.get_tag(tag)?.into_string() - } - - /// Returns an iterator over all tags in the current image, along with their values. - pub fn tag_iter(&mut self) -> impl Iterator> + '_ { - self.image.ifd.as_ref().unwrap().iter().map(|(tag, entry)| { - entry - .val(&self.limits, self.bigtiff, &mut self.reader) - .map(|value| (tag, value)) - }) + self.value_reader.reader.goto_offset(offset) } fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> { @@ -1003,9 +817,7 @@ impl Decoder { output_width: usize, ) -> TiffResult<()> { let offset = self.image.chunk_file_range(chunk_index)?.0; - self.reader.goto_offset(offset)?; - - let byte_order = self.reader.byte_order; + self.goto_offset_u64(offset)?; let output_row_stride = (output_width as u64) .saturating_mul(self.image.samples_per_pixel() as u64) @@ -1013,12 +825,10 @@ impl Decoder { / 8; self.image.expand_chunk( - self.reader.inner(), + &mut self.value_reader, buffer.as_bytes_mut(), output_row_stride.try_into()?, - byte_order, chunk_index, - &self.limits, )?; Ok(()) @@ -1041,29 +851,31 @@ impl Decoder { .ok_or(TiffError::LimitsExceeded)?; let max_sample_bits = self.image().bits_per_sample; + let limits = &self.value_reader.limits; + match self.image().sample_format { 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), - n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits), - n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits), + n if n <= 8 => DecodingResult::new_u8(buffer_size, limits), + n if n <= 16 => DecodingResult::new_u16(buffer_size, limits), + n if n <= 32 => DecodingResult::new_u32(buffer_size, limits), + n if n <= 64 => DecodingResult::new_u64(buffer_size, limits), n => Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), )), }, SampleFormat::IEEEFP => match max_sample_bits { - 16 => DecodingResult::new_f16(buffer_size, &self.limits), - 32 => DecodingResult::new_f32(buffer_size, &self.limits), - 64 => DecodingResult::new_f64(buffer_size, &self.limits), + 16 => DecodingResult::new_f16(buffer_size, limits), + 32 => DecodingResult::new_f32(buffer_size, limits), + 64 => DecodingResult::new_f64(buffer_size, limits), n => Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), )), }, SampleFormat::Int => match max_sample_bits { - n if n <= 8 => DecodingResult::new_i8(buffer_size, &self.limits), - n if n <= 16 => DecodingResult::new_i16(buffer_size, &self.limits), - n if n <= 32 => DecodingResult::new_i32(buffer_size, &self.limits), - n if n <= 64 => DecodingResult::new_i64(buffer_size, &self.limits), + n if n <= 8 => DecodingResult::new_i8(buffer_size, limits), + n if n <= 16 => DecodingResult::new_i16(buffer_size, limits), + n if n <= 32 => DecodingResult::new_i32(buffer_size, limits), + n if n <= 64 => DecodingResult::new_i64(buffer_size, limits), n => Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), )), @@ -1148,23 +960,324 @@ impl Decoder { // * pass requested band as parameter // * collect bands to a RGB encoding result in case of RGB bands for chunk in 0..image_chunks { - self.reader.goto_offset(self.image().chunk_offsets[chunk])?; + self.goto_offset_u64(self.image().chunk_offsets[chunk])?; let x = chunk % chunks_across; let y = chunk / chunks_across; let buffer_offset = y * output_row_stride * chunk_dimensions.1 as usize + x * chunk_row_bytes; - let byte_order = self.reader.byte_order; self.image.expand_chunk( - self.reader.inner(), + &mut self.value_reader, &mut result.as_buffer(0).as_bytes_mut()[buffer_offset..], output_row_stride, - byte_order, chunk as u32, - &self.limits, )?; } Ok(result) } + + /// Get the IFD decoder for our current image IFD. + fn image_ifd(&mut self) -> IfdDecoder<'_> { + IfdDecoder { + decoder: &mut self.value_reader, + ifd: self.image.ifd.as_ref().unwrap(), + } + } + + /// Tries to retrieve a tag from the current image directory. + /// Return `Ok(None)` if the tag is not present. + pub fn find_tag(&mut self, tag: Tag) -> TiffResult> { + self.image_ifd().find_tag(tag) + } + + /// Tries to retrieve a tag in the current image directory and convert it to the desired + /// unsigned type. + pub fn find_tag_unsigned>(&mut self, tag: Tag) -> TiffResult> { + self.image_ifd().find_tag_unsigned(tag) + } + + /// Tries to retrieve a tag from the current image directory and convert it to the desired + /// unsigned type. Returns an error if the tag is not present. + pub fn get_tag_unsigned>(&mut self, tag: Tag) -> TiffResult { + self.image_ifd().get_tag_unsigned(tag) + } + + /// Tries to retrieve a tag from the current image directory. + /// Returns an error if the tag is not present + pub fn get_tag(&mut self, tag: Tag) -> TiffResult { + self.image_ifd().get_tag(tag) + } + + pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_u32() + } + + pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_u64() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f32(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_f32() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f64(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_f64() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u32_vec() + } + + pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u16_vec() + } + + pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u64_vec() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f32_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_f32_vec() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f64_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_f64_vec() + } + + /// Tries to retrieve a tag and convert it to a 8bit vector. + pub fn get_tag_u8_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u8_vec() + } + + /// Tries to retrieve a tag and convert it to a ascii vector. + pub fn get_tag_ascii_string(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_string() + } + + pub fn tag_iter(&mut self) -> impl Iterator> + '_ { + self.image_ifd().tag_iter() + } +} + +impl ValueReader { + pub(crate) fn read_directory(&mut self, ptr: IfdPointer) -> Result { + Self::read_ifd(&mut self.reader, self.bigtiff, ptr) + } + + /// Reads a IFD entry. + // An IFD entry has four fields: + // + // Tag 2 bytes + // Type 2 bytes + // Count 4 bytes + // Value 4 bytes either a pointer the value itself + fn read_entry( + reader: &mut EndianReader, + bigtiff: bool, + ) -> TiffResult> { + let tag = Tag::from_u16_exhaustive(reader.read_u16()?); + let type_ = match Type::from_u16(reader.read_u16()?) { + Some(t) => t, + None => { + // Unknown type. Skip this entry according to spec. + reader.read_u32()?; + reader.read_u32()?; + return Ok(None); + } + }; + let entry = if bigtiff { + let mut offset = [0; 8]; + + let count = reader.read_u64()?; + reader.inner().read_exact(&mut offset)?; + ifd::Entry::new_u64(type_, count, offset) + } else { + let mut offset = [0; 4]; + + let count = reader.read_u32()?; + reader.inner().read_exact(&mut offset)?; + ifd::Entry::new(type_, count, offset) + }; + Ok(Some((tag, entry))) + } + + /// Reads the IFD starting at the indicated location. + fn read_ifd( + reader: &mut EndianReader, + bigtiff: bool, + ifd_location: IfdPointer, + ) -> TiffResult { + reader.goto_offset(ifd_location.0)?; + + let mut entries: BTreeMap<_, _> = BTreeMap::new(); + + let num_tags = if bigtiff { + reader.read_u64()? + } else { + reader.read_u16()?.into() + }; + + for _ in 0..num_tags { + let (tag, entry) = match Self::read_entry(reader, bigtiff)? { + Some(val) => val, + None => { + continue; + } // Unknown data type in tag, skip + }; + + entries.insert(tag.to_u16(), entry); + } + + let next_ifd = if bigtiff { + reader.read_u64()? + } else { + reader.read_u32()?.into() + }; + + let next_ifd = core::num::NonZeroU64::new(next_ifd); + + Ok(Directory { entries, next_ifd }) + } +} + +trait EntryDecoder { + /// Turn an entry into a value by fetching the required bytes in the stream. + fn entry_val(&mut self, entry: &Entry) -> TiffResult; +} + +impl EntryDecoder for ValueReader { + fn entry_val(&mut self, entry: &Entry) -> TiffResult { + entry.val(&self.limits, self.bigtiff, &mut self.reader) + } +} + +impl IfdDecoder<'_> { + /// 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.get(tag) { + None => return Ok(None), + Some(entry) => entry.clone(), + }; + + self.decoder.entry_val(&entry).map(Some) + } + + /// Tries to retrieve a tag and convert it to the desired unsigned type. + pub fn find_tag_unsigned>(&mut self, tag: Tag) -> TiffResult> { + self.find_tag(tag)? + .map(|v| v.into_u64()) + .transpose()? + .map(|value| { + T::try_from(value).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()) + }) + .transpose() + } + + /// Tries to retrieve a vector of all a tag's values and convert them to + /// the desired unsigned type. + pub fn find_tag_unsigned_vec>( + &mut self, + tag: Tag, + ) -> TiffResult>> { + self.find_tag(tag)? + .map(|v| v.into_u64_vec()) + .transpose()? + .map(|v| { + v.into_iter() + .map(|u| { + T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()) + }) + .collect() + }) + .transpose() + } + + /// Tries to retrieve a tag and convert it to the desired unsigned type. + /// Returns an error if the tag is not present. + pub fn get_tag_unsigned>(&mut self, tag: Tag) -> TiffResult { + self.find_tag_unsigned(tag)? + .ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into()) + } + + /// Tries to retrieve a tag. + /// Returns an error if the tag is not present + pub fn get_tag(&mut self, tag: Tag) -> TiffResult { + match self.find_tag(tag)? { + Some(val) => Ok(val), + None => Err(TiffError::FormatError( + TiffFormatError::RequiredTagNotFound(tag), + )), + } + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_u32() + } + + pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_u64() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f32(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_f32() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f64(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_f64() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u32_vec() + } + + pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u16_vec() + } + + pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u64_vec() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f32_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_f32_vec() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f64_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_f64_vec() + } + + /// Tries to retrieve a tag and convert it to a 8bit vector. + pub fn get_tag_u8_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u8_vec() + } + + /// Tries to retrieve a tag and convert it to a ascii vector. + pub fn get_tag_ascii_string(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_string() + } +} + +impl<'l> IfdDecoder<'l> { + /// Returns an iterator over all tags in the current image, along with their values. + pub fn tag_iter(self) -> impl Iterator> + 'l { + self.ifd + .iter() + .map(|(tag, entry)| match self.decoder.entry_val(entry) { + Ok(value) => Ok((tag, value)), + Err(err) => Err(err), + }) + } } diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 979f080..242128f 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -18,7 +18,7 @@ pub struct EndianReader { pub(crate) byte_order: ByteOrder, } -impl EndianReader { +impl EndianReader { pub fn new(reader: R, byte_order: ByteOrder) -> Self { Self { reader, byte_order } } @@ -27,7 +27,10 @@ impl EndianReader { &mut self.reader } - pub fn goto_offset(&mut self, offset: u64) -> io::Result<()> { + pub fn goto_offset(&mut self, offset: u64) -> io::Result<()> + where + R: Seek, + { self.reader.seek(io::SeekFrom::Start(offset))?; Ok(()) }