Compare commits

..
4 Commits
Author SHA1 Message Date
A. Molzer ab0ce47230 Track non-image Directory sequence in Reader
A bit nasty, since we are storing the current Directory within the Image
instead of a separate attribute so we can not have one without the other
directly. Instead we prioritize with another attribute.
2025-07-25 21:26:57 +02:00
A. Molzer f993cfb940 Check full subsubifds structure 2025-07-25 21:21:37 +02:00
A. Molzer 8c4bd58493 Complement directory reading methods by iteration 2025-07-25 21:21:37 +02:00
A. Molzer c073841a9d Add Decoder::restart_ifd
This serves two purposes: reliably resetting the state of the decoder
after kinds of failure; and moving the decoder to a SubIFD chain.
2025-07-25 21:21:37 +02:00
8 changed files with 307 additions and 110 deletions

No files matched your search

-21
View File
@@ -1,24 +1,3 @@
# Version 0.10.2
Republished 0.10.1, fixing an accidentally removed method (`Decoder::find_tag_unsigned_vec`).
# Version 0.10.1
New features:
- Added a dedicated `Directory` type (#277)
- Expose methods related to `Directory` publicly (#278)
- Implement writing directories (#281, #283)
- Add ICC (color profile) to the set of known tags (#287)
- Added `Decoder.inner()` to access the underlying reader (#289)
- Added methods for reading into a pre-allocated byte buffer (#285)
Changes:
- No longer perform color conversion when decoding embedded JPEG. This fixes decoding for JPEG images with alpha channel. (#274)
- More lenient LZW decoding to match libtiff (#231)
Fixes:
- Fixed seeking multiple times in a row (#279)
# Version 0.10.0 # Version 0.10.0
New features: New features:
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tiff" name = "tiff"
version = "0.10.2" version = "0.10.0"
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"
@@ -28,7 +28,7 @@ zune-jpeg = { version = "0.4.17", optional = true }
[dev-dependencies] [dev-dependencies]
criterion = "0.3.1" criterion = "0.3.1"
# We may already depend on this via flate2 # We may already depend on this via flate2
crc32fast = "1.5" crc32fast = "1"
[features] [features]
default = ["deflate", "jpeg", "lzw"] default = ["deflate", "jpeg", "lzw"]
+14 -2
View File
@@ -86,7 +86,19 @@ impl Image {
decoder: &mut ValueReader<R>, decoder: &mut ValueReader<R>,
ifd: Directory, ifd: Directory,
) -> TiffResult<Image> { ) -> TiffResult<Image> {
let mut tag_reader = TagReader { decoder, ifd: &ifd }; let img = Self::from_ref(decoder, &ifd)?;
Ok(Image {
ifd: Some(ifd),
..img
})
}
pub(crate) fn from_ref<R: Read + Seek>(
decoder: &mut ValueReader<R>,
ifd: &Directory,
) -> TiffResult<Image> {
let mut tag_reader = TagReader { decoder, ifd };
let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?;
let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?; let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?;
@@ -288,7 +300,7 @@ impl Image {
}; };
Ok(Image { Ok(Image {
ifd: Some(ifd), ifd: None,
width, width,
height, height,
bits_per_sample: bits_per_sample[0], bits_per_sample: bits_per_sample[0],
+132 -54
View File
@@ -23,7 +23,7 @@ mod stream;
mod tag_reader; mod tag_reader;
/// Result of a decoding process /// Result of a decoding process
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub enum DecodingResult { pub enum DecodingResult {
/// A vector of unsigned bytes /// A vector of unsigned bytes
U8(Vec<u8>), U8(Vec<u8>),
@@ -138,7 +138,7 @@ impl DecodingResult {
} }
} }
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer<'_> { pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
match *self { match *self {
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]), DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]), DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
@@ -349,6 +349,10 @@ where
ifd_offsets: Vec<IfdPointer>, ifd_offsets: Vec<IfdPointer>,
/// Map from the ifd into the `ifd_offsets` ordered list. /// Map from the ifd into the `ifd_offsets` ordered list.
seen_ifds: cycles::IfdCycles, seen_ifds: cycles::IfdCycles,
/// The directory, if we have not yet read it as an image.
/// This is prioritized _over_ the image. Hence it must _not_ be set if we are currently
/// reading a true image IFD that is instead stored in the `image` attribute.
non_image_ifd: Option<Directory>,
image: Image, image: Image,
} }
@@ -624,6 +628,7 @@ impl<R: Read + Seek> Decoder<R> {
chunk_offsets: Vec::new(), chunk_offsets: Vec::new(),
chunk_bytes: Vec::new(), chunk_bytes: Vec::new(),
}, },
non_image_ifd: None,
}; };
decoder.next_image()?; decoder.next_image()?;
Ok(decoder) Ok(decoder)
@@ -653,48 +658,85 @@ impl<R: Read + Seek> Decoder<R> {
/// Loads the IFD at the specified index in the list, if one exists /// Loads the IFD at the specified index in the list, if one exists
pub fn seek_to_image(&mut self, ifd_index: usize) -> TiffResult<()> { pub fn seek_to_image(&mut self, ifd_index: usize) -> TiffResult<()> {
// Check whether we have seen this IFD before, if so then the index will be less than the length of the list of ifd offsets let ifd = self.seek_to_directory(ifd_index)?;
if ifd_index >= self.ifd_offsets.len() { self.non_image_ifd = None;
// We possibly need to load in the next IFD self.image = Image::from_reader(&mut self.value_reader, ifd)?;
if self.next_ifd.is_none() { Ok(())
}
fn seek_to_directory(&mut self, ifd_index: usize) -> TiffResult<Directory> {
if ifd_index < self.ifd_offsets.len() {
// If the index is within the list of ifds then we can load the selected image/IFD
let ifd_offset = self.ifd_offsets[ifd_index];
let ifd = self.value_reader.read_directory(ifd_offset)?;
self.next_ifd = ifd.next();
self.current_ifd = Some(ifd_offset);
return Ok(ifd);
}
// Follow the list until we find the one we want, or we reach the end, whichever happens
// first. How many IFDs to read only for their `next` field?
let step_over = self.ifd_offsets.len() - ifd_index;
for _ in 0..step_over {
// FIXME: for optimization we only need to read the offset of this one, not its whole
// data. However on buffered files this should be a rather small difference unless
// you're traversing a lot of directories.
// Detecting an end-of-file is done by `next_ifd`. We ignore the IFD itself (but not
// via an ignore pattern to avoid silencing must_use accidentally).
self.next_ifd()?;
}
// self.next_ifd, self.current_ifd will be setup by `next_ifd`.
self.next_ifd()
}
/// Start the chain of image directories from a new location.
///
/// This enters a new chain of image file directories with the indicated offset as the new root
/// (similar to the file having its initial offset at the given IFD position). After this call,
/// [`Self::seek_to_image`] has the new root at index `0` and works relative to the new root.
///
/// This is not atomic with regards to errors. If the function returns an error, the decoder is
/// left in an intermediate state where it does not point to any image. A valid state can be
/// recovered by calling [`Self::restart_at_image`] with a valid offset and a successful seek.
pub fn restart_at_image(&mut self, offset: IfdPointer) -> TiffResult<()> {
let ifd = self.restart_at_offset(offset)?;
self.non_image_ifd = None;
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
Ok(())
}
/// Start the chain of non-image directories from a new location.
///
/// See [`Self::restart_at_image`] for details, except this method does not attempt to
/// interpret the directory in the sequence as an image. Instead, it may be used to read a
/// sequence of auxiliary IFDs that are not necessarily images. For instance, a directory
/// referred to in the SubIfd tag may be a thumbnail
pub fn restart_at_directory(&mut self, offset: IfdPointer) -> TiffResult<()> {
let ifd = self.restart_at_offset(offset)?;
self.non_image_ifd = Some(ifd);
Ok(())
}
fn restart_at_offset(&mut self, offset: IfdPointer) -> TiffResult<Directory> {
self.ifd_offsets.clear();
self.ifd_offsets.push(offset);
self.next_ifd = Some(offset);
self.current_ifd = None; self.current_ifd = None;
return Err(TiffError::FormatError( self.next_ifd()
TiffFormatError::ImageFileDirectoryNotFound,
));
}
loop {
// Follow the list until we find the one we want, or we reach the end, whichever happens first
let ifd = self.next_ifd()?;
if ifd.next().is_none() {
break;
}
if ifd_index < self.ifd_offsets.len() {
break;
}
}
}
// 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.value_reader.read_directory(*ifd_offset)?;
self.next_ifd = ifd.next();
self.current_ifd = Some(*ifd_offset);
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
Ok(())
} else {
Err(TiffError::FormatError(
TiffFormatError::ImageFileDirectoryNotFound,
))
}
} }
fn next_ifd(&mut self) -> TiffResult<Directory> { fn next_ifd(&mut self) -> TiffResult<Directory> {
let Some(next_ifd) = self.next_ifd.take() else { let Some(next_ifd) = self.next_ifd.take() else {
self.current_ifd = None;
self.non_image_ifd = None;
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::ImageFileDirectoryNotFound, TiffFormatError::ImageFileDirectoryNotFound,
)); ));
@@ -721,10 +763,44 @@ 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.non_image_ifd = None;
self.image = Image::from_reader(&mut self.value_reader, ifd)?; self.image = Image::from_reader(&mut self.value_reader, ifd)?;
Ok(()) Ok(())
} }
/// Read the next directory without interpreting it as an image.
///
/// 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_directories` instead.
pub fn next_directory(&mut self) -> TiffResult<()> {
let ifd = self.next_ifd()?;
self.non_image_ifd = Some(ifd);
Ok(())
}
/// Interpret the current directory as an image.
///
/// This method is used after having called [`Self::restart_at`] or [`Self::next_directory`] to
/// iterate the sequence of image file directories, having read a directory without having read
/// its tags as image data.
pub fn current_directory_as_image(&mut self) -> TiffResult<()> {
let current_ifd = self.current_ifd.ok_or(TiffError::FormatError(
TiffFormatError::ImageFileDirectoryNotFound,
))?;
if let Some(ifd) = &self.non_image_ifd {
self.image = Image::from_ref(&mut self.value_reader, ifd)?;
// Definitely sets an IFD but this works without unwraps.
self.image.ifd = self.non_image_ifd.take();
} else {
// Probably re-reading an image but that's fine.
let ifd = self.read_directory(current_ifd)?;
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
}
Ok(())
}
/// Returns `true` if there is at least one more image available. /// Returns `true` if there is at least one more image available.
pub fn more_images(&self) -> bool { pub fn more_images(&self) -> bool {
self.next_ifd.is_some() self.next_ifd.is_some()
@@ -1227,11 +1303,22 @@ impl<R: Read + Seek> Decoder<R> {
} }
/// Get the IFD decoder for our current image IFD. /// Get the IFD decoder for our current image IFD.
fn image_ifd(&mut self) -> IfdDecoder<'_> { fn current_directory_ifd(&mut self) -> IfdDecoder<'_> {
// Special fallback. We do not want to error handle not having read a current directory, in
// particular as the behavior having a directory without tags will produce these errors
// anyways (most likely). Note that an empty directory is invalid in a TIFF.
static NO_IFD: Directory = Directory::empty();
let ifd = self
.non_image_ifd
.as_ref()
.or(self.image.ifd.as_ref())
.unwrap_or(&NO_IFD);
IfdDecoder { IfdDecoder {
inner: tag_reader::TagReader { inner: tag_reader::TagReader {
decoder: &mut self.value_reader, decoder: &mut self.value_reader,
ifd: self.image.ifd.as_ref().unwrap(), ifd,
}, },
} }
} }
@@ -1272,34 +1359,25 @@ impl<R: Read + Seek> Decoder<R> {
/// Tries to retrieve a tag from the current image directory. /// Tries to retrieve a tag from the current image directory.
/// Return `Ok(None)` if the tag is not present. /// Return `Ok(None)` if the tag is not present.
pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> { pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> {
self.image_ifd().find_tag(tag) self.current_directory_ifd().find_tag(tag)
} }
/// Tries to retrieve a tag in the current image directory and convert it to the desired /// Tries to retrieve a tag in the current image directory and convert it to the desired
/// unsigned type. /// unsigned type.
pub fn find_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> { pub fn find_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
self.image_ifd().find_tag_unsigned(tag) self.current_directory_ifd().find_tag_unsigned(tag)
}
/// 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.image_ifd().find_tag_unsigned_vec(tag)
} }
/// Tries to retrieve a tag from the current image directory and convert it to the desired /// 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. /// 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> { pub fn get_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
self.image_ifd().get_tag_unsigned(tag) self.current_directory_ifd().get_tag_unsigned(tag)
} }
/// Tries to retrieve a tag from the current image directory. /// Tries to retrieve a tag from the current image directory.
/// Returns an error if the tag is not present /// Returns an error if the tag is not present
pub fn get_tag(&mut self, tag: Tag) -> TiffResult<ifd::Value> { pub fn get_tag(&mut self, tag: Tag) -> TiffResult<ifd::Value> {
self.image_ifd().get_tag(tag) self.current_directory_ifd().get_tag(tag)
} }
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> { pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
@@ -1354,7 +1432,7 @@ impl<R: Read + Seek> Decoder<R> {
} }
pub fn tag_iter(&mut self) -> impl Iterator<Item = TiffResult<(Tag, ifd::Value)>> + '_ { pub fn tag_iter(&mut self) -> impl Iterator<Item = TiffResult<(Tag, ifd::Value)>> + '_ {
self.image_ifd().tag_iter() self.current_directory_ifd().tag_iter()
} }
} }
+1 -1
View File
@@ -29,7 +29,7 @@ pub struct Directory {
impl Directory { impl Directory {
/// Create a directory in an initial state without entries. Note that an empty directory can /// Create a directory in an initial state without entries. Note that an empty directory can
/// not be encoded in a file, it must contain at least one entry. /// not be encoded in a file, it must contain at least one entry.
pub fn empty() -> Self { pub const fn empty() -> Self {
Directory { Directory {
entries: BTreeMap::new(), entries: BTreeMap::new(),
next_ifd: None, next_ifd: None,
+1 -1
View File
@@ -198,7 +198,7 @@ impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
&mut self, &mut self,
width: u32, width: u32,
height: u32, height: u32,
) -> TiffResult<ImageEncoder<'_, W, C, K>> { ) -> TiffResult<ImageEncoder<W, C, K>> {
let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?; let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?;
ImageEncoder::new(encoder, width, height, self.compression, self.predictor) ImageEncoder::new(encoder, width, height, self.compression, self.predictor)
} }
+28 -28
View File
@@ -15,7 +15,7 @@ pub trait TiffValue {
/// Access this value as an contiguous sequence of bytes. /// Access this value as an contiguous sequence of bytes.
/// If their is no trivial representation, allocate it on the heap. /// If their is no trivial representation, allocate it on the heap.
fn data(&self) -> Cow<'_, [u8]>; fn data(&self) -> Cow<[u8]>;
/// Write this value to a TiffWriter. /// Write this value to a TiffWriter.
/// While the default implementation will work in all cases, it may require unnecessary allocations. /// While the default implementation will work in all cases, it may require unnecessary allocations.
@@ -34,7 +34,7 @@ impl TiffValue for [u8] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(self) Cow::Borrowed(self)
} }
} }
@@ -47,7 +47,7 @@ impl TiffValue for [i8] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i8_as_ne_bytes(self)) Cow::Borrowed(bytecast::i8_as_ne_bytes(self))
} }
} }
@@ -60,7 +60,7 @@ impl TiffValue for [u16] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u16_as_ne_bytes(self)) Cow::Borrowed(bytecast::u16_as_ne_bytes(self))
} }
} }
@@ -73,7 +73,7 @@ impl TiffValue for [i16] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i16_as_ne_bytes(self)) Cow::Borrowed(bytecast::i16_as_ne_bytes(self))
} }
} }
@@ -86,7 +86,7 @@ impl TiffValue for [u32] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u32_as_ne_bytes(self)) Cow::Borrowed(bytecast::u32_as_ne_bytes(self))
} }
} }
@@ -99,7 +99,7 @@ impl TiffValue for [i32] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i32_as_ne_bytes(self)) Cow::Borrowed(bytecast::i32_as_ne_bytes(self))
} }
} }
@@ -112,7 +112,7 @@ impl TiffValue for [u64] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u64_as_ne_bytes(self)) Cow::Borrowed(bytecast::u64_as_ne_bytes(self))
} }
} }
@@ -125,7 +125,7 @@ impl TiffValue for [i64] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i64_as_ne_bytes(self)) Cow::Borrowed(bytecast::i64_as_ne_bytes(self))
} }
} }
@@ -138,7 +138,7 @@ impl TiffValue for [f32] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
// We write using native endian so this should be safe // We write using native endian so this should be safe
Cow::Borrowed(bytecast::f32_as_ne_bytes(self)) Cow::Borrowed(bytecast::f32_as_ne_bytes(self))
} }
@@ -152,7 +152,7 @@ impl TiffValue for [f64] {
self.len() self.len()
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
// We write using native endian so this should be safe // We write using native endian so this should be safe
Cow::Borrowed(bytecast::f64_as_ne_bytes(self)) Cow::Borrowed(bytecast::f64_as_ne_bytes(self))
} }
@@ -171,7 +171,7 @@ impl TiffValue for u8 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(from_ref(self)) Cow::Borrowed(from_ref(self))
} }
} }
@@ -189,7 +189,7 @@ impl TiffValue for i8 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i8_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::i8_as_ne_bytes(from_ref(self)))
} }
} }
@@ -207,7 +207,7 @@ impl TiffValue for u16 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u16_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::u16_as_ne_bytes(from_ref(self)))
} }
} }
@@ -225,7 +225,7 @@ impl TiffValue for i16 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i16_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::i16_as_ne_bytes(from_ref(self)))
} }
} }
@@ -243,7 +243,7 @@ impl TiffValue for u32 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u32_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::u32_as_ne_bytes(from_ref(self)))
} }
} }
@@ -261,7 +261,7 @@ impl TiffValue for i32 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i32_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::i32_as_ne_bytes(from_ref(self)))
} }
} }
@@ -279,7 +279,7 @@ impl TiffValue for u64 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u64_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::u64_as_ne_bytes(from_ref(self)))
} }
} }
@@ -297,7 +297,7 @@ impl TiffValue for i64 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::i64_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::i64_as_ne_bytes(from_ref(self)))
} }
} }
@@ -315,7 +315,7 @@ impl TiffValue for f32 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::f32_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::f32_as_ne_bytes(from_ref(self)))
} }
} }
@@ -333,7 +333,7 @@ impl TiffValue for f64 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::f64_as_ne_bytes(from_ref(self))) Cow::Borrowed(bytecast::f64_as_ne_bytes(from_ref(self)))
} }
} }
@@ -351,7 +351,7 @@ impl TiffValue for Ifd {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u32_as_ne_bytes(from_ref(&self.0))) Cow::Borrowed(bytecast::u32_as_ne_bytes(from_ref(&self.0)))
} }
} }
@@ -369,7 +369,7 @@ impl TiffValue for Ifd8 {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Borrowed(bytecast::u64_as_ne_bytes(from_ref(&self.0))) Cow::Borrowed(bytecast::u64_as_ne_bytes(from_ref(&self.0)))
} }
} }
@@ -388,7 +388,7 @@ impl TiffValue for Rational {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Owned({ Cow::Owned({
let first_dword = bytecast::u32_as_ne_bytes(from_ref(&self.n)); let first_dword = bytecast::u32_as_ne_bytes(from_ref(&self.n));
let second_dword = bytecast::u32_as_ne_bytes(from_ref(&self.d)); let second_dword = bytecast::u32_as_ne_bytes(from_ref(&self.d));
@@ -411,7 +411,7 @@ impl TiffValue for SRational {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Owned({ Cow::Owned({
let first_dword = bytecast::i32_as_ne_bytes(from_ref(&self.n)); let first_dword = bytecast::i32_as_ne_bytes(from_ref(&self.n));
let second_dword = bytecast::i32_as_ne_bytes(from_ref(&self.d)); let second_dword = bytecast::i32_as_ne_bytes(from_ref(&self.d));
@@ -438,7 +438,7 @@ impl TiffValue for str {
} }
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
Cow::Owned({ Cow::Owned({
if self.is_ascii() && !self.bytes().any(|b| b == 0) { if self.is_ascii() && !self.bytes().any(|b| b == 0) {
let bytes: &[u8] = self.as_bytes(); let bytes: &[u8] = self.as_bytes();
@@ -462,7 +462,7 @@ impl<T: TiffValue + ?Sized> TiffValue for &'_ T {
(*self).write(writer) (*self).write(writer)
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
T::data(self) T::data(self)
} }
} }
@@ -484,7 +484,7 @@ macro_rules! impl_tiff_value_for_contiguous_sequence {
Ok(()) Ok(())
} }
fn data(&self) -> Cow<'_, [u8]> { fn data(&self) -> Cow<[u8]> {
let mut buf: Vec<u8> = Vec::with_capacity(Self::BYTE_LEN as usize * self.len()); let mut buf: Vec<u8> = Vec::with_capacity(Self::BYTE_LEN as usize * self.len());
for x in self { for x in self {
buf.extend_from_slice(&x.data()); buf.extend_from_slice(&x.data());
+129 -1
View File
@@ -1,4 +1,7 @@
use tiff::decoder::Decoder; use tiff::{
decoder::Decoder,
tags::{IfdPointer, Tag},
};
// This file has the following IFD structure: // This file has the following IFD structure:
// //
@@ -39,3 +42,128 @@ fn decode_seek_chain() {
assert_eq!(Some(offset2), decoder.ifd_pointer()); assert_eq!(Some(offset2), decoder.ifd_pointer());
assert!(!decoder.more_images()); assert!(!decoder.more_images());
} }
#[test]
fn decode_seek_recover() {
let file = File::open(TEST_IMAGE_SUBIFD).expect("Cannot open test image");
let mut decoder = Decoder::new(file).expect("Invalid format to create decoder");
// Remember the offsets in the first iteration.
let offset0 = decoder.ifd_pointer().expect("First IFD pointer not found");
decoder.next_image().unwrap();
let offset1 = decoder.ifd_pointer().expect("Second IFD pointer not found");
decoder.next_image().unwrap();
let offset2 = decoder.ifd_pointer().expect("Third IFD pointer not found");
// oops!
let fails = decoder.restart_at_image(IfdPointer(0xdead_beef));
assert!(fails.is_err());
// However we can recover by restarting our seek.
decoder
.restart_at_image(offset0)
.expect("Failed to restart IFDs from first image");
assert_eq!(Some(offset0), decoder.ifd_pointer());
decoder.next_image().unwrap();
assert_eq!(Some(offset1), decoder.ifd_pointer());
decoder.next_image().unwrap();
assert_eq!(Some(offset2), decoder.ifd_pointer());
}
#[test]
fn decode_seek_directory() {
let file = File::open(TEST_IMAGE_SUBIFD).expect("Cannot open test image");
let mut decoder = Decoder::new(file).expect("Invalid format to create decoder");
let offset = decoder.ifd_pointer().expect("First IFD pointer not found");
let img0 = decoder.read_image().unwrap();
decoder.next_image().unwrap();
let img1 = decoder.read_image().unwrap();
assert_ne!(img0, img1);
{
// Due to the special test file structure.
let subifd = decoder
.get_tag(Tag::SubIfd)
.unwrap()
.into_ifd_vec()
.unwrap()[0];
// Let's try to manually interpret this as an image.
decoder
.restart_at_directory(subifd)
.expect("Failed to restart IFDs at SubIfd");
decoder
.current_directory_as_image()
.expect("Failed to read SubIfd image");
assert_eq!(decoder.dimensions().unwrap(), (32, 32));
let subimage = decoder.read_image().unwrap();
// Verify we did not accidentally read another image.
assert_ne!(img0, subimage);
assert_ne!(img1, subimage);
}
// And finally, seek back to the first image.
decoder
.restart_at_image(offset)
.expect("First IFD no longer readable");
assert_eq!(Some(offset), decoder.ifd_pointer());
let img0_try2 = decoder.read_image().unwrap();
assert_eq!(img0, img0_try2);
}
#[test]
fn decode_subifds() {
let explore_chain = |decoder: &mut Decoder<_>, subifds: &mut Vec<_>| {
let mut len = 0usize;
loop {
len += 1;
if let Ok(subifd) = decoder.get_tag(Tag::SubIfd) {
let prelen = subifds.len();
subifds.extend(subifd.into_ifd_vec().expect("Failed to decode SubIfd tag"));
eprintln!("New roots: {:?}", &subifds[prelen..]);
} else {
eprintln!("No roots");
}
if !decoder.more_images() {
break;
}
decoder.next_image().unwrap();
}
len
};
let file = File::open(TEST_IMAGE_SUBIFD).expect("Cannot open test image");
let mut decoder = Decoder::new(file).expect("Invalid format to create decoder");
let mut subifds = vec![];
let mut chain_lengths = vec![];
chain_lengths.push(explore_chain(&mut decoder, &mut subifds));
while !subifds.is_empty() {
let root = subifds.remove(0);
let manual_directory = decoder.read_directory(root).unwrap();
eprintln!("Manual directory: {:?}", manual_directory);
decoder.restart_at_directory(root).unwrap();
let prelen = subifds.len();
chain_lengths.push(explore_chain(&mut decoder, &mut subifds));
eprintln!("Chains roots: {:?}", &subifds[prelen..]);
}
// Traversal Order in this graph:
// Image 0 -> Image 1 -> Image 2
// |-> Image 3
// |-> Image 4 -> Image 5
// | \-> Image 7 -> Image 8
// \-> Image 6
assert_eq!(chain_lengths, [3, 1, 2, 1, 2]);
}