diff --git a/src/bytecast.rs b/src/bytecast.rs index 906ac79..0d8869f 100644 --- a/src/bytecast.rs +++ b/src/bytecast.rs @@ -23,9 +23,9 @@ macro_rules! integral_slice_as_bytes{($int:ty, $const:ident $(,$mut:ident)*) => })* }} -integral_slice_as_bytes!(i8, i8_as_ne_bytes); +integral_slice_as_bytes!(i8, i8_as_ne_bytes, i8_as_ne_mut_bytes); integral_slice_as_bytes!(u16, u16_as_ne_bytes, u16_as_ne_mut_bytes); -integral_slice_as_bytes!(i16, i16_as_ne_bytes); +integral_slice_as_bytes!(i16, i16_as_ne_bytes, i16_as_ne_mut_bytes); integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes); integral_slice_as_bytes!(i32, i32_as_ne_bytes); integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes); diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 6ff7163..8ac4512 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -32,6 +32,10 @@ pub enum DecodingResult { F32(Vec), /// A vector of 64 bit IEEE floats F64(Vec), + /// A vector of 8 bit signed ints + I8(Vec), + /// A vector of 16 bit signed ints + I16(Vec), } impl DecodingResult { @@ -83,6 +87,22 @@ impl DecodingResult { } } + fn new_i8(size: usize, limits: &Limits) -> TiffResult { + if size > limits.decoding_buffer_size / std::mem::size_of::() { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::I8(vec![0; size])) + } + } + + fn new_i16(size: usize, limits: &Limits) -> TiffResult { + if size > limits.decoding_buffer_size / 2 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::I16(vec![0; size])) + } + } + pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer { match *self { DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]), @@ -91,6 +111,8 @@ impl DecodingResult { 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..]), + DecodingResult::I8(ref mut buf) => DecodingBuffer::I8(&mut buf[start..]), + DecodingResult::I16(ref mut buf) => DecodingBuffer::I16(&mut buf[start..]), } } } @@ -109,6 +131,10 @@ pub enum DecodingBuffer<'a> { F32(&'a mut [f32]), /// A slice of 64 bit IEEE floats F64(&'a mut [f64]), + /// A slice of 8 bits signed ints + I8(&'a mut [i8]), + /// A slice of 16 bits signed ints + I16(&'a mut [i16]), } impl<'a> DecodingBuffer<'a> { @@ -120,6 +146,8 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U64(ref buf) => buf.len(), DecodingBuffer::F32(ref buf) => buf.len(), DecodingBuffer::F64(ref buf) => buf.len(), + DecodingBuffer::I8(ref buf) => buf.len(), + DecodingBuffer::I16(ref buf) => buf.len(), } } @@ -131,6 +159,8 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U64(_) => 8, DecodingBuffer::F32(_) => 4, DecodingBuffer::F64(_) => 8, + DecodingBuffer::I8(_) => 1, + DecodingBuffer::I16(_) => 2, } } @@ -145,6 +175,8 @@ impl<'a> DecodingBuffer<'a> { DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf), DecodingBuffer::F32(ref mut buf) => DecodingBuffer::F32(buf), DecodingBuffer::F64(ref mut buf) => DecodingBuffer::F64(buf), + DecodingBuffer::I8(ref mut buf) => DecodingBuffer::I8(buf), + DecodingBuffer::I16(ref mut buf) => DecodingBuffer::I16(buf), } } } @@ -257,6 +289,18 @@ impl Wrapping for u64 { } } +impl Wrapping for i8 { + fn wrapping_add(&self, other: Self) -> Self { + i8::wrapping_add(*self, other) + } +} + +impl Wrapping for i16 { + fn wrapping_add(&self, other: Self) -> Self { + i16::wrapping_add(*self, other) + } +} + fn rev_hpredict_nsamp(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()> where T: Copy + Wrapping, @@ -320,6 +364,12 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) TiffUnsupportedError::HorizontalPredictor(color_type), )); } + DecodingBuffer::I8(buf) => { + rev_hpredict_nsamp(buf, size, samples)?; + } + DecodingBuffer::I16(buf) => { + rev_hpredict_nsamp(buf, size, samples)?; + } } Ok(()) } @@ -872,6 +922,19 @@ impl Decoder { reader.read_u64_into(&mut buffer[..bytes / 8])?; bytes / 8 } + (ColorType::RGBA(8), DecodingBuffer::I8(ref mut buffer)) + | (ColorType::RGB(8), DecodingBuffer::I8(ref mut buffer)) + | (ColorType::CMYK(8), DecodingBuffer::I8(ref mut buffer)) => { + reader.read_i8_into(&mut buffer[..bytes])?; + bytes + } + (ColorType::RGBA(16), DecodingBuffer::I16(ref mut buffer)) + | (ColorType::RGB(16), DecodingBuffer::I16(ref mut buffer)) + | (ColorType::CMYK(16), DecodingBuffer::I16(ref mut buffer)) => { + reader.read_i16_into(&mut buffer[..bytes / 2])?; + bytes / 2 + } + (ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => { reader.read_u64_into(&mut buffer[..bytes / 8])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { @@ -899,11 +962,31 @@ impl Decoder { } bytes / 2 } + (ColorType::Gray(8), DecodingBuffer::I8(ref mut buffer)) => { + reader.read_i8_into(&mut buffer[..bytes])?; + if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + for datum in buffer[..bytes].iter_mut() { + *datum = !*datum; + } + } + bytes + } + // The following conversions interpret the image as in libtiff. + // In particular, MIN is white and MAX is black and not Zero as the name would imply. + (ColorType::Gray(16), DecodingBuffer::I16(ref mut buffer)) => { + reader.read_i16_into(&mut buffer[..bytes / 2])?; + if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + for datum in buffer[..bytes / 2].iter_mut() { + *datum = !*datum; + } + } + bytes / 2 + } (ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => { reader.read_exact(&mut buffer[..bytes])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { for byte in buffer[..bytes].iter_mut() { - *byte = 0xff - *byte + *byte = !*byte; } } bytes @@ -1017,7 +1100,6 @@ impl Decoder { pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> { self.initialize_strip_decoder()?; - let index = self.strip_decoder.as_ref().unwrap().strip_index; let offset = *self .strip_decoder @@ -1037,7 +1119,6 @@ impl Decoder { .ok_or(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, ))?; - let tag_rows = self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height); let rows_per_strip = usize::try_from(tag_rows)?; @@ -1055,13 +1136,11 @@ impl Decoder { } let units_read = self.expand_strip(buffer.copy(), offset, byte_count, buffer_size)?; - self.strip_decoder.as_mut().unwrap().strip_index += 1; if u32::try_from(index)? == self.strip_count()? { self.strip_decoder = None; } - if units_read < buffer_size { return Err(TiffError::FormatError( TiffFormatError::InconsistentStripSamples { @@ -1112,6 +1191,13 @@ impl Decoder { TiffUnsupportedError::UnsupportedBitsPerChannel(n), )), }, + SampleFormat::Int => match max_sample_bits { + n if n <= 8 => DecodingResult::new_i8(buffer_size, &self.limits), + n if (n > 8) & (n <= 16) => DecodingResult::new_i16(buffer_size, &self.limits), + n => Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedBitsPerChannel(n), + )), + }, format => { Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format.clone()]).into()) } @@ -1132,7 +1218,6 @@ impl Decoder { ); let mut result = self.result_buffer(strip_height)?; - self.read_strip_to_buffer(result.as_buffer(0))?; Ok(result) @@ -1154,7 +1239,8 @@ impl Decoder { let mut result = self.result_buffer(usize::try_from(self.height)?)?; for i in 0..usize::try_from(self.strip_count()?)? { - self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?; + let r = result.as_buffer(samples_per_strip * i); + self.read_strip_to_buffer(r)?; } Ok(result) } diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 9b89046..862da76 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -48,6 +48,35 @@ pub trait EndianReader: Read { Ok(()) } + /// Reads an i8 + #[inline(always)] + fn read_i8(&mut self) -> Result { + let mut n = [0u8; 1]; + self.read_exact(&mut n)?; + Ok(match self.byte_order() { + ByteOrder::LittleEndian => i8::from_le_bytes(n), + ByteOrder::BigEndian => i8::from_be_bytes(n), + }) + } + + #[inline(always)] + fn read_i8_into(&mut self, buffer: &mut [i8]) -> Result<(), io::Error> { + self.read_exact(bytecast::i8_as_ne_mut_bytes(buffer))?; + match self.byte_order() { + ByteOrder::LittleEndian => { + for n in buffer { + *n = i8::from_le(*n); + } + } + ByteOrder::BigEndian => { + for n in buffer { + *n = i8::from_be(*n); + } + } + } + Ok(()) + } + /// Reads an i16 #[inline(always)] fn read_i16(&mut self) -> Result { @@ -59,6 +88,24 @@ pub trait EndianReader: Read { }) } + #[inline(always)] + fn read_i16_into(&mut self, buffer: &mut [i16]) -> Result<(), io::Error> { + self.read_exact(bytecast::i16_as_ne_mut_bytes(buffer))?; + match self.byte_order() { + ByteOrder::LittleEndian => { + for n in buffer { + *n = i16::from_le(*n); + } + } + ByteOrder::BigEndian => { + for n in buffer { + *n = i16::from_be(*n); + } + } + } + Ok(()) + } + /// Reads an u32 #[inline(always)] fn read_u32(&mut self) -> Result { diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 2336178..1af340f 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -29,7 +29,9 @@ macro_rules! test_image_sum { } test_image_sum!(test_image_sum_u8, U8, u64); +test_image_sum!(test_image_sum_i8, I8, i64); test_image_sum!(test_image_sum_u16, U16, u64); +test_image_sum!(test_image_sum_i16, I16, i64); 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); @@ -135,6 +137,26 @@ fn test_rgb_f32() { test_image_sum_f32("gradient-3c-32b-float.tiff", ColorType::RGB(32), 472.8405); } +#[test] +fn test_int8() { + test_image_sum_i8("int8.tif", ColorType::Gray(8), 3111) +} + +#[test] +fn test_int8_rgb() { + test_image_sum_i8("int8_rgb.tif", ColorType::RGB(8), -10344) +} + +#[test] +fn test_int16() { + test_image_sum_i16("int16.tif", ColorType::Gray(16), 354396); +} + +#[test] +fn test_int16_rgb() { + test_image_sum_i16("int16_rgb.tif", ColorType::RGB(16), 1063188); +} + #[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/int16.tif b/tests/images/int16.tif new file mode 100755 index 0000000..5853b06 Binary files /dev/null and b/tests/images/int16.tif differ diff --git a/tests/images/int16_rgb.tif b/tests/images/int16_rgb.tif new file mode 100755 index 0000000..950a9ac Binary files /dev/null and b/tests/images/int16_rgb.tif differ diff --git a/tests/images/int8.tif b/tests/images/int8.tif new file mode 100644 index 0000000..807e0fd Binary files /dev/null and b/tests/images/int8.tif differ diff --git a/tests/images/int8_rgb.tif b/tests/images/int8_rgb.tif new file mode 100644 index 0000000..a0f6db0 Binary files /dev/null and b/tests/images/int8_rgb.tif differ