Simplify encoder writing of dictionary

This commit is contained in:
A. Molzer committed 2025-07-13 02:22:17 +02:00
1 parent 54f733eaa3
commit 525bb2b9b1
2 files changed
+65 -37

No files matched your search

+4
View File
@@ -371,6 +371,10 @@ impl Entry {
self.count self.count
} }
pub(crate) fn offset(&self) -> &[u8] {
&self.offset
}
/// Returns a mem_reader for the offset/value field /// Returns a mem_reader for the offset/value field
fn r(&self, byte_order: ByteOrder) -> EndianReader<io::Cursor<Vec<u8>>> { fn r(&self, byte_order: ByteOrder) -> EndianReader<io::Cursor<Vec<u8>>> {
EndianReader::new(io::Cursor::new(self.offset.to_vec()), byte_order) EndianReader::new(io::Cursor::new(self.offset.to_vec()), byte_order)
+61 -37
View File
@@ -10,9 +10,10 @@ use std::{
}; };
use crate::{ use crate::{
decoder::ifd::Entry,
error::{TiffResult, UsageError}, error::{TiffResult, UsageError},
tags::{CompressionMethod, ResolutionUnit, SampleFormat, Tag}, tags::{CompressionMethod, ResolutionUnit, SampleFormat, Tag, Type},
TiffError, TiffFormatError, Directory, TiffError, TiffFormatError,
}; };
pub mod colortype; pub mod colortype;
@@ -220,6 +221,7 @@ pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
/// 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>,
// 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,
ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>, ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>,
dropped: bool, dropped: bool,
} }
@@ -237,6 +239,7 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
writer, writer,
chained_ifd_pos, chained_ifd_pos,
write_chain: chain_into, write_chain: chain_into,
directory: Directory::empty(),
ifd: BTreeMap::new(), ifd: BTreeMap::new(),
dropped: false, dropped: false,
}) })
@@ -253,7 +256,7 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
self.ifd.insert( self.ifd.insert(
tag.to_u16(), tag.to_u16(),
DirectoryEntry { DirectoryEntry {
data_type: <T>::FIELD_TYPE.to_u16(), data_type: <T>::FIELD_TYPE,
count: value.count().try_into()?, count: value.count().try_into()?,
data: bytes, data: bytes,
}, },
@@ -263,48 +266,69 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
} }
fn write_directory(&mut self) -> TiffResult<u64> { fn write_directory(&mut self) -> TiffResult<u64> {
// Start by writing out all values // Start by turning all buffered unwritten values into entries.
for &mut DirectoryEntry { for (&tag, value) in &self.ifd {
data: ref mut bytes, let entry = Self::write_value(&mut self.writer, value)?;
.. self.directory.extend([(Tag::Unknown(tag), entry)]);
} in self.ifd.values_mut()
{
let data_bytes = mem::size_of::<K::OffsetType>();
if bytes.len() > data_bytes {
let offset = self.writer.offset();
self.writer.write_bytes(bytes)?;
*bytes = vec![0; data_bytes];
let mut writer = TiffWriter::new(bytes as &mut [u8]);
K::write_offset(&mut writer, offset)?;
} else {
while bytes.len() < data_bytes {
bytes.push(0);
}
}
} }
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.ifd.len())?; let offset_bytes = mem::size_of::<K::OffsetType>();
for ( for (tag, entry) in self.directory.iter() {
tag, self.writer.write_u16(tag.to_u16())?;
DirectoryEntry { self.writer.write_u16(entry.field_type().to_u16())?;
data_type: field_type, let count = K::convert_offset(entry.count())?;
count, count.write(self.writer)?;
data: offset, self.writer.write_bytes(&entry.offset()[..offset_bytes])?;
},
) in self.ifd.iter()
{
self.writer.write_u16(*tag)?;
self.writer.write_u16(*field_type)?;
(*count).write(self.writer)?;
self.writer.write_bytes(offset)?;
} }
Ok(offset) Ok(offset)
} }
fn write_value(
writer: &mut TiffWriter<W>,
value: &DirectoryEntry<K::OffsetType>,
) -> TiffResult<Entry> {
let &DirectoryEntry {
data: ref bytes,
ref count,
data_type,
} = value;
let in_entry_bytes = mem::size_of::<K::OffsetType>();
let mut offset_bytes = [0; 8];
if bytes.len() > in_entry_bytes {
let offset = writer.offset();
writer.write_bytes(&bytes)?;
let offset = K::convert_offset(offset)?;
offset_bytes[..offset.bytes()].copy_from_slice(&offset.data());
} else {
// Note: we have indicated our native byte order in the header, hence this
// corresponds to our byte order no matter the value type.
offset_bytes[..bytes.len()].copy_from_slice(&bytes);
}
// Undoing some hidden type. Offset is either u32 or u64. Due to the trait API being public
// and some oversight, we can not clone the `count: K::OffsetType` and thus not convert it.
// Instead, write it to a buffer...
let mut count_bytes = [0; 8];
// Nominally Cow but we only expect `Cow::Borrowed`.
count_bytes[..count.bytes()].copy_from_slice(&count.data());
Ok(if in_entry_bytes == 4 {
let count = u32::from_ne_bytes(count_bytes[..4].try_into().unwrap());
Entry::new(data_type, count, offset_bytes[..4].try_into().unwrap())
} else {
debug_assert_eq!(in_entry_bytes, 8);
let count = u64::from_ne_bytes(count_bytes);
Entry::new_u64(data_type, count, offset_bytes)
})
}
/// Write some data to the tiff file, the offset of the data is returned. /// Write some data to the tiff file, the offset of the data is returned.
/// ///
/// This could be used to write tiff strips. /// This could be used to write tiff strips.
@@ -653,7 +677,7 @@ impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W
} }
struct DirectoryEntry<S> { struct DirectoryEntry<S> {
data_type: u16, data_type: Type,
count: S, count: S,
data: Vec<u8>, data: Vec<u8>,
} }