Skip to content

Commit df4e42e

Browse files
authored
Add a dedicated Directory type (#277)
1 parent 816fb85 commit df4e42e

File tree

7 files changed

+239
-43
lines changed

7 files changed

+239
-43
lines changed

src/decoder/ifd.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ impl Entry {
333333
}
334334
}
335335

336+
pub fn count(&self) -> u64 {
337+
self.count
338+
}
339+
336340
/// Returns a mem_reader for the offset/value field
337341
fn r(&self, byte_order: ByteOrder) -> EndianReader<io::Cursor<Vec<u8>>> {
338342
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> {
668672
}
669673

670674
/// Type representing an Image File Directory
675+
#[doc(hidden)]
676+
#[deprecated = "Use struct `tiff::Directory` instead which contains all fields relevant to an Image File Directory, including the offset to the next directory"]
671677
pub type Directory = HashMap<Tag, Entry>;

src/decoder/image.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
use super::ifd::{Directory, Value};
1+
use super::ifd::Value;
22
use super::stream::{ByteOrder, PackBitsReader};
33
use super::tag_reader::TagReader;
44
use super::{predict_f16, predict_f32, predict_f64, Limits};
55
use super::{stream::EndianReader, ChunkType};
66
use crate::tags::{
77
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag,
88
};
9-
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
9+
use crate::{
10+
ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
11+
};
1012
use std::io::{self, Cursor, Read, Seek};
1113
use std::sync::Arc;
1214

@@ -115,7 +117,7 @@ impl Image {
115117
};
116118

117119
let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG
118-
&& ifd.contains_key(&Tag::JPEGTables)
120+
&& ifd.contains(Tag::JPEGTables)
119121
{
120122
let vec = tag_reader
121123
.find_tag(Tag::JPEGTables)?
@@ -210,10 +212,10 @@ impl Image {
210212
let strip_decoder;
211213
let tile_attributes;
212214
match (
213-
ifd.contains_key(&Tag::StripByteCounts),
214-
ifd.contains_key(&Tag::StripOffsets),
215-
ifd.contains_key(&Tag::TileByteCounts),
216-
ifd.contains_key(&Tag::TileOffsets),
215+
ifd.contains(Tag::StripByteCounts),
216+
ifd.contains(Tag::StripOffsets),
217+
ifd.contains(Tag::TileByteCounts),
218+
ifd.contains(Tag::TileOffsets),
217219
) {
218220
(true, true, false, false) => {
219221
chunk_type = ChunkType::Strip;
@@ -676,7 +678,7 @@ impl Image {
676678
}
677679
}
678680
} else {
679-
for (i, row) in buf
681+
for (_i, row) in buf
680682
.chunks_mut(output_row_stride)
681683
.take(data_dims.1 as usize)
682684
.enumerate()

src/decoder/mod.rs

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
use std::collections::{HashMap, HashSet};
1+
use std::collections::{BTreeMap, HashSet};
22
use std::io::{self, Read, Seek};
33

44
use crate::tags::{
5-
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat,
6-
Tag, Type,
5+
CompressionMethod, IfdPointer, PhotometricInterpretation, PlanarConfiguration, Predictor,
6+
SampleFormat, Tag, Type,
77
};
88
use crate::{
9-
bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
9+
bytecast, ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError,
10+
UsageError,
1011
};
1112
use half::f16;
1213

13-
use self::ifd::Directory;
1414
use self::image::Image;
1515
use self::stream::{ByteOrder, EndianReader};
1616

@@ -263,9 +263,9 @@ where
263263
reader: EndianReader<R>,
264264
bigtiff: bool,
265265
limits: Limits,
266-
next_ifd: Option<u64>,
267-
ifd_offsets: Vec<u64>,
268-
seen_ifds: HashSet<u64>,
266+
next_ifd: Option<IfdPointer>,
267+
ifd_offsets: Vec<IfdPointer>,
268+
seen_ifds: HashSet<IfdPointer>,
269269
image: Image,
270270
}
271271

@@ -487,11 +487,13 @@ impl<R: Read + Seek> Decoder<R> {
487487
))
488488
}
489489
};
490+
490491
let next_ifd = if bigtiff {
491492
Some(reader.read_u64()?)
492493
} else {
493494
Some(u64::from(reader.read_u32()?))
494-
};
495+
}
496+
.map(IfdPointer);
495497

