From c2084835fa85f011c087ed9a766262bc72803b61 Mon Sep 17 00:00:00 2001 From: Andrew Champion Date: Thu, 6 Feb 2020 19:05:21 +0000 Subject: [PATCH] Add support for IEEEFP Gray/RGB/RGBA/CMYK(64) decoding --- README.md | 8 ++--- src/decoder/mod.rs | 42 ++++++++++++++++++++++++ src/decoder/stream.rs | 21 ++++++++++++ tests/decode_images.rs | 6 ++++ tests/images/gradient-1c-64b-float.tiff | Bin 0 -> 2368 bytes 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 tests/images/gradient-1c-64b-float.tiff diff --git a/README.md b/README.md index d1b8453..b807724 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,12 @@ This table lists photometric interpretations and sample formats which are suppor | `PhotometricInterpretation` | UINT Format | IEEEFP Format | |-|-|-| -| `WhiteIsZero` | Gray(8\|16\|32\|64) | Gray(32) | -| `BlackIsZero` | Gray(8\|16\|32\|64) | Gray(32) | -| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | RGB(32), RGBA(32) | +| `WhiteIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) | +| `BlackIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) | +| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | RGB(32\|64), RGBA(32\|64) | | `RGBPalette` | | | | `Mask` | | | -| `CMYK` | CMYK(8\|16\|32\|64) | CMYK(32) | +| `CMYK` | CMYK(8\|16\|32\|64) | CMYK(32\|64) | | `YCbCr` | | | | `CIELab` | | | diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 00456bd..6ec0168 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -26,6 +26,8 @@ pub enum DecodingResult { U64(Vec), /// A vector of 32 bit IEEE floats F32(Vec), + /// A vector of 64 bit IEEE floats + F64(Vec), } impl DecodingResult { @@ -69,6 +71,14 @@ impl DecodingResult { } } + fn new_f64(size: usize, limits: &Limits) -> TiffResult { + if size > limits.decoding_buffer_size / std::mem::size_of::() { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::F64(vec![0.0; size])) + } + } + pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer { match *self { DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]), @@ -76,6 +86,7 @@ impl DecodingResult { DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]), DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]), DecodingResult::F32(ref mut buf) => DecodingBuffer::F32(&mut buf[start..]), + DecodingResult::F64(ref mut buf) => DecodingBuffer::F64(&mut buf[start..]), } } } @@ -92,6 +103,8 @@ pub enum DecodingBuffer<'a> { U64(&'a mut [u64]), /// A slice of 32 bit IEEE floats F32(&'a mut [f32]), + /// A slice of 64 bit IEEE floats + F64(&'a mut [f64]), } impl<'a> DecodingBuffer<'a> { @@ -102,6 +115,7 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U32(ref buf) => buf.len(), DecodingBuffer::U64(ref buf) => buf.len(), DecodingBuffer::F32(ref buf) => buf.len(), + DecodingBuffer::F64(ref buf) => buf.len(), } } @@ -112,6 +126,7 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U32(_) => 4, DecodingBuffer::U64(_) => 8, DecodingBuffer::F32(_) => 4, + DecodingBuffer::F64(_) => 8, } } @@ -125,6 +140,7 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf), DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf), DecodingBuffer::F32(ref mut buf) => DecodingBuffer::F32(buf), + DecodingBuffer::F64(ref mut buf) => DecodingBuffer::F64(buf), } } } @@ -220,6 +236,12 @@ impl Wrapping for f32 { } } +impl Wrapping for f64 { + fn wrapping_add(&self, other: Self) -> Self { + self + other + } +} + fn rev_hpredict_nsamp(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()> where T: Copy + Wrapping, @@ -264,6 +286,9 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) DecodingBuffer::F32(buf) => { rev_hpredict_nsamp(buf, size, samples)?; } + DecodingBuffer::F64(buf) => { + rev_hpredict_nsamp(buf, size, samples)?; + } } Ok(()) } @@ -678,6 +703,12 @@ impl Decoder { reader.read_f32_into(&mut buffer[..bytes / 4])?; bytes / 4 } + (ColorType::RGBA(64), DecodingBuffer::F64(ref mut buffer)) + | (ColorType::RGB(64), DecodingBuffer::F64(ref mut buffer)) + | (ColorType::CMYK(64), DecodingBuffer::F64(ref mut buffer)) => { + reader.read_f64_into(&mut buffer[..bytes / 8])?; + bytes / 8 + } (ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer)) | (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer)) | (ColorType::CMYK(64), DecodingBuffer::U64(ref mut buffer)) => { @@ -730,6 +761,16 @@ impl Decoder { } bytes / 4 } + (ColorType::Gray(64), DecodingBuffer::F64(ref mut buffer)) => { + reader.read_f64_into(&mut buffer[..bytes / 8])?; + if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + for datum in buffer[..bytes / 8].iter_mut() { + // FIXME: assumes [0, 1) range for floats + *datum = 1.0 - *datum + } + } + bytes / 8 + } (type_, _) => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedColorType(type_), @@ -857,6 +898,7 @@ impl Decoder { }, SampleFormat::IEEEFP => match max_sample_bits { 32 => DecodingResult::new_f32(buffer_size, &self.limits), + 64 => DecodingResult::new_f64(buffer_size, &self.limits), n => { Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index d7645e1..cdee074 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -121,6 +121,27 @@ pub trait EndianReader: Read { } } } + + /// Reads an f64 + #[inline(always)] + fn read_f64(&mut self) -> Result { + match self.byte_order() { + ByteOrder::LittleEndian => ::read_f64::(self), + ByteOrder::BigEndian => ::read_f64::(self), + } + } + + #[inline(always)] + fn read_f64_into(&mut self, buffer: &mut [f64]) -> Result<(), io::Error> { + match self.byte_order() { + ByteOrder::LittleEndian => { + ::read_f64_into::(self, buffer) + } + ByteOrder::BigEndian => { + ::read_f64_into::(self, buffer) + } + } + } } /// Reader that decompresses DEFLATE streams diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 5e05002..f8642c2 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -33,6 +33,7 @@ test_image_sum!(test_image_sum_u16, U16, u64); test_image_sum!(test_image_sum_u32, U32, u64); test_image_sum!(test_image_sum_u64, U64, u64); test_image_sum!(test_image_sum_f32, F32, f32); +test_image_sum!(test_image_sum_f64, F64, f64); /// Tests that a decoder can be constructed for an image and the color type /// read from the IFD and is of the appropriate type, but the type is @@ -93,6 +94,11 @@ fn test_gray_f32() { test_image_sum_f32("gradient-1c-32b-float.tiff", ColorType::Gray(32), 128.03194); } +#[test] +fn test_gray_f64() { + test_image_sum_f64("gradient-1c-64b-float.tiff", ColorType::Gray(64), 128.0319210877642); +} + #[test] fn test_rgb_u8() { test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108); diff --git a/tests/images/gradient-1c-64b-float.tiff b/tests/images/gradient-1c-64b-float.tiff new file mode 100644 index 0000000000000000000000000000000000000000..a2f843cdbed9f4a1042e1c75840991f5b90ea38a GIT binary patch literal 2368 zcmbu)dr(wW90%~TZaT>%pqM$@*kvhm7-nrL%Wx*ol#g+gQC?2!sMiP-9}|MQqCJiy zjW|XosN*wk7!c_CN)%Ay073%dgNK^Wn+h2SWMV!lr+dD?b%y`jndNi7zwiB>yG)uC z%${eA`Ln0&IcBp?_}IK>Yhd%9RoBaA$1~hI+r4Ks@6+S#YwN`ZTC+3Ox4(CnTMxb1 z@KCYBkVh+NS>7qqle`vxg#rADKqBeWBzYP0p>yV9?YM- zasbXxI0)BmJEYWR79CdVL92d*b^Hh{cNUU0N8#}7V@mDb<|3t$UsH^E%8nAu!-|i? z!%I$}zm{K$T=nr!N_|XanNq$RE}?b;vgwiK$R#JCXrED`GGB8A%G$Y=DvJ+qlNSO{ zqW7tRV(FYJm08n8$bSQz7Z3H8L(o{$ z3gw@!^QxEI-B2X$fqK^inwN}fz&?emP zUJO~-akAnP ztB^-JE~(y3TLiV_GRQ(#ZZtp%HP4`u-0n7xn#B-J^dK zLRL?PqB;e#&$mJ?C7;|^M*Wr!>WzUn)xMTF0-7TtAq$vCxnVQZqYgs3p%Q8v?o(gr zcMI!=z@}L>i0QE0#(tPcJJ8Iv2>?9piAonbR zY)d-SLOf7Btc0?(iyS=oceSs-7YYTN1dZm^FTPP15(608iTce?Ap8~~%)zC;!rCe74`3D!EIk1Ji5hUjFAnmit&|V#rCeVP+2c+q0(y6;eHr;W6b0dw&o6@f$|lO8M=0-X Kq+D~aYwUl_b(c#3 literal 0 HcmV?d00001