pub use tiff_value::*;
use std::{
cmp,
io::{self, Seek, Write},
marker::PhantomData,
mem,
num::{NonZeroU64, TryFromIntError},
};
use crate::{
decoder::ifd::Entry,
error::{TiffResult, UsageError},
tags::{
ByteOrder, CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation,
ResolutionUnit, SampleFormat, Tag, Type, ValueBuffer,
},
Directory, TiffError, TiffFormatError,
};
pub mod colortype;
pub mod compression;
mod tiff_value;
mod writer;
use self::colortype::*;
use self::compression::Compression as Comp;
use self::compression::*;
use self::writer::*;
pub type Predictor = crate::tags::Predictor;
#[cfg(feature = "deflate")]
pub type DeflateLevel = compression::DeflateLevel;
#[derive(Clone, Copy, PartialEq, Default)]
pub enum Compression {
#[default]
Uncompressed,
#[cfg(feature = "lzw")]
Lzw,
#[cfg(feature = "deflate")]
Deflate(DeflateLevel),
Packbits,
}
impl Compression {
fn tag(&self) -> CompressionMethod {
match self {
Compression::Uncompressed => CompressionMethod::None,
#[cfg(feature = "lzw")]
Compression::Lzw => CompressionMethod::LZW,
#[cfg(feature = "deflate")]
Compression::Deflate(_) => CompressionMethod::Deflate,
Compression::Packbits => CompressionMethod::PackBits,
}
}
fn get_algorithm(&self) -> Compressor {
match self {
Compression::Uncompressed => compression::Uncompressed {}.get_algorithm(),
#[cfg(feature = "lzw")]
Compression::Lzw => compression::Lzw {}.get_algorithm(),
#[cfg(feature = "deflate")]
Compression::Deflate(level) => compression::Deflate::with_level(*level).get_algorithm(),
Compression::Packbits => compression::Packbits {}.get_algorithm(),
}
}
}
pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
writer: TiffWriter<W>,
kind: PhantomData<K>,
predictor: Predictor,
compression: Compression,
last_ifd_chain: NonZeroU64,
}
impl<W: Write + Seek> TiffEncoder<W> {
pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> {
TiffEncoder::new_generic(writer)
}
}
impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> {
pub fn new_big(writer: W) -> TiffResult<Self> {
TiffEncoder::new_generic(writer)
}
}
impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
pub fn new_generic(writer: W) -> TiffResult<Self> {
let mut writer = TiffWriter::new(writer);
K::write_header(&mut writer)?;
let last_ifd_chain = NonZeroU64::new(writer.previous_ifd_pointer::<K>())
.expect("Header is at a non-zero offset");
Ok(TiffEncoder {
writer,
kind: PhantomData,
predictor: Predictor::None,
compression: Compression::Uncompressed,
last_ifd_chain,
})
}
pub fn with_predictor(mut self, predictor: Predictor) -> Self {
self.predictor = predictor;
self
}
pub fn with_compression(mut self, compression: Compression) -> Self {
self.compression = compression;
self
}
#[deprecated = "`image_directory` replaced the old behavior and clarifies the intent"]
#[doc(hidden)]
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)
}
pub fn image_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)
}
pub fn extra_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
Self::unchained_directory(&mut self.writer)
}
pub fn new_image<C: ColorType>(
&mut self,
width: u32,
height: u32,
) -> TiffResult<ImageEncoder<'_, W, C, K>> {
let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?;
ImageEncoder::new(encoder, width, height, self.compression, self.predictor)
}
pub fn write_image<C: ColorType>(
&mut self,
width: u32,
height: u32,
data: &[C::Inner],
) -> TiffResult<()>
where
[C::Inner]: TiffValue,
{
let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?;
let image: ImageEncoder<W, C, K> =
ImageEncoder::new(encoder, width, height, self.compression, self.predictor)?;
image.write_data(data)
}
fn chain_directory<'lt>(
writer: &'lt mut TiffWriter<W>,
last_ifd_chain: &'lt mut NonZeroU64,
) -> TiffResult<DirectoryEncoder<'lt, W, K>> {
let last_ifd = *last_ifd_chain;
DirectoryEncoder::new(writer, Some(last_ifd), Some(last_ifd_chain))
}
fn unchained_directory(writer: &mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'_, W, K>> {
DirectoryEncoder::new(writer, None, None)
}
}
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
writer: &'a mut TiffWriter<W>,
chained_ifd_pos: Option<NonZeroU64>,
write_chain: Option<&'a mut NonZeroU64>,
kind: PhantomData<K>,
directory: Directory,
dropped: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DirectoryOffset<K: TiffKind> {
pub offset: K::OffsetType,
pub pointer: IfdPointer,
ifd_chain: NonZeroU64,
kind: PhantomData<K>,
}
impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
fn new(
writer: &'a mut TiffWriter<W>,
chained_ifd_pos: Option<NonZeroU64>,
chain_into: Option<&'a mut NonZeroU64>,
) -> TiffResult<Self> {
writer.pad_word_boundary()?; Ok(Self {
writer,
chained_ifd_pos,
write_chain: chain_into,
kind: PhantomData,
directory: Directory::empty(),
dropped: false,
})
}
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
let mut bytes = Vec::with_capacity(value.bytes());
{
let mut writer = TiffWriter::new(&mut bytes);
value.write(&mut writer)?;
}
let entry = Self::write_entry_inner(
self.writer,
DirectoryEntry {
data_type: <T>::FIELD_TYPE,
count: value.count().try_into()?,
data: bytes.into(),
},
)?;
self.directory.extend([(tag, entry)]);
Ok(())
}
pub fn write_tag_buf(&mut self, tag: Tag, value: &ValueBuffer) -> TiffResult<()> {
let entry = self.write_entry_buf(value)?;
self.directory.extend([(tag, entry)]);
Ok(())
}
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
let offset = self.writer.offset();
value.write(self.writer)?;
Ok(offset)
}
pub fn write_data_buf(&mut self, value: &ValueBuffer) -> TiffResult<u64> {
self.check_value_byteorder(value)?;
let offset = self.writer.offset();
self.writer.write_bytes(value.as_bytes())?;
Ok(offset)
}
pub fn write_entry<T: TiffValue>(&mut self, value: T) -> TiffResult<Entry> {
Self::write_entry_inner(
self.writer,
DirectoryEntry {
data_type: T::FIELD_TYPE,
count: K::convert_offset(value.count() as u64)?,
data: value.data(),
},
)
}
pub fn write_entry_buf(&mut self, value: &ValueBuffer) -> TiffResult<Entry> {
self.check_value_byteorder(value)?;
Self::write_entry_inner(
self.writer,
DirectoryEntry {
data_type: value.data_type(),
count: K::convert_offset(value.count())?,
data: value.as_bytes().into(),
},
)
}
pub fn write_entry_bytes(&mut self, ty: Type, data: &[u8]) -> TiffResult<Entry> {
if data.len() % usize::from(ty.byte_len()) != 0 {
return Err(TiffError::UsageError(UsageError::MismatchedEntryLength {
ty,
found: data.len(),
}));
}
let count = data.len() / usize::from(ty.byte_len());
let count = u64::try_from(count)?;
Self::write_entry_inner(
self.writer,
DirectoryEntry {
data_type: ty,
count: K::convert_offset(count)?,
data: data.into(),
},
)
}
pub fn extend_from(&mut self, dir: &Directory) {
let entries = dir.iter().map(|(tag, val)| (tag, val.clone()));
self.directory.extend(entries);
}
pub fn set_parent(&mut self, offset: &DirectoryOffset<K>) {
self.chained_ifd_pos = Some(offset.ifd_chain);
}
pub fn finish(mut self) -> TiffResult<()> {
self.finish_internal()?;
Ok(())
}
pub fn finish_with_offsets(mut self) -> TiffResult<DirectoryOffset<K>> {
self.finish_internal()
}
fn write_directory(&mut self) -> TiffResult<u64> {
let offset = self.writer.offset();
K::write_entry_count(self.writer, self.directory.len())?;
let offset_bytes = mem::size_of::<K::OffsetType>();
for (tag, entry) in self.directory.iter() {
self.writer.write_u16(tag.to_u16())?;
self.writer.write_u16(entry.field_type().to_u16())?;
let count = K::convert_offset(entry.count())?;
count.write(self.writer)?;
self.writer.write_bytes(&entry.offset()[..offset_bytes])?;
}
Ok(offset)
}
fn write_entry_inner(
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 {
offset_bytes[..bytes.len()].copy_from_slice(bytes);
}
let mut count_bytes = [0; 8];
debug_assert!(in_entry_bytes == 4 || in_entry_bytes == 8);
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)
})
}
fn check_value_byteorder(&self, value: &ValueBuffer) -> TiffResult<()> {
if value.byte_order() != ByteOrder::native() {
Err(TiffError::UsageError(UsageError::ByteOrderMismatch))
} else {
Ok(())
}
}
fn last_written(&self) -> u64 {
self.writer.last_written()
}
fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> {
let ifd_pointer = self.write_directory()?;
let offset = K::convert_offset(ifd_pointer)?;
if let Some(prior) = self.chained_ifd_pos {
let curr_pos = self.writer.offset();
self.writer.goto_offset(prior.get())?;
K::write_offset(self.writer, ifd_pointer)?;
self.writer.goto_offset(curr_pos)?;
}
K::write_offset(self.writer, 0)?;
let ifd_chain = NonZeroU64::new(self.writer.previous_ifd_pointer::<K>())
.expect("IFD chain field is at a non-zero offset");
if let Some(prior) = self.write_chain.take() {
*prior = ifd_chain;
}
self.dropped = true;
Ok(DirectoryOffset {
pointer: IfdPointer(ifd_pointer),
offset,
ifd_chain,
kind: PhantomData,
})
}
}
impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
}
}
}
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType, K: TiffKind> {
encoder: DirectoryEncoder<'a, W, K>,
strip_idx: u64,
strip_count: u64,
row_samples: u64,
width: u32,
height: u32,
extra_samples: Vec<u16>,
rows_per_strip: u64,
strip_offsets: Vec<K::OffsetType>,
strip_byte_count: Vec<K::OffsetType>,
dropped: bool,
compression: Compression,
predictor: Predictor,
_phantom: ::std::marker::PhantomData<C>,
}
impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, K> {
fn sanity_check(compression: Compression, predictor: Predictor) -> TiffResult<()> {
match (predictor, compression, T::SAMPLE_FORMAT[0]) {
(Predictor::Horizontal, _, SampleFormat::IEEEFP | SampleFormat::Void) => {
Err(TiffError::UsageError(UsageError::PredictorIncompatible))
}
(Predictor::FloatingPoint, _, _) => {
Err(TiffError::UsageError(UsageError::PredictorUnavailable))
}
_ => Ok(()),
}
}
fn new(
mut encoder: DirectoryEncoder<'a, W, K>,
width: u32,
height: u32,
compression: Compression,
predictor: Predictor,
) -> TiffResult<Self> {
if width == 0 || height == 0 {
return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
width, height,
)));
}
Self::sanity_check(compression, predictor)?;
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
let rows_per_strip = {
match compression.tag() {
CompressionMethod::PackBits => 1, _ => 1_000_000_u64.div_ceil(row_bytes),
}
};
let strip_count = u64::from(height).div_ceil(rows_per_strip);
encoder.write_tag(Tag::ImageWidth, width)?;
encoder.write_tag(Tag::ImageLength, height)?;
encoder.write_tag(Tag::Compression, compression.tag().to_u16())?;
encoder.write_tag(Tag::Predictor, predictor.to_u16())?;
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
if matches!(<T>::TIFF_VALUE, PhotometricInterpretation::YCbCr) {
encoder.write_tag(Tag::ChromaSubsampling, &[1u16, 1u16][..])?;
}
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
encoder.write_tag(
Tag::SamplesPerPixel,
u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
)?;
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None)?;
Ok(ImageEncoder {
encoder,
strip_count,
strip_idx: 0,
row_samples,
rows_per_strip,
extra_samples: vec![],
width,
height,
strip_offsets: Vec::new(),
strip_byte_count: Vec::new(),
dropped: false,
compression,
predictor,
_phantom: ::std::marker::PhantomData,
})
}
pub fn extra_samples(&mut self, extra: &[ExtraSamples]) -> Result<(), TiffError> {
if self.strip_idx != 0 {
return Err(TiffError::UsageError(
UsageError::ReconfiguredAfterImageWrite,
));
}
let samples = self.extra_samples.len() + extra.len() + <T>::BITS_PER_SAMPLE.len();
let row_samples = u64::from(self.width) * u64::try_from(samples)?;
self.extra_samples
.extend(extra.iter().map(ExtraSamples::to_u16));
self.encoder
.write_tag(Tag::ExtraSamples, &self.extra_samples[..])?;
self.row_samples = row_samples;
Ok(())
}
pub fn next_strip_sample_count(&self) -> u64 {
if self.strip_idx >= self.strip_count {
return 0;
}
let raw_start_row = self.strip_idx * self.rows_per_strip;
let start_row = cmp::min(u64::from(self.height), raw_start_row);
let end_row = cmp::min(u64::from(self.height), raw_start_row + self.rows_per_strip);
(end_row - start_row) * self.row_samples
}
pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()>
where
[T::Inner]: TiffValue,
{
let samples = self.next_strip_sample_count();
if u64::try_from(value.len())? != samples {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Slice is wrong size for strip",
)
.into());
}
let offset = match self.predictor {
Predictor::None => self.encoder.write_data(value)?,
Predictor::Horizontal => {
let mut row_result = Vec::with_capacity(value.len());
for row in value.chunks_exact(self.row_samples as usize) {
T::horizontal_predict(row, &mut row_result);
}
self.encoder.write_data(row_result.as_slice())?
}
_ => unimplemented!(),
};
let byte_count = self.encoder.last_written() as usize;
self.strip_offsets.push(K::convert_offset(offset)?);
self.strip_byte_count.push(byte_count.try_into()?);
self.strip_idx += 1;
Ok(())
}
pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
where
[T::Inner]: TiffValue,
{
let num_pix = usize::try_from(self.width)?
.checked_mul(usize::try_from(self.height)?)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Image width * height exceeds usize",
)
})?;
if data.len() < num_pix {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Input data slice is undersized for provided dimensions",
)
.into());
}
self.encoder
.writer
.set_compression(self.compression.get_algorithm());
let mut idx = 0;
while self.next_strip_sample_count() > 0 {
let sample_count = usize::try_from(self.next_strip_sample_count())?;
self.write_strip(&data[idx..idx + sample_count])?;
idx += sample_count;
}
self.encoder.writer.reset_compression();
self.finish()?;
Ok(())
}
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
self.encoder
.write_tag(Tag::ResolutionUnit, unit.to_u16())
.unwrap();
self.encoder
.write_tag(Tag::XResolution, value.clone())
.unwrap();
self.encoder.write_tag(Tag::YResolution, value).unwrap();
}
pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
self.encoder
.write_tag(Tag::ResolutionUnit, unit.to_u16())
.unwrap();
}
pub fn x_resolution(&mut self, value: Rational) {
self.encoder.write_tag(Tag::XResolution, value).unwrap();
}
pub fn y_resolution(&mut self, value: Rational) {
self.encoder.write_tag(Tag::YResolution, value).unwrap();
}
pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
if self.strip_idx != 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Cannot change strip size after data was written",
)
.into());
}
self.encoder.write_tag(Tag::RowsPerStrip, value)?;
let value: u64 = value as u64;
self.strip_count = (self.height as u64).div_ceil(value);
self.rows_per_strip = value;
Ok(())
}
fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> {
if self.extra_samples.is_empty() {
self.encoder
.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
} else {
let mut sample_format: Vec<_> = <T>::BITS_PER_SAMPLE.to_vec();
let replicated =
core::iter::repeat_n(<T>::BITS_PER_SAMPLE[0], self.extra_samples.len());
sample_format.extend(replicated);
self.encoder
.write_tag(Tag::BitsPerSample, &sample_format[..])?;
self.encoder.write_tag(
Tag::SamplesPerPixel,
u16::try_from(<T>::BITS_PER_SAMPLE.len() + self.extra_samples.len())?,
)?;
}
let mut sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
let extra_format = sample_format
.first()
.copied()
.unwrap_or(SampleFormat::Void.to_u16());
sample_format.extend(core::iter::repeat_n(extra_format, self.extra_samples.len()));
self.encoder
.write_tag(Tag::SampleFormat, &sample_format[..])?;
self.encoder
.write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?;
self.encoder.write_tag(
Tag::StripByteCounts,
K::convert_slice(&self.strip_byte_count),
)?;
self.dropped = true;
self.encoder.finish_internal()
}
pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> {
&mut self.encoder
}
pub fn finish(mut self) -> TiffResult<()> {
self.finish_internal()?;
Ok(())
}
}
impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W, C, K> {
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
}
}
}
struct DirectoryEntry<'data, S> {
data_type: Type,
count: S,
data: std::borrow::Cow<'data, [u8]>,
}
pub trait TiffKind {
type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue;
type OffsetArrayType: ?Sized + TiffValue;
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>;
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>;
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>;
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType;
}
impl<K: TiffKind> DirectoryOffset<K> {
pub fn new(pointer: IfdPointer, dir: &Directory) -> TiffResult<Self> {
let offset = K::convert_offset(pointer.0)?;
let encoded = dir.encoded_len::<K>();
let offset_field_offset = encoded - mem::size_of_val(&offset) as u64;
let ifd_chain = pointer
.0
.checked_add(offset_field_offset)
.and_then(NonZeroU64::new)
.ok_or(TiffError::UsageError(UsageError::ZeroIfdPointer))?;
Ok(DirectoryOffset {
pointer,
offset,
ifd_chain,
kind: PhantomData,
})
}
}
pub struct TiffKindStandard;
impl TiffKind for TiffKindStandard {
type OffsetType = u32;
type OffsetArrayType = [u32];
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
write_tiff_header(writer)?;
writer.write_u32(0)?;
Ok(())
}
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
Ok(Self::OffsetType::try_from(offset)?)
}
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
writer.write_u32(u32::try_from(offset)?)?;
Ok(())
}
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
writer.write_u16(u16::try_from(count)?)?;
Ok(())
}
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
slice
}
}
pub struct TiffKindBig;
impl TiffKind for TiffKindBig {
type OffsetType = u64;
type OffsetArrayType = [u64];
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
write_bigtiff_header(writer)?;
writer.write_u64(0)?;
Ok(())
}
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
Ok(offset)
}
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
writer.write_u64(offset)?;
Ok(())
}
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
writer.write_u64(u64::try_from(count)?)?;
Ok(())
}
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
slice
}
}
#[test]
fn directory_offset_new_equivalent_to_writing() {
type K = TiffKindStandard;
let dir = Directory::from_iter(vec![
(
Tag::ImageWidth,
Entry::new(Type::LONG, 1, 100u32.to_ne_bytes()),
),
(
Tag::ImageLength,
Entry::new(Type::LONG, 1, 200u32.to_ne_bytes()),
),
]);
let data = std::io::Cursor::new(vec![]);
let mut file = TiffEncoder::new(data).unwrap();
let mut as_dir_encoder = file.extra_directory().unwrap();
as_dir_encoder.extend_from(&dir);
let dir_extent = as_dir_encoder.finish_with_offsets().unwrap();
let synth = DirectoryOffset::<K>::new(dir_extent.pointer, &dir).unwrap();
assert_eq!(synth.pointer, dir_extent.pointer);
assert_eq!(synth.offset, dir_extent.offset);
assert_eq!(synth.ifd_chain, dir_extent.ifd_chain);
}