diff --git a/src/encoder/colortype.rs b/src/encoder/colortype.rs index a625b78..9ee9bc6 100644 --- a/src/encoder/colortype.rs +++ b/src/encoder/colortype.rs @@ -1,4 +1,4 @@ -use tags::PhotometricInterpretation; +use tags::{PhotometricInterpretation, SampleFormat}; /// Trait for different colortypes that can be encoded. pub trait ColorType { @@ -8,6 +8,8 @@ pub trait ColorType { const TIFF_VALUE: PhotometricInterpretation; /// The value of the tiff tag `BitsPerSample` const BITS_PER_SAMPLE: &'static [u16]; + /// The value of the tiff tag `SampleFormat` + const SAMPLE_FORMAT: &'static [SampleFormat]; } pub struct Gray8; @@ -15,6 +17,7 @@ impl ColorType for Gray8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[8]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT]; } pub struct Gray16; @@ -22,6 +25,7 @@ impl ColorType for Gray16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[16]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT]; } pub struct Gray32; @@ -29,6 +33,15 @@ impl ColorType for Gray32 { type Inner = u32; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT]; +} + +pub struct Gray32Float; +impl ColorType for Gray32Float { + type Inner = f32; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; + const BITS_PER_SAMPLE: &'static [u16] = &[32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP]; } pub struct Gray64; @@ -36,6 +49,15 @@ impl ColorType for Gray64 { type Inner = u64; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT]; +} + +pub struct Gray64Float; +impl ColorType for Gray64Float { + type Inner = f64; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; + const BITS_PER_SAMPLE: &'static [u16] = &[64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP]; } pub struct RGB8; @@ -43,6 +65,7 @@ impl ColorType for RGB8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 3]; } pub struct RGB16; @@ -50,6 +73,7 @@ impl ColorType for RGB16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 3]; } pub struct RGB32; @@ -57,6 +81,15 @@ impl ColorType for RGB32 { type Inner = u32; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 3]; +} + +pub struct RGB32Float; +impl ColorType for RGB32Float { + type Inner = f32; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 3]; } pub struct RGB64; @@ -64,6 +97,15 @@ impl ColorType for RGB64 { type Inner = u64; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 3]; +} + +pub struct RGB64Float; +impl ColorType for RGB64Float { + type Inner = f64; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 3]; } pub struct RGBA8; @@ -71,6 +113,7 @@ impl ColorType for RGBA8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; } pub struct RGBA16; @@ -78,6 +121,7 @@ impl ColorType for RGBA16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; } pub struct RGBA32; @@ -85,6 +129,15 @@ impl ColorType for RGBA32 { type Inner = u32; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; +} + +pub struct RGBA32Float; +impl ColorType for RGBA32Float { + type Inner = f32; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4]; } pub struct RGBA64; @@ -92,6 +145,15 @@ impl ColorType for RGBA64 { type Inner = u64; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; +} + +pub struct RGBA64Float; +impl ColorType for RGBA64Float { + type Inner = f64; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4]; } pub struct CMYK8; @@ -99,6 +161,7 @@ impl ColorType for CMYK8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; } pub struct CMYK16; @@ -106,6 +169,7 @@ impl ColorType for CMYK16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; } pub struct CMYK32; @@ -113,6 +177,15 @@ impl ColorType for CMYK32 { type Inner = u32; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; +} + +pub struct CMYK32Float; +impl ColorType for CMYK32Float { + type Inner = f32; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; + const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4]; } pub struct CMYK64; @@ -120,4 +193,13 @@ impl ColorType for CMYK64 { type Inner = u64; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::UINT; 4]; +} + +pub struct CMYK64Float; +impl ColorType for CMYK64Float { + type Inner = f64; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; + const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; + const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4]; } diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 22e39d3..1d4ac3f 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -153,6 +153,40 @@ impl TiffValue for [u64] { } } +impl TiffValue for [f32] { + const BYTE_LEN: u32 = 4; + const FIELD_TYPE: Type = Type::FLOAT; + + fn count(&self) -> u32 { + self.len() as u32 + } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + // We write using nativeedian so this sould be safe + let slice = + unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 4) }; + writer.write_bytes(slice)?; + Ok(()) + } +} + +impl TiffValue for [f64] { + const BYTE_LEN: u32 = 8; + const FIELD_TYPE: Type = Type::DOUBLE; + + fn count(&self) -> u32 { + self.len() as u32 + } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + // We write using nativeedian so this sould be safe + let slice = + unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) }; + writer.write_bytes(slice)?; + Ok(()) + } +} + impl TiffValue for [Rational] { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::RATIONAL; @@ -283,6 +317,34 @@ impl TiffValue for u64 { } } +impl TiffValue for f32 { + const BYTE_LEN: u32 = 4; + const FIELD_TYPE: Type = Type::FLOAT; + + fn count(&self) -> u32 { + 1 + } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + writer.write_f32(*self)?; + Ok(()) + } +} + +impl TiffValue for f64 { + const BYTE_LEN: u32 = 8; + const FIELD_TYPE: Type = Type::DOUBLE; + + fn count(&self) -> u32 { + 1 + } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + writer.write_f64(*self)?; + Ok(()) + } +} + impl TiffValue for Rational { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::RATIONAL; @@ -593,6 +655,8 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?; encoder.write_tag(Tag::BitsPerSample, ::BITS_PER_SAMPLE)?; + let sample_format: Vec<_> = ::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect(); + encoder.write_tag(Tag::SampleFormat, &sample_format[..])?; encoder.write_tag(Tag::PhotometricInterpretation, ::TIFF_VALUE.to_u16())?; encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?; diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs index ba4c4c5..8d3929d 100644 --- a/src/encoder/writer.rs +++ b/src/encoder/writer.rs @@ -93,6 +93,20 @@ impl TiffWriter { Ok(()) } + pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> { + self.writer.write_f32::(n)?; + self.offset += 4; + + Ok(()) + } + + pub fn write_f64(&mut self, n: f64) -> Result<(), io::Error> { + self.writer.write_f64::(n)?; + self.offset += 8; + + Ok(()) + } + pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> { if self.offset % 4 != 0 { let padding = [0, 0, 0]; diff --git a/src/tags.rs b/src/tags.rs index 21b9709..240873e 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -127,6 +127,8 @@ pub enum Type(u16) { SSHORT = 8, SLONG = 9, SRATIONAL = 10, + FLOAT = 11, + DOUBLE = 12, /// BigTIFF 64-bit unsigned integer LONG8 = 16, } diff --git a/tests/encode_images.rs b/tests/encode_images.rs index 9d1ecfd..6902cff 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -94,6 +94,8 @@ test_roundtrip!(test_u8_roundtrip, U8, u8); test_roundtrip!(test_u16_roundtrip, U16, u16); test_roundtrip!(test_u32_roundtrip, U32, u32); test_roundtrip!(test_u64_roundtrip, U64, u64); +test_roundtrip!(test_f32_roundtrip, F32, f32); +test_roundtrip!(test_f64_roundtrip, F64, f64); #[test] fn test_gray_u8_roundtrip() { @@ -145,6 +147,26 @@ fn test_rgb_u64_roundtrip() { test_u64_roundtrip::("gradient-3c-64b.tiff", ColorType::RGB(64)); } +#[test] +fn test_gray_f32_roundtrip() { + test_f32_roundtrip::("gradient-1c-32b-float.tiff", ColorType::Gray(32)); +} + +#[test] +fn test_rgb_f32_roundtrip() { + test_f32_roundtrip::("gradient-3c-32b-float.tiff", ColorType::RGB(32)); +} + +#[test] +fn test_cmyk_f32_roundtrip() { + test_f32_roundtrip::("cmyk-3c-32b-float.tiff", ColorType::CMYK(32)); +} + +#[test] +fn test_gray_f64_roundtrip() { + test_f64_roundtrip::("gradient-1c-64b-float.tiff", ColorType::Gray(64)); +} + #[test] fn test_multiple_byte() { let mut data = Cursor::new(Vec::new());