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.
This commit is contained in:
A. Molzer committed 2025-07-11 14:12:34 +02:00
1 parent 6275b7a36d
commit 98cd3dc2d2
3 files changed
+398 -271

No files matched your search

+21 -10
View File
@@ -1,8 +1,8 @@
use super::ifd::Value; use super::ifd::Value;
use super::stream::{ByteOrder, PackBitsReader}; use super::stream::PackBitsReader;
use super::tag_reader::TagReader; use super::tag_reader::TagReader;
use super::{predict_f16, predict_f32, predict_f64, Limits}; use super::ChunkType;
use super::{stream::EndianReader, ChunkType}; use super::{predict_f16, predict_f32, predict_f64, ValueReader};
use crate::tags::{ use crate::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag, CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag,
}; };
@@ -82,16 +82,20 @@ pub(crate) struct Image {
impl Image { impl Image {
pub fn from_reader<R: Read + Seek>( pub fn from_reader<R: Read + Seek>(
reader: &mut EndianReader<R>, reader: &mut ValueReader<R>,
ifd: Directory, ifd: Directory,
limits: &Limits,
bigtiff: bool,
) -> TiffResult<Image> { ) -> TiffResult<Image> {
let ValueReader {
reader,
bigtiff,
limits,
} = reader;
let mut tag_reader = TagReader { let mut tag_reader = TagReader {
reader, reader,
limits, limits,
ifd: &ifd, ifd: &ifd,
bigtiff, bigtiff: *bigtiff,
}; };
let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?;
@@ -540,13 +544,20 @@ impl Image {
pub(crate) fn expand_chunk( pub(crate) fn expand_chunk(
&self, &self,
reader: impl Read, reader: &mut ValueReader<impl Read>,
buf: &mut [u8], buf: &mut [u8],
output_row_stride: usize, output_row_stride: usize,
byte_order: ByteOrder,
chunk_index: u32, chunk_index: u32,
limits: &Limits,
) -> TiffResult<()> { ) -> TiffResult<()> {
let ValueReader {
reader,
bigtiff: _,
limits,
} = reader;
let byte_order = reader.byte_order;
let reader = reader.inner();
// Validate that the color type is supported. // Validate that the color type is supported.
let color_type = self.colortype()?; let color_type = self.colortype()?;
match color_type { match color_type {
+372 -259
View File
@@ -10,6 +10,7 @@ use crate::{
UsageError, UsageError,
}; };
use half::f16; use half::f16;
use ifd::Entry;
use self::image::Image; use self::image::Image;
use self::stream::{ByteOrder, EndianReader}; use self::stream::{ByteOrder, EndianReader};
@@ -261,9 +262,9 @@ pub struct Decoder<R>
where where
R: Read + Seek, R: Read + Seek,
{ {
reader: EndianReader<R>, /// There are grouped for borrow checker reasons. This allows us to implement methods that
bigtiff: bool, /// borrow the stream access and the other fields mutably at the same time.
limits: Limits, value_reader: ValueReader<R>,
current_ifd: Option<IfdPointer>, current_ifd: Option<IfdPointer>,
next_ifd: Option<IfdPointer>, next_ifd: Option<IfdPointer>,
/// The IFDs we visited already in this chain of IFDs. /// The IFDs we visited already in this chain of IFDs.
@@ -273,6 +274,22 @@ where
image: Image, 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<R> {
reader: EndianReader<R>,
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) { fn rev_hpredict_nsamp(buf: &mut [u8], bit_depth: u8, samples: usize) {
match bit_depth { match bit_depth {
0..=8 => { 0..=8 => {
@@ -503,9 +520,11 @@ impl<R: Read + Seek> Decoder<R> {
let ifd_offsets = vec![current_ifd]; let ifd_offsets = vec![current_ifd];
let mut decoder = Decoder { let mut decoder = Decoder {
reader, value_reader: ValueReader {
bigtiff, reader,
limits: Default::default(), bigtiff,
limits: Default::default(),
},
next_ifd, next_ifd,
ifd_offsets, ifd_offsets,
current_ifd: None, current_ifd: None,
@@ -534,7 +553,7 @@ impl<R: Read + Seek> Decoder<R> {
} }
pub fn with_limits(mut self, limits: Limits) -> Decoder<R> { pub fn with_limits(mut self, limits: Limits) -> Decoder<R> {
self.limits = limits; self.value_reader.limits = limits;
self self
} }
@@ -584,10 +603,10 @@ impl<R: Read + Seek> Decoder<R> {
// If the index is within the list of ifds then we can load the selected image/IFD // 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) { 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.next_ifd = ifd.next();
self.current_ifd = Some(*ifd_offset); 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(()) Ok(())
} else { } else {
@@ -598,24 +617,23 @@ impl<R: Read + Seek> Decoder<R> {
} }
fn next_ifd(&mut self) -> TiffResult<Directory> { fn next_ifd(&mut self) -> TiffResult<Directory> {
if self.next_ifd.is_none() { let Some(next_ifd) = self.next_ifd.take() else {
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::ImageFileDirectoryNotFound, TiffFormatError::ImageFileDirectoryNotFound,
)); ));
} };
let ifd_offset = self.next_ifd.take().unwrap(); let ifd = self.value_reader.read_directory(next_ifd)?;
let ifd = Self::read_ifd(&mut self.reader, self.bigtiff, ifd_offset)?;
// Ensure this walk does not get us into a cycle. // 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. // Extend the list of known IFD offsets in this chain, if needed.
if self.ifd_offsets.last().copied() == self.current_ifd { 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(); self.next_ifd = ifd.next();
Ok(ifd) Ok(ifd)
@@ -626,7 +644,7 @@ impl<R: Read + Seek> Decoder<R> {
/// To determine whether there are more images call `TIFFDecoder::more_images` instead. /// To determine whether there are more images call `TIFFDecoder::more_images` instead.
pub fn next_image(&mut self) -> TiffResult<()> { pub fn next_image(&mut self) -> TiffResult<()> {
let ifd = self.next_ifd()?; 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(()) Ok(())
} }
@@ -637,12 +655,12 @@ impl<R: Read + Seek> Decoder<R> {
/// Returns the byte_order of the file. /// Returns the byte_order of the file.
pub fn byte_order(&self) -> ByteOrder { pub fn byte_order(&self) -> ByteOrder {
self.reader.byte_order self.value_reader.reader.byte_order
} }
#[inline] #[inline]
pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> { pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> {
if self.bigtiff { if self.value_reader.bigtiff {
self.read_long8() self.read_long8()
} else { } else {
self.read_long().map(u64::from) self.read_long().map(u64::from)
@@ -653,61 +671,61 @@ impl<R: Read + Seek> Decoder<R> {
#[inline] #[inline]
pub fn read_byte(&mut self) -> Result<u8, io::Error> { pub fn read_byte(&mut self) -> Result<u8, io::Error> {
let mut buf = [0; 1]; let mut buf = [0; 1];
self.reader.inner().read_exact(&mut buf)?; self.value_reader.reader.inner().read_exact(&mut buf)?;
Ok(buf[0]) Ok(buf[0])
} }
/// Reads a TIFF short value /// Reads a TIFF short value
#[inline] #[inline]
pub fn read_short(&mut self) -> Result<u16, io::Error> { pub fn read_short(&mut self) -> Result<u16, io::Error> {
self.reader.read_u16() self.value_reader.reader.read_u16()
} }
/// Reads a TIFF sshort value /// Reads a TIFF sshort value
#[inline] #[inline]
pub fn read_sshort(&mut self) -> Result<i16, io::Error> { pub fn read_sshort(&mut self) -> Result<i16, io::Error> {
self.reader.read_i16() self.value_reader.reader.read_i16()
} }
/// Reads a TIFF long value /// Reads a TIFF long value
#[inline] #[inline]
pub fn read_long(&mut self) -> Result<u32, io::Error> { pub fn read_long(&mut self) -> Result<u32, io::Error> {
self.reader.read_u32() self.value_reader.reader.read_u32()
} }
/// Reads a TIFF slong value /// Reads a TIFF slong value
#[inline] #[inline]
pub fn read_slong(&mut self) -> Result<i32, io::Error> { pub fn read_slong(&mut self) -> Result<i32, io::Error> {
self.reader.read_i32() self.value_reader.reader.read_i32()
} }
/// Reads a TIFF float value /// Reads a TIFF float value
#[inline] #[inline]
pub fn read_float(&mut self) -> Result<f32, io::Error> { pub fn read_float(&mut self) -> Result<f32, io::Error> {
self.reader.read_f32() self.value_reader.reader.read_f32()
} }
/// Reads a TIFF double value /// Reads a TIFF double value
#[inline] #[inline]
pub fn read_double(&mut self) -> Result<f64, io::Error> { pub fn read_double(&mut self) -> Result<f64, io::Error> {
self.reader.read_f64() self.value_reader.reader.read_f64()
} }
#[inline] #[inline]
pub fn read_long8(&mut self) -> Result<u64, io::Error> { pub fn read_long8(&mut self) -> Result<u64, io::Error> {
self.reader.read_u64() self.value_reader.reader.read_u64()
} }
#[inline] #[inline]
pub fn read_slong8(&mut self) -> Result<i64, io::Error> { pub fn read_slong8(&mut self) -> Result<i64, io::Error> {
self.reader.read_i64() self.value_reader.reader.read_i64()
} }
/// Reads a string /// Reads a string
#[inline] #[inline]
pub fn read_string(&mut self, length: usize) -> TiffResult<String> { pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
let mut out = vec![0; length]; 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 // Strings may be null-terminated, so we trim anything downstream of the null byte
if let Some(first) = out.iter().position(|&b| b == 0) { if let Some(first) = out.iter().position(|&b| b == 0) {
out.truncate(first); out.truncate(first);
@@ -718,13 +736,13 @@ impl<R: Read + Seek> Decoder<R> {
/// Reads a TIFF IFA offset/value field /// Reads a TIFF IFA offset/value field
#[inline] #[inline]
pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> { pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> {
if self.bigtiff { if self.value_reader.bigtiff {
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
)); ));
} }
let mut val = [0; 4]; let mut val = [0; 4];
self.reader.inner().read_exact(&mut val)?; self.value_reader.reader.inner().read_exact(&mut val)?;
Ok(val) Ok(val)
} }
@@ -732,7 +750,7 @@ impl<R: Read + Seek> Decoder<R> {
#[inline] #[inline]
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> { pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
let mut val = [0; 8]; let mut val = [0; 8];
self.reader.inner().read_exact(&mut val)?; self.value_reader.reader.inner().read_exact(&mut val)?;
Ok(val) Ok(val)
} }
@@ -744,211 +762,7 @@ impl<R: Read + Seek> Decoder<R> {
#[inline] #[inline]
pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> { pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> {
self.reader.goto_offset(offset) self.value_reader.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<Directory> {
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<R>,
bigtiff: bool,
) -> TiffResult<Option<(Tag, ifd::Entry)>> {
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<R>,
bigtiff: bool,
ifd_location: IfdPointer,
) -> TiffResult<Directory> {
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<Option<ifd::Value>> {
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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
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<T: TryFrom<u64>>(
&mut self,
tag: Tag,
) -> TiffResult<Option<Vec<T>>> {
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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
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<ifd::Value> {
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<u32> {
self.get_tag(tag)?.into_u32()
}
pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> {
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<f32> {
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<f64> {
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<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec()
}
pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult<Vec<u16>> {
self.get_tag(tag)?.into_u16_vec()
}
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
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<Vec<f32>> {
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<Vec<f64>> {
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<Vec<u8>> {
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<String> {
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<Item = TiffResult<(Tag, ifd::Value)>> + '_ {
self.image.ifd.as_ref().unwrap().iter().map(|(tag, entry)| {
entry
.val(&self.limits, self.bigtiff, &mut self.reader)
.map(|value| (tag, value))
})
} }
fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> { fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> {
@@ -1003,9 +817,7 @@ impl<R: Read + Seek> Decoder<R> {
output_width: usize, output_width: usize,
) -> TiffResult<()> { ) -> TiffResult<()> {
let offset = self.image.chunk_file_range(chunk_index)?.0; let offset = self.image.chunk_file_range(chunk_index)?.0;
self.reader.goto_offset(offset)?; self.goto_offset_u64(offset)?;
let byte_order = self.reader.byte_order;
let output_row_stride = (output_width as u64) let output_row_stride = (output_width as u64)
.saturating_mul(self.image.samples_per_pixel() as u64) .saturating_mul(self.image.samples_per_pixel() as u64)
@@ -1013,12 +825,10 @@ impl<R: Read + Seek> Decoder<R> {
/ 8; / 8;
self.image.expand_chunk( self.image.expand_chunk(
self.reader.inner(), &mut self.value_reader,
buffer.as_bytes_mut(), buffer.as_bytes_mut(),
output_row_stride.try_into()?, output_row_stride.try_into()?,
byte_order,
chunk_index, chunk_index,
&self.limits,
)?; )?;
Ok(()) Ok(())
@@ -1041,29 +851,31 @@ impl<R: Read + Seek> Decoder<R> {
.ok_or(TiffError::LimitsExceeded)?; .ok_or(TiffError::LimitsExceeded)?;
let max_sample_bits = self.image().bits_per_sample; let max_sample_bits = self.image().bits_per_sample;
let limits = &self.value_reader.limits;
match self.image().sample_format { match self.image().sample_format {
SampleFormat::Uint => match max_sample_bits { SampleFormat::Uint => match max_sample_bits {
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits), n if n <= 8 => DecodingResult::new_u8(buffer_size, limits),
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits), n if n <= 16 => DecodingResult::new_u16(buffer_size, limits),
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits), n if n <= 32 => DecodingResult::new_u32(buffer_size, limits),
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits), n if n <= 64 => DecodingResult::new_u64(buffer_size, limits),
n => Err(TiffError::UnsupportedError( n => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedBitsPerChannel(n), TiffUnsupportedError::UnsupportedBitsPerChannel(n),
)), )),
}, },
SampleFormat::IEEEFP => match max_sample_bits { SampleFormat::IEEEFP => match max_sample_bits {
16 => DecodingResult::new_f16(buffer_size, &self.limits), 16 => DecodingResult::new_f16(buffer_size, limits),
32 => DecodingResult::new_f32(buffer_size, &self.limits), 32 => DecodingResult::new_f32(buffer_size, limits),
64 => DecodingResult::new_f64(buffer_size, &self.limits), 64 => DecodingResult::new_f64(buffer_size, limits),
n => Err(TiffError::UnsupportedError( n => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedBitsPerChannel(n), TiffUnsupportedError::UnsupportedBitsPerChannel(n),
)), )),
}, },
SampleFormat::Int => match max_sample_bits { SampleFormat::Int => match max_sample_bits {
n if n <= 8 => DecodingResult::new_i8(buffer_size, &self.limits), n if n <= 8 => DecodingResult::new_i8(buffer_size, limits),
n if n <= 16 => DecodingResult::new_i16(buffer_size, &self.limits), n if n <= 16 => DecodingResult::new_i16(buffer_size, limits),
n if n <= 32 => DecodingResult::new_i32(buffer_size, &self.limits), n if n <= 32 => DecodingResult::new_i32(buffer_size, limits),
n if n <= 64 => DecodingResult::new_i64(buffer_size, &self.limits), n if n <= 64 => DecodingResult::new_i64(buffer_size, limits),
n => Err(TiffError::UnsupportedError( n => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedBitsPerChannel(n), TiffUnsupportedError::UnsupportedBitsPerChannel(n),
)), )),
@@ -1148,23 +960,324 @@ impl<R: Read + Seek> Decoder<R> {
// * pass requested band as parameter // * pass requested band as parameter
// * collect bands to a RGB encoding result in case of RGB bands // * collect bands to a RGB encoding result in case of RGB bands
for chunk in 0..image_chunks { 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 x = chunk % chunks_across;
let y = chunk / chunks_across; let y = chunk / chunks_across;
let buffer_offset = let buffer_offset =
y * output_row_stride * chunk_dimensions.1 as usize + x * chunk_row_bytes; 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.image.expand_chunk(
self.reader.inner(), &mut self.value_reader,
&mut result.as_buffer(0).as_bytes_mut()[buffer_offset..], &mut result.as_buffer(0).as_bytes_mut()[buffer_offset..],
output_row_stride, output_row_stride,
byte_order,
chunk as u32, chunk as u32,
&self.limits,
)?; )?;
} }
Ok(result) 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<Option<ifd::Value>> {
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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
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<ifd::Value> {
self.image_ifd().get_tag(tag)
}
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
self.get_tag(tag)?.into_u32()
}
pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> {
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<f32> {
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<f64> {
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<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec()
}
pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult<Vec<u16>> {
self.get_tag(tag)?.into_u16_vec()
}
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
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<Vec<f32>> {
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<Vec<f64>> {
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<Vec<u8>> {
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<String> {
self.get_tag(tag)?.into_string()
}
pub fn tag_iter(&mut self) -> impl Iterator<Item = TiffResult<(Tag, ifd::Value)>> + '_ {
self.image_ifd().tag_iter()
}
}
impl<R: Seek + Read> ValueReader<R> {
pub(crate) fn read_directory(&mut self, ptr: IfdPointer) -> Result<Directory, TiffError> {
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<R>,
bigtiff: bool,
) -> TiffResult<Option<(Tag, ifd::Entry)>> {
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<R>,
bigtiff: bool,
ifd_location: IfdPointer,
) -> TiffResult<Directory> {
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<ifd::Value>;
}
impl<R: Seek + Read> EntryDecoder for ValueReader<R> {
fn entry_val(&mut self, entry: &Entry) -> TiffResult<ifd::Value> {
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<Option<ifd::Value>> {
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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
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<T: TryFrom<u64>>(
&mut self,
tag: Tag,
) -> TiffResult<Option<Vec<T>>> {
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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
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<ifd::Value> {
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<u32> {
self.get_tag(tag)?.into_u32()
}
pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> {
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<f32> {
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<f64> {
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<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec()
}
pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult<Vec<u16>> {
self.get_tag(tag)?.into_u16_vec()
}
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
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<Vec<f32>> {
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<Vec<f64>> {
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<Vec<u8>> {
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<String> {
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<Item = TiffResult<(Tag, ifd::Value)>> + 'l {
self.ifd
.iter()
.map(|(tag, entry)| match self.decoder.entry_val(entry) {
Ok(value) => Ok((tag, value)),
Err(err) => Err(err),
})
}
} }
+5 -2
View File
@@ -18,7 +18,7 @@ pub struct EndianReader<R> {
pub(crate) byte_order: ByteOrder, pub(crate) byte_order: ByteOrder,
} }
impl<R: Read + Seek> EndianReader<R> { impl<R: Read> EndianReader<R> {
pub fn new(reader: R, byte_order: ByteOrder) -> Self { pub fn new(reader: R, byte_order: ByteOrder) -> Self {
Self { reader, byte_order } Self { reader, byte_order }
} }
@@ -27,7 +27,10 @@ impl<R: Read + Seek> EndianReader<R> {
&mut self.reader &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))?; self.reader.seek(io::SeekFrom::Start(offset))?;
Ok(()) Ok(())
} }