pub use tiff_value::*;
use std::{
cmp,
collections::BTreeMap,
convert::{TryFrom, TryInto},
io::{self, Seek, Write},
marker::PhantomData,
mem,
num::TryFromIntError,
};
use crate::{
error::TiffResult,
tags::{CompressionMethod, ResolutionUnit, Tag},
TiffError, TiffFormatError,
};
pub mod colortype;
pub mod compression;
mod tiff_value;
mod writer;
use self::colortype::*;
use self::compression::*;
use self::writer::*;
pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
writer: TiffWriter<W>,
kind: PhantomData<K>,
}
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 encoder = TiffEncoder {
writer: TiffWriter::new(writer),
kind: PhantomData,
};
K::write_header(&mut encoder.writer)?;
Ok(encoder)
}
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W, K>> {
DirectoryEncoder::new(&mut self.writer)
}
pub fn new_image<C: ColorType>(
&mut self,
width: u32,
height: u32,
) -> TiffResult<ImageEncoder<W, C, K, Uncompressed>> {
let encoder = DirectoryEncoder::new(&mut self.writer)?;
ImageEncoder::new(encoder, width, height)
}
pub fn new_image_with_compression<C: ColorType, D: Compression>(
&mut self,
width: u32,
height: u32,
compression: D,
) -> TiffResult<ImageEncoder<W, C, K, D>> {
let encoder = DirectoryEncoder::new(&mut self.writer)?;
ImageEncoder::with_compression(encoder, width, height, compression)
}
pub fn write_image<C: ColorType>(
&mut self,
width: u32,
height: u32,
data: &[C::Inner],
) -> TiffResult<()>
where
[C::Inner]: TiffValue,
{
let encoder = DirectoryEncoder::new(&mut self.writer)?;
let image: ImageEncoder<W, C, K> = ImageEncoder::new(encoder, width, height)?;
image.write_data(data)
}
pub fn write_image_with_compression<C: ColorType, D: Compression>(
&mut self,
width: u32,
height: u32,
compression: D,
data: &[C::Inner],
) -> TiffResult<()>
where
[C::Inner]: TiffValue,
{
let encoder = DirectoryEncoder::new(&mut self.writer)?;
let image: ImageEncoder<W, C, K, D> =
ImageEncoder::with_compression(encoder, width, height, compression)?;
image.write_data(data)
}
}
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
writer: &'a mut TiffWriter<W>,
dropped: bool,
ifd_pointer_pos: u64,
ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>,
}
impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<Self> {
let ifd_pointer_pos = writer.offset() - mem::size_of::<K::OffsetType>() as u64;
writer.pad_word_boundary()?; Ok(DirectoryEncoder {
writer,
dropped: false,
ifd_pointer_pos,
ifd: BTreeMap::new(),
})
}
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)?;
}
self.ifd.insert(
tag.to_u16(),
DirectoryEntry {
data_type: <T>::FIELD_TYPE.to_u16(),
count: value.count().try_into()?,
data: bytes,
},
);
Ok(())
}
fn write_directory(&mut self) -> TiffResult<u64> {
for &mut DirectoryEntry {
data: ref mut bytes,
..
} 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();
K::write_entry_count(self.writer, self.ifd.len())?;
for (
tag,
DirectoryEntry {
data_type: field_type,
count,
data: offset,
},
) 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)
}
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
let offset = self.writer.offset();
value.write(self.writer)?;
Ok(offset)
}
fn last_written(&self) -> u64 {
self.writer.last_written()
}
fn finish_internal(&mut self) -> TiffResult<()> {
let ifd_pointer = self.write_directory()?;
let curr_pos = self.writer.offset();
self.writer.goto_offset(self.ifd_pointer_pos)?;
K::write_offset(self.writer, ifd_pointer)?;
self.writer.goto_offset(curr_pos)?;
K::write_offset(self.writer, 0)?;
self.dropped = true;
Ok(())
}
pub fn finish(mut self) -> TiffResult<()> {
self.finish_internal()
}
}
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,
D: Compression = Uncompressed,
> {
encoder: DirectoryEncoder<'a, W, K>,
strip_idx: u64,
strip_count: u64,
row_samples: u64,
width: u32,
height: u32,
rows_per_strip: u64,
strip_offsets: Vec<K::OffsetType>,
strip_byte_count: Vec<K::OffsetType>,
dropped: bool,
compression: D,
_phantom: ::std::marker::PhantomData<C>,
}
impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind, D: Compression>
ImageEncoder<'a, W, T, K, D>
{
fn new(encoder: DirectoryEncoder<'a, W, K>, width: u32, height: u32) -> TiffResult<Self>
where
D: Default,
{
Self::with_compression(encoder, width, height, D::default())
}
fn with_compression(
mut encoder: DirectoryEncoder<'a, W, K>,
width: u32,
height: u32,
compression: D,
) -> TiffResult<Self> {
if width == 0 || height == 0 {
return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
width, height,
)));
}
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 D::COMPRESSION_METHOD {
CompressionMethod::PackBits => 1, _ => (1_000_000 + row_bytes - 1) / row_bytes,
}
};
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
encoder.write_tag(Tag::ImageWidth, width)?;
encoder.write_tag(Tag::ImageLength, height)?;
encoder.write_tag(Tag::Compression, D::COMPRESSION_METHOD.to_u16())?;
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
let sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
encoder.write_tag(Tag::SampleFormat, &sample_format[..])?;
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
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.to_u16())?;
Ok(ImageEncoder {
encoder,
strip_count,
strip_idx: 0,
row_samples,
rows_per_strip,
width,
height,
strip_offsets: Vec::new(),
strip_byte_count: Vec::new(),
dropped: false,
compression,
_phantom: ::std::marker::PhantomData,
})
}
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 = self.encoder.write_data(value)?;
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 + value - 1) / value;
self.rows_per_strip = value;
Ok(())
}
fn finish_internal(&mut self) -> TiffResult<()> {
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()
}
}
impl<'a, W: Write + Seek, C: ColorType, K: TiffKind, D: Compression> Drop
for ImageEncoder<'a, W, C, K, D>
{
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
}
}
}
struct DirectoryEntry<S> {
data_type: u16,
count: S,
data: Vec<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;
}
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
}
}