496498
let mut seen_ifds = HashSet::new();
497499
seen_ifds.insert(*next_ifd.as_ref().unwrap());
@@ -557,9 +559,9 @@ impl<R: Read + Seek> Decoder<R> {
557559

558560
loop {
559561
// Follow the list until we find the one we want, or we reach the end, whichever happens first
560-
let (_ifd, next_ifd) = self.next_ifd()?;
562+
let ifd = self.next_ifd()?;
561563

562-
if next_ifd.is_none() {
564+
if ifd.next().is_none() {
563565
break;
564566
}
565567

@@ -571,8 +573,7 @@ impl<R: Read + Seek> Decoder<R> {
571573

572574
// If the index is within the list of ifds then we can load the selected image/IFD
573575
if let Some(ifd_offset) = self.ifd_offsets.get(ifd_index) {
574-
let (ifd, _next_ifd) = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?;
575-
576+
let ifd = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?;
576577
self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?;
577578

578579
Ok(())
@@ -583,36 +584,35 @@ impl<R: Read + Seek> Decoder<R> {
583584
}
584585
}
585586

586-
fn next_ifd(&mut self) -> TiffResult<(Directory, Option<u64>)> {
587+
fn next_ifd(&mut self) -> TiffResult<Directory> {
587588
if self.next_ifd.is_none() {
588589
return Err(TiffError::FormatError(
589590
TiffFormatError::ImageFileDirectoryNotFound,
590591
));
591592
}
592593

593-
let (ifd, next_ifd) = Self::read_ifd(
594+
let ifd = Self::read_ifd(
594595
&mut self.reader,
595596
self.bigtiff,
596597
self.next_ifd.take().unwrap(),
597598
)?;
598599

599-
if let Some(next) = next_ifd {
600+
if let Some(next) = ifd.next() {
600601
if !self.seen_ifds.insert(next) {
601602
return Err(TiffError::FormatError(TiffFormatError::CycleInOffsets));
602603
}
603604
self.next_ifd = Some(next);
604605
self.ifd_offsets.push(next);
605606
}
606607

607-
Ok((ifd, next_ifd))
608+
Ok(ifd)
608609
}
609610

610611
/// Reads in the next image.
611612
/// If there is no further image in the TIFF file a format error is returned.
612613
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
613614
pub fn next_image(&mut self) -> TiffResult<()> {
614-
let (ifd, _next_ifd) = self.next_ifd()?;
615-
615+
let ifd = self.next_ifd()?;
616616
self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?;
617617
Ok(())
618618
}
@@ -734,6 +734,13 @@ impl<R: Read + Seek> Decoder<R> {
734734
self.reader.goto_offset(offset)
735735
}
736736

737+
/// Read a directory from a known offset.
738+
///
739+
/// This may always modify the position of the reader.
740+
pub fn read_directory(&mut self, ptr: IfdPointer) -> TiffResult<Directory> {
741+
Self::read_ifd(&mut self.reader, self.bigtiff, ptr)
742+
}
743+
737744
/// Reads a IFD entry.
738745
// An IFD entry has four fields:
739746
//
@@ -775,25 +782,27 @@ impl<R: Read + Seek> Decoder<R> {
775782
fn read_ifd(
776783
reader: &mut EndianReader<R>,
777784
bigtiff: bool,
778-
ifd_location: u64,
779-
) -> TiffResult<(Directory, Option<u64>)> {
780-
reader.goto_offset(ifd_location)?;
785+
ifd_location: IfdPointer,
786+
) -> TiffResult<Directory> {
787+
reader.goto_offset(ifd_location.0)?;
781788

782-
let mut dir: Directory = HashMap::new();
789+
let mut entries: BTreeMap<_, _> = BTreeMap::new();
783790

784791
let num_tags = if bigtiff {
785792
reader.read_u64()?
786793
} else {
787794
reader.read_u16()?.into()
788795
};
796+
789797
for _ in 0..num_tags {
790798
let (tag, entry) = match Self::read_entry(reader, bigtiff)? {
791799
Some(val) => val,
792800
None => {
793801
continue;
794802
} // Unknown data type in tag, skip
795803
};
796-
dir.insert(tag, entry);
804+
805+
entries.insert(tag.to_u16(), entry);
797806
}
798807

799808
let next_ifd = if bigtiff {
@@ -802,18 +811,15 @@ impl<R: Read + Seek> Decoder<R> {
802811
reader.read_u32()?.into()
803812
};
804813

805-
let next_ifd = match next_ifd {
806-
0 => None,
807-
_ => Some(next_ifd),
808-
};
814+
let next_ifd = core::num::NonZeroU64::new(next_ifd);
809815

810-
Ok((dir, next_ifd))
816+
Ok(Directory { entries, next_ifd })
811817
}
812818

813819
/// Tries to retrieve a tag.
814820
/// Return `Ok(None)` if the tag is not present.
815821
pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> {
816-
let entry = match self.image().ifd.as_ref().unwrap().get(&tag) {
822+
let entry = match self.image().ifd.as_ref().unwrap().get(tag) {
817823
None => return Ok(None),
818824
Some(entry) => entry.clone(),
819825
};
@@ -928,7 +934,7 @@ impl<R: Read + Seek> Decoder<R> {
928934
self.image.ifd.as_ref().unwrap().iter().map(|(tag, entry)| {
929935
entry
930936
.val(&self.limits, self.bigtiff, &mut self.reader)
931-
.map(|value| (*tag, value))
937+
.map(|value| (tag, value))
932938
})
933939
}
934940

src/decoder/tag_reader.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::io::{Read, Seek};
22

3-
use crate::tags::Tag;
3+
use crate::{tags::Tag, Directory};
44
use crate::{TiffError, TiffFormatError, TiffResult};
55

6-
use super::ifd::{Directory, Value};
6+
use super::ifd::Value;
77
use super::stream::EndianReader;
88
use super::Limits;
99

@@ -15,7 +15,7 @@ pub(crate) struct TagReader<'a, R: Read + Seek> {
1515
}
1616
impl<'a, R: Read + Seek> TagReader<'a, R> {
1717
pub(crate) fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<Value>> {
18-
Ok(match self.ifd.get(&tag) {
18+
Ok(match self.ifd.get(tag) {
1919
Some(entry) => Some(entry.clone().val(self.limits, self.bigtiff, self.reader)?),
2020
None => None,
2121
})

0 commit comments

Comments
 (0)