Merge pull request #154 from fintelia/decoder-refactor
Decoder refactor
This commit is contained in:
6 files changed
+689
-519
No files matched your search
+82
-61
@@ -341,14 +341,15 @@ impl Entry {
|
|||||||
pub fn val<R: Read + Seek>(
|
pub fn val<R: Read + Seek>(
|
||||||
&self,
|
&self,
|
||||||
limits: &super::Limits,
|
limits: &super::Limits,
|
||||||
decoder: &mut super::Decoder<R>,
|
bigtiff: bool,
|
||||||
|
reader: &mut SmartReader<R>,
|
||||||
) -> TiffResult<Value> {
|
) -> TiffResult<Value> {
|
||||||
// Case 1: there are no values so we can return immediately.
|
// Case 1: there are no values so we can return immediately.
|
||||||
if self.count == 0 {
|
if self.count == 0 {
|
||||||
return Ok(List(Vec::new()));
|
return Ok(List(Vec::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bo = decoder.byte_order();
|
let bo = reader.byte_order();
|
||||||
|
|
||||||
let tag_size = match self.type_ {
|
let tag_size = match self.type_ {
|
||||||
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
|
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
|
||||||
@@ -373,7 +374,7 @@ impl Entry {
|
|||||||
// Case 2: there is one value.
|
// Case 2: there is one value.
|
||||||
if self.count == 1 {
|
if self.count == 1 {
|
||||||
// 2a: the value is 5-8 bytes and we're in BigTiff mode.
|
// 2a: the value is 5-8 bytes and we're in BigTiff mode.
|
||||||
if decoder.bigtiff && value_bytes > 4 && value_bytes <= 8 {
|
if bigtiff && value_bytes > 4 && value_bytes <= 8 {
|
||||||
return Ok(match self.type_ {
|
return Ok(match self.type_ {
|
||||||
Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?),
|
Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?),
|
||||||
Type::SLONG8 => SignedBig(self.r(bo).read_i64()?),
|
Type::SLONG8 => SignedBig(self.r(bo).read_i64()?),
|
||||||
@@ -419,36 +420,36 @@ impl Entry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::LONG8 => {
|
Type::LONG8 => {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
reader.goto_offset(self.r(bo).read_u32()?.into())?;
|
||||||
UnsignedBig(decoder.read_long8()?)
|
UnsignedBig(reader.read_u64()?)
|
||||||
}
|
}
|
||||||
Type::SLONG8 => {
|
Type::SLONG8 => {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
reader.goto_offset(self.r(bo).read_u32()?.into())?;
|
||||||
SignedBig(decoder.read_slong8()?)
|
SignedBig(reader.read_i64()?)
|
||||||
}
|
}
|
||||||
Type::DOUBLE => {
|
Type::DOUBLE => {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
reader.goto_offset(self.r(bo).read_u32()?.into())?;
|
||||||
Double(decoder.read_double()?)
|
Double(reader.read_f64()?)
|
||||||
}
|
}
|
||||||
Type::RATIONAL => {
|
Type::RATIONAL => {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
reader.goto_offset(self.r(bo).read_u32()?.into())?;
|
||||||
Rational(decoder.read_long()?, decoder.read_long()?)
|
Rational(reader.read_u32()?, reader.read_u32()?)
|
||||||
}
|
}
|
||||||
Type::SRATIONAL => {
|
Type::SRATIONAL => {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
reader.goto_offset(self.r(bo).read_u32()?.into())?;
|
||||||
SRational(decoder.read_slong()?, decoder.read_slong()?)
|
SRational(reader.read_i32()?, reader.read_i32()?)
|
||||||
}
|
}
|
||||||
Type::IFD => Ifd(self.r(bo).read_u32()?),
|
Type::IFD => Ifd(self.r(bo).read_u32()?),
|
||||||
Type::IFD8 => {
|
Type::IFD8 => {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
reader.goto_offset(self.r(bo).read_u32()?.into())?;
|
||||||
IfdBig(decoder.read_long8()?)
|
IfdBig(reader.read_u64()?)
|
||||||
}
|
}
|
||||||
Type::__NonExhaustive => unreachable!(),
|
Type::__NonExhaustive => unreachable!(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 3: There is more than one value, but it fits in the offset field.
|
// Case 3: There is more than one value, but it fits in the offset field.
|
||||||
if value_bytes <= 4 || decoder.bigtiff && value_bytes <= 8 {
|
if value_bytes <= 4 || bigtiff && value_bytes <= 8 {
|
||||||
match self.type_ {
|
match self.type_ {
|
||||||
Type::BYTE => return Ok(offset_to_bytes(self.count as usize, self)?),
|
Type::BYTE => return Ok(offset_to_bytes(self.count as usize, self)?),
|
||||||
Type::SBYTE => return Ok(offset_to_sbytes(self.count as usize, self)?),
|
Type::SBYTE => return Ok(offset_to_sbytes(self.count as usize, self)?),
|
||||||
@@ -535,64 +536,80 @@ impl Entry {
|
|||||||
match self.type_ {
|
match self.type_ {
|
||||||
// TODO check if this could give wrong results
|
// TODO check if this could give wrong results
|
||||||
// at a different endianess of file/computer.
|
// at a different endianess of file/computer.
|
||||||
Type::BYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::BYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
|
let mut buf = [0; 1];
|
||||||
|
reader.read_exact(&mut buf)?;
|
||||||
|
Ok(UnsignedBig(u64::from(buf[0])))
|
||||||
}),
|
}),
|
||||||
Type::SBYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::SBYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
|
Ok(SignedBig(i64::from(reader.read_i8()? as i8)))
|
||||||
}),
|
}),
|
||||||
Type::SHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::SHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
|
Ok(UnsignedBig(u64::from(reader.read_u16()?)))
|
||||||
}),
|
}),
|
||||||
Type::SSHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::SSHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
|
Ok(SignedBig(i64::from(reader.read_i16()?)))
|
||||||
}),
|
}),
|
||||||
Type::LONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::LONG => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(Unsigned(decoder.read_long()?))
|
Ok(Unsigned(reader.read_u32()?))
|
||||||
}),
|
}),
|
||||||
Type::SLONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::SLONG => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(Signed(decoder.read_slong()?))
|
Ok(Signed(reader.read_i32()?))
|
||||||
}),
|
}),
|
||||||
Type::FLOAT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::FLOAT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(Float(decoder.read_float()?))
|
Ok(Float(reader.read_f32()?))
|
||||||
}),
|
}),
|
||||||
Type::DOUBLE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::DOUBLE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(Double(decoder.read_double()?))
|
Ok(Double(reader.read_f64()?))
|
||||||
}),
|
}),
|
||||||
Type::RATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::RATIONAL => {
|
||||||
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
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::SRATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::SLONG8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
|
Ok(SignedBig(reader.read_i64()?))
|
||||||
}),
|
}),
|
||||||
Type::LONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::IFD => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(UnsignedBig(decoder.read_long8()?))
|
Ok(Ifd(reader.read_u32()?))
|
||||||
}),
|
}),
|
||||||
Type::SLONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
Type::IFD8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(SignedBig(decoder.read_slong8()?))
|
Ok(IfdBig(reader.read_u64()?))
|
||||||
}),
|
|
||||||
Type::IFD => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(Ifd(decoder.read_long()?))
|
|
||||||
}),
|
|
||||||
Type::IFD8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(IfdBig(decoder.read_long8()?))
|
|
||||||
}),
|
|
||||||
Type::UNDEFINED => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(Byte(u8::from(decoder.read_byte()?)))
|
|
||||||
}),
|
}),
|
||||||
|
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 => {
|
Type::ASCII => {
|
||||||
let n = usize::try_from(self.count)?;
|
let n = usize::try_from(self.count)?;
|
||||||
if n > limits.decoding_buffer_size {
|
if n > limits.decoding_buffer_size {
|
||||||
return Err(TiffError::LimitsExceeded);
|
return Err(TiffError::LimitsExceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
if decoder.bigtiff {
|
if bigtiff {
|
||||||
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
reader.goto_offset(self.r(bo).read_u64()?)?
|
||||||
} else {
|
} else {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?
|
reader.goto_offset(self.r(bo).read_u32()?.into())?
|
||||||
}
|
}
|
||||||
let string = decoder.read_string(n)?;
|
|
||||||
Ok(Ascii(string))
|
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)?))
|
||||||
}
|
}
|
||||||
Type::__NonExhaustive => unreachable!(),
|
Type::__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
@@ -603,13 +620,14 @@ impl Entry {
|
|||||||
&self,
|
&self,
|
||||||
value_count: u64,
|
value_count: u64,
|
||||||
bo: ByteOrder,
|
bo: ByteOrder,
|
||||||
|
bigtiff: bool,
|
||||||
limits: &super::Limits,
|
limits: &super::Limits,
|
||||||
decoder: &mut super::Decoder<R>,
|
reader: &mut SmartReader<R>,
|
||||||
decode_fn: F,
|
decode_fn: F,
|
||||||
) -> TiffResult<Value>
|
) -> TiffResult<Value>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
|
F: Fn(&mut SmartReader<R>) -> TiffResult<Value>,
|
||||||
{
|
{
|
||||||
let value_count = usize::try_from(value_count)?;
|
let value_count = usize::try_from(value_count)?;
|
||||||
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||||
@@ -617,13 +635,16 @@ impl Entry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut v = Vec::with_capacity(value_count);
|
let mut v = Vec::with_capacity(value_count);
|
||||||
if decoder.bigtiff {
|
|
||||||
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
let offset = if bigtiff {
|
||||||
|
self.r(bo).read_u64()?
|
||||||
} else {
|
} else {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?
|
self.r(bo).read_u32()?.into()
|
||||||
}
|
};
|
||||||
|
reader.goto_offset(offset)?;
|
||||||
|
|
||||||
for _ in 0..value_count {
|
for _ in 0..value_count {
|
||||||
v.push(decode_fn(decoder)?)
|
v.push(decode_fn(reader)?)
|
||||||
}
|
}
|
||||||
Ok(List(v))
|
Ok(List(v))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,290 @@
|
|||||||
|
use super::ifd::{Directory, Value};
|
||||||
|
use super::tag_reader::TagReader;
|
||||||
|
use super::Limits;
|
||||||
|
use super::{stream::SmartReader, ChunkType};
|
||||||
|
use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag};
|
||||||
|
use crate::{TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
|
#[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 samples_per_pixel: 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
|
||||||
|
}
|
||||||
|
fn padding_down(&self) -> usize {
|
||||||
|
self.tile_length - self.image_height % self.tile_length
|
||||||
|
}
|
||||||
|
pub fn row_samples(&self) -> usize {
|
||||||
|
self.tile_width * self.samples_per_pixel
|
||||||
|
}
|
||||||
|
pub fn tile_samples(&self) -> usize {
|
||||||
|
self.tile_length * self.tile_width * self.samples_per_pixel
|
||||||
|
}
|
||||||
|
fn tile_strip_samples(&self) -> usize {
|
||||||
|
(self.tile_samples() * self.tiles_across())
|
||||||
|
- (self.padding_right() * self.tile_length * self.samples_per_pixel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the tile offset in the result buffer, counted in samples
|
||||||
|
pub fn get_offset(&self, tile: usize) -> usize {
|
||||||
|
let row = tile / self.tiles_across();
|
||||||
|
let column = tile % self.tiles_across();
|
||||||
|
|
||||||
|
(row * self.tile_strip_samples()) + (column * self.row_samples())
|
||||||
|
}
|
||||||
|
|
||||||
|
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<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()?;
|
||||||
|
|
||||||
|
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(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() {
|
||||||
|
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)?,
|
||||||
|
samples_per_pixel: bits_per_sample.len(),
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+258
-425
File diff suppressed because it is too large.
Load diff
@@ -339,6 +339,14 @@ where
|
|||||||
pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> {
|
pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> {
|
||||||
SmartReader { reader, byte_order }
|
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>
|
impl<R> EndianReader for SmartReader<R>
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-33
@@ -344,7 +344,7 @@ fn test_bad_strip_count() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_too_big_buffer_size() {
|
fn fuzzer_testcase1() {
|
||||||
let image = [
|
let image = [
|
||||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 99, 255, 255, 254, 1, 1, 4, 0, 1,
|
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 99, 255, 255, 254, 1, 1, 4, 0, 1,
|
||||||
0, 0, 0, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0,
|
0, 0, 0, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0,
|
||||||
@@ -353,14 +353,7 @@ fn test_too_big_buffer_size() {
|
|||||||
178,
|
178,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||||
|
|
||||||
let err = decoder.read_image().unwrap_err();
|
|
||||||
|
|
||||||
match err {
|
|
||||||
tiff::TiffError::LimitsExceeded => {}
|
|
||||||
unexpected => panic!("Unexpected error {}", unexpected),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -422,10 +415,7 @@ fn oom_2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_jpeg_tag() {
|
fn fuzzer_testcase2() {
|
||||||
use tiff::tags;
|
|
||||||
use tiff::{TiffError, TiffFormatError};
|
|
||||||
|
|
||||||
let image = [
|
let image = [
|
||||||
73, 73, 42, 0, 8, 0, 0, 0, 15, 0, 0, 254, 44, 1, 0, 0, 0, 0, 0, 32, 0, 0, 0, 1, 4, 0, 1, 0,
|
73, 73, 42, 0, 8, 0, 0, 0, 15, 0, 0, 254, 44, 1, 0, 0, 0, 0, 0, 32, 0, 0, 0, 1, 4, 0, 1, 0,
|
||||||
0, 0, 0, 1, 0, 0, 91, 1, 1, 0, 0, 0, 0, 0, 242, 4, 0, 0, 0, 22, 0, 56, 77, 0, 77, 1, 0, 0,
|
0, 0, 0, 1, 0, 0, 91, 1, 1, 0, 0, 0, 0, 0, 242, 4, 0, 0, 0, 22, 0, 56, 77, 0, 77, 1, 0, 0,
|
||||||
@@ -437,14 +427,7 @@ fn invalid_jpeg_tag() {
|
|||||||
73,
|
73,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||||
|
|
||||||
let err = decoder.read_image().unwrap_err();
|
|
||||||
|
|
||||||
match err {
|
|
||||||
TiffError::FormatError(TiffFormatError::InvalidTagValueType(tags::Tag::JPEGTables)) => {}
|
|
||||||
unexpected => panic!("Unexpected error {}", unexpected),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -474,9 +457,7 @@ fn invalid_jpeg_tag_2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multiple_images_strip_height() {
|
fn fuzzer_testcase3() {
|
||||||
use tiff::TiffError;
|
|
||||||
|
|
||||||
let image = [
|
let image = [
|
||||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 2, 0, 0, 0, 61, 1, 9, 0, 46, 22,
|
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 2, 0, 0, 0, 61, 1, 9, 0, 46, 22,
|
||||||
128, 0, 0, 0, 0, 1, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 17, 1, 4, 0, 27, 0, 0, 0, 0, 0, 0,
|
128, 0, 0, 0, 0, 1, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 17, 1, 4, 0, 27, 0, 0, 0, 0, 0, 0,
|
||||||
@@ -485,15 +466,7 @@ fn multiple_images_strip_height() {
|
|||||||
255, 255,
|
255, 255,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||||
|
|
||||||
let _ = decoder.read_image().unwrap();
|
|
||||||
let err = decoder.read_image().unwrap_err();
|
|
||||||
|
|
||||||
match err {
|
|
||||||
TiffError::IntSizeError => {}
|
|
||||||
unexpected => panic!("Unexpected error {}", unexpected),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in new issue
Block a user