From fca4e1f88a877b942525dad572d5bc7af40e7b03 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Tue, 10 Dec 2019 18:46:12 +0000 Subject: [PATCH 01/11] Support 32-bit grayscale decoding --- src/decoder/mod.rs | 38 +++++++++++++++++++++++++++++- src/decoder/stream.rs | 12 ++++++++++ tests/decode_images.rs | 20 ++++++++++++++++ tests/images/gradient-1c-32b.tiff | Bin 0 -> 1341 bytes 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/images/gradient-1c-32b.tiff diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index a3aff91..cfdbbe0 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -19,6 +19,8 @@ pub enum DecodingResult { U8(Vec), /// A vector of unsigned words U16(Vec), + /// A vector of 32 bit unsigned ints + U32(Vec), } impl DecodingResult { @@ -38,10 +40,19 @@ impl DecodingResult { } } + fn new_u32(size: usize, limits: &Limits) -> TiffResult { + if size > limits.decoding_buffer_size / 4 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::U32(vec![0; size])) + } + } + pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer { match *self { DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]), DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]), + DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]), } } } @@ -52,6 +63,8 @@ pub enum DecodingBuffer<'a> { U8(&'a mut [u8]), /// A slice of unsigned words U16(&'a mut [u16]), + /// A slice of 32 bit unsigned ints + U32(&'a mut [u32]), } impl<'a> DecodingBuffer<'a> { @@ -59,6 +72,7 @@ impl<'a> DecodingBuffer<'a> { match *self { DecodingBuffer::U8(ref buf) => buf.len(), DecodingBuffer::U16(ref buf) => buf.len(), + DecodingBuffer::U32(ref buf) => buf.len(), } } @@ -66,6 +80,7 @@ impl<'a> DecodingBuffer<'a> { match *self { DecodingBuffer::U8(_) => 1, DecodingBuffer::U16(_) => 2, + DecodingBuffer::U32(_) => 4, } } @@ -76,6 +91,7 @@ impl<'a> DecodingBuffer<'a> { match *self { DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf), DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf), + DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf), } } } @@ -233,6 +249,12 @@ impl Wrapping for u16 { } } +impl Wrapping for u32 { + fn wrapping_add(&self, other: Self) -> Self { + u32::wrapping_add(*self, other) + } +} + fn rev_hpredict_nsamp(image: &mut [T], size: (u32, u32), samples: usize) where T: Copy + Wrapping, @@ -250,7 +272,7 @@ where fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> { let samples = match color_type { - ColorType::Gray(8) | ColorType::Gray(16) => 1, + ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) => 1, ColorType::RGB(8) | ColorType::RGB(16) => 3, ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::CMYK(8) => 4, _ => { @@ -266,6 +288,9 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) DecodingBuffer::U16(buf) => { rev_hpredict_nsamp(buf, size, samples); } + DecodingBuffer::U32(buf) => { + rev_hpredict_nsamp(buf, size, samples); + } } Ok(()) } @@ -659,6 +684,15 @@ impl Decoder { reader.read_u16_into(&mut buffer[..bytes / 2])?; bytes / 2 } + (ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => { + reader.read_u32_into(&mut buffer[..bytes / 4])?; + if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + for datum in buffer[..bytes / 4].iter_mut() { + *datum = 0xffff_ffff - *datum + } + } + bytes / 4 + } (ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => { reader.read_u16_into(&mut buffer[..bytes / 2])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { @@ -806,6 +840,7 @@ impl Decoder { let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) { n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?, n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?, + n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits)?, n => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), @@ -834,6 +869,7 @@ impl Decoder { let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) { n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?, n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?, + n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits)?, n => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index fe5d20c..997e096 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -59,6 +59,18 @@ pub trait EndianReader: Read { } } + #[inline(always)] + fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> { + match self.byte_order() { + ByteOrder::LittleEndian => { + ::read_u32_into::(self, buffer) + } + ByteOrder::BigEndian => { + ::read_u32_into::(self, buffer) + } + } + } + /// Reads an i32 #[inline(always)] fn read_i32(&mut self) -> Result { diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 7c91988..9a6076a 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -64,6 +64,26 @@ fn test_gray_u16() { } } +#[test] +fn test_gray_u32() { + let img_file = + File::open("./tests/images/gradient-1c-32b.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(32)); + let img_res = decoder.read_image().unwrap(); + if let DecodingResult::U32(res) = img_res { + let mut res_sum: u64 = 0; + for x in res { + res_sum += x as u64; + } + + assert_eq!(res_sum, 549892913787); + } + else { + panic!("Wrong bit depth") + } +} + #[test] fn test_rgb_u16() { let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!"); diff --git a/tests/images/gradient-1c-32b.tiff b/tests/images/gradient-1c-32b.tiff new file mode 100644 index 0000000000000000000000000000000000000000..3b05740b7eff3ee0dfa47ae20a17cef01870a4b7 GIT binary patch literal 1341 zcmYk6dtA&}7{=e}wz)1LikQVFZOJyWHkU4143b@hkeQ~MhDNh9Q7*HeGqpuDWhhax zX_Ky!$)(%|r8MN8+ZMa+l1MJQF1wq@k57Nh`Qx1L^PcB<-_QBy2!+mMZ9&wU;z@OT z6g=-mDP|{DRG4oy7FqPk&9;2|MrPGoE%>aD|88qTrE52D=hW_eT|f_!u&k%y{hD4m zx`(|*ovEK2q_RGSjXV4DgO1sVMy%^6GJ5wHof$JgbmoGs$T4>y|3%uMoWcusqP3a! ze9y|k{6q2~IZcHQ{F>0AqKW5*F;ym?`NV!fC3(Zy>ZB2LOFxpC9sQD}pLV43x=~a* z)rs}rHkw*aI8#B{S8Tq)g@sITWi1Y4NVUt2C7&Nld+NVtHXZIPs7XMQhH>QJ=)pAk z<5_&dHx%4BfktV*C5?|K70;YVhKxyU_?F4ct78gPMR>7ERo<-ME+3X&>&vV%r;@SO zkEG4tQN4B=J9%q5J!zc50@8%cB621>Hf|QJPnpeZI{m4aeh#~>4q%%4xpcq$duo09 z16fPvQG4us78kyNiV6aWSG17LDO$v0jf?55I*7dwT*6$NmeNf1GB$8aFsoE9Ct;F^ zjW{4?7du0U=1AD7h)^~uEsQo-h0}I}l%{NoAls`l)>*WIUI|yy&F*sYZk4k{zg4WM zIf}f*3MyInBRM`&lBPgK&c)GG*sz+?D%Q~E>oLSjjHQGdYpK0u9aUUjPY3lIsLfYR z+Q&b^#gd_PFhg6i2JLxq2sjmwcYZ%3>0knqeG{>*yYPeW#0I+hLDfkoLFIH2B%HjiCUmt>+KFbgdi z*;smMH^!da16AHH5GZpX-M1HwA-PD7{}qPWdAL%(4|5~(QC+(qGLHht6ou%SR0N0l z#qgM3g0YFE$WJ_g)1_swk3R^OSq|$>hj6c(9*Ny6uugCoMxFE3hrTiW>bjSe?BNN#PAxmfeIts}YVaw{UOk zZ49ctgQ23k5Zt|o$dSKcNnjJ~4mBg+=|0W}K7cXdcLa?31G#Z6a1Qwsi--M%Y3m#4T>)b@4}S*2R2| z%}wf7Cfti>Mx(j=*W6Fin`5pU@hyq^@+>~}wC1#piGP3z+jcSIJOjJ@#PzY~S$@hs zSmmtXX4=`xqN(+$?3L06{b|VNg~8@g)C~7*v&6Y zwK6(N;U;hoQAn0cmEyHyqGjRXL{pVv;^?qYevB+Sf Date: Tue, 10 Dec 2019 19:02:27 +0000 Subject: [PATCH 02/11] Support 32-bit rgb and rgba decoding --- src/decoder/mod.rs | 15 +++++++++++++-- tests/decode_images.rs | 19 +++++++++++++++++++ tests/images/gradient-3c-32b.tiff | Bin 0 -> 3401 bytes 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 tests/images/gradient-3c-32b.tiff diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index cfdbbe0..d754b94 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -273,8 +273,8 @@ where fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> { let samples = match color_type { ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) => 1, - ColorType::RGB(8) | ColorType::RGB(16) => 3, - ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::CMYK(8) => 4, + ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) => 3, + ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::CMYK(8) => 4, _ => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::HorizontalPredictor(color_type), @@ -339,6 +339,12 @@ impl Decoder { PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16] => { Ok(ColorType::RGB(16)) } + PhotometricInterpretation::RGB if self.bits_per_sample == [32, 32, 32, 32] => { + Ok(ColorType::RGBA(32)) + } + PhotometricInterpretation::RGB if self.bits_per_sample == [32, 32, 32] => { + Ok(ColorType::RGB(32)) + } PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => { Ok(ColorType::CMYK(8)) } @@ -684,6 +690,11 @@ impl Decoder { reader.read_u16_into(&mut buffer[..bytes / 2])?; bytes / 2 } + (ColorType::RGBA(32), DecodingBuffer::U32(ref mut buffer)) + | (ColorType::RGB(32), DecodingBuffer::U32(ref mut buffer)) => { + reader.read_u32_into(&mut buffer[..bytes / 4])?; + bytes / 4 + } (ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => { reader.read_u32_into(&mut buffer[..bytes / 4])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 9a6076a..900606f 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -103,6 +103,25 @@ fn test_rgb_u16() { } } +#[test] +fn test_rgb_u32() { + let img_file = File::open("./tests/images/gradient-3c-32b.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(32)); + let img_res = decoder.read_image().unwrap(); + if let DecodingResult::U32(res) = img_res { + let mut res_sum: u64 = 0; + for x in res { + res_sum += x as u64; + } + + assert_eq!(res_sum, 2030834111716); + } + else { + panic!("Wrong bit depth") + } +} + #[test] fn test_string_tags() { // these files have null-terminated strings for their Software tag. One has extra bytes after diff --git a/tests/images/gradient-3c-32b.tiff b/tests/images/gradient-3c-32b.tiff new file mode 100644 index 0000000000000000000000000000000000000000..d76596e2ce205a891b20bef66a51c81a911f8d12 GIT binary patch literal 3401 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 Date: Tue, 10 Dec 2019 19:15:32 +0000 Subject: [PATCH 03/11] Fix warnings in encoding test --- tests/encode_images.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/encode_images.rs b/tests/encode_images.rs index 6256cec..af31025 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -273,10 +273,10 @@ fn test_multipage_image() { // write first grayscale image (2x2 16-bit) let img1: Vec = [1, 2, 3, 4].to_vec(); - img_encoder.write_image::(2, 2, &img1[..]); + img_encoder.write_image::(2, 2, &img1[..]).unwrap(); // write second grayscale image (3x3 8-bit) let img2: Vec = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec(); - img_encoder.write_image::(3, 3, &img2[..]); + img_encoder.write_image::(3, 3, &img2[..]).unwrap(); } // seek to the beginning of the file, so that it can be decoded From af010edcfd236e1418f90c60fb5f5d2c92a2a8b3 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Tue, 10 Dec 2019 19:15:46 +0000 Subject: [PATCH 04/11] Support 32-bit encoding --- src/encoder/colortype.rs | 21 ++++++++++++++ tests/encode_images.rs | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/encoder/colortype.rs b/src/encoder/colortype.rs index a348ee7..6dcceae 100644 --- a/src/encoder/colortype.rs +++ b/src/encoder/colortype.rs @@ -24,6 +24,13 @@ impl ColorType for Gray16 { const BITS_PER_SAMPLE: &'static [u16] = &[16]; } +pub struct Gray32; +impl ColorType for Gray32 { + type Inner = u32; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; + const BITS_PER_SAMPLE: &'static [u16] = &[32]; +} + pub struct RGB8; impl ColorType for RGB8 { type Inner = u8; @@ -38,6 +45,13 @@ impl ColorType for RGB16 { const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16]; } +pub struct RGB32; +impl ColorType for RGB32 { + type Inner = u32; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32]; +} + pub struct RGBA8; impl ColorType for RGBA8 { type Inner = u8; @@ -52,6 +66,13 @@ impl ColorType for RGBA16 { const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16]; } +pub struct RGBA32; +impl ColorType for RGBA32 { + type Inner = u16; + const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; + const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; +} + pub struct CMYK8; impl ColorType for CMYK8 { type Inner = u8; diff --git a/tests/encode_images.rs b/tests/encode_images.rs index af31025..080a8b1 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -177,6 +177,67 @@ fn test_rgb_u16_roundtrip() { } } +#[test] +fn test_gray_u32_roundtrip() { + let img_file = + File::open("./tests/images/gradient-1c-32b.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(32)); + + let image_data = match decoder.read_image().unwrap() { + DecodingResult::U32(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::U32(img_res) = decoder.read_image().unwrap() { + assert_eq!(image_data, img_res); + } else { + panic!("Wrong data type"); + } + } +} + +#[test] +fn test_rgb_u32_roundtrip() { + let img_file = File::open("./tests/images/gradient-3c-32b.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(32)); + + let image_data = match decoder.read_image().unwrap() { + DecodingResult::U32(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::U32(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()); From f4e985e0bdf6314ce5aba0e4ca7c59c96a4e35c9 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Tue, 10 Dec 2019 19:37:46 +0000 Subject: [PATCH 05/11] Support 64-bit grayscale decoding --- src/decoder/mod.rs | 38 +++++++++++++++++++++++++++++- src/decoder/stream.rs | 21 +++++++++++++++++ tests/decode_images.rs | 20 ++++++++++++++++ tests/images/gradient-1c-64b.tiff | Bin 0 -> 2368 bytes 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tests/images/gradient-1c-64b.tiff diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index d754b94..0b6719e 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -21,6 +21,8 @@ pub enum DecodingResult { U16(Vec), /// A vector of 32 bit unsigned ints U32(Vec), + /// A vector of 64 bit unsigned ints + U64(Vec), } impl DecodingResult { @@ -48,11 +50,20 @@ impl DecodingResult { } } + fn new_u64(size: usize, limits: &Limits) -> TiffResult { + if size > limits.decoding_buffer_size / 8 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::U64(vec![0; size])) + } + } + pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer { match *self { DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]), DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]), DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]), + DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]), } } } @@ -65,6 +76,8 @@ pub enum DecodingBuffer<'a> { U16(&'a mut [u16]), /// A slice of 32 bit unsigned ints U32(&'a mut [u32]), + /// A slice of 64 bit unsigned ints + U64(&'a mut [u64]), } impl<'a> DecodingBuffer<'a> { @@ -73,6 +86,7 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U8(ref buf) => buf.len(), DecodingBuffer::U16(ref buf) => buf.len(), DecodingBuffer::U32(ref buf) => buf.len(), + DecodingBuffer::U64(ref buf) => buf.len(), } } @@ -81,6 +95,7 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U8(_) => 1, DecodingBuffer::U16(_) => 2, DecodingBuffer::U32(_) => 4, + DecodingBuffer::U64(_) => 8, } } @@ -92,6 +107,7 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf), DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf), DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf), + DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf), } } } @@ -255,6 +271,12 @@ impl Wrapping for u32 { } } +impl Wrapping for u64 { + fn wrapping_add(&self, other: Self) -> Self { + u64::wrapping_add(*self, other) + } +} + fn rev_hpredict_nsamp(image: &mut [T], size: (u32, u32), samples: usize) where T: Copy + Wrapping, @@ -272,7 +294,7 @@ where fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> { let samples = match color_type { - ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) => 1, + ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1, ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) => 3, ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::CMYK(8) => 4, _ => { @@ -291,6 +313,9 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) DecodingBuffer::U32(buf) => { rev_hpredict_nsamp(buf, size, samples); } + DecodingBuffer::U64(buf) => { + rev_hpredict_nsamp(buf, size, samples); + } } Ok(()) } @@ -695,6 +720,15 @@ impl Decoder { reader.read_u32_into(&mut buffer[..bytes / 4])?; bytes / 4 } + (ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => { + reader.read_u64_into(&mut buffer[..bytes / 8])?; + if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + for datum in buffer[..bytes / 8].iter_mut() { + *datum = 0xffff_ffff_ffff_ffff - *datum + } + } + bytes / 8 + } (ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => { reader.read_u32_into(&mut buffer[..bytes / 4])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { @@ -852,6 +886,7 @@ impl Decoder { n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?, n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?, n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits)?, + n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits)?, n => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), @@ -881,6 +916,7 @@ impl Decoder { n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?, n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?, n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits)?, + n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits)?, n => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 997e096..caf4e3c 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -71,6 +71,27 @@ pub trait EndianReader: Read { } } + /// Reads an u64 + #[inline(always)] + fn read_u64(&mut self) -> Result { + match self.byte_order() { + ByteOrder::LittleEndian => ::read_u64::(self), + ByteOrder::BigEndian => ::read_u64::(self), + } + } + + #[inline(always)] + fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> { + match self.byte_order() { + ByteOrder::LittleEndian => { + ::read_u64_into::(self, buffer) + } + ByteOrder::BigEndian => { + ::read_u64_into::(self, buffer) + } + } + } + /// Reads an i32 #[inline(always)] fn read_i32(&mut self) -> Result { diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 900606f..04ca7ef 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -84,6 +84,26 @@ fn test_gray_u32() { } } +#[test] +fn test_gray_u64() { + 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 img_res = decoder.read_image().unwrap(); + if let DecodingResult::U64(res) = img_res { + let mut res_sum: u64 = 0; + for x in res { + res_sum += x as u64; + } + + assert_eq!(res_sum, 549892913787); + } + else { + panic!("Wrong bit depth") + } +} + #[test] fn test_rgb_u16() { let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!"); diff --git a/tests/images/gradient-1c-64b.tiff b/tests/images/gradient-1c-64b.tiff new file mode 100644 index 0000000000000000000000000000000000000000..43dec0c21fb2d793f4a553e288688e47db02a84c GIT binary patch literal 2368 zcmbukED(M6P~SX$Cm zQZD6oq?97}+*WLDBw8-pu&wNT-={rK&;H$cp5J+WKcDaS_x=6O`90@2JDW@NBoaw) zNr$AHM5-R~l6H>Og|u@l=Cq{Rtg%;*wL8b6*X=TXN2(#(aZ<$`j8xRZVY)(%~*23wsGj* zU`6j!Hy({lt?9$EY%n77BYMyJ2{Hif>--;O+~ z%pUvia=@f2N7PN3DwNb#IiX+UCs-3ZO{hG1b2|BP{S2~eqBC|2n2E>6&tg6_ZZ^4J zy9>Ej@f`9kg)4^D%%#6q^eHw!`3&`Z=3#5de4#Sj_j7V?wi`-w7tqhiU5FvIi|ET0 z?&J=)#c0{Egnp*NgFI-9Czh^UO79#kBab-Xg%{er=_PY~$fx|5;iSan^qb3kal6V7 zr)=^^{VQ^zvORYN`GxZrc%w%k+Bc&z%IQmyl?{zw(c5_iVg7=z(e&X;49gBi^So7< zQ@0uuOV;4#Yim&&6@ro1H9zgvruF2KOQCqMcmuXLYW`MYAAKY88w(vJ77WqY5)(#l z%?wA^QxW*u>069G7>O~CQMfUCBUZRo3TGegAw&ED9o)wF-c8z%f@#Hs| zTXCmT0!~ZVhRJ%{aYt4nri3S<$Iuj* z7@YYXTCGe+zkPeL-a7+hBECn}>`c5|v=8U{XJJLvew15hYYfW4p3%8zG(Qimr|09C zr~=H2I)JAO3ehm)ASzReP;b*AyxYAPqk5F!daJ`|v-AjF-G3C54Ugfo+@&ju-H<=|z-=)uF@TOQXu(apPcKcQ+NZ5Q|r;x;wIkR zdJ6|v-Ns?EJ7{(1E(VPJ0T;V9puwR=%rd)&=iKgNZRC&WI_f9P2ya4j@1Jqe@LzCR z=mXqd`w+bhno%D22%p-upq<}iY+mvN?}xNvtoyI16V!(FE1qKfnrC=4^*K(qe1Ut9 w{D!d}zoXpd53E}I5>w4yVUq1@oEP{f#(MsRx8wfS% Date: Tue, 10 Dec 2019 19:41:57 +0000 Subject: [PATCH 06/11] Support 64-bit rgb and rgba decoding --- src/decoder/mod.rs | 15 +++++++++++++-- tests/decode_images.rs | 19 +++++++++++++++++++ tests/images/gradient-3c-64b.tiff | Bin 0 -> 6480 bytes 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 tests/images/gradient-3c-64b.tiff diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 0b6719e..933d6c1 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -295,8 +295,8 @@ where fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> { let samples = match color_type { ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1, - ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) => 3, - ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::CMYK(8) => 4, + ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3, + ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64) | ColorType::CMYK(8) => 4, _ => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::HorizontalPredictor(color_type), @@ -370,6 +370,12 @@ impl Decoder { PhotometricInterpretation::RGB if self.bits_per_sample == [32, 32, 32] => { Ok(ColorType::RGB(32)) } + PhotometricInterpretation::RGB if self.bits_per_sample == [64, 64, 64, 64] => { + Ok(ColorType::RGBA(64)) + } + PhotometricInterpretation::RGB if self.bits_per_sample == [64, 64, 64] => { + Ok(ColorType::RGB(64)) + } PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => { Ok(ColorType::CMYK(8)) } @@ -720,6 +726,11 @@ impl Decoder { reader.read_u32_into(&mut buffer[..bytes / 4])?; bytes / 4 } + (ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer)) + | (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer)) => { + reader.read_u64_into(&mut buffer[..bytes / 8])?; + bytes / 8 + } (ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => { reader.read_u64_into(&mut buffer[..bytes / 8])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 04ca7ef..ae1e573 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -142,6 +142,25 @@ fn test_rgb_u32() { } } +#[test] +fn test_rgb_u64() { + 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 img_res = decoder.read_image().unwrap(); + if let DecodingResult::U64(res) = img_res { + let mut res_sum: u64 = 0; + for x in res { + res_sum += x as u64; + } + + assert_eq!(res_sum, 2030834111716); + } + else { + panic!("Wrong bit depth") + } +} + #[test] fn test_string_tags() { // these files have null-terminated strings for their Software tag. One has extra bytes after diff --git a/tests/images/gradient-3c-64b.tiff b/tests/images/gradient-3c-64b.tiff new file mode 100644 index 0000000000000000000000000000000000000000..0c5c4dcb5820a7cfbbf4fe644b7530451f88dc90 GIT binary patch literal 6480 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 SK%@M=QfKMKyj6Wa Date: Tue, 10 Dec 2019 20:11:45 +0000 Subject: [PATCH 07/11] 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()); From 6e61de81b695027dc4658b525cc393120181f5d6 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Wed, 11 Dec 2019 12:56:11 +0000 Subject: [PATCH 08/11] Simplify color type matching --- src/decoder/mod.rs | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 933d6c1..fd75e1c 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -352,30 +352,24 @@ impl Decoder { pub fn colortype(&mut self) -> TiffResult { match self.photometric_interpretation { // TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm - PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8, 8] => { - Ok(ColorType::RGBA(8)) - } - PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8] => { - Ok(ColorType::RGB(8)) - } - PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16, 16] => { - Ok(ColorType::RGBA(16)) - } - PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16] => { - Ok(ColorType::RGB(16)) - } - PhotometricInterpretation::RGB if self.bits_per_sample == [32, 32, 32, 32] => { - Ok(ColorType::RGBA(32)) - } - PhotometricInterpretation::RGB if self.bits_per_sample == [32, 32, 32] => { - Ok(ColorType::RGB(32)) - } - PhotometricInterpretation::RGB if self.bits_per_sample == [64, 64, 64, 64] => { - Ok(ColorType::RGBA(64)) - } - PhotometricInterpretation::RGB if self.bits_per_sample == [64, 64, 64] => { - Ok(ColorType::RGB(64)) - } + PhotometricInterpretation::RGB => { + match self.bits_per_sample.as_slice() { + [8, 8, 8] => Ok(ColorType::RGB(8)), + [8, 8, 8, 8] => Ok(ColorType::RGBA(8)), + [16, 16, 16] => Ok(ColorType::RGB(16)), + [16, 16, 16, 16] => Ok(ColorType::RGBA(16)), + [32, 32, 32] => Ok(ColorType::RGB(32)), + [32, 32, 32, 32] => Ok(ColorType::RGBA(32)), + [64, 64, 64] => Ok(ColorType::RGB(64)), + [64, 64, 64, 64] => Ok(ColorType::RGBA(64)), + _ => Err(TiffError::UnsupportedError( + TiffUnsupportedError::InterpretationWithBits( + self.photometric_interpretation, + self.bits_per_sample.clone(), + ), + )), + } + }, PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => { Ok(ColorType::CMYK(8)) } From e5d2e7862c8338091b606ec2718e4bd235bf3873 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Wed, 11 Dec 2019 15:03:05 +0000 Subject: [PATCH 09/11] Parse all homogeneous bit depth RGB(A) color types Make the behavior of RGB and RGBA like Gray in returning ColorTypes for all homogeneous bit depths, even if those depths are not 8, 16, 32 or 64. Note that this does not change which color types can actually be decoded. --- src/decoder/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index fd75e1c..5bd3a81 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -353,15 +353,9 @@ impl Decoder { match self.photometric_interpretation { // TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm PhotometricInterpretation::RGB => { - match self.bits_per_sample.as_slice() { - [8, 8, 8] => Ok(ColorType::RGB(8)), - [8, 8, 8, 8] => Ok(ColorType::RGBA(8)), - [16, 16, 16] => Ok(ColorType::RGB(16)), - [16, 16, 16, 16] => Ok(ColorType::RGBA(16)), - [32, 32, 32] => Ok(ColorType::RGB(32)), - [32, 32, 32, 32] => Ok(ColorType::RGBA(32)), - [64, 64, 64] => Ok(ColorType::RGB(64)), - [64, 64, 64, 64] => Ok(ColorType::RGBA(64)), + match self.bits_per_sample[..] { + [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)), + [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)), _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( self.photometric_interpretation, From c72a8d364deffd9c149a85f1e5a68079450c6c08 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Mon, 16 Dec 2019 18:53:44 +0000 Subject: [PATCH 10/11] Add tests for exotic 12-bit sample images These images should correctly report their color depths, but fail to decode as they are unsupported. --- tests/decode_images.rs | 24 ++++++++++++++++++++++++ tests/images/12bit.cropped.rgb.tiff | Bin 0 -> 18748 bytes tests/images/12bit.cropped.tiff | Bin 0 -> 6366 bytes 3 files changed, 24 insertions(+) create mode 100644 tests/images/12bit.cropped.rgb.tiff create mode 100644 tests/images/12bit.cropped.tiff diff --git a/tests/decode_images.rs b/tests/decode_images.rs index ae1e573..910d43e 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -44,6 +44,18 @@ fn test_rgb_u8() { } } +#[test] +fn test_gray_u12() { + let img_file = + File::open("./tests/images/12bit.cropped.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(12)); + assert!(match decoder.read_image() { + Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true, + _ => false, + }); +} + #[test] fn test_gray_u16() { let img_file = @@ -104,6 +116,18 @@ fn test_gray_u64() { } } +#[test] +fn test_rgb_u12() { + let img_file = + File::open("./tests/images/12bit.cropped.rgb.tiff").expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(12)); + assert!(match decoder.read_image() { + Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true, + _ => false, + }); +} + #[test] fn test_rgb_u16() { let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!"); diff --git a/tests/images/12bit.cropped.rgb.tiff b/tests/images/12bit.cropped.rgb.tiff new file mode 100644 index 0000000000000000000000000000000000000000..8c8bb58f4b30777f3652e4a5d7eb2f277ff9eb96 GIT binary patch literal 18748 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 literal 0 HcmV?d00001 From abd6aee19faa4b2ebb26149c1d48049caec62014 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Mon, 16 Dec 2019 22:10:57 +0000 Subject: [PATCH 11/11] Remove outdate and ambiguous TODO (minor) --- src/decoder/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 5bd3a81..f879df6 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -351,7 +351,6 @@ impl Decoder { pub fn colortype(&mut self) -> TiffResult { match self.photometric_interpretation { - // TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm PhotometricInterpretation::RGB => { match self.bits_per_sample[..] { [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),