Encode values immediately

This currently avoids buffering the collection, but not buffering them
individually where we still format it into a byte vector.

The main drive here is making the DirectoryEncoder less dependent on the
values. That is, it should be concerned with the Entry representation of
values only. A few decoders expect all values to strictly follow the
image directory and the data layout of such files should be controllable
by the user. This could be achieved by encoding dummy entries which are
later corrected once the actual item is known by seeking back and
writing the directory anew.

By no longer assuming values to be available on finishing a directory we
also prepare an interface that encodes an existing directory.
This commit is contained in:
A. Molzer committed 2025-07-13 03:06:40 +02:00
1 parent 525bb2b9b1
commit f6e2aeffe5
2 files changed
+13 -15

No files matched your search

+11 -13
View File
@@ -2,7 +2,6 @@ pub use tiff_value::*;
use std::{ use std::{
cmp, cmp,
collections::BTreeMap,
io::{self, Seek, Write}, io::{self, Seek, Write},
marker::PhantomData, marker::PhantomData,
mem, mem,
@@ -220,9 +219,9 @@ pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
chained_ifd_pos: Option<NonZeroU64>, chained_ifd_pos: Option<NonZeroU64>,
/// An output to write the `next` field offset on completion. /// An output to write the `next` field offset on completion.
write_chain: Option<&'a mut NonZeroU64>, write_chain: Option<&'a mut NonZeroU64>,
kind: PhantomData<K>,
// We use BTreeMap to make sure tags are written in correct order // We use BTreeMap to make sure tags are written in correct order
directory: Directory, directory: Directory,
ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>,
dropped: bool, dropped: bool,
} }
@@ -235,43 +234,42 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
chain_into: Option<&'a mut NonZeroU64>, chain_into: Option<&'a mut NonZeroU64>,
) -> TiffResult<Self> { ) -> TiffResult<Self> {
writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff? writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff?
Ok(DirectoryEncoder { Ok(Self {
writer, writer,
chained_ifd_pos, chained_ifd_pos,
write_chain: chain_into, write_chain: chain_into,
kind: PhantomData,
directory: Directory::empty(), directory: Directory::empty(),
ifd: BTreeMap::new(),
dropped: false, dropped: false,
}) })
} }
/// Write a single ifd tag. /// Write a single ifd tag.
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> { pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
// Encodes the value if necessary. In the current bytes all integers are taken as native
// endian and thus transparent but this keeps the interface generic.
let mut bytes = Vec::with_capacity(value.bytes()); let mut bytes = Vec::with_capacity(value.bytes());
{ {
let mut writer = TiffWriter::new(&mut bytes); let mut writer = TiffWriter::new(&mut bytes);
value.write(&mut writer)?; value.write(&mut writer)?;
} }
self.ifd.insert( let entry = Self::write_value(
tag.to_u16(), &mut self.writer,
DirectoryEntry { &DirectoryEntry {
data_type: <T>::FIELD_TYPE, data_type: <T>::FIELD_TYPE,
count: value.count().try_into()?, count: value.count().try_into()?,
data: bytes, data: bytes,
}, },
); )?;
self.directory.extend([(tag, entry)]);
Ok(()) Ok(())
} }
fn write_directory(&mut self) -> TiffResult<u64> { fn write_directory(&mut self) -> TiffResult<u64> {
// Start by turning all buffered unwritten values into entries. // Start by turning all buffered unwritten values into entries.
for (&tag, value) in &self.ifd {
let entry = Self::write_value(&mut self.writer, value)?;
self.directory.extend([(Tag::Unknown(tag), entry)]);
}
let offset = self.writer.offset(); let offset = self.writer.offset();
K::write_entry_count(self.writer, self.directory.len())?; K::write_entry_count(self.writer, self.directory.len())?;
+2 -2
View File
@@ -57,7 +57,7 @@ fn encode_decode() {
), ),
(Tag::Compression, ifd::Value::Short(1)), (Tag::Compression, ifd::Value::Short(1)),
(Tag::PhotometricInterpretation, ifd::Value::Short(2)), (Tag::PhotometricInterpretation, ifd::Value::Short(2)),
(Tag::StripOffsets, ifd::Value::Unsigned(8)), (Tag::StripOffsets, ifd::Value::Unsigned(47)),
(Tag::SamplesPerPixel, ifd::Value::Short(3)), (Tag::SamplesPerPixel, ifd::Value::Short(3)),
(Tag::RowsPerStrip, ifd::Value::Unsigned(3334)), (Tag::RowsPerStrip, ifd::Value::Unsigned(3334)),
(Tag::StripByteCounts, ifd::Value::Unsigned(30000)), (Tag::StripByteCounts, ifd::Value::Unsigned(30000)),
@@ -137,7 +137,7 @@ fn encode_decode_big() {
), ),
(Tag::Compression, ifd::Value::Short(1)), (Tag::Compression, ifd::Value::Short(1)),
(Tag::PhotometricInterpretation, ifd::Value::Short(2)), (Tag::PhotometricInterpretation, ifd::Value::Short(2)),
(Tag::StripOffsets, ifd::Value::UnsignedBig(16)), (Tag::StripOffsets, ifd::Value::UnsignedBig(27)),
(Tag::SamplesPerPixel, ifd::Value::Short(3)), (Tag::SamplesPerPixel, ifd::Value::Short(3)),
(Tag::RowsPerStrip, ifd::Value::Unsigned(3334)), (Tag::RowsPerStrip, ifd::Value::Unsigned(3334)),
(Tag::StripByteCounts, ifd::Value::UnsignedBig(30000)), (Tag::StripByteCounts, ifd::Value::UnsignedBig(30000)),