From 8c4cea5d97cf3bb9995ae622ee55c12cf187564c Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Tue, 10 Dec 2019 20:11:45 +0000 Subject: [PATCH] Support 64-bit encoding Note that this adds the LONG8 data type from BigTIFF because of bounds requiring TiffValue. However, this datatype should not actually be written to tags. --- src/decoder/ifd.rs | 2 ++ src/encoder/colortype.rs | 21 ++++++++++++++ src/encoder/mod.rs | 31 ++++++++++++++++++++ tests/encode_images.rs | 61 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 2be016b..51553d2 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -122,6 +122,8 @@ pub enum Type(u16) { SSHORT = 8, SLONG = 9, SRATIONAL = 10, + /// BigTIFF 64-bit unsigned integer + LONG8 = 16, } } diff --git a/src/encoder/colortype.rs b/src/encoder/colortype.rs index 6dcceae..2f49fdb 100644 --- a/src/encoder/colortype.rs +++ b/src/encoder/colortype.rs @@ -31,6 +31,13 @@ impl ColorType for Gray32 { const BITS_PER_SAMPLE: &'static [u16] = &[32]; } +pub struct Gray64; +impl ColorType for Gray64 { + type Inner = u64; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; + const BITS_PER_SAMPLE: &'static [u16] = &[64]; +} + pub struct RGB8; impl ColorType for RGB8 { type Inner = u8; @@ -52,6 +59,13 @@ impl ColorType for RGB32 { const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32]; } +pub struct RGB64; +impl ColorType for RGB64 { + type Inner = u64; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64]; +} + pub struct RGBA8; impl ColorType for RGBA8 { type Inner = u8; @@ -73,6 +87,13 @@ impl ColorType for RGBA32 { const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; } +pub struct RGBA64; +impl ColorType for RGBA64 { + type Inner = u16; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; +} + pub struct CMYK8; impl ColorType for CMYK8 { type Inner = u8; diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index f5b662c..9eda804 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -142,6 +142,23 @@ impl TiffValue for [i32] { } } +impl TiffValue for [u64] { + const BYTE_LEN: u32 = 8; + const FIELD_TYPE: ifd::Type = ifd::Type::LONG8; + + 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: ifd::Type = ifd::Type::RATIONAL; @@ -258,6 +275,20 @@ impl TiffValue for i32 { } } +impl TiffValue for u64 { + const BYTE_LEN: u32 = 8; + const FIELD_TYPE: ifd::Type = ifd::Type::LONG8; + + fn count(&self) -> u32 { + 1 + } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + writer.write_u64(*self)?; + Ok(()) + } +} + impl TiffValue for Rational { const BYTE_LEN: u32 = 8; const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL; diff --git a/tests/encode_images.rs b/tests/encode_images.rs index 080a8b1..f3ca92f 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -238,6 +238,67 @@ fn test_rgb_u32_roundtrip() { } } +#[test] +fn test_gray_u64_roundtrip() { + let img_file = + File::open("./tests/images/gradient-1c-64b.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(64)); + + let image_data = match decoder.read_image().unwrap() { + DecodingResult::U64(res) => res, + _ => panic!("Wrong data type"), + }; + + let mut file = tempfile::tempfile().unwrap(); + { + let mut tiff = TiffEncoder::new(&mut file).unwrap(); + + let (width, height) = decoder.dimensions().unwrap(); + tiff.write_image::(width, height, &image_data) + .unwrap(); + } + file.seek(SeekFrom::Start(0)).unwrap(); + { + let mut decoder = Decoder::new(&mut file).unwrap(); + if let DecodingResult::U64(img_res) = decoder.read_image().unwrap() { + assert_eq!(image_data, img_res); + } else { + panic!("Wrong data type"); + } + } +} + +#[test] +fn test_rgb_u64_roundtrip() { + let img_file = File::open("./tests/images/gradient-3c-64b.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(64)); + + let image_data = match decoder.read_image().unwrap() { + DecodingResult::U64(res) => res, + _ => panic!("Wrong data type"), + }; + + let mut file = tempfile::tempfile().unwrap(); + { + let mut tiff = TiffEncoder::new(&mut file).unwrap(); + + let (width, height) = decoder.dimensions().unwrap(); + tiff.write_image::(width, height, &image_data) + .unwrap(); + } + file.seek(SeekFrom::Start(0)).unwrap(); + { + let mut decoder = Decoder::new(&mut file).unwrap(); + if let DecodingResult::U64(img_res) = decoder.read_image().unwrap() { + assert_eq!(image_data, img_res); + } else { + panic!("Wrong data type"); + } + } +} + #[test] fn test_multiple_byte() { let mut data = Cursor::new(Vec::new());