aboutsummaryrefslogtreecommitdiff
path: root/vendor/tiff/src/decoder
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/tiff/src/decoder
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-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.rs670
-rw-r--r--vendor/tiff/src/decoder/image.rs601
-rw-r--r--vendor/tiff/src/decoder/mod.rs1176
-rw-r--r--vendor/tiff/src/decoder/stream.rs435
-rw-r--r--vendor/tiff/src/decoder/tag_reader.rs45
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()
+ }
+}