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
13 changes: 13 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# Version 0.11.1 (Unreleased)

Fixes:
- The enumeration types in `tags` are now all marked with a representation of
their underlying TIFF type (e.g. `repr(u16)`) and variants are explicitly
assigned their corresponding values. That is you may _read_ the raw
discriminant and interpret that as the value—except for `Unknown` variants.

Additions:
- Types in `tags` now generally implement `TiffValue` and can be handed to the
`DirectoryEncoder::write_tag` method. Unlike primitive types they do _not_
always implement the trait for slices of themselves.

# Version 0.11.0

- `Directory` now implements `FromIterator<(Tag, Value)>`.
Expand Down
2 changes: 1 addition & 1 deletion src/encoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
)?;
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())?;
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None)?;

Ok(ImageEncoder {
encoder,
Expand Down
124 changes: 123 additions & 1 deletion src/encoder/tiff_value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{borrow::Cow, io::Write, slice::from_ref};

use crate::{bytecast, tags::Type, TiffError, TiffFormatError, TiffResult};
use crate::{bytecast, tags, tags::Type, TiffError, TiffFormatError, TiffResult};

use super::writer::TiffWriter;

Expand Down Expand Up @@ -458,6 +458,128 @@ impl TiffValue for str {
}
}

// FIXME: held up on reading the discriminant for all but the unknown variant.
//
// It needs to have `repr(u16)` to have a defined layout and then we may read the tag from its
// value representation by casting a u16 pointer to a pointer to the enum type.
// unsafe { *<*const _>::from(self).cast::<u16>() }
// But that is quite unsafe and needs careful review to maintain the safety requirements. Maybe we
// could get `bytemuck` to add a trait for this (it has `TransparentWrapper`) with a derive for
// enum types that have an explicit repr.
//
// This would allow returning borrowed data in more cases. For all types without an unknown, in all
// cases including as slices, and for all types with and unknown variant when we have a single
// value (either returning a borrowed discriminant or a borrowed value within `Unknown`).
impl TiffValue for tags::CompressionMethod {
const BYTE_LEN: u8 = <u16 as TiffValue>::BYTE_LEN;
const FIELD_TYPE: Type = Type::SHORT;

fn count(&self) -> usize {
1
}

fn data(&self) -> Cow<'_, [u8]> {
let bytes = self.to_u16().to_ne_bytes();
Cow::Owned(bytes.to_vec())
}
}

impl TiffValue for tags::PhotometricInterpretation {
const BYTE_LEN: u8 = <u16 as TiffValue>::BYTE_LEN;
const FIELD_TYPE: Type = Type::SHORT;

fn count(&self) -> usize {
1
}

fn data(&self) -> Cow<'_, [u8]> {
self.to_u16().to_ne_bytes().to_vec().into()
}
}

impl TiffValue for tags::PlanarConfiguration {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SHORT;

fn count(&self) -> usize {
1
}

fn data(&self) -> Cow<'_, [u8]> {
self.to_u16().to_ne_bytes().to_vec().into()
}
}

impl TiffValue for tags::Predictor {
const BYTE_LEN: u8 = <u16 as TiffValue>::BYTE_LEN;
const FIELD_TYPE: Type = Type::SHORT;

fn count(&self) -> usize {
1
}

fn data(&self) -> Cow<'_, [u8]> {
self.to_u16().to_ne_bytes().to_vec().into()
}
}

impl TiffValue for tags::ResolutionUnit {
const BYTE_LEN: u8 = <u16 as TiffValue>::BYTE_LEN;
const FIELD_TYPE: Type = Type::SHORT;

fn count(&self) -> usize {
1
}

fn data(&self) -> Cow<'_, [u8]> {
self.to_u16().to_ne_bytes().to_vec().into()
}
}

/// This is implemented for slices as the `SampleFormat` tag takes a count of `N`.
///
/// Use `core::slice::from_ref` if you really need to write a single element.
///
/// See: <https://web.archive.org/web/20191120220815/https://www.awaresystems.be/imaging/tiff/tifftags/sampleformat.html>
impl TiffValue for [tags::SampleFormat] {
const BYTE_LEN: u8 = <u16 as TiffValue>::BYTE_LEN;
const FIELD_TYPE: Type = Type::SHORT;

fn count(&self) -> usize {
self.len()
}

fn data(&self) -> Cow<'_, [u8]> {
let mut buf: Vec<u8> = Vec::with_capacity(self.len() * 2);
for x in self {
buf.extend_from_slice(&x.to_u16().to_ne_bytes());
}
Cow::Owned(buf)
}
}

/// This is implemented for slices as the `ExtraSamples` tag takes a count of `N`.
///
/// Use `core::slice::from_ref` if you really need to write a single element.
///
/// See: <https://web.archive.org/web/20191120220815/https://www.awaresystems.be/imaging/tiff/tifftags/extrasamples.html>
impl TiffValue for [tags::ExtraSamples] {
const BYTE_LEN: u8 = <u16 as TiffValue>::BYTE_LEN;
const FIELD_TYPE: Type = Type::SHORT;

fn count(&self) -> usize {
self.len()
}

fn data(&self) -> Cow<'_, [u8]> {
let mut buf: Vec<u8> = Vec::with_capacity(self.len() * 2);
for x in self {
buf.extend_from_slice(&x.to_u16().to_ne_bytes());
}
Cow::Owned(buf)
}
}

// If you pass `&Vec<_>` then you'd get at first sight the complaint:
//
// `Vec<T>` does not implement `TiffValue`
Expand Down
3 changes: 2 additions & 1 deletion src/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ macro_rules! tags {
$( #[$enum_attr] )*
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[non_exhaustive]
#[repr($ty)]
pub enum $name {
$($(#[$ident_attr])* $tag,)*
$($(#[$ident_attr])* $tag = $val,)*
$(
#[$unknown_meta]
Unknown($ty),
Expand Down
Loading