Skip to content

Commit cfff04c

Browse files
authored
Merge pull request #324 from image-rs/encoder-tiff-value
A byte-valued endian-formatted tiff value buffer
2 parents 855eb4e + 915cc20 commit cfff04c

File tree

4 files changed

+172
-11
lines changed

4 files changed

+172
-11
lines changed

src/encoder/mod.rs

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::{
1212
decoder::ifd::Entry,
1313
error::{TiffResult, UsageError},
1414
tags::{
15-
CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation, ResolutionUnit,
16-
SampleFormat, Tag, Type,
15+
ByteOrder, CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation,
16+
ResolutionUnit, SampleFormat, Tag, Type, ValueBuffer,
1717
},
1818
Directory, TiffError, TiffFormatError,
1919
};
@@ -291,6 +291,21 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
291291
})
292292
}
293293

294+
/// Write a tag-value pair with prepared byte data.
295+
///
296+
/// Note that the library will _not_ attempt to verify that the data type or the count of the
297+
/// buffered value is permissible for the given tag. The data will be associated with the tag
298+
/// exactly as-is.
299+
///
300+
/// An error is returned if the byte order of the presented value does not match the byte order
301+
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
302+
pub fn write_tag_value(&mut self, tag: Tag, value: &ValueBuffer) -> TiffResult<()> {
303+
let entry = self.write_entry_buf(value)?;
304+
self.directory.extend([(tag, entry)]);
305+
306+
Ok(())
307+
}
308+
294309
/// Write a single ifd tag.
295310
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
296311
// 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> {
301316
value.write(&mut writer)?;
302317
}
303318

304-
let entry = Self::write_value(
319+
let entry = Self::write_entry_inner(
305320
self.writer,
306-
&DirectoryEntry {
321+
DirectoryEntry {
307322
data_type: <T>::FIELD_TYPE,
308323
count: value.count().try_into()?,
309-
data: bytes,
324+
data: bytes.into(),
310325
},
311326
)?;
312327

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

318333
/// Write some data to the tiff file, the offset of the data is returned.
319334
///
320-
/// This could be used to write tiff strips.
335+
/// This could be used to write tiff strips. All of the data will be written into the file as a
336+
/// slice of bytes. It can not be used as an entry in a directory directly, where very short
337+
/// values are instead represented in the offset field directly. Use [`Self::write_entry`]
338+
/// instead.
321339
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
322340
let offset = self.writer.offset();
323341
value.write(self.writer)?;
324342
Ok(offset)
325343
}
326344

345+
/// Write some data directly to the tiff file, the offset of the data is returned.
346+
///
347+
/// All of the data will be written into the file as a slice of bytes. It can not be used as an
348+
/// entry in a directory directly, where very short values are instead represented in the
349+
/// offset field directly. Use [`Self::write_entry_buf`] instead.
350+
///
351+
/// An error is returned if the byte order of the presented value does not match the byte order
352+
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
353+
pub fn write_data_buf(&mut self, value: &ValueBuffer) -> TiffResult<u64> {
354+
self.check_value_byteorder(value)?;
355+
let offset = self.writer.offset();
356+
self.writer.write_bytes(value.as_bytes())?;
357+
Ok(offset)
358+
}
359+
360+
/// Write a directory value into the file.
361+
///
362+
/// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
363+
/// stored in the entry's offset field.
364+
pub fn write_entry<T: TiffValue>(&mut self, value: T) -> TiffResult<Entry> {
365+
Self::write_entry_inner(
366+
self.writer,
367+
DirectoryEntry {
368+
data_type: T::FIELD_TYPE,
369+
count: K::convert_offset(value.count() as u64)?,
370+
// FIXME: not optimal we always allocate here. But the only other method we have
371+
// available is `T::write(&mut TiffWriter<W>)` and we must pass that into
372+
// `write_entry_inner` but then have a combinatorial explosion of instantiations
373+
// which is absurd.
374+
data: value.data(),
375+
},
376+
)
377+
}
378+
379+
/// Write a directory value into the file.
380+
///
381+
/// An error is returned if the byte order of the presented value does not match the byte order
382+
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
383+
///
384+
/// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
385+
/// stored in the entry's offset field.
386+
pub fn write_entry_buf(&mut self, value: &ValueBuffer) -> TiffResult<Entry> {
387+
self.check_value_byteorder(value)?;
388+
389+
Self::write_entry_inner(
390+
self.writer,
391+
DirectoryEntry {
392+
data_type: value.data_type(),
393+
count: K::convert_offset(value.count())?,
394+
data: value.as_bytes().into(),
395+
},
396+
)
397+
}
398+
327399
/// Insert previously written key-value entries.
328400
///
329401
/// 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> {
377449
Ok(offset)
378450
}
379451

