From ee8d13249e2f1f9e7b3ec675d39951e092655caf Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+197g@users.noreply.github.com> Date: Thu, 8 Jan 2026 14:19:30 +0100 Subject: [PATCH] Utils for working with tags Implement the encoder's `TiffValue` trait for the tags commonly defined and represented as separate types in our own code. We don't tie the tag for an entry of these to the value type (yet). This lets us keep the trait exactly as-is and we avoid unnecessary churn. We can always add a separate trait on top. --- src/encoder/mod.rs | 2 +- src/encoder/tiff_value.rs | 124 +++++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index c56c012..93256d1 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 16ab2c1..b40d0f4 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`