Add a dedicated Directory type (#277)

This commit is contained in:
Aurelia Molzer authored and GitHub committed 2025-07-07 19:59:37 -07:00
1 parent 816fb85304
commit df4e42eecc
7 files changed
+239 -43

No files matched your search

+6
View File
@@ -333,6 +333,10 @@ impl Entry {
}
}
pub fn count(&self) -> u64 {
self.count
}
/// Returns a mem_reader for the offset/value field
fn r(&self, byte_order: ByteOrder) -> EndianReader<io::Cursor<Vec<u8>>> {
EndianReader::new(io::Cursor::new(self.offset.to_vec()), byte_order)
@@ -668,4 +672,6 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> {
}
/// Type representing an Image File Directory
#[doc(hidden)]
#[deprecated = "Use struct `tiff::Directory` instead which contains all fields relevant to an Image File Directory, including the offset to the next directory"]
pub type Directory = HashMap<Tag, Entry>;
+10 -8
View File
@@ -1,4 +1,4 @@
use super::ifd::{Directory, Value};
use super::ifd::Value;
use super::stream::{ByteOrder, PackBitsReader};
use super::tag_reader::TagReader;
use super::{predict_f16, predict_f32, predict_f64, Limits};
@@ -6,7 +6,9 @@ use super::{stream::EndianReader, ChunkType};
use crate::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag,
};
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
use crate::{
ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
};
use std::io::{self, Cursor, Read, Seek};
use std::sync::Arc;
@@ -115,7 +117,7 @@ impl Image {
};
let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG
&& ifd.contains_key(&Tag::JPEGTables)
&& ifd.contains(Tag::JPEGTables)
{
let vec = tag_reader
.find_tag(Tag::JPEGTables)?
@@ -210,10 +212,10 @@ impl Image {
let strip_decoder;
let tile_attributes;
match (
ifd.contains_key(&Tag::StripByteCounts),
ifd.contains_key(&Tag::StripOffsets),
ifd.contains_key(&Tag::TileByteCounts),
ifd.contains_key(&Tag::TileOffsets),
ifd.contains(Tag::StripByteCounts),
ifd.contains(Tag::StripOffsets),
ifd.contains(Tag::TileByteCounts),
ifd.contains(Tag::TileOffsets),
) {
(true, true, false, false) => {
chunk_type = ChunkType::Strip;
@@ -676,7 +678,7 @@ impl Image {
}
}
} else {
for (i, row) in buf
for (_i, row) in buf
.chunks_mut(output_row_stride)
.take(data_dims.1 as usize)
.enumerate()
+37 -31
View File
@@ -1,16 +1,16 @@
use std::collections::{HashMap, HashSet};
use std::collections::{BTreeMap, HashSet};
use std::io::{self, Read, Seek};
use crate::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat,
Tag, Type,
CompressionMethod, IfdPointer, PhotometricInterpretation, PlanarConfiguration, Predictor,
SampleFormat, Tag, Type,
};
use crate::{
bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
bytecast, ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError,
UsageError,
};
use half::f16;
use self::ifd::Directory;
use self::image::Image;
use self::stream::{ByteOrder, EndianReader};
@@ -263,9 +263,9 @@ where
reader: EndianReader<R>,
bigtiff: bool,
limits: Limits,
next_ifd: Option<u64>,
ifd_offsets: Vec<u64>,
seen_ifds: HashSet<u64>,
next_ifd: Option<IfdPointer>,
ifd_offsets: Vec<IfdPointer>,
seen_ifds: HashSet<IfdPointer>,
image: Image,
}
@@ -487,11 +487,13 @@ impl<R: Read + Seek> Decoder<R> {
))
}
};
let next_ifd = if bigtiff {
Some(reader.read_u64()?)
} else {
Some(u64::from(reader.read_u32()?))
};
}
.map(IfdPointer);
let mut seen_ifds = HashSet::new();
seen_ifds.insert(*next_ifd.as_ref().unwrap());
@@ -557,9 +559,9 @@ impl<R: Read + Seek> Decoder<R> {
loop {
// Follow the list until we find the one we want, or we reach the end, whichever happens first
let (_ifd, next_ifd) = self.next_ifd()?;
let ifd = self.next_ifd()?;
if next_ifd.is_none() {
if ifd.next().is_none() {
break;
}
@@ -571,8 +573,7 @@ impl<R: Read + Seek> Decoder<R> {
// 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, _next_ifd) = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?;
let ifd = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?;
self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?;
Ok(())
@@ -583,20 +584,20 @@ impl<R: Read + Seek> Decoder<R> {
}
}
fn next_ifd(&mut self) -> TiffResult<(Directory, Option<u64>)> {
fn next_ifd(&mut self) -> TiffResult<Directory> {
if self.next_ifd.is_none() {
return Err(TiffError::FormatError(
TiffFormatError::ImageFileDirectoryNotFound,
));
}
let (ifd, next_ifd) = Self::read_ifd(
let ifd = Self::read_ifd(
&mut self.reader,
self.bigtiff,
self.next_ifd.take().unwrap(),
)?;
if let Some(next) = next_ifd {
if let Some(next) = ifd.next() {
if !self.seen_ifds.insert(next) {
return Err(TiffError::FormatError(TiffFormatError::CycleInOffsets));
}
@@ -604,15 +605,14 @@ impl<R: Read + Seek> Decoder<R> {
self.ifd_offsets.push(next);
}
Ok((ifd, next_ifd))
Ok(ifd)
}
/// Reads in the next 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_images` instead.
pub fn next_image(&mut self) -> TiffResult<()> {
let (ifd, _next_ifd) = self.next_ifd()?;
let ifd = self.next_ifd()?;
self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?;
Ok(())
}
@@ -734,6 +734,13 @@ impl<R: Read + Seek> Decoder<R> {
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<Directory> {
Self::read_ifd(&mut self.reader, self.bigtiff, ptr)
}
/// Reads a IFD entry.
// An IFD entry has four fields:
//
@@ -775,17 +782,18 @@ impl<R: Read + Seek> Decoder<R> {
fn read_ifd(
reader: &mut EndianReader<R>,
bigtiff: bool,
ifd_location: u64,
) -> TiffResult<(Directory, Option<u64>)> {
reader.goto_offset(ifd_location)?;
ifd_location: IfdPointer,
) -> TiffResult<Directory> {
reader.goto_offset(ifd_location.0)?;
let mut dir: Directory = HashMap::new();
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,
@@ -793,7 +801,8 @@ impl<R: Read + Seek> Decoder<R> {
continue;
} // Unknown data type in tag, skip
};
dir.insert(tag, entry);
entries.insert(tag.to_u16(), entry);
}
let next_ifd = if bigtiff {
@@ -802,18 +811,15 @@ impl<R: Read + Seek> Decoder<R> {
reader.read_u32()?.into()
};
let next_ifd = match next_ifd {
0 => None,
_ => Some(next_ifd),
};
let next_ifd = core::num::NonZeroU64::new(next_ifd);
Ok((dir, 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) {
let entry = match self.image().ifd.as_ref().unwrap().get(tag) {
None => return Ok(None),
Some(entry) => entry.clone(),
};
@@ -928,7 +934,7 @@ impl<R: Read + Seek> Decoder<R> {
self.image.ifd.as_ref().unwrap().iter().map(|(tag, entry)| {
entry
.val(&self.limits, self.bigtiff, &mut self.reader)
.map(|value| (*tag, value))
.map(|value| (tag, value))
})
}
+3 -3
View File
@@ -1,9 +1,9 @@
use std::io::{Read, Seek};
use crate::tags::Tag;
use crate::{tags::Tag, Directory};
use crate::{TiffError, TiffFormatError, TiffResult};
use super::ifd::{Directory, Value};
use super::ifd::Value;
use super::stream::EndianReader;
use super::Limits;
@@ -15,7 +15,7 @@ pub(crate) struct TagReader<'a, R: Read + Seek> {
}
impl<'a, R: Read + Seek> TagReader<'a, R> {
pub(crate) fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<Value>> {
Ok(match self.ifd.get(&tag) {
Ok(match self.ifd.get(tag) {
Some(entry) => Some(entry.clone().val(self.limits, self.bigtiff, self.reader)?),
None => None,
})