diff options
author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/tiff/src/decoder | |
parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/tiff/src/decoder')
-rw-r--r-- | vendor/tiff/src/decoder/ifd.rs | 670 | ||||
-rw-r--r-- | vendor/tiff/src/decoder/image.rs | 601 | ||||
-rw-r--r-- | vendor/tiff/src/decoder/mod.rs | 1176 | ||||
-rw-r--r-- | vendor/tiff/src/decoder/stream.rs | 435 | ||||
-rw-r--r-- | vendor/tiff/src/decoder/tag_reader.rs | 45 |
5 files changed, 2927 insertions, 0 deletions
diff --git a/vendor/tiff/src/decoder/ifd.rs b/vendor/tiff/src/decoder/ifd.rs new file mode 100644 index 0000000..b05513d --- /dev/null +++ b/vendor/tiff/src/decoder/ifd.rs @@ -0,0 +1,670 @@ +//! Function for reading TIFF tags + +use std::collections::HashMap; +use std::convert::{TryFrom, TryInto}; +use std::io::{self, Read, Seek}; +use std::mem; +use std::str; + +use super::stream::{ByteOrder, EndianReader, SmartReader}; +use crate::tags::{Tag, Type}; +use crate::{TiffError, TiffFormatError, TiffResult}; + +use self::Value::{ + Ascii, Byte, Double, Float, Ifd, IfdBig, List, Rational, RationalBig, SRational, SRationalBig, + Short, Signed, SignedBig, Unsigned, UnsignedBig, +}; + +#[allow(unused_qualifications)] +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum Value { + Byte(u8), + Short(u16), + Signed(i32), + SignedBig(i64), + Unsigned(u32), + UnsignedBig(u64), + Float(f32), + Double(f64), + List(Vec<Value>), + Rational(u32, u32), + RationalBig(u64, u64), + SRational(i32, i32), + SRationalBig(i64, i64), + Ascii(String), + Ifd(u32), + IfdBig(u64), +} + +impl Value { + pub fn into_u8(self) -> TiffResult<u8> { + match self { + Byte(val) => Ok(val), + val => Err(TiffError::FormatError(TiffFormatError::ByteExpected(val))), + } + } + + pub fn into_u16(self) -> TiffResult<u16> { + match self { + Short(val) => Ok(val), + Unsigned(val) => Ok(u16::try_from(val)?), + UnsignedBig(val) => Ok(u16::try_from(val)?), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_u32(self) -> TiffResult<u32> { + match self { + Short(val) => Ok(val.into()), + Unsigned(val) => Ok(val), + UnsignedBig(val) => Ok(u32::try_from(val)?), + Ifd(val) => Ok(val), + IfdBig(val) => Ok(u32::try_from(val)?), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_i32(self) -> TiffResult<i32> { + match self { + Signed(val) => Ok(val), + SignedBig(val) => Ok(i32::try_from(val)?), + val => Err(TiffError::FormatError( + TiffFormatError::SignedIntegerExpected(val), + )), + } + } + + pub fn into_u64(self) -> TiffResult<u64> { + match self { + Short(val) => Ok(val.into()), + Unsigned(val) => Ok(val.into()), + UnsignedBig(val) => Ok(val), + Ifd(val) => Ok(val.into()), + IfdBig(val) => Ok(val), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_i64(self) -> TiffResult<i64> { + match self { + Signed(val) => Ok(val.into()), + SignedBig(val) => Ok(val), + val => Err(TiffError::FormatError( + TiffFormatError::SignedIntegerExpected(val), + )), + } + } + + pub fn into_f32(self) -> TiffResult<f32> { + match self { + Float(val) => Ok(val), + val => Err(TiffError::FormatError( + TiffFormatError::SignedIntegerExpected(val), + )), + } + } + + pub fn into_f64(self) -> TiffResult<f64> { + match self { + Double(val) => Ok(val), + val => Err(TiffError::FormatError( + TiffFormatError::SignedIntegerExpected(val), + )), + } + } + + pub fn into_string(self) -> TiffResult<String> { + match self { + Ascii(val) => Ok(val), + val => Err(TiffError::FormatError( + TiffFormatError::SignedIntegerExpected(val), + )), + } + } + + pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> { + 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![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)?]) + } + Ifd(val) => Ok(vec![val]), + IfdBig(val) => Ok(vec![u32::try_from(val)?]), + Ascii(val) => Ok(val.chars().map(u32::from).collect()), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_u8_vec(self) -> TiffResult<Vec<u8>> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_u8()?) + } + Ok(new_vec) + } + Byte(val) => Ok(vec![val]), + + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_u16_vec(self) -> TiffResult<Vec<u16>> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_u16()?) + } + Ok(new_vec) + } + Short(val) => Ok(vec![val]), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> { + 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(numerator); + new_vec.push(denominator); + } + SRationalBig(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![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), + )), + } + } + + pub fn into_f32_vec(self) -> TiffResult<Vec<f32>> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_f32()?) + } + Ok(new_vec) + } + Float(val) => Ok(vec![val]), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_f64_vec(self) -> TiffResult<Vec<f64>> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_f64()?) + } + Ok(new_vec) + } + Double(val) => Ok(vec![val]), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_u64_vec(self) -> TiffResult<Vec<u64>> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_u64()?) + } + Ok(new_vec) + } + 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]), + Ifd(val) => Ok(vec![val.into()]), + IfdBig(val) => Ok(vec![val]), + Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_i64_vec(self) -> TiffResult<Vec<i64>> { + 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(numerator.into()); + new_vec.push(denominator.into()); + } + SRationalBig(numerator, denominator) => { + new_vec.push(numerator); + new_vec.push(denominator); + } + _ => new_vec.push(v.into_i64()?), + } + } + Ok(new_vec) + } + 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), + )), + } + } +} + +#[derive(Clone)] +pub struct Entry { + type_: Type, + count: u64, + offset: [u8; 8], +} + +impl ::std::fmt::Debug for Entry { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + fmt.write_str(&format!( + "Entry {{ type_: {:?}, count: {:?}, offset: {:?} }}", + self.type_, self.count, &self.offset + )) + } +} + +impl 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, + offset, + } + } + + /// Returns a mem_reader for the offset/value field + fn r(&self, byte_order: ByteOrder) -> SmartReader<io::Cursor<Vec<u8>>> { + SmartReader::wrap(io::Cursor::new(self.offset.to_vec()), byte_order) + } + + pub fn val<R: Read + Seek>( + &self, + limits: &super::Limits, + bigtiff: bool, + reader: &mut SmartReader<R>, + ) -> TiffResult<Value> { + // Case 1: there are no values so we can return immediately. + if self.count == 0 { + return Ok(List(Vec::new())); + } + + let bo = reader.byte_order(); + + let tag_size = match self.type_ { + Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1, + Type::SHORT | Type::SSHORT => 2, + Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4, + Type::LONG8 + | Type::SLONG8 + | Type::DOUBLE + | Type::RATIONAL + | Type::SRATIONAL + | Type::IFD8 => 8, + }; + + let value_bytes = match self.count.checked_mul(tag_size) { + Some(n) => n, + None => { + return Err(TiffError::LimitsExceeded); + } + }; + + // Case 2: there is one value. + if self.count == 1 { + // 2a: the value is 5-8 bytes and we're in BigTiff mode. + if bigtiff && value_bytes > 4 && value_bytes <= 8 { + return Ok(match self.type_ { + Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?), + Type::SLONG8 => SignedBig(self.r(bo).read_i64()?), + Type::DOUBLE => Double(self.r(bo).read_f64()?), + Type::RATIONAL => { + let mut r = self.r(bo); + Rational(r.read_u32()?, r.read_u32()?) + } + Type::SRATIONAL => { + let mut r = self.r(bo); + SRational(r.read_i32()?, r.read_i32()?) + } + Type::IFD8 => IfdBig(self.r(bo).read_u64()?), + Type::BYTE + | Type::SBYTE + | Type::ASCII + | Type::UNDEFINED + | Type::SHORT + | Type::SSHORT + | Type::LONG + | Type::SLONG + | Type::FLOAT + | Type::IFD => unreachable!(), + }); + } + + // 2b: the value is at most 4 bytes or doesn't fit in the offset field. + return Ok(match self.type_ { + Type::BYTE => Unsigned(u32::from(self.offset[0])), + Type::SBYTE => Signed(i32::from(self.offset[0] as i8)), + Type::UNDEFINED => Byte(self.offset[0]), + Type::SHORT => Unsigned(u32::from(self.r(bo).read_u16()?)), + Type::SSHORT => Signed(i32::from(self.r(bo).read_i16()?)), + Type::LONG => Unsigned(self.r(bo).read_u32()?), + Type::SLONG => Signed(self.r(bo).read_i32()?), + Type::FLOAT => Float(self.r(bo).read_f32()?), + Type::ASCII => { + if self.offset[0] == 0 { + Ascii("".to_string()) + } else { + return Err(TiffError::FormatError(TiffFormatError::InvalidTag)); + } + } + Type::LONG8 => { + reader.goto_offset(self.r(bo).read_u32()?.into())?; + UnsignedBig(reader.read_u64()?) + } + Type::SLONG8 => { + reader.goto_offset(self.r(bo).read_u32()?.into())?; + SignedBig(reader.read_i64()?) + } + Type::DOUBLE => { + reader.goto_offset(self.r(bo).read_u32()?.into())?; + Double(reader.read_f64()?) + } + Type::RATIONAL => { + reader.goto_offset(self.r(bo).read_u32()?.into())?; + Rational(reader.read_u32()?, reader.read_u32()?) + } + Type::SRATIONAL => { + reader.goto_offset(self.r(bo).read_u32()?.into())?; + SRational(reader.read_i32()?, reader.read_i32()?) + } + Type::IFD => Ifd(self.r(bo).read_u32()?), + Type::IFD8 => { + reader.goto_offset(self.r(bo).read_u32()?.into())?; + IfdBig(reader.read_u64()?) + } + }); + } + + // Case 3: There is more than one value, but it fits in the offset field. + if value_bytes <= 4 || bigtiff && value_bytes <= 8 { + match self.type_ { + Type::BYTE => return offset_to_bytes(self.count as usize, self), + Type::SBYTE => return offset_to_sbytes(self.count as usize, self), + Type::ASCII => { + let mut buf = vec![0; self.count as usize]; + self.r(bo).read_exact(&mut buf)?; + if buf.is_ascii() && buf.ends_with(&[0]) { + let v = str::from_utf8(&buf)?; + let v = v.trim_matches(char::from(0)); + return Ok(Ascii(v.into())); + } else { + return Err(TiffError::FormatError(TiffFormatError::InvalidTag)); + } + } + Type::UNDEFINED => { + return Ok(List( + self.offset[0..self.count as usize] + .iter() + .map(|&b| Byte(b)) + .collect(), + )); + } + Type::SHORT => { + let mut r = self.r(bo); + let mut v = Vec::new(); + for _ in 0..self.count { + v.push(Short(r.read_u16()?)); + } + return Ok(List(v)); + } + Type::SSHORT => { + let mut r = self.r(bo); + let mut v = Vec::new(); + for _ in 0..self.count { + v.push(Signed(i32::from(r.read_i16()?))); + } + return Ok(List(v)); + } + Type::LONG => { + let mut r = self.r(bo); + let mut v = Vec::new(); + for _ in 0..self.count { + v.push(Unsigned(r.read_u32()?)); + } + return Ok(List(v)); + } + Type::SLONG => { + let mut r = self.r(bo); + let mut v = Vec::new(); + for _ in 0..self.count { + v.push(Signed(r.read_i32()?)); + } + return Ok(List(v)); + } + Type::FLOAT => { + let mut r = self.r(bo); + let mut v = Vec::new(); + for _ in 0..self.count { + v.push(Float(r.read_f32()?)); + } + return Ok(List(v)); + } + Type::IFD => { + let mut r = self.r(bo); + let mut v = Vec::new(); + for _ in 0..self.count { + v.push(Ifd(r.read_u32()?)); + } + return Ok(List(v)); + } + Type::LONG8 + | Type::SLONG8 + | Type::RATIONAL + | Type::SRATIONAL + | Type::DOUBLE + | Type::IFD8 => { + unreachable!() + } + } + } + + // Case 4: there is more than one value, and it doesn't fit in the offset field. + match self.type_ { + // TODO check if this could give wrong results + // at a different endianess of file/computer. + Type::BYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + let mut buf = [0; 1]; + reader.read_exact(&mut buf)?; + Ok(UnsignedBig(u64::from(buf[0]))) + }), + Type::SBYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(SignedBig(i64::from(reader.read_i8()? as i8))) + }), + Type::SHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(UnsignedBig(u64::from(reader.read_u16()?))) + }), + Type::SSHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(SignedBig(i64::from(reader.read_i16()?))) + }), + Type::LONG => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(Unsigned(reader.read_u32()?)) + }), + Type::SLONG => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(Signed(reader.read_i32()?)) + }), + Type::FLOAT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(Float(reader.read_f32()?)) + }), + Type::DOUBLE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(Double(reader.read_f64()?)) + }), + Type::RATIONAL => { + self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(Rational(reader.read_u32()?, reader.read_u32()?)) + }) + } + Type::SRATIONAL => { + self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(SRational(reader.read_i32()?, reader.read_i32()?)) + }) + } + Type::LONG8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(UnsignedBig(reader.read_u64()?)) + }), + Type::SLONG8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(SignedBig(reader.read_i64()?)) + }), + Type::IFD => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(Ifd(reader.read_u32()?)) + }), + Type::IFD8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + Ok(IfdBig(reader.read_u64()?)) + }), + Type::UNDEFINED => { + self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { + let mut buf = [0; 1]; + reader.read_exact(&mut buf)?; + Ok(Byte(buf[0])) + }) + } + Type::ASCII => { + let n = usize::try_from(self.count)?; + if n > limits.decoding_buffer_size { + return Err(TiffError::LimitsExceeded); + } + + if bigtiff { + reader.goto_offset(self.r(bo).read_u64()?)? + } else { + reader.goto_offset(self.r(bo).read_u32()?.into())? + } + + let mut out = vec![0; n]; + reader.read_exact(&mut out)?; + // Strings may be null-terminated, so we trim anything downstream of the null byte + if let Some(first) = out.iter().position(|&b| b == 0) { + out.truncate(first); + } + Ok(Ascii(String::from_utf8(out)?)) + } + } + } + + #[inline] + fn decode_offset<R, F>( + &self, + value_count: u64, + bo: ByteOrder, + bigtiff: bool, + limits: &super::Limits, + reader: &mut SmartReader<R>, + decode_fn: F, + ) -> TiffResult<Value> + where + R: Read + Seek, + F: Fn(&mut SmartReader<R>) -> TiffResult<Value>, + { + let value_count = usize::try_from(value_count)?; + if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() { + return Err(TiffError::LimitsExceeded); + } + + let mut v = Vec::with_capacity(value_count); + + let offset = if bigtiff { + self.r(bo).read_u64()? + } else { + self.r(bo).read_u32()?.into() + }; + reader.goto_offset(offset)?; + + for _ in 0..value_count { + v.push(decode_fn(reader)?) + } + Ok(List(v)) + } +} + +/// Extracts a list of BYTE tags stored in an offset +#[inline] +fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult<Value> { + Ok(List( + entry.offset[0..n] + .iter() + .map(|&e| Unsigned(u32::from(e))) + .collect(), + )) +} + +/// Extracts a list of SBYTE tags stored in an offset +#[inline] +fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> { + Ok(List( + entry.offset[0..n] + .iter() + .map(|&e| Signed(i32::from(e as i8))) + .collect(), + )) +} + +/// Type representing an Image File Directory +pub type Directory = HashMap<Tag, Entry>; diff --git a/vendor/tiff/src/decoder/image.rs b/vendor/tiff/src/decoder/image.rs new file mode 100644 index 0000000..c037e31 --- /dev/null +++ b/vendor/tiff/src/decoder/image.rs @@ -0,0 +1,601 @@ +use super::ifd::{Directory, Value}; +use super::stream::{ByteOrder, DeflateReader, JpegReader, LZWReader, PackBitsReader}; +use super::tag_reader::TagReader; +use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits}; +use super::{stream::SmartReader, ChunkType}; +use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag}; +use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError}; +use std::convert::{TryFrom, TryInto}; +use std::io::{self, Cursor, Read, Seek}; +use std::sync::Arc; + +#[derive(Debug)] +pub(crate) struct StripDecodeState { + pub rows_per_strip: u32, +} + +#[derive(Debug)] +/// Computed values useful for tile decoding +pub(crate) struct TileAttributes { + pub image_width: usize, + pub image_height: usize, + + pub tile_width: usize, + pub tile_length: usize, +} + +impl TileAttributes { + pub fn tiles_across(&self) -> usize { + (self.image_width + self.tile_width - 1) / self.tile_width + } + pub fn tiles_down(&self) -> usize { + (self.image_height + self.tile_length - 1) / self.tile_length + } + fn padding_right(&self) -> usize { + (self.tile_width - self.image_width % self.tile_width) % self.tile_width + } + fn padding_down(&self) -> usize { + (self.tile_length - self.image_height % self.tile_length) % self.tile_length + } + pub fn get_padding(&self, tile: usize) -> (usize, usize) { + let row = tile / self.tiles_across(); + let column = tile % self.tiles_across(); + + let padding_right = if column == self.tiles_across() - 1 { + self.padding_right() + } else { + 0 + }; + + let padding_down = if row == self.tiles_down() - 1 { + self.padding_down() + } else { + 0 + }; + + (padding_right, padding_down) + } +} + +#[derive(Debug)] +pub(crate) struct Image { + pub ifd: Option<Directory>, + pub width: u32, + pub height: u32, + pub bits_per_sample: Vec<u8>, + #[allow(unused)] + pub samples: u8, + pub sample_format: Vec<SampleFormat>, + pub photometric_interpretation: PhotometricInterpretation, + pub compression_method: CompressionMethod, + pub predictor: Predictor, + pub jpeg_tables: Option<Arc<Vec<u8>>>, + pub chunk_type: ChunkType, + pub strip_decoder: Option<StripDecodeState>, + pub tile_attributes: Option<TileAttributes>, + pub chunk_offsets: Vec<u64>, + pub chunk_bytes: Vec<u64>, +} + +impl Image { + pub fn from_reader<R: Read + Seek>( + reader: &mut SmartReader<R>, + ifd: Directory, + limits: &Limits, + bigtiff: bool, + ) -> TiffResult<Image> { + let mut tag_reader = TagReader { + reader, + limits, + ifd: &ifd, + bigtiff, + }; + + let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; + let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?; + if width == 0 || height == 0 { + return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions( + width, height, + ))); + } + + let photometric_interpretation = tag_reader + .find_tag(Tag::PhotometricInterpretation)? + .map(Value::into_u16) + .transpose()? + .and_then(PhotometricInterpretation::from_u16) + .ok_or(TiffUnsupportedError::UnknownInterpretation)?; + + // Try to parse both the compression method and the number, format, and bits of the included samples. + // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images. + let compression_method = match tag_reader.find_tag(Tag::Compression)? { + Some(val) => CompressionMethod::from_u16(val.into_u16()?) + .ok_or(TiffUnsupportedError::UnknownCompressionMethod)?, + None => CompressionMethod::None, + }; + + let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG + && ifd.contains_key(&Tag::JPEGTables) + { + let vec = tag_reader + .find_tag(Tag::JPEGTables)? + .unwrap() + .into_u8_vec()?; + if vec.len() < 2 { + return Err(TiffError::FormatError( + TiffFormatError::InvalidTagValueType(Tag::JPEGTables), + )); + } + + Some(Arc::new(vec)) + } else { + None + }; + + let samples = tag_reader + .find_tag(Tag::SamplesPerPixel)? + .map(Value::into_u16) + .transpose()? + .unwrap_or(1) + .try_into()?; + + let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? { + Some(vals) => { + let sample_format: Vec<_> = vals + .into_iter() + .map(SampleFormat::from_u16_exhaustive) + .collect(); + + // TODO: for now, only homogenous formats across samples are supported. + if !sample_format.windows(2).all(|s| s[0] == s[1]) { + return Err(TiffUnsupportedError::UnsupportedSampleFormat(sample_format).into()); + } + + sample_format + } + None => vec![SampleFormat::Uint], + }; + + let bits_per_sample = match samples { + 1 | 3 | 4 => tag_reader + .find_tag_uint_vec(Tag::BitsPerSample)? + .unwrap_or_else(|| vec![1]), + _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(samples).into()), + }; + + let predictor = tag_reader + .find_tag(Tag::Predictor)? + .map(Value::into_u16) + .transpose()? + .map(|p| { + Predictor::from_u16(p) + .ok_or(TiffError::FormatError(TiffFormatError::UnknownPredictor(p))) + }) + .transpose()? + .unwrap_or(Predictor::None); + + let chunk_type; + let chunk_offsets; + let chunk_bytes; + let strip_decoder; + let tile_attributes; + match ( + ifd.contains_key(&Tag::StripByteCounts), + ifd.contains_key(&Tag::StripOffsets), + ifd.contains_key(&Tag::TileByteCounts), + ifd.contains_key(&Tag::TileOffsets), + ) { + (true, true, false, false) => { + chunk_type = ChunkType::Strip; + + chunk_offsets = tag_reader + .find_tag(Tag::StripOffsets)? + .unwrap() + .into_u64_vec()?; + chunk_bytes = tag_reader + .find_tag(Tag::StripByteCounts)? + .unwrap() + .into_u64_vec()?; + let rows_per_strip = tag_reader + .find_tag(Tag::RowsPerStrip)? + .map(Value::into_u32) + .transpose()? + .unwrap_or(height); + strip_decoder = Some(StripDecodeState { rows_per_strip }); + tile_attributes = None; + + if chunk_offsets.len() != chunk_bytes.len() + || rows_per_strip == 0 + || u32::try_from(chunk_offsets.len())? + != height.saturating_sub(1) / rows_per_strip + 1 + { + return Err(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + )); + } + } + (false, false, true, true) => { + chunk_type = ChunkType::Tile; + + let tile_width = + usize::try_from(tag_reader.require_tag(Tag::TileWidth)?.into_u32()?)?; + let tile_length = + usize::try_from(tag_reader.require_tag(Tag::TileLength)?.into_u32()?)?; + + if tile_width == 0 { + return Err(TiffFormatError::InvalidTagValueType(Tag::TileWidth).into()); + } else if tile_length == 0 { + return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into()); + } + + strip_decoder = None; + tile_attributes = Some(TileAttributes { + image_width: usize::try_from(width)?, + image_height: usize::try_from(height)?, + tile_width, + tile_length, + }); + chunk_offsets = tag_reader + .find_tag(Tag::TileOffsets)? + .unwrap() + .into_u64_vec()?; + chunk_bytes = tag_reader + .find_tag(Tag::TileByteCounts)? + .unwrap() + .into_u64_vec()?; + + let tile = tile_attributes.as_ref().unwrap(); + if chunk_offsets.len() != chunk_bytes.len() + || chunk_offsets.len() != tile.tiles_down() * tile.tiles_across() + { + return Err(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + )); + } + } + (_, _, _, _) => { + return Err(TiffError::FormatError( + TiffFormatError::StripTileTagConflict, + )) + } + }; + + Ok(Image { + ifd: Some(ifd), + width, + height, + bits_per_sample, + samples, + sample_format, + photometric_interpretation, + compression_method, + jpeg_tables, + predictor, + chunk_type, + strip_decoder, + tile_attributes, + chunk_offsets, + chunk_bytes, + }) + } + + pub(crate) fn colortype(&self) -> TiffResult<ColorType> { + match self.photometric_interpretation { + PhotometricInterpretation::RGB => 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)), + // FIXME: We should _ignore_ other components. In particular: + // > Beware of extra components. Some TIFF files may have more components per pixel + // than you think. A Baseline TIFF reader must skip over them gracefully,using the + // values of the SamplesPerPixel and BitsPerSample fields. + // > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements. + _ => Err(TiffError::UnsupportedError( + TiffUnsupportedError::InterpretationWithBits( + self.photometric_interpretation, + self.bits_per_sample.clone(), + ), + )), + }, + PhotometricInterpretation::CMYK => match self.bits_per_sample[..] { + [c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)), + _ => Err(TiffError::UnsupportedError( + TiffUnsupportedError::InterpretationWithBits( + self.photometric_interpretation, + self.bits_per_sample.clone(), + ), + )), + }, + PhotometricInterpretation::YCbCr => match self.bits_per_sample[..] { + [y, cb, cr] if [y, y] == [cb, cr] => Ok(ColorType::YCbCr(y)), + _ => Err(TiffError::UnsupportedError( + TiffUnsupportedError::InterpretationWithBits( + self.photometric_interpretation, + self.bits_per_sample.clone(), + ), + )), + }, + PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero + if self.bits_per_sample.len() == 1 => + { + Ok(ColorType::Gray(self.bits_per_sample[0])) + } + + // TODO: this is bad we should not fail at this point + _ => Err(TiffError::UnsupportedError( + TiffUnsupportedError::InterpretationWithBits( + self.photometric_interpretation, + self.bits_per_sample.clone(), + ), + )), + } + } + + fn create_reader<'r, R: 'r + Read>( + reader: R, + photometric_interpretation: PhotometricInterpretation, + compression_method: CompressionMethod, + compressed_length: u64, + jpeg_tables: Option<Arc<Vec<u8>>>, + ) -> TiffResult<Box<dyn Read + 'r>> { + Ok(match compression_method { + CompressionMethod::None => Box::new(reader), + CompressionMethod::LZW => { + Box::new(LZWReader::new(reader, usize::try_from(compressed_length)?)) + } + CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)), + CompressionMethod::Deflate | CompressionMethod::OldDeflate => { + Box::new(DeflateReader::new(reader)) + } + CompressionMethod::ModernJPEG => { + if jpeg_tables.is_some() && compressed_length < 2 { + return Err(TiffError::FormatError( + TiffFormatError::InvalidTagValueType(Tag::JPEGTables), + )); + } + + let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?; + let mut decoder = jpeg::Decoder::new(jpeg_reader); + + match photometric_interpretation { + PhotometricInterpretation::RGB => { + decoder.set_color_transform(jpeg::ColorTransform::RGB) + } + PhotometricInterpretation::WhiteIsZero => { + decoder.set_color_transform(jpeg::ColorTransform::None) + } + PhotometricInterpretation::BlackIsZero => { + decoder.set_color_transform(jpeg::ColorTransform::None) + } + PhotometricInterpretation::TransparencyMask => { + decoder.set_color_transform(jpeg::ColorTransform::None) + } + PhotometricInterpretation::CMYK => { + decoder.set_color_transform(jpeg::ColorTransform::CMYK) + } + PhotometricInterpretation::YCbCr => { + decoder.set_color_transform(jpeg::ColorTransform::YCbCr) + } + photometric_interpretation => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedInterpretation( + photometric_interpretation, + ), + )); + } + } + + let data = decoder.decode()?; + + Box::new(Cursor::new(data)) + } + method => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedCompressionMethod(method), + )) + } + }) + } + + pub(crate) fn chunk_file_range(&self, chunk: u32) -> TiffResult<(u64, u64)> { + let file_offset = self + .chunk_offsets + .get(chunk as usize) + .ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; + + let compressed_bytes = + self.chunk_bytes + .get(chunk as usize) + .ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; + + Ok((*file_offset, *compressed_bytes)) + } + + pub(crate) fn chunk_dimensions(&self) -> TiffResult<(u32, u32)> { + match self.chunk_type { + ChunkType::Strip => { + let strip_attrs = self.strip_decoder.as_ref().unwrap(); + Ok((self.width, strip_attrs.rows_per_strip)) + } + ChunkType::Tile => { + let tile_attrs = self.tile_attributes.as_ref().unwrap(); + Ok(( + u32::try_from(tile_attrs.tile_width)?, + u32::try_from(tile_attrs.tile_length)?, + )) + } + } + } + + pub(crate) fn chunk_data_dimensions(&self, chunk_index: u32) -> TiffResult<(u32, u32)> { + let dims = self.chunk_dimensions()?; + + match self.chunk_type { + ChunkType::Strip => { + let strip_height_without_padding = chunk_index + .checked_mul(dims.1) + .and_then(|x| self.height.checked_sub(x)) + .ok_or(TiffError::UsageError(UsageError::InvalidChunkIndex( + chunk_index, + )))?; + + // Ignore potential vertical padding on the bottommost strip + let strip_height = dims.1.min(strip_height_without_padding); + + Ok((dims.0, strip_height)) + } + ChunkType::Tile => { + let tile_attrs = self.tile_attributes.as_ref().unwrap(); + let (padding_right, padding_down) = tile_attrs.get_padding(chunk_index as usize); + + let tile_width = tile_attrs.tile_width - padding_right; + let tile_length = tile_attrs.tile_length - padding_down; + + Ok((u32::try_from(tile_width)?, u32::try_from(tile_length)?)) + } + } + } + + pub(crate) fn expand_chunk( + &self, + reader: impl Read, + mut buffer: DecodingBuffer, + output_width: usize, + byte_order: ByteOrder, + chunk_index: u32, + ) -> TiffResult<()> { + // Validate that the provided buffer is of the expected type. + let color_type = self.colortype()?; + match (color_type, &buffer) { + (ColorType::RGB(n), _) + | (ColorType::RGBA(n), _) + | (ColorType::CMYK(n), _) + | (ColorType::YCbCr(n), _) + | (ColorType::Gray(n), _) + if usize::from(n) == buffer.byte_len() * 8 => {} + (ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor { + Predictor::None => {} + Predictor::Horizontal => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::HorizontalPredictor(color_type), + )) + } + Predictor::FloatingPoint => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::FloatingPointPredictor(color_type), + )); + } + }, + (type_, _) => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedColorType(type_), + )) + } + } + + // Validate that the predictor is supported for the sample type. + match (self.predictor, &buffer) { + (Predictor::Horizontal, DecodingBuffer::F32(_)) + | (Predictor::Horizontal, DecodingBuffer::F64(_)) => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::HorizontalPredictor(color_type), + )); + } + (Predictor::FloatingPoint, DecodingBuffer::F32(_)) + | (Predictor::FloatingPoint, DecodingBuffer::F64(_)) => {} + (Predictor::FloatingPoint, _) => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::FloatingPointPredictor(color_type), + )); + } + _ => {} + } + + let compressed_bytes = + self.chunk_bytes + .get(chunk_index as usize) + .ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; + + let byte_len = buffer.byte_len(); + let compression_method = self.compression_method; + let photometric_interpretation = self.photometric_interpretation; + let predictor = self.predictor; + let samples = self.bits_per_sample.len(); + + let chunk_dims = self.chunk_dimensions()?; + let data_dims = self.chunk_data_dimensions(chunk_index)?; + + let padding_right = chunk_dims.0 - data_dims.0; + + let jpeg_tables = self.jpeg_tables.clone(); + let mut reader = Self::create_reader( + reader, + photometric_interpretation, + compression_method, + *compressed_bytes, + jpeg_tables, + )?; + + if output_width == data_dims.0 as usize && padding_right == 0 { + let total_samples = data_dims.0 as usize * data_dims.1 as usize * samples; + let tile = &mut buffer.as_bytes_mut()[..total_samples * byte_len]; + reader.read_exact(tile)?; + + for row in 0..data_dims.1 as usize { + let row_start = row as usize * output_width as usize * samples; + let row_end = (row + 1) * output_width as usize * samples; + let row = buffer.subrange(row_start..row_end); + super::fix_endianness_and_predict(row, samples, byte_order, predictor); + } + if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + super::invert_colors(&mut buffer.subrange(0..total_samples), color_type); + } + } else if padding_right > 0 && self.predictor == Predictor::FloatingPoint { + // The floating point predictor shuffles the padding bytes into the encoded output, so + // this case is handled specially when needed. + let mut encoded = vec![0u8; chunk_dims.0 as usize * samples * byte_len]; + + for row in 0..data_dims.1 as usize { + let row_start = row * output_width as usize * samples; + let row_end = row_start + data_dims.0 as usize * samples; + + reader.read_exact(&mut encoded)?; + match buffer.subrange(row_start..row_end) { + DecodingBuffer::F32(buf) => fp_predict_f32(&mut encoded, buf, samples), + DecodingBuffer::F64(buf) => fp_predict_f64(&mut encoded, buf, samples), + _ => unreachable!(), + } + if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + super::invert_colors(&mut buffer.subrange(row_start..row_end), color_type); + } + } + } else { + for row in 0..data_dims.1 as usize { + let row_start = row * output_width as usize * samples; + let row_end = row_start + data_dims.0 as usize * samples; + + let row = &mut buffer.as_bytes_mut()[(row_start * byte_len)..(row_end * byte_len)]; + reader.read_exact(row)?; + + // Skip horizontal padding + if padding_right > 0 { + let len = u64::try_from(padding_right as usize * samples * byte_len)?; + io::copy(&mut reader.by_ref().take(len), &mut io::sink())?; + } + + let mut row = buffer.subrange(row_start..row_end); + super::fix_endianness_and_predict(row.copy(), samples, byte_order, predictor); + if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { + super::invert_colors(&mut row, color_type); + } + } + } + + Ok(()) + } +} diff --git a/vendor/tiff/src/decoder/mod.rs b/vendor/tiff/src/decoder/mod.rs new file mode 100644 index 0000000..5fa1812 --- /dev/null +++ b/vendor/tiff/src/decoder/mod.rs @@ -0,0 +1,1176 @@ +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; +use std::io::{self, Read, Seek}; +use std::ops::Range; + +use crate::{ + bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError, +}; + +use self::ifd::Directory; +use self::image::Image; +use crate::tags::{ + CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type, +}; + +use self::stream::{ByteOrder, EndianReader, SmartReader}; + +pub mod ifd; +mod image; +mod stream; +mod tag_reader; + +/// Result of a decoding process +#[derive(Debug)] +pub enum DecodingResult { + /// A vector of unsigned bytes + U8(Vec<u8>), + /// A vector of unsigned words + U16(Vec<u16>), + /// A vector of 32 bit unsigned ints + U32(Vec<u32>), + /// A vector of 64 bit unsigned ints + U64(Vec<u64>), + /// A vector of 32 bit IEEE floats + F32(Vec<f32>), + /// A vector of 64 bit IEEE floats + F64(Vec<f64>), + /// A vector of 8 bit signed ints + I8(Vec<i8>), + /// A vector of 16 bit signed ints + I16(Vec<i16>), + /// A vector of 32 bit signed ints + I32(Vec<i32>), + /// A vector of 64 bit signed ints + I64(Vec<i64>), +} + +impl DecodingResult { + fn new_u8(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::U8(vec![0; size])) + } + } + + fn new_u16(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / 2 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::U16(vec![0; size])) + } + } + + fn new_u32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / 4 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::U32(vec![0; size])) + } + } + + fn new_u64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / 8 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::U64(vec![0; size])) + } + } + + fn new_f32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / std::mem::size_of::<f32>() { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::F32(vec![0.0; size])) + } + } + + fn new_f64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / std::mem::size_of::<f64>() { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::F64(vec![0.0; size])) + } + } + + fn new_i8(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / std::mem::size_of::<i8>() { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::I8(vec![0; size])) + } + } + + fn new_i16(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / 2 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::I16(vec![0; size])) + } + } + + fn new_i32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / 4 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::I32(vec![0; size])) + } + } + + fn new_i64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> { + if size > limits.decoding_buffer_size / 8 { + Err(TiffError::LimitsExceeded) + } else { + Ok(DecodingResult::I64(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..]), + 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..]), + DecodingResult::I32(ref mut buf) => DecodingBuffer::I32(&mut buf[start..]), + DecodingResult::I64(ref mut buf) => DecodingBuffer::I64(&mut buf[start..]), + } + } +} + +// A buffer for image decoding +pub enum DecodingBuffer<'a> { + /// A slice of unsigned bytes + U8(&'a mut [u8]), + /// A slice of unsigned words + 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]), + /// A slice of 32 bit IEEE floats + 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]), + /// A slice of 32 bits signed ints + I32(&'a mut [i32]), + /// A slice of 64 bits signed ints + I64(&'a mut [i64]), +} + +impl<'a> DecodingBuffer<'a> { + fn byte_len(&self) -> usize { + match *self { + DecodingBuffer::U8(_) => 1, + DecodingBuffer::U16(_) => 2, + DecodingBuffer::U32(_) => 4, + DecodingBuffer::U64(_) => 8, + DecodingBuffer::F32(_) => 4, + DecodingBuffer::F64(_) => 8, + DecodingBuffer::I8(_) => 1, + DecodingBuffer::I16(_) => 2, + DecodingBuffer::I32(_) => 4, + DecodingBuffer::I64(_) => 8, + } + } + + fn copy<'b>(&'b mut self) -> DecodingBuffer<'b> + where + 'a: 'b, + { + 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), + 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), + DecodingBuffer::I32(ref mut buf) => DecodingBuffer::I32(buf), + DecodingBuffer::I64(ref mut buf) => DecodingBuffer::I64(buf), + } + } + + fn subrange<'b>(&'b mut self, range: Range<usize>) -> DecodingBuffer<'b> + where + 'a: 'b, + { + match *self { + DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[range]), + DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[range]), + DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[range]), + DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[range]), + DecodingBuffer::F32(ref mut buf) => DecodingBuffer::F32(&mut buf[range]), + DecodingBuffer::F64(ref mut buf) => DecodingBuffer::F64(&mut buf[range]), + DecodingBuffer::I8(ref mut buf) => DecodingBuffer::I8(&mut buf[range]), + DecodingBuffer::I16(ref mut buf) => DecodingBuffer::I16(&mut buf[range]), + DecodingBuffer::I32(ref mut buf) => DecodingBuffer::I32(&mut buf[range]), + DecodingBuffer::I64(ref mut buf) => DecodingBuffer::I64(&mut buf[range]), + } + } + + fn as_bytes_mut(&mut self) -> &mut [u8] { + match self { + DecodingBuffer::U8(buf) => &mut *buf, + DecodingBuffer::I8(buf) => bytecast::i8_as_ne_mut_bytes(buf), + DecodingBuffer::U16(buf) => bytecast::u16_as_ne_mut_bytes(buf), + DecodingBuffer::I16(buf) => bytecast::i16_as_ne_mut_bytes(buf), + DecodingBuffer::U32(buf) => bytecast::u32_as_ne_mut_bytes(buf), + DecodingBuffer::I32(buf) => bytecast::i32_as_ne_mut_bytes(buf), + DecodingBuffer::U64(buf) => bytecast::u64_as_ne_mut_bytes(buf), + DecodingBuffer::I64(buf) => bytecast::i64_as_ne_mut_bytes(buf), + DecodingBuffer::F32(buf) => bytecast::f32_as_ne_mut_bytes(buf), + DecodingBuffer::F64(buf) => bytecast::f64_as_ne_mut_bytes(buf), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +/// Chunk type of the internal representation +pub enum ChunkType { + Strip, + Tile, +} + +/// Decoding limits +#[derive(Clone, Debug)] +pub struct Limits { + /// The maximum size of any `DecodingResult` in bytes, the default is + /// 256MiB. If the entire image is decoded at once, then this will + /// be the maximum size of the image. If it is decoded one strip at a + /// time, this will be the maximum size of a strip. + pub decoding_buffer_size: usize, + /// The maximum size of any ifd value in bytes, the default is + /// 1MiB. + pub ifd_value_size: usize, + /// Maximum size for intermediate buffer which may be used to limit the amount of data read per + /// segment even if the entire image is decoded at once. + pub intermediate_buffer_size: usize, + /// The purpose of this is to prevent all the fields of the struct from + /// being public, as this would make adding new fields a major version + /// bump. + _non_exhaustive: (), +} + +impl Limits { + /// A configuration that does not impose any limits. + /// + /// This is a good start if the caller only wants to impose selective limits, contrary to the + /// default limits which allows selectively disabling limits. + /// + /// Note that this configuration is likely to crash on excessively large images since, + /// naturally, the machine running the program does not have infinite memory. + pub fn unlimited() -> Limits { + Limits { + decoding_buffer_size: usize::max_value(), + ifd_value_size: usize::max_value(), + intermediate_buffer_size: usize::max_value(), + _non_exhaustive: (), + } + } +} + +impl Default for Limits { + fn default() -> Limits { + Limits { + decoding_buffer_size: 256 * 1024 * 1024, + intermediate_buffer_size: 128 * 1024 * 1024, + ifd_value_size: 1024 * 1024, + _non_exhaustive: (), + } + } +} + +/// The representation of a TIFF decoder +/// +/// Currently does not support decoding of interlaced images +#[derive(Debug)] +pub struct Decoder<R> +where + R: Read + Seek, +{ + reader: SmartReader<R>, + bigtiff: bool, + limits: Limits, + next_ifd: Option<u64>, + ifd_offsets: Vec<u64>, + seen_ifds: HashSet<u64>, + image: Image, +} + +trait Wrapping { + fn wrapping_add(&self, other: Self) -> Self; +} + +impl Wrapping for u8 { + fn wrapping_add(&self, other: Self) -> Self { + u8::wrapping_add(*self, other) + } +} + +impl Wrapping for u16 { + fn wrapping_add(&self, other: Self) -> Self { + u16::wrapping_add(*self, other) + } +} + +impl Wrapping for u32 { + fn wrapping_add(&self, other: Self) -> Self { + u32::wrapping_add(*self, other) + } +} + +impl Wrapping for u64 { + fn wrapping_add(&self, other: Self) -> Self { + u64::wrapping_add(*self, other) + } +} + +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) + } +} + +impl Wrapping for i32 { + fn wrapping_add(&self, other: Self) -> Self { + i32::wrapping_add(*self, other) + } +} + +impl Wrapping for i64 { + fn wrapping_add(&self, other: Self) -> Self { + i64::wrapping_add(*self, other) + } +} + +fn rev_hpredict_nsamp<T: Copy + Wrapping>(image: &mut [T], samples: usize) { + for col in samples..image.len() { + image[col] = image[col].wrapping_add(image[col - samples]); + } +} + +pub fn fp_predict_f32(input: &mut [u8], output: &mut [f32], samples: usize) { + rev_hpredict_nsamp(input, samples); + for i in 0..output.len() { + // TODO: use f32::from_be_bytes() when we can (version 1.40) + output[i] = f32::from_bits(u32::from_be_bytes([ + input[input.len() / 4 * 0 + i], + input[input.len() / 4 * 1 + i], + input[input.len() / 4 * 2 + i], + input[input.len() / 4 * 3 + i], + ])); + } +} + +pub fn fp_predict_f64(input: &mut [u8], output: &mut [f64], samples: usize) { + rev_hpredict_nsamp(input, samples); + for i in 0..output.len() { + // TODO: use f64::from_be_bytes() when we can (version 1.40) + output[i] = f64::from_bits(u64::from_be_bytes([ + input[input.len() / 8 * 0 + i], + input[input.len() / 8 * 1 + i], + input[input.len() / 8 * 2 + i], + input[input.len() / 8 * 3 + i], + input[input.len() / 8 * 4 + i], + input[input.len() / 8 * 5 + i], + input[input.len() / 8 * 6 + i], + input[input.len() / 8 * 7 + i], + ])); + } +} + +fn fix_endianness_and_predict( + mut image: DecodingBuffer, + samples: usize, + byte_order: ByteOrder, + predictor: Predictor, +) { + match predictor { + Predictor::None => { + fix_endianness(&mut image, byte_order); + } + Predictor::Horizontal => { + fix_endianness(&mut image, byte_order); + match image { + DecodingBuffer::U8(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::U16(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::U32(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::U64(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::I8(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::I16(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::I32(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::I64(buf) => rev_hpredict_nsamp(buf, samples), + DecodingBuffer::F32(_) | DecodingBuffer::F64(_) => { + unreachable!("Caller should have validated arguments. Please file a bug.") + } + } + } + Predictor::FloatingPoint => { + let mut buffer_copy = image.as_bytes_mut().to_vec(); + match image { + DecodingBuffer::F32(buf) => fp_predict_f32(&mut buffer_copy, buf, samples), + DecodingBuffer::F64(buf) => fp_predict_f64(&mut buffer_copy, buf, samples), + _ => unreachable!("Caller should have validated arguments. Please file a bug."), + } + } + } +} + +fn invert_colors_unsigned<T>(buffer: &mut [T], max: T) +where + T: std::ops::Sub<T> + std::ops::Sub<Output = T> + Copy, +{ + for datum in buffer.iter_mut() { + *datum = max - *datum + } +} + +fn invert_colors_fp<T>(buffer: &mut [T], max: T) +where + T: std::ops::Sub<T> + std::ops::Sub<Output = T> + Copy, +{ + for datum in buffer.iter_mut() { + // FIXME: assumes [0, 1) range for floats + *datum = max - *datum + } +} + +fn invert_colors(buf: &mut DecodingBuffer, color_type: ColorType) { + match (color_type, buf) { + (ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => { + invert_colors_unsigned(buffer, 0xffff_ffff_ffff_ffff); + } + (ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => { + invert_colors_unsigned(buffer, 0xffff_ffff); + } + (ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => { + invert_colors_unsigned(buffer, 0xffff); + } + (ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => { + invert_colors_unsigned(buffer, 0xff); + } + (ColorType::Gray(32), DecodingBuffer::F32(ref mut buffer)) => { + invert_colors_fp(buffer, 1.0); + } + (ColorType::Gray(64), DecodingBuffer::F64(ref mut buffer)) => { + invert_colors_fp(buffer, 1.0); + } + _ => {} + } +} + +/// Fix endianness. If `byte_order` matches the host, then conversion is a no-op. +fn fix_endianness(buf: &mut DecodingBuffer, byte_order: ByteOrder) { + match byte_order { + ByteOrder::LittleEndian => match buf { + DecodingBuffer::U8(_) | DecodingBuffer::I8(_) => {} + DecodingBuffer::U16(b) => b.iter_mut().for_each(|v| *v = u16::from_le(*v)), + DecodingBuffer::I16(b) => b.iter_mut().for_each(|v| *v = i16::from_le(*v)), + DecodingBuffer::U32(b) => b.iter_mut().for_each(|v| *v = u32::from_le(*v)), + DecodingBuffer::I32(b) => b.iter_mut().for_each(|v| *v = i32::from_le(*v)), + DecodingBuffer::U64(b) => b.iter_mut().for_each(|v| *v = u64::from_le(*v)), + DecodingBuffer::I64(b) => b.iter_mut().for_each(|v| *v = i64::from_le(*v)), + DecodingBuffer::F32(b) => b + .iter_mut() + .for_each(|v| *v = f32::from_bits(u32::from_le(v.to_bits()))), + DecodingBuffer::F64(b) => b + .iter_mut() + .for_each(|v| *v = f64::from_bits(u64::from_le(v.to_bits()))), + }, + ByteOrder::BigEndian => match buf { + DecodingBuffer::U8(_) | DecodingBuffer::I8(_) => {} + DecodingBuffer::U16(b) => b.iter_mut().for_each(|v| *v = u16::from_be(*v)), + DecodingBuffer::I16(b) => b.iter_mut().for_each(|v| *v = i16::from_be(*v)), + DecodingBuffer::U32(b) => b.iter_mut().for_each(|v| *v = u32::from_be(*v)), + DecodingBuffer::I32(b) => b.iter_mut().for_each(|v| *v = i32::from_be(*v)), + DecodingBuffer::U64(b) => b.iter_mut().for_each(|v| *v = u64::from_be(*v)), + DecodingBuffer::I64(b) => b.iter_mut().for_each(|v| *v = i64::from_be(*v)), + DecodingBuffer::F32(b) => b + .iter_mut() + .for_each(|v| *v = f32::from_bits(u32::from_be(v.to_bits()))), + DecodingBuffer::F64(b) => b + .iter_mut() + .for_each(|v| *v = f64::from_bits(u64::from_be(v.to_bits()))), + }, + }; +} + +impl<R: Read + Seek> Decoder<R> { + /// Create a new decoder that decodes from the stream ```r``` + pub fn new(mut r: R) -> TiffResult<Decoder<R>> { + let mut endianess = Vec::with_capacity(2); + (&mut r).take(2).read_to_end(&mut endianess)?; + let byte_order = match &*endianess { + b"II" => ByteOrder::LittleEndian, + b"MM" => ByteOrder::BigEndian, + _ => { + return Err(TiffError::FormatError( + TiffFormatError::TiffSignatureNotFound, + )) + } + }; + let mut reader = SmartReader::wrap(r, byte_order); + + let bigtiff = match reader.read_u16()? { + 42 => false, + 43 => { + // Read bytesize of offsets (in bigtiff it's alway 8 but provide a way to move to 16 some day) + if reader.read_u16()? != 8 { + return Err(TiffError::FormatError( + TiffFormatError::TiffSignatureNotFound, + )); + } + // This constant should always be 0 + if reader.read_u16()? != 0 { + return Err(TiffError::FormatError( + TiffFormatError::TiffSignatureNotFound, + )); + } + true + } + _ => { + return Err(TiffError::FormatError( + TiffFormatError::TiffSignatureInvalid, + )) + } + }; + let next_ifd = if bigtiff { + Some(reader.read_u64()?) + } else { + Some(u64::from(reader.read_u32()?)) + }; + + let mut seen_ifds = HashSet::new(); + seen_ifds.insert(*next_ifd.as_ref().unwrap()); + let ifd_offsets = vec![*next_ifd.as_ref().unwrap()]; + + let mut decoder = Decoder { + reader, + bigtiff, + limits: Default::default(), + next_ifd, + ifd_offsets, + seen_ifds, + image: Image { + ifd: None, + width: 0, + height: 0, + bits_per_sample: vec![1], + samples: 1, + sample_format: vec![SampleFormat::Uint], + photometric_interpretation: PhotometricInterpretation::BlackIsZero, + compression_method: CompressionMethod::None, + jpeg_tables: None, + predictor: Predictor::None, + chunk_type: ChunkType::Strip, + strip_decoder: None, + tile_attributes: None, + chunk_offsets: Vec::new(), + chunk_bytes: Vec::new(), + }, + }; + decoder.next_image()?; + Ok(decoder) + } + + pub fn with_limits(mut self, limits: Limits) -> Decoder<R> { + self.limits = limits; + self + } + + pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> { + Ok((self.image().width, self.image().height)) + } + + pub fn colortype(&mut self) -> TiffResult<ColorType> { + self.image().colortype() + } + + fn image(&self) -> &Image { + &self.image + } + + /// Loads the IFD at the specified index in the list, if one exists + pub fn seek_to_image(&mut self, ifd_index: usize) -> TiffResult<()> { + // Check whether we have seen this IFD before, if so then the index will be less than the length of the list of ifd offsets + if ifd_index >= self.ifd_offsets.len() { + // We possibly need to load in the next IFD + if self.next_ifd.is_none() { + return Err(TiffError::FormatError( + TiffFormatError::ImageFileDirectoryNotFound, + )); + } + + loop { + // Follow the list until we find the one we want, or we reach the end, whichever happens first + let (_ifd, next_ifd) = self.next_ifd()?; + + if next_ifd.is_none() { + break; + } + + if ifd_index < self.ifd_offsets.len() { + break; + } + } + } + + // If the index is within the list of ifds then we can load the selected image/IFD + if let Some(ifd_offset) = self.ifd_offsets.get(ifd_index) { + let (ifd, _next_ifd) = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?; + + self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?; + + Ok(()) + } else { + Err(TiffError::FormatError( + TiffFormatError::ImageFileDirectoryNotFound, + )) + } + } + + fn next_ifd(&mut self) -> TiffResult<(Directory, Option<u64>)> { + if self.next_ifd.is_none() { + return Err(TiffError::FormatError( + TiffFormatError::ImageFileDirectoryNotFound, + )); + } + + let (ifd, next_ifd) = Self::read_ifd( + &mut self.reader, + self.bigtiff, + self.next_ifd.take().unwrap(), + )?; + + if let Some(next) = next_ifd { + if !self.seen_ifds.insert(next) { + return Err(TiffError::FormatError(TiffFormatError::CycleInOffsets)); + } + self.next_ifd = Some(next); + self.ifd_offsets.push(next); + } + + Ok((ifd, next_ifd)) + } + + /// Reads in the next image. + /// If there is no further image in the TIFF file a format error is returned. + /// To determine whether there are more images call `TIFFDecoder::more_images` instead. + pub fn next_image(&mut self) -> TiffResult<()> { + let (ifd, _next_ifd) = self.next_ifd()?; + + self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?; + Ok(()) + } + + /// Returns `true` if there is at least one more image available. + pub fn more_images(&self) -> bool { + self.next_ifd.is_some() + } + + /// Returns the byte_order + pub fn byte_order(&self) -> ByteOrder { + self.reader.byte_order + } + + #[inline] + pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> { + 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<u8, io::Error> { + let mut buf = [0; 1]; + self.reader.read_exact(&mut buf)?; + Ok(buf[0]) + } + + /// Reads a TIFF short value + #[inline] + pub fn read_short(&mut self) -> Result<u16, io::Error> { + self.reader.read_u16() + } + + /// Reads a TIFF sshort value + #[inline] + pub fn read_sshort(&mut self) -> Result<i16, io::Error> { + self.reader.read_i16() + } + + /// Reads a TIFF long value + #[inline] + pub fn read_long(&mut self) -> Result<u32, io::Error> { + self.reader.read_u32() + } + + /// Reads a TIFF slong value + #[inline] + pub fn read_slong(&mut self) -> Result<i32, io::Error> { + self.reader.read_i32() + } + + /// Reads a TIFF float value + #[inline] + pub fn read_float(&mut self) -> Result<f32, io::Error> { + self.reader.read_f32() + } + + /// Reads a TIFF double value + #[inline] + pub fn read_double(&mut self) -> Result<f64, io::Error> { + self.reader.read_f64() + } + + #[inline] + pub fn read_long8(&mut self) -> Result<u64, io::Error> { + self.reader.read_u64() + } + + #[inline] + pub fn read_slong8(&mut self) -> Result<i64, io::Error> { + self.reader.read_i64() + } + + /// Reads a string + #[inline] + pub fn read_string(&mut self, length: usize) -> TiffResult<String> { + let mut out = vec![0; length]; + self.reader.read_exact(&mut out)?; + // Strings may be null-terminated, so we trim anything downstream of the null byte + if let Some(first) = out.iter().position(|&b| b == 0) { + out.truncate(first); + } + Ok(String::from_utf8(out)?) + } + + /// Reads a TIFF IFA offset/value field + #[inline] + pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> { + if self.bigtiff { + return Err(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + )); + } + 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: 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(|_| ()) + } + + /// Reads a IFD entry. + // An IFD entry has four fields: + // + // Tag 2 bytes + // Type 2 bytes + // Count 4 bytes + // Value 4 bytes either a pointer the value itself + fn read_entry( + reader: &mut SmartReader<R>, + bigtiff: bool, + ) -> TiffResult<Option<(Tag, ifd::Entry)>> { + let tag = Tag::from_u16_exhaustive(reader.read_u16()?); + let type_ = match Type::from_u16(reader.read_u16()?) { + Some(t) => t, + None => { + // Unknown type. Skip this entry according to spec. + reader.read_u32()?; + reader.read_u32()?; + return Ok(None); + } + }; + let entry = if bigtiff { + let mut offset = [0; 8]; + + let count = reader.read_u64()?; + reader.read_exact(&mut offset)?; + ifd::Entry::new_u64(type_, count, offset) + } else { + let mut offset = [0; 4]; + + let count = reader.read_u32()?; + reader.read_exact(&mut offset)?; + ifd::Entry::new(type_, count, offset) + }; + Ok(Some((tag, entry))) + } + + /// Reads the IFD starting at the indicated location. + fn read_ifd( + reader: &mut SmartReader<R>, + bigtiff: bool, + ifd_location: u64, + ) -> TiffResult<(Directory, Option<u64>)> { + reader.goto_offset(ifd_location)?; + + let mut dir: Directory = HashMap::new(); + + let num_tags = if bigtiff { + reader.read_u64()? + } else { + reader.read_u16()?.into() + }; + for _ in 0..num_tags { + let (tag, entry) = match Self::read_entry(reader, bigtiff)? { + Some(val) => val, + None => { + continue; + } // Unknown data type in tag, skip + }; + dir.insert(tag, entry); + } + + let next_ifd = if bigtiff { + reader.read_u64()? + } else { + reader.read_u32()?.into() + }; + + let next_ifd = match next_ifd { + 0 => None, + _ => Some(next_ifd), + }; + + Ok((dir, next_ifd)) + } + + /// Tries to retrieve a tag. + /// Return `Ok(None)` if the tag is not present. + pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> { + let entry = match self.image().ifd.as_ref().unwrap().get(&tag) { + None => return Ok(None), + Some(entry) => entry.clone(), + }; + + Ok(Some(entry.val( + &self.limits, + self.bigtiff, + &mut self.reader, + )?)) + } + + /// Tries to retrieve a tag and convert it to the desired unsigned type. + pub fn find_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> { + self.find_tag(tag)? + .map(|v| v.into_u64()) + .transpose()? + .map(|value| { + T::try_from(value).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()) + }) + .transpose() + } + + /// 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<T: TryFrom<u64>>( + &mut self, + tag: Tag, + ) -> TiffResult<Option<Vec<T>>> { + self.find_tag(tag)? + .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() + }) + .transpose() + } + + /// 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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> { + self.find_tag_unsigned(tag)? + .ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into()) + } + + /// Tries to retrieve a tag. + /// Returns an error if the tag is not present + pub fn get_tag(&mut self, tag: Tag) -> TiffResult<ifd::Value> { + match self.find_tag(tag)? { + Some(val) => Ok(val), + None => Err(TiffError::FormatError( + TiffFormatError::RequiredTagNotFound(tag), + )), + } + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> { + self.get_tag(tag)?.into_u32() + } + pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> { + self.get_tag(tag)?.into_u64() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f32(&mut self, tag: Tag) -> TiffResult<f32> { + self.get_tag(tag)?.into_f32() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f64(&mut self, tag: Tag) -> TiffResult<f64> { + self.get_tag(tag)?.into_f64() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> { + self.get_tag(tag)?.into_u32_vec() + } + + pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult<Vec<u16>> { + self.get_tag(tag)?.into_u16_vec() + } + pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> { + self.get_tag(tag)?.into_u64_vec() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f32_vec(&mut self, tag: Tag) -> TiffResult<Vec<f32>> { + self.get_tag(tag)?.into_f32_vec() + } + + /// Tries to retrieve a tag and convert it to the desired type. + pub fn get_tag_f64_vec(&mut self, tag: Tag) -> TiffResult<Vec<f64>> { + self.get_tag(tag)?.into_f64_vec() + } + + /// Tries to retrieve a tag and convert it to a 8bit vector. + pub fn get_tag_u8_vec(&mut self, tag: Tag) -> TiffResult<Vec<u8>> { + self.get_tag(tag)?.into_u8_vec() + } + + /// Tries to retrieve a tag and convert it to a ascii vector. + pub fn get_tag_ascii_string(&mut self, tag: Tag) -> TiffResult<String> { + self.get_tag(tag)?.into_string() + } + + fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> { + if expected != self.image().chunk_type { + return Err(TiffError::UsageError(UsageError::InvalidChunkType( + expected, + self.image().chunk_type, + ))); + } + + Ok(()) + } + + /// The chunk type (Strips / Tiles) of the image + pub fn get_chunk_type(&self) -> ChunkType { + self.image().chunk_type + } + + /// Number of strips in image + pub fn strip_count(&mut self) -> TiffResult<u32> { + self.check_chunk_type(ChunkType::Strip)?; + let rows_per_strip = self.image().strip_decoder.as_ref().unwrap().rows_per_strip; + + if rows_per_strip == 0 { + return Ok(0); + } + + // rows_per_strip - 1 can never fail since we know it's at least 1 + let height = match self.image().height.checked_add(rows_per_strip - 1) { + Some(h) => h, + None => return Err(TiffError::IntSizeError), + }; + + Ok(height / rows_per_strip) + } + + /// Number of tiles in image + pub fn tile_count(&mut self) -> TiffResult<u32> { + self.check_chunk_type(ChunkType::Tile)?; + Ok(u32::try_from(self.image().chunk_offsets.len())?) + } + + pub fn read_chunk_to_buffer( + &mut self, + mut buffer: DecodingBuffer, + chunk_index: u32, + output_width: usize, + ) -> TiffResult<()> { + let offset = self.image.chunk_file_range(chunk_index)?.0; + self.goto_offset_u64(offset)?; + + let byte_order = self.reader.byte_order; + + self.image.expand_chunk( + &mut self.reader, + buffer.copy(), + output_width, + byte_order, + chunk_index, + )?; + + Ok(()) + } + + fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> { + let buffer_size = match width + .checked_mul(height) + .and_then(|x| x.checked_mul(self.image().bits_per_sample.len())) + { + Some(s) => s, + None => return Err(TiffError::LimitsExceeded), + }; + + let max_sample_bits = self + .image() + .bits_per_sample + .iter() + .cloned() + .max() + .unwrap_or(8); + match self + .image() + .sample_format + .first() + .unwrap_or(&SampleFormat::Uint) + { + SampleFormat::Uint => match max_sample_bits { + 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 => Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedBitsPerChannel(n), + )), + }, + 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), + )), + }, + SampleFormat::Int => match max_sample_bits { + n if n <= 8 => DecodingResult::new_i8(buffer_size, &self.limits), + n if n <= 16 => DecodingResult::new_i16(buffer_size, &self.limits), + n if n <= 32 => DecodingResult::new_i32(buffer_size, &self.limits), + n if n <= 64 => DecodingResult::new_i64(buffer_size, &self.limits), + n => Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedBitsPerChannel(n), + )), + }, + format => { + Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format.clone()]).into()) + } + } + } + + /// Read the specified chunk (at index `chunk_index`) and return the binary data as a Vector. + pub fn read_chunk(&mut self, chunk_index: u32) -> TiffResult<DecodingResult> { + let data_dims = self.image().chunk_data_dimensions(chunk_index)?; + + let mut result = self.result_buffer(data_dims.0 as usize, data_dims.1 as usize)?; + + self.read_chunk_to_buffer(result.as_buffer(0), chunk_index, data_dims.0 as usize)?; + + Ok(result) + } + + /// Returns the default chunk size for the current image. Any given chunk in the image is at most as large as + /// the value returned here. For the size of the data (chunk minus padding), use `chunk_data_dimensions`. + pub fn chunk_dimensions(&self) -> (u32, u32) { + self.image().chunk_dimensions().unwrap() + } + + /// Returns the size of the data in the chunk with the specified index. This is the default size of the chunk, + /// minus any padding. + pub fn chunk_data_dimensions(&self, chunk_index: u32) -> (u32, u32) { + self.image() + .chunk_data_dimensions(chunk_index) + .expect("invalid chunk_index") + } + + /// Decodes the entire image and return it as a Vector + pub fn read_image(&mut self) -> TiffResult<DecodingResult> { + let width = self.image().width; + let height = self.image().height; + let mut result = self.result_buffer(width as usize, height as usize)?; + if width == 0 || height == 0 { + return Ok(result); + } + + let chunk_dimensions = self.image().chunk_dimensions()?; + let chunk_dimensions = ( + chunk_dimensions.0.min(width), + chunk_dimensions.1.min(height), + ); + if chunk_dimensions.0 == 0 || chunk_dimensions.1 == 0 { + return Err(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + )); + } + + let samples = self.image().bits_per_sample.len(); + if samples == 0 { + return Err(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + )); + } + + let chunks_across = ((width - 1) / chunk_dimensions.0 + 1) as usize; + let strip_samples = width as usize * chunk_dimensions.1 as usize * samples; + + for chunk in 0..self.image().chunk_offsets.len() { + self.goto_offset_u64(self.image().chunk_offsets[chunk])?; + + let x = chunk % chunks_across; + let y = chunk / chunks_across; + let buffer_offset = y * strip_samples + x * chunk_dimensions.0 as usize * samples; + let byte_order = self.reader.byte_order; + self.image.expand_chunk( + &mut self.reader, + result.as_buffer(buffer_offset).copy(), + width as usize, + byte_order, + chunk as u32, + )?; + } + + Ok(result) + } +} diff --git a/vendor/tiff/src/decoder/stream.rs b/vendor/tiff/src/decoder/stream.rs new file mode 100644 index 0000000..e0323c2 --- /dev/null +++ b/vendor/tiff/src/decoder/stream.rs @@ -0,0 +1,435 @@ +//! All IO functionality needed for TIFF decoding + +use std::convert::TryFrom; +use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Take}; +use std::sync::Arc; + +/// Byte order of the TIFF file. +#[derive(Clone, Copy, Debug)] +pub enum ByteOrder { + /// little endian byte order + LittleEndian, + /// big endian byte order + BigEndian, +} + +/// Reader that is aware of the byte order. +pub trait EndianReader: Read { + /// Byte order that should be adhered to + fn byte_order(&self) -> ByteOrder; + + /// Reads an u16 + #[inline(always)] + fn read_u16(&mut self) -> Result<u16, io::Error> { + 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), + }) + } + + /// Reads an i8 + #[inline(always)] + fn read_i8(&mut self) -> Result<i8, io::Error> { + 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), + }) + } + + /// Reads an i16 + #[inline(always)] + fn read_i16(&mut self) -> Result<i16, io::Error> { + 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<u32, io::Error> { + 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), + }) + } + + /// Reads an i32 + #[inline(always)] + fn read_i32(&mut self) -> Result<i32, io::Error> { + 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<u64, io::Error> { + 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), + }) + } + + /// Reads an i64 + #[inline(always)] + fn read_i64(&mut self) -> Result<i64, io::Error> { + let mut n = [0u8; 8]; + self.read_exact(&mut n)?; + Ok(match self.byte_order() { + ByteOrder::LittleEndian => i64::from_le_bytes(n), + ByteOrder::BigEndian => i64::from_be_bytes(n), + }) + } + + /// Reads an f32 + #[inline(always)] + fn read_f32(&mut self) -> Result<f32, io::Error> { + let mut n = [0u8; 4]; + self.read_exact(&mut n)?; + Ok(f32::from_bits(match self.byte_order() { + ByteOrder::LittleEndian => u32::from_le_bytes(n), + ByteOrder::BigEndian => u32::from_be_bytes(n), + })) + } + + /// Reads an f64 + #[inline(always)] + fn read_f64(&mut self) -> Result<f64, io::Error> { + let mut n = [0u8; 8]; + self.read_exact(&mut n)?; + Ok(f64::from_bits(match self.byte_order() { + ByteOrder::LittleEndian => u64::from_le_bytes(n), + ByteOrder::BigEndian => u64::from_be_bytes(n), + })) + } +} + +/// +/// # READERS +/// + +/// +/// ## Deflate Reader +/// + +pub type DeflateReader<R> = flate2::read::ZlibDecoder<R>; + +/// +/// ## LZW Reader +/// + +/// Reader that decompresses LZW streams +pub struct LZWReader<R: Read> { + reader: BufReader<Take<R>>, + decoder: weezl::decode::Decoder, +} + +impl<R: Read> LZWReader<R> { + /// Wraps a reader + pub fn new(reader: R, compressed_length: usize) -> LZWReader<R> { + Self { + reader: BufReader::with_capacity( + (32 * 1024).min(compressed_length), + reader.take(u64::try_from(compressed_length).unwrap()), + ), + decoder: weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8), + } + } +} + +impl<R: Read> Read for LZWReader<R> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + loop { + let result = self.decoder.decode_bytes(self.reader.fill_buf()?, buf); + self.reader.consume(result.consumed_in); + + match result.status { + Ok(weezl::LzwStatus::Ok) => { + if result.consumed_out == 0 { + continue; + } else { + return Ok(result.consumed_out); + } + } + Ok(weezl::LzwStatus::NoProgress) => { + assert_eq!(result.consumed_in, 0); + assert_eq!(result.consumed_out, 0); + assert!(self.reader.buffer().is_empty()); + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "no lzw end code found", + )); + } + Ok(weezl::LzwStatus::Done) => { + return Ok(result.consumed_out); + } + Err(err) => return Err(io::Error::new(io::ErrorKind::InvalidData, err)), + } + } + } +} + +/// +/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7)) +/// + +pub(crate) struct JpegReader { + jpeg_tables: Option<Arc<Vec<u8>>>, + + buffer: io::Cursor<Vec<u8>>, + + offset: usize, +} + +impl JpegReader { + /// Constructs new JpegReader wrapping a SmartReader. + /// Because JPEG compression in TIFF allows to save quantization and/or huffman tables in one + /// central location, the constructor accepts this data as `jpeg_tables` here containing either + /// or both. + /// These `jpeg_tables` are simply prepended to the remaining jpeg image data. + /// Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker + /// which is also at the beginning of the remaining JPEG image data and would + /// confuse the JPEG renderer, one of these has to be taken off. In this case the first two + /// bytes of the remaining JPEG data is removed because it follows `jpeg_tables`. + /// Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker, + /// this has to be removed as well (last two bytes of `jpeg_tables`). + pub fn new<R: Read>( + mut reader: R, + length: u64, + jpeg_tables: Option<Arc<Vec<u8>>>, + ) -> io::Result<JpegReader> { + // Read jpeg image data + let mut segment = vec![0; length as usize]; + + reader.read_exact(&mut segment[..])?; + + match jpeg_tables { + Some(jpeg_tables) => { + assert!( + jpeg_tables.len() >= 2, + "jpeg_tables, if given, must be at least 2 bytes long. Got {:?}", + jpeg_tables + ); + + assert!( + length >= 2, + "if jpeg_tables is given, length must be at least 2 bytes long, got {}", + length + ); + + let mut buffer = io::Cursor::new(segment); + // Skip the first two bytes (marker bytes) + buffer.seek(SeekFrom::Start(2))?; + + Ok(JpegReader { + buffer, + jpeg_tables: Some(jpeg_tables), + offset: 0, + }) + } + None => Ok(JpegReader { + buffer: io::Cursor::new(segment), + jpeg_tables: None, + offset: 0, + }), + } + } +} + +impl Read for JpegReader { + // #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let mut start = 0; + + if let Some(jpeg_tables) = &self.jpeg_tables { + if jpeg_tables.len() - 2 > self.offset { + // Read (rest of) jpeg_tables to buf (without the last two bytes) + let size_remaining = jpeg_tables.len() - self.offset - 2; + let to_copy = size_remaining.min(buf.len()); + + buf[start..start + to_copy] + .copy_from_slice(&jpeg_tables[self.offset..self.offset + to_copy]); + + self.offset += to_copy; + + if to_copy == buf.len() { + return Ok(to_copy); + } + + start += to_copy; + } + } + + let read = self.buffer.read(&mut buf[start..])?; + self.offset += read; + + Ok(read + start) + } +} + +/// +/// ## PackBits Reader +/// + +enum PackBitsReaderState { + Header, + Literal, + Repeat { value: u8 }, +} + +/// Reader that unpacks Apple's `PackBits` format +pub struct PackBitsReader<R: Read> { + reader: Take<R>, + state: PackBitsReaderState, + count: usize, +} + +impl<R: Read> PackBitsReader<R> { + /// Wraps a reader + pub fn new(reader: R, length: u64) -> Self { + Self { + reader: reader.take(length), + state: PackBitsReaderState::Header, + count: 0, + } + } +} + +impl<R: Read> Read for PackBitsReader<R> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + while let PackBitsReaderState::Header = self.state { + if self.reader.limit() == 0 { + return Ok(0); + } + let mut header: [u8; 1] = [0]; + self.reader.read_exact(&mut header)?; + let h = header[0] as i8; + if h >= -127 && h <= -1 { + let mut data: [u8; 1] = [0]; + self.reader.read_exact(&mut data)?; + self.state = PackBitsReaderState::Repeat { value: data[0] }; + self.count = (1 - h as isize) as usize; + } else if h >= 0 { + self.state = PackBitsReaderState::Literal; + self.count = h as usize + 1; + } else { + // h = -128 is a no-op. + } + } + + let length = buf.len().min(self.count); + let actual = match self.state { + PackBitsReaderState::Literal => self.reader.read(&mut buf[..length])?, + PackBitsReaderState::Repeat { value } => { + for b in &mut buf[..length] { + *b = value; + } + + length + } + PackBitsReaderState::Header => unreachable!(), + }; + + self.count -= actual; + if self.count == 0 { + self.state = PackBitsReaderState::Header; + } + return Ok(actual); + } +} + +/// +/// ## SmartReader Reader +/// + +/// Reader that is aware of the byte order. +#[derive(Debug)] +pub struct SmartReader<R> +where + R: Read, +{ + reader: R, + pub byte_order: ByteOrder, +} + +impl<R> SmartReader<R> +where + R: Read, +{ + /// Wraps a reader + pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> { + SmartReader { reader, byte_order } + } + pub fn into_inner(self) -> R { + self.reader + } +} +impl<R: Read + Seek> SmartReader<R> { + pub fn goto_offset(&mut self, offset: u64) -> io::Result<()> { + self.seek(io::SeekFrom::Start(offset)).map(|_| ()) + } +} + +impl<R> EndianReader for SmartReader<R> +where + R: Read, +{ + #[inline(always)] + fn byte_order(&self) -> ByteOrder { + self.byte_order + } +} + +impl<R: Read> Read for SmartReader<R> { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.reader.read(buf) + } +} + +impl<R: Read + Seek> Seek for SmartReader<R> { + #[inline] + fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> { + self.reader.seek(pos) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_packbits() { + let encoded = vec![ + 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, + 0xAA, + ]; + let encoded_len = encoded.len(); + + let buff = io::Cursor::new(encoded); + let mut decoder = PackBitsReader::new(buff, encoded_len as u64); + + let mut decoded = Vec::new(); + decoder.read_to_end(&mut decoded).unwrap(); + + let expected = vec![ + 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + ]; + assert_eq!(decoded, expected); + } +} diff --git a/vendor/tiff/src/decoder/tag_reader.rs b/vendor/tiff/src/decoder/tag_reader.rs new file mode 100644 index 0000000..837da40 --- /dev/null +++ b/vendor/tiff/src/decoder/tag_reader.rs @@ -0,0 +1,45 @@ +use std::convert::TryFrom; +use std::io::{Read, Seek}; + +use crate::tags::Tag; +use crate::{TiffError, TiffFormatError, TiffResult}; + +use super::ifd::{Directory, Value}; +use super::stream::SmartReader; +use super::Limits; + +pub(crate) struct TagReader<'a, R: Read + Seek> { + pub reader: &'a mut SmartReader<R>, + pub ifd: &'a Directory, + pub limits: &'a Limits, + pub bigtiff: bool, +} +impl<'a, R: Read + Seek> TagReader<'a, R> { + pub(crate) fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<Value>> { + Ok(match self.ifd.get(&tag) { + Some(entry) => Some(entry.clone().val(self.limits, self.bigtiff, self.reader)?), + None => None, + }) + } + pub(crate) fn require_tag(&mut self, tag: Tag) -> TiffResult<Value> { + match self.find_tag(tag)? { + Some(val) => Ok(val), + None => Err(TiffError::FormatError( + TiffFormatError::RequiredTagNotFound(tag), + )), + } + } + pub fn find_tag_uint_vec<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<Vec<T>>> { + self.find_tag(tag)? + .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() + }) + .transpose() + } +} |