Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 97 additions & 11 deletions src/encoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use crate::{
decoder::ifd::Entry,
error::{TiffResult, UsageError},
tags::{
CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation, ResolutionUnit,
SampleFormat, Tag, Type,
ByteOrder, CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation,
ResolutionUnit, SampleFormat, Tag, Type, ValueBuffer,
},
Directory, TiffError, TiffFormatError,
};
Expand Down Expand Up @@ -291,6 +291,21 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
})
}

/// Write a tag-value pair with prepared byte data.
///
/// Note that the library will _not_ attempt to verify that the data type or the count of the
/// buffered value is permissible for the given tag. The data will be associated with the tag
/// exactly as-is.
///
/// An error is returned if the byte order of the presented value does not match the byte order
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
pub fn write_tag_value(&mut self, tag: Tag, value: &ValueBuffer) -> TiffResult<()> {
let entry = self.write_entry_buf(value)?;
self.directory.extend([(tag, entry)]);

Ok(())
}

/// Write a single ifd tag.
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
Expand All @@ -301,12 +316,12 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
value.write(&mut writer)?;
}

let entry = Self::write_value(
let entry = Self::write_entry_inner(
self.writer,
&DirectoryEntry {
DirectoryEntry {
data_type: <T>::FIELD_TYPE,
count: value.count().try_into()?,
data: bytes,
data: bytes.into(),
},
)?;

Expand All @@ -317,13 +332,70 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {

/// 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. All of the data will be written into the file as a
/// slice of bytes. It can not be used as an entry in a directory directly, where very short
/// values are instead represented in the offset field directly. Use [`Self::write_entry`]
/// instead.
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
let offset = self.writer.offset();
value.write(self.writer)?;
Ok(offset)
}

/// Write some data directly to the tiff file, the offset of the data is returned.
///
/// All of the data will be written into the file as a slice of bytes. It can not be used as an
/// entry in a directory directly, where very short values are instead represented in the
/// offset field directly. Use [`Self::write_entry_buf`] instead.
///
/// An error is returned if the byte order of the presented value does not match the byte order
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
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)
}

/// Write a directory value into the file.
///
/// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
/// stored in the entry's offset field.
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)?,
// FIXME: not optimal we always allocate here. But the only other method we have
// available is `T::write(&mut TiffWriter<W>)` and we must pass that into
// `write_entry_inner` but then have a combinatorial explosion of instantiations
// which is absurd.
data: value.data(),
},
)
}

/// Write a directory value into the file.
///
/// An error is returned if the byte order of the presented value does not match the byte order
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
///
/// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
/// stored in the entry's offset field.
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(),
},
)
}

/// Insert previously written key-value entries.
///
/// It is the caller's responsibility to ensure that the data referred to by the entries is
Expand Down Expand Up @@ -377,17 +449,21 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
Ok(offset)
}

fn write_value(
// Does it really make sense that this would be a (potentially) compressible write? For actual
// directory values their size must match the data written so there's going to be a silent
// problem if that actually occurs.
fn write_entry_inner(
writer: &mut TiffWriter<W>,
value: &DirectoryEntry<K::OffsetType>,
value: DirectoryEntry<K::OffsetType>,
) -> TiffResult<Entry> {
let &DirectoryEntry {
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 {
Expand All @@ -406,6 +482,7 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
// 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];
debug_assert!(in_entry_bytes == 4 || in_entry_bytes == 8);
// Nominally Cow but we only expect `Cow::Borrowed`.
count_bytes[..count.bytes()].copy_from_slice(&count.data());

Expand All @@ -419,6 +496,15 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
})
}

fn check_value_byteorder(&self, value: &ValueBuffer) -> TiffResult<()> {
// FIXME: we should enable writing files with any chosen byte order.
if value.byte_order() != ByteOrder::native() {
Err(TiffError::UsageError(UsageError::ByteOrderMismatch))
} else {
Ok(())
}
}

/// Provides the number of bytes written by the underlying TiffWriter during the last call.
fn last_written(&self) -> u64 {
self.writer.last_written()
Expand Down Expand Up @@ -824,10 +910,10 @@ impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W
}
}

struct DirectoryEntry<S> {
struct DirectoryEntry<'data, S> {
data_type: Type,
count: S,
data: Vec<u8>,
data: std::borrow::Cow<'data, [u8]>,
}

/// Trait to abstract over Tiff/BigTiff differences.
Expand Down
5 changes: 5 additions & 0 deletions src/encoder/tiff_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ use super::writer::TiffWriter;
pub trait TiffValue {
const BYTE_LEN: u8;
const FIELD_TYPE: Type;

// FIXME: If we want this to mean 'count' in the sense of `Entry` it should return `u32` or
// `u64`, probably the latter with `convert_offset` taking care of the check against `u32::MAX`
// for classic tiff.
fn count(&self) -> usize;

fn bytes(&self) -> usize {
self.count() * usize::from(Self::BYTE_LEN)
}
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ quick_error! {
ReconfiguredAfterImageWrite {
display("attempted to reconfigure the encoder after image writing has started")
}
ByteOrderMismatch {
display("attempted to use data with a byte order that does not match the required one")
}
}
}

Expand Down
67 changes: 67 additions & 0 deletions src/tags.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::encoder::TiffValue;

macro_rules! tags {
{
// Permit arbitrary meta items, which include documentation.
Expand Down Expand Up @@ -336,6 +338,71 @@ pub enum ExtraSamples(u16) {
}
}

/// A value represented as in-memory bytes with flexible byteorder.
pub struct ValueBuffer {
/// The raw bytes of the value.
bytes: Vec<u8>,

/// The type of the value.
ty: Type,

/// The byte order of the value.
byte_order: ByteOrder,
}

impl ValueBuffer {
/// A value with a count of zero.
///
/// The byte order is set to the native byte order of the platform.
pub fn empty(ty: Type) -> Self {
ValueBuffer {
bytes: vec![],
ty,
byte_order: ByteOrder::native(),
}
}

/// Create a value with native byte order from in-memory data.
pub fn from_value<T: TiffValue>(value: &T) -> Self {
ValueBuffer {
bytes: value.data().into_owned(),
ty: <T as TiffValue>::FIELD_TYPE,
byte_order: ByteOrder::native(),
}
}

pub fn byte_order(&self) -> ByteOrder {
self.byte_order
}

pub fn data_type(&self) -> Type {
self.ty
}

/// The count of items in the value.
pub fn count(&self) -> u64 {
(self.bytes.len() / usize::from(self.ty.byte_len())) as u64
}

/// View the underlying raw bytes of this value.
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}

/// View the underlying mutable raw bytes of this value.
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.bytes
}

/// Change the byte order of the value representation.
pub fn set_byte_order(&mut self, byte_order: ByteOrder) {
self.byte_order
.convert(self.ty, self.bytes.as_mut_slice(), byte_order);

self.byte_order = byte_order;
}
}

/// Byte order of the TIFF file.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ByteOrder {
Expand Down
Loading