From 23751bd8c5729d7af551959ea05d8b799559371b Mon Sep 17 00:00:00 2001 From: Etienne Prothon Date: Thu, 23 Jul 2020 01:33:41 +0200 Subject: [PATCH 1/9] feat: Support decoding big tiff --- src/decoder/ifd.rs | 267 ++++++++++++++++++++++++++--------------- src/decoder/mod.rs | 119 ++++++++++++------ tests/encode_images.rs | 40 +++--- 3 files changed, 270 insertions(+), 156 deletions(-) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 58965d1..d5b9e0e 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -15,18 +15,18 @@ use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational}; #[allow(unused_qualifications)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Value { - Signed(i32), - Unsigned(u32), + Signed(i64), + Unsigned(u64), List(Vec), - Rational(u32, u32), - SRational(i32, i32), + Rational(u64, u64), + SRational(i64, i64), Ascii(String), #[doc(hidden)] // Do not match against this. __NonExhaustive, } impl Value { - pub fn into_u32(self) -> TiffResult { + pub fn into_u64(self) -> TiffResult { match self { Unsigned(val) => Ok(val), val => Err(TiffError::FormatError( @@ -35,7 +35,7 @@ impl Value { } } - pub fn into_i32(self) -> TiffResult { + pub fn into_i64(self) -> TiffResult { match self { Signed(val) => Ok(val), val => Err(TiffError::FormatError( @@ -44,25 +44,25 @@ impl Value { } } - pub fn into_u32_vec(self) -> TiffResult> { + pub fn into_u64_vec(self) -> TiffResult> { match self { List(vec) => { let mut new_vec = Vec::with_capacity(vec.len()); for v in vec { - new_vec.push(v.into_u32()?) + new_vec.push(v.into_u64()?) } Ok(new_vec) } Unsigned(val) => Ok(vec![val]), Rational(numerator, denominator) => Ok(vec![numerator, denominator]), - Ascii(val) => Ok(val.chars().map(u32::from).collect()), + Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()), val => Err(TiffError::FormatError( TiffFormatError::UnsignedIntegerExpected(val), )), } } - pub fn into_i32_vec(self) -> TiffResult> { + pub fn into_i64_vec(self) -> TiffResult> { match self { List(vec) => { let mut new_vec = Vec::with_capacity(vec.len()); @@ -72,7 +72,7 @@ impl Value { new_vec.push(numerator); new_vec.push(denominator); } - _ => new_vec.push(v.into_i32()?) + _ => new_vec.push(v.into_i64()?), } } Ok(new_vec) @@ -85,12 +85,12 @@ impl Value { } } } - + #[derive(Clone)] pub struct Entry { type_: Type, - count: u32, - offset: [u8; 4], + count: u64, + offset: [u8; 8], } impl ::std::fmt::Debug for Entry { @@ -103,7 +103,7 @@ impl ::std::fmt::Debug for Entry { } impl Entry { - pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry { + pub fn new(type_: Type, count: u64, offset: [u8; 8]) -> Entry { Entry { type_, count, @@ -122,94 +122,156 @@ impl Entry { decoder: &mut super::Decoder, ) -> TiffResult { let bo = decoder.byte_order(); - match (self.type_, self.count) { - // TODO check if this could give wrong results - // at a different endianess of file/computer. - (Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))), - (Type::BYTE, 2) => offset_to_bytes(2, self), - (Type::BYTE, 3) => offset_to_bytes(3, self), - (Type::BYTE, 4) => offset_to_bytes(4, self), - (Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Unsigned(u32::from(decoder.read_byte()?))) - }), - (Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))), - (Type::SBYTE, 2) => offset_to_sbytes(2, self), - (Type::SBYTE, 3) => offset_to_sbytes(3, self), - (Type::SBYTE, 4) => offset_to_sbytes(4, self), - (Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Signed(i32::from(decoder.read_byte()? as i8))) - }), - (Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))), - (Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))), - (Type::SHORT, 2) => { - let mut r = self.r(bo); - Ok(List(vec![ - Unsigned(u32::from(r.read_u16()?)), - Unsigned(u32::from(r.read_u16()?)), - ])) - } - (Type::SSHORT, 2) => { - let mut r = self.r(bo); - Ok(List(vec![ - Signed(i32::from(r.read_i16()?)), - Signed(i32::from(r.read_i16()?)), - ])) - } - (Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Unsigned(u32::from(decoder.read_short()?))) - }), - (Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Signed(i32::from(decoder.read_sshort()?))) - }), - (Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)), - (Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)), - (Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Unsigned(decoder.read_long()?)) - }), - (Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Signed(decoder.read_slong()?)) - }), - (Type::RATIONAL, 1) => { - decoder.goto_offset(self.r(bo).read_u32()?)?; - let numerator = decoder.read_long()?; - let denominator = decoder.read_long()?; - Ok(Rational(numerator, denominator)) - } - (Type::SRATIONAL, 1) => { - decoder.goto_offset(self.r(bo).read_u32()?)?; - let numerator = decoder.read_slong()?; - let denominator = decoder.read_slong()?; - Ok(SRational(numerator, denominator)) - } - (Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Rational( - decoder.read_long()?, - decoder.read_long()?, - )) - }), - (Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(SRational( - decoder.read_slong()?, - decoder.read_slong()?, - )) - }), - (Type::ASCII, n) => { - let n = usize::try_from(n)?; - if n > limits.decoding_buffer_size { - return Err(TiffError::LimitsExceeded); + let bigtiff_value = if decoder.bigtiff && 4 < self.count && self.count <= 8 { + match (self.type_, self.count) { + (Type::BYTE, n) => Some(offset_to_bytes(n as usize, self)?), + (Type::SBYTE, n) => Some(offset_to_sbytes(n as usize, self)?), + (Type::SHORT, 3) => { + let mut r = self.r(bo); + Some(List(vec![ + Unsigned(u64::from(r.read_u16()?)), + Unsigned(u64::from(r.read_u16()?)), + Unsigned(u64::from(r.read_u16()?)), + ])) } - decoder.goto_offset(self.r(bo).read_u32()?)?; - let string = decoder.read_string(n)?; - Ok(Ascii(string)) + (Type::SSHORT, 3) => { + let mut r = self.r(bo); + Some(List(vec![ + Signed(i64::from(r.read_i16()?)), + Signed(i64::from(r.read_i16()?)), + Signed(i64::from(r.read_i16()?)), + ])) + } + (Type::SHORT, 4) => { + let mut r = self.r(bo); + Some(List(vec![ + Unsigned(u64::from(r.read_u16()?)), + Unsigned(u64::from(r.read_u16()?)), + Unsigned(u64::from(r.read_u16()?)), + Unsigned(u64::from(r.read_u16()?)), + ])) + } + (Type::SSHORT, 4) => { + let mut r = self.r(bo); + Some(List(vec![ + Signed(i64::from(r.read_i16()?)), + Signed(i64::from(r.read_i16()?)), + Signed(i64::from(r.read_i16()?)), + Signed(i64::from(r.read_i16()?)), + ])) + } + (Type::LONG, 2) => { + let mut r = self.r(bo); + Some(List(vec![ + Unsigned(r.read_u32()?.into()), + Unsigned(r.read_u32()?.into()), + ])) + } + (Type::SLONG, 2) => { + let mut r = self.r(bo); + Some(List(vec![ + Signed(r.read_i32()?.into()), + Signed(r.read_i32()?.into()), + ])) + } + (Type::RATIONAL, 1) => { + let mut r = self.r(bo); + let numerator = r.read_u32()?.into(); + let denominator = r.read_u32()?.into(); + Some(Rational(numerator, denominator)) + } + (Type::SRATIONAL, 1) => { + let mut r = self.r(bo); + let numerator = r.read_i32()?.into(); + let denominator = r.read_i32()?.into(); + Some(SRational(numerator, denominator)) + } + _ => None + } + } else { + None + }; + + if let Some(v) = bigtiff_value { + Ok(v) + } else { + match (self.type_, self.count) { + // TODO check if this could give wrong results + // at a different endianess of file/computer. + (Type::BYTE, 1) => Ok(Unsigned(u64::from(self.offset[0]))), + (Type::BYTE, 2) => offset_to_bytes(2, self), + (Type::BYTE, 3) => offset_to_bytes(3, self), + (Type::BYTE, 4) => offset_to_bytes(4, self), + (Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Unsigned(u64::from(decoder.read_byte()?))) + }), + (Type::SBYTE, 1) => Ok(Signed(i64::from(self.offset[0] as i8))), + (Type::SBYTE, 2) => offset_to_sbytes(2, self), + (Type::SBYTE, 3) => offset_to_sbytes(3, self), + (Type::SBYTE, 4) => offset_to_sbytes(4, self), + (Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Signed(i64::from(decoder.read_byte()? as i8))) + }), + (Type::SHORT, 1) => Ok(Unsigned(u64::from(self.r(bo).read_u16()?))), + (Type::SSHORT, 1) => Ok(Signed(i64::from(self.r(bo).read_i16()?))), + (Type::SHORT, 2) => { + let mut r = self.r(bo); + Ok(List(vec![ + Unsigned(u64::from(r.read_u16()?)), + Unsigned(u64::from(r.read_u16()?)), + ])) + } + (Type::SSHORT, 2) => { + let mut r = self.r(bo); + Ok(List(vec![ + Signed(i64::from(r.read_i16()?)), + Signed(i64::from(r.read_i16()?)), + ])) + } + (Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Unsigned(u64::from(decoder.read_short()?))) + }), + (Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Signed(i64::from(decoder.read_sshort()?))) + }), + (Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?.into())), + (Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?.into())), + (Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Unsigned(decoder.read_long()?.into())) + }), + (Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Signed(decoder.read_slong()?.into())) + }), + (Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Rational(decoder.read_long()?.into(), decoder.read_long()?.into())) + }), + (Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(SRational(decoder.read_slong()?.into(), decoder.read_slong()?.into())) + }), + (Type::ASCII, n) => { + println!("ASCIII COUNT {}", n); + let n = usize::try_from(n)?; + if n > limits.decoding_buffer_size { + return Err(TiffError::LimitsExceeded); + } + let offset = if decoder.bigtiff { + self.r(bo).read_u64()? + } else { + self.r(bo).read_u32()?.into() + }; + decoder.goto_offset(offset)?; + let string = decoder.read_string(n)?; + Ok(Ascii(string)) + } + _ => Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedDataType, + )), } - _ => Err(TiffError::UnsupportedError( - TiffUnsupportedError::UnsupportedDataType, - )), } } #[inline] - fn decode_offset(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder, decode_fn: F) -> TiffResult + fn decode_offset(&self, value_count: u64, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder, decode_fn: F) -> TiffResult where R: Read + Seek, F: Fn(&mut super::Decoder) -> TiffResult, @@ -220,7 +282,12 @@ impl Entry { } let mut v = Vec::with_capacity(value_count); - decoder.goto_offset(self.r(bo).read_u32()?)?; + let offset = if decoder.bigtiff { + self.r(bo).read_u64()? + } else { + self.r(bo).read_u32()?.into() + }; + decoder.goto_offset(offset)?; for _ in 0..value_count { v.push(decode_fn(decoder)?) } @@ -234,7 +301,7 @@ fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult { Ok(List( entry.offset[0..n] .iter() - .map(|&e| Unsigned(u32::from(e))) + .map(|&e| Unsigned(u64::from(e))) .collect() )) } @@ -245,7 +312,7 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult { Ok(List( entry.offset[0..n] .iter() - .map(|&e| Signed(i32::from(e as i8))) + .map(|&e| Signed(i64::from(e as i8))) .collect() )) } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index a2cd6a0..c34d228 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -1,4 +1,4 @@ -use std::convert::TryFrom; +use std::convert::{TryInto, TryFrom}; use std::collections::HashMap; use std::io::{self, Read, Seek}; use std::cmp; @@ -116,8 +116,8 @@ impl<'a> DecodingBuffer<'a> { #[derive(Debug)] struct StripDecodeState { strip_index: usize, - strip_offsets: Vec, - strip_bytes: Vec, + strip_offsets: Vec, + strip_bytes: Vec, } /// Decoding limits @@ -157,8 +157,9 @@ where { reader: SmartReader, byte_order: ByteOrder, + bigtiff: bool, limits: Limits, - next_ifd: Option, + next_ifd: Option, ifd: Option, width: u32, height: u32, @@ -248,6 +249,7 @@ impl Decoder { Decoder { reader: SmartReader::wrap(r, ByteOrder::LittleEndian), byte_order: ByteOrder::LittleEndian, + bigtiff: false, limits: Default::default(), next_ifd: None, ifd: None, @@ -330,12 +332,28 @@ impl Decoder { )) } } - if self.read_short()? != 42 { - return Err(TiffError::FormatError( + match self.read_short()? { + 42 => self.bigtiff = false, + 43 => { + self.bigtiff = true; + // Read bytesize of offsets (in bigtiff it's alway 8 but provide a way to move to 16 some day) + if self.read_short()? != 8 { + return Err(TiffError::FormatError( + TiffFormatError::TiffSignatureNotFound, + )) + } + // This constant should always be 0 + if self.read_short()? != 0 { + return Err(TiffError::FormatError( + TiffFormatError::TiffSignatureNotFound, + )) + } + }, + _ => return Err(TiffError::FormatError( TiffFormatError::TiffSignatureInvalid, - )); + )) } - self.next_ifd = match self.read_long()? { + self.next_ifd = match self.read_ifd_offset()? { 0 => None, n => Some(n), }; @@ -354,8 +372,8 @@ impl Decoder { /// To determine whether there are more images call `TIFFDecoder::more_images` instead. pub fn next_image(&mut self) -> TiffResult<()> { self.ifd = Some(self.read_ifd()?); - self.width = self.get_tag_u32(Tag::ImageWidth)?; - self.height = self.get_tag_u32(Tag::ImageLength)?; + self.width = u32::try_from(self.get_tag_u64(Tag::ImageWidth)?)?; + self.height = u32::try_from(self.get_tag_u64(Tag::ImageLength)?)?; self.strip_decoder = None; self.photometric_interpretation = self @@ -389,6 +407,15 @@ impl Decoder { self.byte_order } + #[inline] + pub fn read_ifd_offset(&mut self) -> Result { + if self.bigtiff { + self.read_long8() + } else { + self.read_long().map(u64::from) + } + } + /// Reads a TIFF byte value #[inline] pub fn read_byte(&mut self) -> Result { @@ -421,6 +448,12 @@ impl Decoder { self.reader.read_i32() } + #[inline] + pub fn read_long8(&mut self) -> Result { + self.reader.read_u64() + } + + /// Reads a string #[inline] pub fn read_string(&mut self, length: usize) -> TiffResult { @@ -433,17 +466,21 @@ impl Decoder { /// Reads a TIFF IFA offset/value field #[inline] - pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> { - let mut val = [0; 4]; + pub fn read_offset(&mut self) -> Result<[u8; 8], io::Error> { + let bytes_to_read = if self.bigtiff { 8 } else { 4 }; + let mut val = vec![0; bytes_to_read]; self.reader.read_exact(&mut val)?; - Ok(val) + if ! self.bigtiff { + val.append(&mut vec![0; 4]) + } + Ok(val[..].try_into().unwrap()) } /// Moves the cursor to the specified offset #[inline] - pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> { + pub fn goto_offset(&mut self, offset: u64) -> io::Result<()> { self.reader - .seek(io::SeekFrom::Start(u64::from(offset))) + .seek(io::SeekFrom::Start(offset)) .map(|_| ()) } @@ -465,12 +502,17 @@ impl Decoder { return Ok(None); } }; + let count= if self.bigtiff { + self.read_long8()? + } else { + self.read_long()?.into() + }; Ok(Some(( tag, ifd::Entry::new( type_, - self.read_long()?, // count - self.read_offset()?, // offset + count, + self.read_offset()? ), ))) } @@ -493,7 +535,7 @@ impl Decoder { }; dir.insert(tag, entry); } - self.next_ifd = match self.read_long()? { + self.next_ifd = match self.read_ifd_offset()? { 0 => None, n => Some(n), }; @@ -503,6 +545,7 @@ impl Decoder { /// Tries to retrieve a tag. /// Return `Ok(None)` if the tag is not present. pub fn find_tag(&mut self, tag: Tag) -> TiffResult> { + println!("IFD {:?}", self.ifd.as_ref().unwrap().get(&tag)); let entry = match self.ifd.as_ref().unwrap().get(&tag) { None => return Ok(None), Some(entry) => entry.clone(), @@ -514,9 +557,9 @@ impl Decoder { } /// Tries to retrieve a tag and convert it to the desired unsigned type. - pub fn find_tag_unsigned>(&mut self, tag: Tag) -> TiffResult> { + pub fn find_tag_unsigned>(&mut self, tag: Tag) -> TiffResult> { self.find_tag(tag)? - .map(|v| v.into_u32()).transpose()? + .map(|v| v.into_u64()).transpose()? .map(|value| T::try_from(value) .map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())) .transpose() @@ -524,9 +567,9 @@ impl Decoder { /// Tries to retrieve a vector of all a tag's values and convert them to /// the desired unsigned type. - pub fn find_tag_unsigned_vec>(&mut self, tag: Tag) -> TiffResult>> { + pub fn find_tag_unsigned_vec>(&mut self, tag: Tag) -> TiffResult>> { self.find_tag(tag)? - .map(|v| v.into_u32_vec()).transpose()? + .map(|v| v.into_u64_vec()).transpose()? .map(|v| v.into_iter() .map(|u| T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())) .collect()) @@ -535,7 +578,7 @@ impl Decoder { /// Tries to retrieve a tag and convert it to the desired unsigned type. /// Returns an error if the tag is not present. - pub fn get_tag_unsigned>(&mut self, tag: Tag) -> TiffResult { + pub fn get_tag_unsigned>(&mut self, tag: Tag) -> TiffResult { self.find_tag_unsigned(tag)? .ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into()) } @@ -552,13 +595,13 @@ impl Decoder { } /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult { - self.get_tag(tag)?.into_u32() + pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_u64() } /// Tries to retrieve a tag and convert it to the desired type. - pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult> { - self.get_tag(tag)?.into_u32_vec() + pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u64_vec() } /// Decompresses the strip into the supplied buffer. @@ -566,13 +609,13 @@ impl Decoder { fn expand_strip<'a>( &mut self, buffer: DecodingBuffer<'a>, - offset: u32, - length: u32, + offset: u64, + length: u64, max_uncompressed_length: usize, ) -> TiffResult { let color_type = self.colortype()?; self.goto_offset(offset)?; - let (bytes, mut reader): (usize, Box) = match self.compression_method { + let (bytes, mut reader): (usize, Box) = match self.compression_method { CompressionMethod::None => { let order = self.reader.byte_order; ( @@ -686,7 +729,8 @@ impl Decoder { /// Number of strips in image pub fn strip_count(&mut self) -> TiffResult { let rows_per_strip = self - .get_tag_u32(Tag::RowsPerStrip) + .get_tag_u64(Tag::RowsPerStrip) + .map(u32::try_from)? .unwrap_or(self.height); if rows_per_strip == 0 { @@ -698,8 +742,8 @@ impl Decoder { fn initialize_strip_decoder(&mut self) -> TiffResult<()> { if self.strip_decoder.is_none() { - let strip_offsets = self.get_tag_u32_vec(Tag::StripOffsets)?; - let strip_bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?; + let strip_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?; + let strip_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?; self.strip_decoder = Some(StripDecodeState { strip_index: 0, @@ -734,7 +778,8 @@ impl Decoder { ))?; let rows_per_strip = usize::try_from(self - .get_tag_u32(Tag::RowsPerStrip) + .get_tag_u64(Tag::RowsPerStrip) + .map(u32::try_from)? .unwrap_or(self.height))?; let strip_height = cmp::min( @@ -806,7 +851,8 @@ impl Decoder { let index = self.strip_decoder.as_ref().unwrap().strip_index; let rows_per_strip = usize::try_from(self - .get_tag_u32(Tag::RowsPerStrip) + .get_tag_u64(Tag::RowsPerStrip) + .map(u32::try_from)? .unwrap_or(self.height))?; let strip_height = cmp::min( @@ -825,7 +871,8 @@ impl Decoder { pub fn read_image(&mut self) -> TiffResult { self.initialize_strip_decoder()?; let rows_per_strip = usize::try_from(self - .get_tag_u32(Tag::RowsPerStrip) + .get_tag_u64(Tag::RowsPerStrip) + .map(u32::try_from)? .unwrap_or(self.height))?; let samples_per_strip = diff --git a/tests/encode_images.rs b/tests/encode_images.rs index 9d1ecfd..c2bd88d 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -165,11 +165,11 @@ fn test_multiple_byte() { { let mut decoder = Decoder::new(&mut data).unwrap(); - assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().unwrap(), [1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u32_vec().unwrap(), [1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u32_vec().unwrap(), [1, 2, 3]); - assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4]); - assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4, 5]); + assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u64_vec().unwrap(), [1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u64_vec().unwrap(), [1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u64_vec().unwrap(), [1, 2, 3]); + assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u64_vec().unwrap(), [1, 2, 3, 4]); + assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u64_vec().unwrap(), [1, 2, 3, 4, 5]); } } @@ -209,24 +209,24 @@ fn test_signed() { { let mut decoder = Decoder::new(&mut data).unwrap(); - assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -1, ); - assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i32_vec().unwrap(), [-1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i32_vec().unwrap(), [-1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); - assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4]); - assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4, -5], ); + assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i64().unwrap(), -1, ); + assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i64_vec().unwrap(), [-1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i64_vec().unwrap(), [-1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3]); + assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3, 4]); + assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3, 4, -5], ); - assert_eq!(decoder.get_tag(Tag::Unknown(65010)).unwrap().into_i32().unwrap(), -1); - assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i32_vec().unwrap(), [-1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i32_vec().unwrap(), [-1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); + assert_eq!(decoder.get_tag(Tag::Unknown(65010)).unwrap().into_i64().unwrap(), -1); + assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i64_vec().unwrap(), [-1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i64_vec().unwrap(), [-1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3]); - assert_eq!(decoder.get_tag(Tag::Unknown(65020)).unwrap().into_i32().unwrap(), -1); - assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i32_vec().unwrap(), [-1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i32_vec().unwrap(), [-1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65020)).unwrap().into_i64().unwrap(), -1); + assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i64_vec().unwrap(), [-1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i64_vec().unwrap(), [-1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i32_vec().unwrap(), [-1, 100]); - assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]); + assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i64_vec().unwrap(), [-1, 100]); + assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i64_vec().unwrap(), [-1_i64, 100, 2, 100]); } } From 797e3dca07b47aefa55f6a3dcda063d15e8fa2e6 Mon Sep 17 00:00:00 2001 From: Etienne Prothon Date: Fri, 24 Jul 2020 00:47:14 +0200 Subject: [PATCH 2/9] fix: Big tiff decoding and add tests --- src/decoder/ifd.rs | 13 +++++-- src/decoder/mod.rs | 4 +- tests/decode_bigtiff_images.rs | 46 +++++++++++++++++++++++ tests/images/bigtiff/BigTIFF.tif | Bin 0 -> 12480 bytes tests/images/bigtiff/BigTIFFLong.tif | Bin 0 -> 12480 bytes tests/images/bigtiff/BigTIFFMotorola.tif | Bin 0 -> 12480 bytes 6 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 tests/decode_bigtiff_images.rs create mode 100644 tests/images/bigtiff/BigTIFF.tif create mode 100644 tests/images/bigtiff/BigTIFFLong.tif create mode 100644 tests/images/bigtiff/BigTIFFMotorola.tif diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index d5b9e0e..64faf99 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -122,10 +122,16 @@ impl Entry { decoder: &mut super::Decoder, ) -> TiffResult { let bo = decoder.byte_order(); - let bigtiff_value = if decoder.bigtiff && 4 < self.count && self.count <= 8 { + let bigtiff_value = if decoder.bigtiff { match (self.type_, self.count) { - (Type::BYTE, n) => Some(offset_to_bytes(n as usize, self)?), - (Type::SBYTE, n) => Some(offset_to_sbytes(n as usize, self)?), + (Type::BYTE, 5) => Some(offset_to_bytes(5, self)?), + (Type::BYTE, 6) => Some(offset_to_bytes(6, self)?), + (Type::BYTE, 7) => Some(offset_to_bytes(7, self)?), + (Type::BYTE, 8) => Some(offset_to_bytes(8, self)?), + (Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?), + (Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?), + (Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?), + (Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?), (Type::SHORT, 3) => { let mut r = self.r(bo); Some(List(vec![ @@ -249,7 +255,6 @@ impl Entry { Ok(SRational(decoder.read_slong()?.into(), decoder.read_slong()?.into())) }), (Type::ASCII, n) => { - println!("ASCIII COUNT {}", n); let n = usize::try_from(n)?; if n > limits.decoding_buffer_size { return Err(TiffError::LimitsExceeded); diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index c34d228..6160af4 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -528,7 +528,8 @@ impl Decoder { } Some(offset) => self.goto_offset(offset)?, } - for _ in 0..self.read_short()? { + let num_tags = if self.bigtiff { self.read_long8()? } else { self.read_short()?.into() }; + for _ in 0..num_tags { let (tag, entry) = match self.read_entry()? { Some(val) => val, None => continue, // Unknown data type in tag, skip @@ -545,7 +546,6 @@ impl Decoder { /// Tries to retrieve a tag. /// Return `Ok(None)` if the tag is not present. pub fn find_tag(&mut self, tag: Tag) -> TiffResult> { - println!("IFD {:?}", self.ifd.as_ref().unwrap().get(&tag)); let entry = match self.ifd.as_ref().unwrap().get(&tag) { None => return Ok(None), Some(entry) => entry.clone(), diff --git a/tests/decode_bigtiff_images.rs b/tests/decode_bigtiff_images.rs new file mode 100644 index 0000000..9113f42 --- /dev/null +++ b/tests/decode_bigtiff_images.rs @@ -0,0 +1,46 @@ +extern crate tiff; + +use tiff::decoder::Decoder; +use tiff::tags::Tag; +use tiff::ColorType; + +use std::fs::File; +use std::path::PathBuf; + +const TEST_IMAGE_DIR: &str = "./tests/images/bigtiff"; + +#[test] +fn test_big_tiff() { + let filenames = ["BigTIFF.tif", "BigTIFFMotorola.tif", "BigTIFFLong.tif"]; + for filename in filenames.iter() { + let path = PathBuf::from(TEST_IMAGE_DIR).join(filename); + let img_file = File::open(path).expect("Cannot find test image!"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + assert_eq!( + decoder.dimensions().expect("Cannot get dimensions"), + (64, 64) + ); + assert_eq!( + decoder.colortype().expect("Cannot get colortype"), + ColorType::RGB(8) + ); + assert_eq!( + decoder + .get_tag_u64(Tag::StripOffsets) + .expect("Cannot get StripOffsets"), + 16 + ); + assert_eq!( + decoder + .get_tag_u64(Tag::RowsPerStrip) + .expect("Cannot get RowsPerStrip"), + 64 + ); + assert_eq!( + decoder + .get_tag_u64(Tag::StripByteCounts) + .expect("Cannot get StripByteCounts"), + 12288 + ) + } +} diff --git a/tests/images/bigtiff/BigTIFF.tif b/tests/images/bigtiff/BigTIFF.tif new file mode 100644 index 0000000000000000000000000000000000000000..8a0e5a2459cdfd006e51efc1ffdee2eba52d6784 GIT binary patch literal 12480 zcmeI3OKux65JX2d5P0bKYc&?hjT2Gd&~pjFXzT;|f2_)It+P+aMbV8qu<$&B(V= zXzO%me84XxvcN+@JnLA^ge*Ev0HpM5e=dZ0MGm*4{Q3+z*TPU!NE$SqwAW*@i3$Lu zc>DGk@z6h)oSl@cgR1CYxD|oL5^hD%Y=y-x>8FDr2hr5YB-oNJRI1u>BaCIAFhOD& zY_c#2n{6jCmeV*WNGyX*76xIn?Igx>8V3c5Ww6P@AZ)gs#8^(_pdhgfHdz>i&9;*m z%V``GB$mM@3xlxPb`oPbje~;3GT3Bc5O!^Q?-Sg&ekMAC*W`m-P7B6=KK}<#;Q9Rh zC$Rj+o-FjI??)#wmeckL{SM1ulZF2D{pcjda@szj-(eYSve2KtADzTlPTME+J1m1u z7W&ioqYVbXL@Hk#<-bkoi=zjBq@098c}NHn$DOD%VD9<&b?EVIjO!~q!L?U z3g^hNzLpdujLr$w!PZ`Fn)r6zgYUs~XwTX9sZZPHOAfb2R9XSZIn}||UOWB)bM3%E zxe0d-zYRfK2Q?$V1ITH0I=1$TGjJb1r$nFF8oV^-OMk?Nw>Xmb&Bx29Xrd zox`m%+n$wQ(zq;}X!sBe(d>LA=+(}O&^jmT9>HvTMsaIjJ1au#oTz&Qv+Wtht$ppR z2(5FX?h(wkXB4;gwX-6$&WXB5Fx#F{+}hX9iqJYI>K?&tdq#0zt^21hefK z#f`q{w*YxvUXM$WALZXPm&SwnBHnAsdy`)hNd4`H$$KfaKaKY;`B!=UGTv+XABf+^ T`zSvZ_+z|7ey5OXkFx#)S4oj9 literal 0 HcmV?d00001 diff --git a/tests/images/bigtiff/BigTIFFLong.tif b/tests/images/bigtiff/BigTIFFLong.tif new file mode 100644 index 0000000000000000000000000000000000000000..1a321ef1b92739fcfeb5c385146606c04e9cda19 GIT binary patch literal 12480 zcmeI3O>P@8428#WQDm7e+8&~t9wFP@$~Si?J@JW>En1Pnodsj&i9e5@1_t7~@W;o$ zxBqgHbq#(Umm~D`!W@32qkJNVQ0vv;On+con6Nje5)B%~=L-y-hJ z?O?j?UCwb0AX8pH$XU(cviJhO494OSxD~-*D+DYh6&?Z+dqtT<5M-H+K-);_YR9E* zw@@HKRzfp^dS+VzFz|HO(wC2x%=C=VGm&K8jw}2KCJQYPZI{Gl*(`M{(ukG~Hlw)B zLfc4p#s~aDA`3he#Iq5rS&&7?34m07>Cc4}uc+a6R9~MV=UNzQ3MqqTB<=N>G*JP7 zlyBc2BObV_E$Bi(Sxw4KCQF5{peu?#lZ7=&GI?|p*%*3U#o@S1#(%Voj%&*%T(2|S;_ z{{)ub*prR^^!@B4#&X#{q3^H^HreP;-_K5BESK#Q`VPxrla2oL{p=*ha@jti@30Iu z+2~K-&o&tR5~+M~l>auRFODAkk#Y(O;f-^OXXmLSj4PYb9V)h6@QY`u{1y~ExZ3zg zk1J>S?J1~5V2uwo#jiF3Q|r|^MMNV(QR><^27M@UYdT|YEQf`@jodp$nUnh4MJfp^ zOyL|wY+PFk5=Q5O8o}0HZJEUFx(DBb>Cm3D^{G$W)=LeyMpRk>$Tc;Bt-W^q1Lo?$ zLD_`6hTo>3Z3I1|cn6Tn>U3=F6=&=s2zFMwEzG&)0l(Cs4A)z78&|JNOSa^a7Z^lR zKz9*tmDze$ektR!Y@!i|V2Z&mjsm^fSrJ<2MBO8pt!EUs`r26$TIWRFBbcpc6u0`? zSrJ<2MBO8pt!EUs`r26$TIWRFBbcpc6u0`?SrJ<2MBO8pt!EUs`r26$TIWRFBbcpc z6gPa+ZvpZ;osUzI@8#b#rzVEri^R^Q?p1zCApO^$rtYQm{yP1j-<#B57AoruUHFDWu+`tWQ_DkuCrL literal 0 HcmV?d00001 diff --git a/tests/images/bigtiff/BigTIFFMotorola.tif b/tests/images/bigtiff/BigTIFFMotorola.tif new file mode 100644 index 0000000000000000000000000000000000000000..4699ecd7bc8ba60d79b8c9449b59affe83e94c1f GIT binary patch literal 12480 zcmeI3O;Q^%5QS%;D#a2ONe+<>N8ke7$u}o0J@r#dw%S6XcUGvGr`Lan~ zwoZ4(JN!Z-3p^CWvyRnF$fDx}KuW*#=R$~A@gJ|kx5^PBqDpl>c5yrAlm>{tX zHdz>i&9;*m%V``GB$mM@3xlxPb`oPbje~;3GT3Bc5H{ORVl1a|P>@&#n=A~%X4^@O z9bp8uUE@Ob|I z6Igy>PZs*q_oI^-%W3Qcgl4yy~3%+}NHn$DOD%VD9fom;0Ub5eb~NF}zy z6wZ-jeJv?S7@ZTUgRQ;VH1X}a1>b||(4MpHQ=hiYmmF@5sI&r*bE<=_y>|Qq=GuXS zaueovLcwO6GjTk4V*7(`M) zcMiA8YgepjSI9LhGEUdjzxX8O5!A?W_o`bE57M%(iC~xAwKO zBDBtlx<@eEo>AP|*UpO2Iw$HL!EAd*acf^YD?;m>sCxvn?HR?beeJ9Wt#hL85zMw{ z6nE42 Date: Fri, 24 Jul 2020 01:22:33 +0200 Subject: [PATCH 3/9] feat: better compatibility with old api --- src/decoder/ifd.rs | 87 ++++++++++++++++++++++++++++++++++++------ src/decoder/mod.rs | 74 +++++++++++++++++++++-------------- tests/encode_images.rs | 40 +++++++++---------- 3 files changed, 141 insertions(+), 60 deletions(-) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 64faf99..b12d428 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -1,7 +1,7 @@ //! Function for reading TIFF tags use std::collections::HashMap; -use std::convert::TryFrom; +use std::convert::{TryInto, TryFrom}; use std::io::{self, Read, Seek}; use std::mem; @@ -26,6 +26,24 @@ pub enum Value { } impl Value { + pub fn into_u32(self) -> TiffResult { + match self { + Unsigned(val) => Ok(u32::try_from(val)?), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_i32(self) -> TiffResult { + match self { + Signed(val) => Ok(i32::try_from(val)?), + val => Err(TiffError::FormatError( + TiffFormatError::SignedIntegerExpected(val), + )), + } + } + pub fn into_u64(self) -> TiffResult { match self { Unsigned(val) => Ok(val), @@ -44,6 +62,47 @@ impl Value { } } + pub fn into_u32_vec(self) -> TiffResult> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_u32()?) + } + Ok(new_vec) + } + Unsigned(val) => Ok(vec![u32::try_from(val)?]), + Rational(numerator, denominator) => Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?]), + Ascii(val) => Ok(val.chars().map(u32::from).collect()), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_i32_vec(self) -> TiffResult> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + match v { + SRational(numerator, denominator) => { + new_vec.push(i32::try_from(numerator)?); + new_vec.push(i32::try_from(denominator)?); + } + _ => new_vec.push(v.into_i32()?), + } + } + Ok(new_vec) + } + Signed(val) => Ok(vec![i32::try_from(val)?]), + SRational(numerator, denominator) => Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?]), + val => Err(TiffError::FormatError( + TiffFormatError::SignedIntegerExpected(val), + )), + } + } + pub fn into_u64_vec(self) -> TiffResult> { match self { List(vec) => { @@ -103,7 +162,13 @@ impl ::std::fmt::Debug for Entry { } impl Entry { - pub fn new(type_: Type, count: u64, offset: [u8; 8]) -> Entry { + pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry { + let mut offset = offset.to_vec(); + offset.append(&mut vec![0; 4]); + Entry::new_u64(type_, count.into(), offset[..].try_into().unwrap()) + } + + pub fn new_u64(type_: Type, count: u64, offset: [u8; 8]) -> Entry { Entry { type_, count, @@ -259,12 +324,11 @@ impl Entry { if n > limits.decoding_buffer_size { return Err(TiffError::LimitsExceeded); } - let offset = if decoder.bigtiff { - self.r(bo).read_u64()? + if decoder.bigtiff { + decoder.goto_offset_u64(self.r(bo).read_u64()?)? } else { - self.r(bo).read_u32()?.into() - }; - decoder.goto_offset(offset)?; + decoder.goto_offset(self.r(bo).read_u32()?)? + } let string = decoder.read_string(n)?; Ok(Ascii(string)) } @@ -287,12 +351,11 @@ impl Entry { } let mut v = Vec::with_capacity(value_count); - let offset = if decoder.bigtiff { - self.r(bo).read_u64()? + if decoder.bigtiff { + decoder.goto_offset_u64(self.r(bo).read_u64()?)? } else { - self.r(bo).read_u32()?.into() - }; - decoder.goto_offset(offset)?; + decoder.goto_offset(self.r(bo).read_u32()?)? + } for _ in 0..value_count { v.push(decode_fn(decoder)?) } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 6160af4..d179cc5 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -372,8 +372,8 @@ impl Decoder { /// To determine whether there are more images call `TIFFDecoder::more_images` instead. pub fn next_image(&mut self) -> TiffResult<()> { self.ifd = Some(self.read_ifd()?); - self.width = u32::try_from(self.get_tag_u64(Tag::ImageWidth)?)?; - self.height = u32::try_from(self.get_tag_u64(Tag::ImageLength)?)?; + self.width = self.get_tag_u32(Tag::ImageWidth)?; + self.height = self.get_tag_u32(Tag::ImageLength)?; self.strip_decoder = None; self.photometric_interpretation = self @@ -466,19 +466,31 @@ impl Decoder { /// Reads a TIFF IFA offset/value field #[inline] - pub fn read_offset(&mut self) -> Result<[u8; 8], io::Error> { - let bytes_to_read = if self.bigtiff { 8 } else { 4 }; - let mut val = vec![0; bytes_to_read]; - self.reader.read_exact(&mut val)?; - if ! self.bigtiff { - val.append(&mut vec![0; 4]) + pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> { + if self.bigtiff { + return Err(TiffError::FormatError(TiffFormatError::InconsistentSizesEncountered)) } - Ok(val[..].try_into().unwrap()) + let mut val = [0; 4]; + self.reader.read_exact(&mut val)?; + Ok(val) + } + + /// Reads a TIFF IFA offset/value field + #[inline] + pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> { + let mut val = [0; 8]; + self.reader.read_exact(&mut val)?; + Ok(val) } /// Moves the cursor to the specified offset #[inline] - pub fn goto_offset(&mut self, offset: u64) -> io::Result<()> { + pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> { + self.goto_offset_u64(offset.into()) + } + + #[inline] + pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> { self.reader .seek(io::SeekFrom::Start(offset)) .map(|_| ()) @@ -502,18 +514,22 @@ impl Decoder { return Ok(None); } }; - let count= if self.bigtiff { - self.read_long8()? + let entry = if self.bigtiff { + ifd::Entry::new_u64( + type_, + self.read_long8()?, + self.read_offset_u64()? + ) } else { - self.read_long()?.into() + ifd::Entry::new( + type_, + self.read_long()?, + self.read_offset()? + ) }; Ok(Some(( tag, - ifd::Entry::new( - type_, - count, - self.read_offset()? - ), + entry, ))) } @@ -526,7 +542,7 @@ impl Decoder { TiffFormatError::ImageFileDirectoryNotFound, )) } - Some(offset) => self.goto_offset(offset)?, + Some(offset) => self.goto_offset_u64(offset)?, } let num_tags = if self.bigtiff { self.read_long8()? } else { self.read_short()?.into() }; for _ in 0..num_tags { @@ -595,11 +611,17 @@ impl Decoder { } /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult { + self.get_tag(tag)?.into_u32() + } pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult { self.get_tag(tag)?.into_u64() } /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult> { + self.get_tag(tag)?.into_u32_vec() + } pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult> { self.get_tag(tag)?.into_u64_vec() } @@ -614,7 +636,7 @@ impl Decoder { max_uncompressed_length: usize, ) -> TiffResult { let color_type = self.colortype()?; - self.goto_offset(offset)?; + self.goto_offset_u64(offset)?; let (bytes, mut reader): (usize, Box) = match self.compression_method { CompressionMethod::None => { let order = self.reader.byte_order; @@ -729,8 +751,7 @@ impl Decoder { /// Number of strips in image pub fn strip_count(&mut self) -> TiffResult { let rows_per_strip = self - .get_tag_u64(Tag::RowsPerStrip) - .map(u32::try_from)? + .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height); if rows_per_strip == 0 { @@ -778,8 +799,7 @@ impl Decoder { ))?; let rows_per_strip = usize::try_from(self - .get_tag_u64(Tag::RowsPerStrip) - .map(u32::try_from)? + .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height))?; let strip_height = cmp::min( @@ -851,8 +871,7 @@ impl Decoder { let index = self.strip_decoder.as_ref().unwrap().strip_index; let rows_per_strip = usize::try_from(self - .get_tag_u64(Tag::RowsPerStrip) - .map(u32::try_from)? + .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height))?; let strip_height = cmp::min( @@ -871,8 +890,7 @@ impl Decoder { pub fn read_image(&mut self) -> TiffResult { self.initialize_strip_decoder()?; let rows_per_strip = usize::try_from(self - .get_tag_u64(Tag::RowsPerStrip) - .map(u32::try_from)? + .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height))?; let samples_per_strip = diff --git a/tests/encode_images.rs b/tests/encode_images.rs index c2bd88d..9d1ecfd 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -165,11 +165,11 @@ fn test_multiple_byte() { { let mut decoder = Decoder::new(&mut data).unwrap(); - assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u64_vec().unwrap(), [1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u64_vec().unwrap(), [1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u64_vec().unwrap(), [1, 2, 3]); - assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u64_vec().unwrap(), [1, 2, 3, 4]); - assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u64_vec().unwrap(), [1, 2, 3, 4, 5]); + assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().unwrap(), [1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u32_vec().unwrap(), [1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u32_vec().unwrap(), [1, 2, 3]); + assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4]); + assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4, 5]); } } @@ -209,24 +209,24 @@ fn test_signed() { { let mut decoder = Decoder::new(&mut data).unwrap(); - assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i64().unwrap(), -1, ); - assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i64_vec().unwrap(), [-1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i64_vec().unwrap(), [-1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3]); - assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3, 4]); - assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3, 4, -5], ); + assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -1, ); + assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i32_vec().unwrap(), [-1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i32_vec().unwrap(), [-1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); + assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4]); + assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4, -5], ); - assert_eq!(decoder.get_tag(Tag::Unknown(65010)).unwrap().into_i64().unwrap(), -1); - assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i64_vec().unwrap(), [-1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i64_vec().unwrap(), [-1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i64_vec().unwrap(), [-1, 2, -3]); + assert_eq!(decoder.get_tag(Tag::Unknown(65010)).unwrap().into_i32().unwrap(), -1); + assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i32_vec().unwrap(), [-1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i32_vec().unwrap(), [-1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); - assert_eq!(decoder.get_tag(Tag::Unknown(65020)).unwrap().into_i64().unwrap(), -1); - assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i64_vec().unwrap(), [-1]); - assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i64_vec().unwrap(), [-1, 2]); + assert_eq!(decoder.get_tag(Tag::Unknown(65020)).unwrap().into_i32().unwrap(), -1); + assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i32_vec().unwrap(), [-1]); + assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i32_vec().unwrap(), [-1, 2]); - assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i64_vec().unwrap(), [-1, 100]); - assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i64_vec().unwrap(), [-1_i64, 100, 2, 100]); + assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i32_vec().unwrap(), [-1, 100]); + assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]); } } From ba3f6e604ae036dde108842eeaa4406d22f93853 Mon Sep 17 00:00:00 2001 From: Etienne Prothon Date: Mon, 27 Jul 2020 10:52:43 +0200 Subject: [PATCH 4/9] feat: Add BigTiff enum values --- src/decoder/ifd.rs | 142 ++++++++++++++++++++++++++------------------- src/decoder/mod.rs | 2 +- 2 files changed, 84 insertions(+), 60 deletions(-) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index b12d428..a77dcdc 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -9,17 +9,21 @@ use super::stream::{ByteOrder, EndianReader, SmartReader}; use tags::{Tag, Type}; use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; -use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational}; +use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational, RationalBig, UnsignedBig, SignedBig, SRationalBig}; #[allow(unused_qualifications)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Value { - Signed(i64), - Unsigned(u64), + Signed(i32), + SignedBig(i64), + Unsigned(u32), + UnsignedBig(u64), List(Vec), - Rational(u64, u64), - SRational(i64, i64), + Rational(u32, u32), + RationalBig(u64, u64), + SRational(i32, i32), + SRationalBig(i64, i64), Ascii(String), #[doc(hidden)] // Do not match against this. __NonExhaustive, @@ -28,7 +32,8 @@ pub enum Value { impl Value { pub fn into_u32(self) -> TiffResult { match self { - Unsigned(val) => Ok(u32::try_from(val)?), + Unsigned(val) => Ok(val), + UnsignedBig(val) => Ok(u32::try_from(val)?), val => Err(TiffError::FormatError( TiffFormatError::UnsignedIntegerExpected(val), )), @@ -37,7 +42,8 @@ impl Value { pub fn into_i32(self) -> TiffResult { match self { - Signed(val) => Ok(i32::try_from(val)?), + Signed(val) => Ok(val), + SignedBig(val) => Ok(i32::try_from(val)?), val => Err(TiffError::FormatError( TiffFormatError::SignedIntegerExpected(val), )), @@ -46,7 +52,8 @@ impl Value { pub fn into_u64(self) -> TiffResult { match self { - Unsigned(val) => Ok(val), + Unsigned(val) => Ok(val.into()), + UnsignedBig(val) => Ok(val), val => Err(TiffError::FormatError( TiffFormatError::UnsignedIntegerExpected(val), )), @@ -55,7 +62,8 @@ impl Value { pub fn into_i64(self) -> TiffResult { match self { - Signed(val) => Ok(val), + Signed(val) => Ok(val.into()), + SignedBig(val) => Ok(val), val => Err(TiffError::FormatError( TiffFormatError::SignedIntegerExpected(val), )), @@ -71,8 +79,10 @@ impl Value { } Ok(new_vec) } - Unsigned(val) => Ok(vec![u32::try_from(val)?]), - Rational(numerator, denominator) => Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?]), + Unsigned(val) => Ok(vec![val]), + UnsignedBig(val) => Ok(vec![u32::try_from(val)?]), + Rational(numerator, denominator) => Ok(vec![numerator, denominator]), + RationalBig(numerator, denominator) => Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?]), Ascii(val) => Ok(val.chars().map(u32::from).collect()), val => Err(TiffError::FormatError( TiffFormatError::UnsignedIntegerExpected(val), @@ -87,6 +97,10 @@ impl Value { for v in vec { match v { SRational(numerator, denominator) => { + new_vec.push(numerator); + new_vec.push(denominator); + } + SRationalBig(numerator, denominator) => { new_vec.push(i32::try_from(numerator)?); new_vec.push(i32::try_from(denominator)?); } @@ -95,8 +109,10 @@ impl Value { } Ok(new_vec) } - Signed(val) => Ok(vec![i32::try_from(val)?]), - SRational(numerator, denominator) => Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?]), + Signed(val) => Ok(vec![val]), + SignedBig(val) => Ok(vec![i32::try_from(val)?]), + SRational(numerator, denominator) => Ok(vec![numerator, denominator]), + SRationalBig(numerator, denominator) => Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?]), val => Err(TiffError::FormatError( TiffFormatError::SignedIntegerExpected(val), )), @@ -112,8 +128,10 @@ impl Value { } Ok(new_vec) } - Unsigned(val) => Ok(vec![val]), - Rational(numerator, denominator) => Ok(vec![numerator, denominator]), + Unsigned(val) => Ok(vec![val.into()]), + UnsignedBig(val) => Ok(vec![val]), + Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]), + RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]), Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()), val => Err(TiffError::FormatError( TiffFormatError::UnsignedIntegerExpected(val), @@ -128,6 +146,10 @@ impl Value { for v in vec { match v { SRational(numerator, denominator) => { + new_vec.push(numerator.into()); + new_vec.push(denominator.into()); + } + SRationalBig(numerator, denominator) => { new_vec.push(numerator); new_vec.push(denominator); } @@ -136,8 +158,10 @@ impl Value { } Ok(new_vec) } - Signed(val) => Ok(vec![val]), - SRational(numerator, denominator) => Ok(vec![numerator, denominator]), + Signed(val) => Ok(vec![val.into()]), + SignedBig(val) => Ok(vec![val]), + SRational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]), + SRationalBig(numerator, denominator) => Ok(vec![numerator, denominator]), val => Err(TiffError::FormatError( TiffFormatError::SignedIntegerExpected(val), )), @@ -200,61 +224,61 @@ impl Entry { (Type::SHORT, 3) => { let mut r = self.r(bo); Some(List(vec![ - Unsigned(u64::from(r.read_u16()?)), - Unsigned(u64::from(r.read_u16()?)), - Unsigned(u64::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), ])) } (Type::SSHORT, 3) => { let mut r = self.r(bo); Some(List(vec![ - Signed(i64::from(r.read_i16()?)), - Signed(i64::from(r.read_i16()?)), - Signed(i64::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), ])) } (Type::SHORT, 4) => { let mut r = self.r(bo); Some(List(vec![ - Unsigned(u64::from(r.read_u16()?)), - Unsigned(u64::from(r.read_u16()?)), - Unsigned(u64::from(r.read_u16()?)), - Unsigned(u64::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), ])) } (Type::SSHORT, 4) => { let mut r = self.r(bo); Some(List(vec![ - Signed(i64::from(r.read_i16()?)), - Signed(i64::from(r.read_i16()?)), - Signed(i64::from(r.read_i16()?)), - Signed(i64::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), ])) } (Type::LONG, 2) => { let mut r = self.r(bo); Some(List(vec![ - Unsigned(r.read_u32()?.into()), - Unsigned(r.read_u32()?.into()), + Unsigned(r.read_u32()?), + Unsigned(r.read_u32()?), ])) } (Type::SLONG, 2) => { let mut r = self.r(bo); Some(List(vec![ - Signed(r.read_i32()?.into()), - Signed(r.read_i32()?.into()), + Signed(r.read_i32()?), + Signed(r.read_i32()?), ])) } (Type::RATIONAL, 1) => { let mut r = self.r(bo); - let numerator = r.read_u32()?.into(); - let denominator = r.read_u32()?.into(); + let numerator = r.read_u32()?; + let denominator = r.read_u32()?; Some(Rational(numerator, denominator)) } (Type::SRATIONAL, 1) => { let mut r = self.r(bo); - let numerator = r.read_i32()?.into(); - let denominator = r.read_i32()?.into(); + let numerator = r.read_i32()?; + let denominator = r.read_i32()?; Some(SRational(numerator, denominator)) } _ => None @@ -269,55 +293,55 @@ impl Entry { match (self.type_, self.count) { // TODO check if this could give wrong results // at a different endianess of file/computer. - (Type::BYTE, 1) => Ok(Unsigned(u64::from(self.offset[0]))), + (Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))), (Type::BYTE, 2) => offset_to_bytes(2, self), (Type::BYTE, 3) => offset_to_bytes(3, self), (Type::BYTE, 4) => offset_to_bytes(4, self), (Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Unsigned(u64::from(decoder.read_byte()?))) + Ok(UnsignedBig(u64::from(decoder.read_byte()?))) }), - (Type::SBYTE, 1) => Ok(Signed(i64::from(self.offset[0] as i8))), + (Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))), (Type::SBYTE, 2) => offset_to_sbytes(2, self), (Type::SBYTE, 3) => offset_to_sbytes(3, self), (Type::SBYTE, 4) => offset_to_sbytes(4, self), (Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Signed(i64::from(decoder.read_byte()? as i8))) + Ok(SignedBig(i64::from(decoder.read_byte()? as i8))) }), - (Type::SHORT, 1) => Ok(Unsigned(u64::from(self.r(bo).read_u16()?))), - (Type::SSHORT, 1) => Ok(Signed(i64::from(self.r(bo).read_i16()?))), + (Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))), + (Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))), (Type::SHORT, 2) => { let mut r = self.r(bo); Ok(List(vec![ - Unsigned(u64::from(r.read_u16()?)), - Unsigned(u64::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), + Unsigned(u32::from(r.read_u16()?)), ])) } (Type::SSHORT, 2) => { let mut r = self.r(bo); Ok(List(vec![ - Signed(i64::from(r.read_i16()?)), - Signed(i64::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), + Signed(i32::from(r.read_i16()?)), ])) } (Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Unsigned(u64::from(decoder.read_short()?))) + Ok(UnsignedBig(u64::from(decoder.read_short()?))) }), (Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Signed(i64::from(decoder.read_sshort()?))) + Ok(SignedBig(i64::from(decoder.read_sshort()?))) }), - (Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?.into())), - (Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?.into())), + (Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)), + (Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)), (Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Unsigned(decoder.read_long()?.into())) + Ok(Unsigned(decoder.read_long()?)) }), (Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Signed(decoder.read_slong()?.into())) + Ok(Signed(decoder.read_slong()?)) }), (Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Rational(decoder.read_long()?.into(), decoder.read_long()?.into())) + Ok(Rational(decoder.read_long()?, decoder.read_long()?)) }), (Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(SRational(decoder.read_slong()?.into(), decoder.read_slong()?.into())) + Ok(SRational(decoder.read_slong()?, decoder.read_slong()?)) }), (Type::ASCII, n) => { let n = usize::try_from(n)?; @@ -369,7 +393,7 @@ fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult { Ok(List( entry.offset[0..n] .iter() - .map(|&e| Unsigned(u64::from(e))) + .map(|&e| Unsigned(u32::from(e))) .collect() )) } @@ -380,7 +404,7 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult { Ok(List( entry.offset[0..n] .iter() - .map(|&e| Signed(i64::from(e as i8))) + .map(|&e| Signed(i32::from(e as i8))) .collect() )) } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index d179cc5..c9e2c2a 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -1,4 +1,4 @@ -use std::convert::{TryInto, TryFrom}; +use std::convert::TryFrom; use std::collections::HashMap; use std::io::{self, Read, Seek}; use std::cmp; From ff874612bad325e2f6b44ae895fa8d9628c4fd44 Mon Sep 17 00:00:00 2001 From: Farkal Date: Mon, 10 Aug 2020 15:32:47 +0200 Subject: [PATCH 5/9] Allow to decode LONG8 (u64) TileOffsets and TileByteCounts have the LONG8 type on bigtiff. --- src/decoder/ifd.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 79c9e72..fae499a 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -343,6 +343,9 @@ impl Entry { (Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(SRational(decoder.read_slong()?, decoder.read_slong()?)) }), + (Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(UnsignedBig(decoder.read_long8()?)) + }), (Type::ASCII, n) => { let n = usize::try_from(n)?; if n > limits.decoding_buffer_size { From 2635bdd7d596da50ae0b7d631f9f0abacb3e6131 Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Sun, 9 Aug 2020 23:21:36 +0200 Subject: [PATCH 6/9] Update release notes for alpha release for BigTIFF --- CHANGES.md | 9 +++++++++ Cargo.toml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index f0d4d0a..63533af 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +# Version 0.6.0-alpha + +* Support for decoding BigTIFF with 64-bit offsets +* Better support for adding auxiliary tags before encoding image data +* Fixed decoding of inline ASCII in tags +* Fixed handling after null terminator in ASCII data +* Recognize tile and sample format tags +* Note: these notes will be merged into 0.6.0 when it is released + # Version 0.5.0 * Added support for 32-bit and 64-bit decoded values. diff --git a/Cargo.toml b/Cargo.toml index ee78534..e97306b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tiff" -version = "0.5.0" +version = "0.6.0-alpha" authors = [ "ccgn", "bvssvni ", From b853d841f390b1f7cd39170402b1e9d4e96d99b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 14 Aug 2020 16:52:51 +0200 Subject: [PATCH 7/9] Drop byteorder dependency Ref: https://github.com/image-rs/image/issues/1000 --- Cargo.toml | 1 - src/decoder/stream.rs | 118 +++++++++++++++++++++++++----------------- src/encoder/mod.rs | 5 +- src/encoder/writer.rs | 44 ++++++---------- src/lib.rs | 1 - 5 files changed, 88 insertions(+), 81 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e97306b..05d5a96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,5 @@ repository = "https://github.com/image-rs/image-tiff" exclude = ["tests/images/*", "tests/fuzz_images/*"] [dependencies] -byteorder = "1.2" lzw = "0.10" miniz_oxide = "0.3" diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index caf4e3c..53595f1 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,10 +1,10 @@ //! All IO functionality needed for TIFF decoding use crate::error::{TiffError, TiffResult}; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use lzw; use miniz_oxide::inflate; use std::io::{self, Read, Seek}; +use std::slice; /// Byte order of the TIFF file. #[derive(Clone, Copy, Debug)] @@ -23,82 +23,104 @@ pub trait EndianReader: Read { /// Reads an u16 #[inline(always)] fn read_u16(&mut self) -> Result { - match self.byte_order() { - ByteOrder::LittleEndian => ::read_u16::(self), - ByteOrder::BigEndian => ::read_u16::(self), - } + let mut n = [0u8; 2]; + self.read_exact(&mut n)?; + Ok(match self.byte_order() { + ByteOrder::LittleEndian => u16::from_le_bytes(n), + ByteOrder::BigEndian => u16::from_be_bytes(n), + }) } #[inline(always)] fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> { + self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 2) })?; match self.byte_order() { - ByteOrder::LittleEndian => { - ::read_u16_into::(self, buffer) - } - ByteOrder::BigEndian => { - ::read_u16_into::(self, buffer) - } + ByteOrder::LittleEndian => + for n in buffer { + *n = u16::from_le(*n); + }, + ByteOrder::BigEndian => + for n in buffer { + *n = u16::from_be(*n); + }, } + Ok(()) } /// Reads an i16 #[inline(always)] fn read_i16(&mut self) -> Result { - match self.byte_order() { - ByteOrder::LittleEndian => ::read_i16::(self), - ByteOrder::BigEndian => ::read_i16::(self), - } + let mut n = [0u8; 2]; + self.read_exact(&mut n)?; + Ok(match self.byte_order() { + ByteOrder::LittleEndian => i16::from_le_bytes(n), + ByteOrder::BigEndian => i16::from_be_bytes(n), + }) } /// Reads an u32 #[inline(always)] fn read_u32(&mut self) -> Result { - match self.byte_order() { - ByteOrder::LittleEndian => ::read_u32::(self), - ByteOrder::BigEndian => ::read_u32::(self), - } + let mut n = [0u8; 4]; + self.read_exact(&mut n)?; + Ok(match self.byte_order() { + ByteOrder::LittleEndian => u32::from_le_bytes(n), + ByteOrder::BigEndian => u32::from_be_bytes(n), + }) } #[inline(always)] fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> { + self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 4) })?; match self.byte_order() { - ByteOrder::LittleEndian => { - ::read_u32_into::(self, buffer) - } - ByteOrder::BigEndian => { - ::read_u32_into::(self, buffer) - } - } - } - - /// 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) - } + ByteOrder::LittleEndian => + for n in buffer { + *n = u32::from_le(*n); + }, + ByteOrder::BigEndian => + for n in buffer { + *n = u32::from_be(*n); + }, } + Ok(()) } /// Reads an i32 #[inline(always)] fn read_i32(&mut self) -> Result { + let mut n = [0u8; 4]; + self.read_exact(&mut n)?; + Ok(match self.byte_order() { + ByteOrder::LittleEndian => i32::from_le_bytes(n), + ByteOrder::BigEndian => i32::from_be_bytes(n), + }) + } + + /// Reads an u64 + #[inline(always)] + fn read_u64(&mut self) -> Result { + let mut n = [0u8; 8]; + self.read_exact(&mut n)?; + Ok(match self.byte_order() { + ByteOrder::LittleEndian => u64::from_le_bytes(n), + ByteOrder::BigEndian => u64::from_be_bytes(n), + }) + } + + #[inline(always)] + fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> { + self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 8) })?; match self.byte_order() { - ByteOrder::LittleEndian => ::read_i32::(self), - ByteOrder::BigEndian => ::read_i32::(self), + ByteOrder::LittleEndian => + for n in buffer { + *n = u64::from_le(*n); + }, + ByteOrder::BigEndian => + for n in buffer { + *n = u64::from_be(*n); + }, } + Ok(()) } } diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index df9a2a5..35cb4b8 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -1,4 +1,3 @@ -use byteorder::NativeEndian; use std::collections::BTreeMap; use std::convert::TryFrom; use std::io::{Seek, Write}; @@ -375,7 +374,7 @@ impl TiffEncoder { writer: TiffWriter::new(writer), }; - NativeEndian::write_header(&mut encoder.writer)?; + write_tiff_header(&mut encoder.writer)?; // blank the IFD offset location encoder.writer.write_u32(0)?; @@ -536,7 +535,7 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> { /// let mut tiff = TiffEncoder::new(&mut file).unwrap(); /// let mut image = tiff.new_image::(100, 100).unwrap(); /// -/// // You can encode tags here +/// // You can encode tags here /// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap(); /// /// let mut idx = 0; diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs index ba4c4c5..bc1679d 100644 --- a/src/encoder/writer.rs +++ b/src/encoder/writer.rs @@ -1,29 +1,17 @@ use error::TiffResult; -use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt}; use std::io::{self, Seek, SeekFrom, Write}; -pub trait TiffByteOrder: ByteOrder { - fn write_header(writer: &mut TiffWriter) -> TiffResult<()>; -} +pub fn write_tiff_header(writer: &mut TiffWriter) -> TiffResult<()> { + #[cfg(target_endian = "little")] + let boi: u8 = 0x49; + #[cfg(not(target_endian = "little"))] + let boi: u8 = 0x4d; -impl TiffByteOrder for LittleEndian { - fn write_header(writer: &mut TiffWriter) -> TiffResult<()> { - writer.writer.write_u16::(0x4949)?; - writer.writer.write_u16::(42)?; - writer.offset += 4; + writer.writer.write_all(&[boi, boi])?; + writer.writer.write_all(&42u16.to_ne_bytes())?; + writer.offset += 4; - Ok(()) - } -} - -impl TiffByteOrder for BigEndian { - fn write_header(writer: &mut TiffWriter) -> TiffResult<()> { - writer.writer.write_u16::(0x4d4d)?; - writer.writer.write_u16::(42)?; - writer.offset += 4; - - Ok(()) - } + Ok(()) } pub struct TiffWriter { @@ -47,47 +35,47 @@ impl TiffWriter { } pub fn write_u8(&mut self, n: u8) -> Result<(), io::Error> { - self.writer.write_u8(n)?; + self.writer.write_all(&n.to_ne_bytes())?; self.offset += 1; Ok(()) } pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> { - self.writer.write_i8(n)?; + self.writer.write_all(&n.to_ne_bytes())?; self.offset += 1; Ok(()) } pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> { - self.writer.write_u16::(n)?; + self.writer.write_all(&n.to_ne_bytes())?; self.offset += 2; Ok(()) } pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> { - self.writer.write_i16::(n)?; + self.writer.write_all(&n.to_ne_bytes())?; self.offset += 2; Ok(()) } pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> { - self.writer.write_u32::(n)?; + self.writer.write_all(&n.to_ne_bytes())?; self.offset += 4; Ok(()) } pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> { - self.writer.write_i32::(n)?; + self.writer.write_all(&n.to_ne_bytes())?; self.offset += 4; Ok(()) } pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> { - self.writer.write_u64::(n)?; + self.writer.write_all(&n.to_ne_bytes())?; self.offset += 8; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 9906732..9f0f1a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,6 @@ //! # Related Links //! * - The TIFF specification -extern crate byteorder; extern crate lzw; extern crate miniz_oxide; From 99c038c92901bf5807713dfb0ee449129c9ad9eb Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Fri, 14 Aug 2020 23:16:45 +0200 Subject: [PATCH 8/9] Move all bytecast unsafety to one module --- src/bytecast.rs | 31 +++++++++++++++++++++++++++++++ src/decoder/stream.rs | 8 ++++---- src/encoder/mod.rs | 25 +++++++------------------ src/lib.rs | 1 + 4 files changed, 43 insertions(+), 22 deletions(-) create mode 100644 src/bytecast.rs diff --git a/src/bytecast.rs b/src/bytecast.rs new file mode 100644 index 0000000..c6a0f50 --- /dev/null +++ b/src/bytecast.rs @@ -0,0 +1,31 @@ +//! Trivial, internal byte transmutation. +//! +//! A dependency like bytemuck would give us extra assurance of the safety but overall would not +//! reduce the amount of total unsafety. We don't use it in the interface where the traits would +//! really become useful. +//! +//! SAFETY: These are benign casts as we apply them to fixed size integer types only. All of them +//! are naturally aligned, valid for all bit patterns and their alignment is surely at most their +//! size (we assert the latter fact since it is 'implementation defined' if following the letter of +//! the unsafe code guidelines). +//! +//! TODO: Would like to use std-lib here. +use std::{mem, slice}; + +macro_rules! integral_slice_as_bytes{($int:ty, $const:ident $(,$mut:ident)*) => { + pub(crate) fn $const(slice: &[$int]) -> &[u8] { + assert!(mem::align_of::<$int>() <= mem::size_of::<$int>()); + unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, mem::size_of_val(slice)) } + } + $(pub(crate) fn $mut(slice: &mut [$int]) -> &mut [u8] { + assert!(mem::align_of::<$int>() <= mem::size_of::<$int>()); + unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, mem::size_of_val(slice)) } + })* +}} + +integral_slice_as_bytes!(i8, i8_as_ne_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!(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/stream.rs b/src/decoder/stream.rs index 53595f1..db9d36c 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,10 +1,10 @@ //! All IO functionality needed for TIFF decoding +use crate::bytecast; use crate::error::{TiffError, TiffResult}; use lzw; use miniz_oxide::inflate; use std::io::{self, Read, Seek}; -use std::slice; /// Byte order of the TIFF file. #[derive(Clone, Copy, Debug)] @@ -33,7 +33,7 @@ pub trait EndianReader: Read { #[inline(always)] fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> { - self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 2) })?; + self.read_exact(bytecast::u16_as_ne_mut_bytes(buffer))?; match self.byte_order() { ByteOrder::LittleEndian => for n in buffer { @@ -71,7 +71,7 @@ pub trait EndianReader: Read { #[inline(always)] fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> { - self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 4) })?; + self.read_exact(bytecast::u32_as_ne_mut_bytes(buffer))?; match self.byte_order() { ByteOrder::LittleEndian => for n in buffer { @@ -109,7 +109,7 @@ pub trait EndianReader: Read { #[inline(always)] fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> { - self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 8) })?; + self.read_exact(bytecast::u64_as_ne_mut_bytes(buffer))?; match self.byte_order() { ByteOrder::LittleEndian => for n in buffer { diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 35cb4b8..eb62d33 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -3,6 +3,7 @@ use std::convert::TryFrom; use std::io::{Seek, Write}; use std::mem; +use bytecast; use tags::{self, ResolutionUnit, Tag, Type}; use error::{TiffError, TiffFormatError, TiffResult}; @@ -59,9 +60,7 @@ impl TiffValue for [i8] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) }; + let slice = bytecast::i8_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -76,9 +75,7 @@ impl TiffValue for [u16] { } 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() * 2) }; + let slice = bytecast::u16_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -93,9 +90,7 @@ impl TiffValue for [i16] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; + let slice = bytecast::i16_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -110,9 +105,7 @@ impl TiffValue for [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) }; + let slice = bytecast::u32_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -127,9 +120,7 @@ impl TiffValue for [i32] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; + let slice = bytecast::i32_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -144,9 +135,7 @@ impl TiffValue for [u64] { } 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) }; + let slice = bytecast::u64_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 9f0f1a2..7bb874b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ extern crate lzw; extern crate miniz_oxide; +mod bytecast; pub mod decoder; pub mod encoder; mod error; From 23090a3513d3e2bc6038b0b9173793a7f70c1750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 30 Jun 2020 10:54:58 +0300 Subject: [PATCH 9/9] Update to miniz_oxide 0.4.1 --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ee78534..370028d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,6 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"] [dependencies] byteorder = "1.2" lzw = "0.10" -miniz_oxide = "0.3" +[dependencies.miniz_oxide] +version = "0.4.1" +features = ["no_extern_crate_alloc"]