Merge pull request #324 from image-rs/encoder-tiff-value
A byte-valued endian-formatted tiff value buffer
This commit is contained in:
4 files changed
+172
-11
No files matched your search
+97
-11
@@ -12,8 +12,8 @@ use crate::{
|
|||||||
decoder::ifd::Entry,
|
decoder::ifd::Entry,
|
||||||
error::{TiffResult, UsageError},
|
error::{TiffResult, UsageError},
|
||||||
tags::{
|
tags::{
|
||||||
CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation, ResolutionUnit,
|
ByteOrder, CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation,
|
||||||
SampleFormat, Tag, Type,
|
ResolutionUnit, SampleFormat, Tag, Type, ValueBuffer,
|
||||||
},
|
},
|
||||||
Directory, TiffError, TiffFormatError,
|
Directory, TiffError, TiffFormatError,
|
||||||
};
|
};
|
||||||
@@ -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.
|
/// 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
|
// Encodes the value if necessary. In the current bytes all integers are taken as native
|
||||||
@@ -301,12 +316,12 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
|||||||
value.write(&mut writer)?;
|
value.write(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = Self::write_value(
|
let entry = Self::write_entry_inner(
|
||||||
self.writer,
|
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.into(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -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.
|
/// 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> {
|
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
|
||||||
let offset = self.writer.offset();
|
let offset = self.writer.offset();
|
||||||
value.write(self.writer)?;
|
value.write(self.writer)?;
|
||||||
Ok(offset)
|
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.
|
/// Insert previously written key-value entries.
|
||||||
///
|
///
|
||||||
/// It is the caller's responsibility to ensure that the data referred to by the entries is
|
/// It is the caller's responsibility to ensure that the data referred to by the entries is
|
||||||
@@ -377,17 +449,21 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
|||||||
Ok(offset)
|
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>,
|
writer: &mut TiffWriter<W>,
|
||||||
value: &DirectoryEntry<K::OffsetType>,
|
value: DirectoryEntry<K::OffsetType>,
|
||||||
) -> TiffResult<Entry> {
|
) -> TiffResult<Entry> {
|
||||||
let &DirectoryEntry {
|
let DirectoryEntry {
|
||||||
data: ref bytes,
|
data: ref bytes,
|
||||||
ref count,
|
ref count,
|
||||||
data_type,
|
data_type,
|
||||||
} = value;
|
} = value;
|
||||||
|
|
||||||
let in_entry_bytes = mem::size_of::<K::OffsetType>();
|
let in_entry_bytes = mem::size_of::<K::OffsetType>();
|
||||||
|
|
||||||
let mut offset_bytes = [0; 8];
|
let mut offset_bytes = [0; 8];
|
||||||
|
|
||||||
if bytes.len() > in_entry_bytes {
|
if bytes.len() > in_entry_bytes {
|
||||||
@@ -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.
|
// and some oversight, we can not clone the `count: K::OffsetType` and thus not convert it.
|
||||||
// Instead, write it to a buffer...
|
// Instead, write it to a buffer...
|
||||||
let mut count_bytes = [0; 8];
|
let mut count_bytes = [0; 8];
|
||||||
|
debug_assert!(in_entry_bytes == 4 || in_entry_bytes == 8);
|
||||||
// Nominally Cow but we only expect `Cow::Borrowed`.
|
// Nominally Cow but we only expect `Cow::Borrowed`.
|
||||||
count_bytes[..count.bytes()].copy_from_slice(&count.data());
|
count_bytes[..count.bytes()].copy_from_slice(&count.data());
|
||||||
|
|
||||||
@@ -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.
|
/// Provides the number of bytes written by the underlying TiffWriter during the last call.
|
||||||
fn last_written(&self) -> u64 {
|
fn last_written(&self) -> u64 {
|
||||||
self.writer.last_written()
|
self.writer.last_written()
|
||||||
@@ -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,
|
data_type: Type,
|
||||||
count: S,
|
count: S,
|
||||||
data: Vec<u8>,
|
data: std::borrow::Cow<'data, [u8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to abstract over Tiff/BigTiff differences.
|
/// Trait to abstract over Tiff/BigTiff differences.
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ use super::writer::TiffWriter;
|
|||||||
pub trait TiffValue {
|
pub trait TiffValue {
|
||||||
const BYTE_LEN: u8;
|
const BYTE_LEN: u8;
|
||||||
const FIELD_TYPE: Type;
|
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 count(&self) -> usize;
|
||||||
|
|
||||||
fn bytes(&self) -> usize {
|
fn bytes(&self) -> usize {
|
||||||
self.count() * usize::from(Self::BYTE_LEN)
|
self.count() * usize::from(Self::BYTE_LEN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,9 @@ quick_error! {
|
|||||||
ReconfiguredAfterImageWrite {
|
ReconfiguredAfterImageWrite {
|
||||||
display("attempted to reconfigure the encoder after image writing has started")
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+67
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::encoder::TiffValue;
|
||||||
|
|
||||||
macro_rules! tags {
|
macro_rules! tags {
|
||||||
{
|
{
|
||||||
// Permit arbitrary meta items, which include documentation.
|
// Permit arbitrary meta items, which include documentation.
|
||||||
@@ -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.
|
/// Byte order of the TIFF file.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum ByteOrder {
|
pub enum ByteOrder {
|
||||||
|
|||||||
Reference in new issue
Block a user