diff --git a/CHANGES.md b/CHANGES.md index 2074e8f7..cf37b34e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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)>`. diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index c56c0123..93256d15 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -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, diff --git a/src/encoder/tiff_value.rs b/src/encoder/tiff_value.rs index 16ab2c18..b40d0f42 100644 --- a/src/encoder/tiff_value.rs +++ b/src/encoder/tiff_value.rs @@ -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; @@ -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::() } +// 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 = ::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 = ::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 = ::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 = ::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: +impl TiffValue for [tags::SampleFormat] { + const BYTE_LEN: u8 = ::BYTE_LEN; + const FIELD_TYPE: Type = Type::SHORT; + + fn count(&self) -> usize { + self.len() + } + + fn data(&self) -> Cow<'_, [u8]> { + let mut buf: Vec = 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: +impl TiffValue for [tags::ExtraSamples] { + const BYTE_LEN: u8 = ::BYTE_LEN; + const FIELD_TYPE: Type = Type::SHORT; + + fn count(&self) -> usize { + self.len() + } + + fn data(&self) -> Cow<'_, [u8]> { + let mut buf: Vec = 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` does not implement `TiffValue` diff --git a/src/tags.rs b/src/tags.rs index 14cc3837..8dc87169 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -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),