380-
fn write_value(
452+
// Does it really make sense that this would be a (potentially) compressible write? For actual
453+
// directory values their size must match the data written so there's going to be a silent
454+
// problem if that actually occurs.
455+
fn write_entry_inner(
381456
writer: &mut TiffWriter<W>,
382-
value: &DirectoryEntry<K::OffsetType>,
457+
value: DirectoryEntry<K::OffsetType>,
383458
) -> TiffResult<Entry> {
384-
let &DirectoryEntry {
459+
let DirectoryEntry {
385460
data: ref bytes,
386461
ref count,
387462
data_type,
388463
} = value;
389464

390465
let in_entry_bytes = mem::size_of::<K::OffsetType>();
466+
391467
let mut offset_bytes = [0; 8];
392468

393469
if bytes.len() > in_entry_bytes {
@@ -406,6 +482,7 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
406482
// and some oversight, we can not clone the `count: K::OffsetType` and thus not convert it.
407483
// Instead, write it to a buffer...
408484
let mut count_bytes = [0; 8];
485+
debug_assert!(in_entry_bytes == 4 || in_entry_bytes == 8);
409486
// Nominally Cow but we only expect `Cow::Borrowed`.
410487
count_bytes[..count.bytes()].copy_from_slice(&count.data());
411488

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

499+
fn check_value_byteorder(&self, value: &ValueBuffer) -> TiffResult<()> {
500+
// FIXME: we should enable writing files with any chosen byte order.
501+
if value.byte_order() != ByteOrder::native() {
502+
Err(TiffError::UsageError(UsageError::ByteOrderMismatch))
503+
} else {
504+
Ok(())
505+
}
506+
}
507+
422508
/// Provides the number of bytes written by the underlying TiffWriter during the last call.
423509
fn last_written(&self) -> u64 {
424510
self.writer.last_written()
@@ -824,10 +910,10 @@ impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W
824910
}
825911
}
826912

827-
struct DirectoryEntry<S> {
913+
struct DirectoryEntry<'data, S> {
828914
data_type: Type,
829915
count: S,
830-
data: Vec<u8>,
916+
data: std::borrow::Cow<'data, [u8]>,
831917
}
832918

833919
/// Trait to abstract over Tiff/BigTiff differences.

src/encoder/tiff_value.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ use super::writer::TiffWriter;
1616
pub trait TiffValue {
1717
const BYTE_LEN: u8;
1818
const FIELD_TYPE: Type;
19+
20+
// FIXME: If we want this to mean 'count' in the sense of `Entry` it should return `u32` or
21+
// `u64`, probably the latter with `convert_offset` taking care of the check against `u32::MAX`
22+
// for classic tiff.
1923
fn count(&self) -> usize;
24+
2025
fn bytes(&self) -> usize {
2126
self.count() * usize::from(Self::BYTE_LEN)
2227
}

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ quick_error! {
208208
ReconfiguredAfterImageWrite {
209209
display("attempted to reconfigure the encoder after image writing has started")
210210
}
211+
ByteOrderMismatch {
212+
display("attempted to use data with a byte order that does not match the required one")
213+
}
211214
}
212215
}
213216

src/tags.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::encoder::TiffValue;
2+
13
macro_rules! tags {
24
{
35
// Permit arbitrary meta items, which include documentation.
@@ -336,6 +338,71 @@ pub enum ExtraSamples(u16) {
336338
}
337339
}
338340

341+
/// A value represented as in-memory bytes with flexible byteorder.
342+
pub struct ValueBuffer {
343+
/// The raw bytes of the value.
344+
bytes: Vec<u8>,
345+
346+
/// The type of the value.
347+
ty: Type,
348+
349+
/// The byte order of the value.
350+
byte_order: ByteOrder,
351+
}
352+
353+
impl ValueBuffer {
354+
/// A value with a count of zero.
355+
///
356+
/// The byte order is set to the native byte order of the platform.
357+
pub fn empty(ty: Type) -> Self {
358+
ValueBuffer {
359+
bytes: vec![],
360+
ty,
361+
byte_order: ByteOrder::native(),
362+
}
363+
}
364+
365+
/// Create a value with native byte order from in-memory data.
366+
pub fn from_value<T: TiffValue>(value: &T) -> Self {
367+
ValueBuffer {
368+
bytes: value.data().into_owned(),
369+
ty: <T as TiffValue>::FIELD_TYPE,
370+
byte_order: ByteOrder::native(),
371+
}
372+
}
373+
374+
pub fn byte_order(&self) -> ByteOrder {
375+
self.byte_order
376+
}
377+
378+
pub fn data_type(&self) -> Type {
379+
self.ty
380+
}
381+
382+
/// The count of items in the value.
383+
pub fn count(&self) -> u64 {
384+
(self.bytes.len() / usize::from(self.ty.byte_len())) as u64
385+
}
386+
387+
/// View the underlying raw bytes of this value.
388+
pub fn as_bytes(&self) -> &[u8] {
389+
&self.bytes
390+
}
391+
392+
/// View the underlying mutable raw bytes of this value.
393+
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
394+
&mut self.bytes
395+
}
396+
397+
/// Change the byte order of the value representation.
398+
pub fn set_byte_order(&mut self, byte_order: ByteOrder) {
399+
self.byte_order
400+
.convert(self.ty, self.bytes.as_mut_slice(), byte_order);
401+
402+
self.byte_order = byte_order;
403+
}
404+
}
405+
339406
/// Byte order of the TIFF file.
340407
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
341408
pub enum ByteOrder {

0 commit comments

Comments
 (0)