Move some decoder fields into a new Image struct

This commit is contained in:
Jonathan Behrens committed 2022-01-12 19:50:06 -05:00
1 parent 6ba5553d20
commit b385a6e752
2 files changed
+242 -177

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
use super::ChunkType;
use super::ifd::Directory;
use crate::tags::{
CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat,
};
#[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>,
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 {
}
+146 -177
View File
@@ -8,6 +8,7 @@ use crate::{
}; };
use self::ifd::Directory; use self::ifd::Directory;
use self::image::{Image, StripDecodeState, TileAttributes};
use crate::tags::{ use crate::tags::{
CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type, CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type,
}; };
@@ -17,6 +18,7 @@ use self::stream::{
}; };
pub mod ifd; pub mod ifd;
mod image;
mod stream; mod stream;
/// Result of a decoding process /// Result of a decoding process
@@ -233,74 +235,6 @@ impl<'a> DecodingBuffer<'a> {
} }
} }
#[derive(Debug)]
struct StripDecodeState {
rows_per_strip: u32,
}
#[derive(Debug)]
/// Computed values useful for tile decoding
struct TileAttributes {
image_width: usize,
image_height: usize,
samples_per_pixel: usize,
tile_width: usize,
tile_length: usize,
}
impl TileAttributes {
fn tiles_across(&self) -> usize {
(self.image_width + self.tile_width - 1) / self.tile_width
}
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
}
fn row_samples(&self) -> usize {
self.tile_width * self.samples_per_pixel
}
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
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())
}
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, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
/// Chunk type of the internal representation /// Chunk type of the internal representation
pub enum ChunkType { pub enum ChunkType {
@@ -369,23 +303,9 @@ where
bigtiff: bool, bigtiff: bool,
limits: Limits, limits: Limits,
next_ifd: Option<u64>, next_ifd: Option<u64>,
ifd: Option<Directory>,
width: u32,
height: u32,
bits_per_sample: Vec<u8>,
samples: u8,
sample_format: Vec<SampleFormat>,
photometric_interpretation: PhotometricInterpretation,
compression_method: CompressionMethod,
predictor: Predictor,
jpeg_tables: Option<Vec<u8>>,
chunk_type: ChunkType,
strip_decoder: Option<StripDecodeState>,
tile_attributes: Option<TileAttributes>,
chunk_offsets: Vec<u64>,
chunk_bytes: Vec<u64>,
current_chunk: usize, current_chunk: usize,
seen_ifds: HashSet<u64>, seen_ifds: HashSet<u64>,
image: Image,
} }
trait Wrapping { trait Wrapping {
@@ -584,6 +504,7 @@ impl<R: Read + Seek> Decoder<R> {
bigtiff, bigtiff,
limits: Default::default(), limits: Default::default(),
next_ifd, next_ifd,
image: Image {
ifd: None, ifd: None,
width: 0, width: 0,
height: 0, height: 0,
@@ -599,6 +520,7 @@ impl<R: Read + Seek> Decoder<R> {
tile_attributes: None, tile_attributes: None,
chunk_offsets: Vec::new(), chunk_offsets: Vec::new(),
chunk_bytes: Vec::new(), chunk_bytes: Vec::new(),
},
current_chunk: 0, current_chunk: 0,
seen_ifds, seen_ifds,
}; };
@@ -612,12 +534,12 @@ impl<R: Read + Seek> Decoder<R> {
} }
pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> { pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> {
Ok((self.width, self.height)) Ok((self.image().width, self.image().height))
} }
pub fn colortype(&mut self) -> TiffResult<ColorType> { pub fn colortype(&mut self) -> TiffResult<ColorType> {
match self.photometric_interpretation { match self.image().photometric_interpretation {
PhotometricInterpretation::RGB => match self.bits_per_sample[..] { PhotometricInterpretation::RGB => match self.image().bits_per_sample[..] {
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)), [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)), [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
// FIXME: We should _ignore_ other components. In particular: // FIXME: We should _ignore_ other components. In particular:
@@ -627,36 +549,40 @@ impl<R: Read + Seek> Decoder<R> {
// > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements. // > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements.
_ => Err(TiffError::UnsupportedError( _ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits( TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation, self.image().photometric_interpretation,
self.bits_per_sample.clone(), self.image().bits_per_sample.clone(),
), ),
)), )),
}, },
PhotometricInterpretation::CMYK => match self.bits_per_sample[..] { PhotometricInterpretation::CMYK => match self.image().bits_per_sample[..] {
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)), [c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
_ => Err(TiffError::UnsupportedError( _ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits( TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation, self.image().photometric_interpretation,
self.bits_per_sample.clone(), self.image().bits_per_sample.clone(),
), ),
)), )),
}, },
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
if self.bits_per_sample.len() == 1 => if self.image().bits_per_sample.len() == 1 =>
{ {
Ok(ColorType::Gray(self.bits_per_sample[0])) Ok(ColorType::Gray(self.image().bits_per_sample[0]))
} }
// TODO: this is bad we should not fail at this point // TODO: this is bad we should not fail at this point
_ => Err(TiffError::UnsupportedError( _ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits( TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation, self.image().photometric_interpretation,
self.bits_per_sample.clone(), self.image().bits_per_sample.clone(),
), ),
)), )),
} }
} }
fn image(&self) -> &Image {
&self.image
}
/// Reset the decoder. /// Reset the decoder.
#[deprecated = "Never should have been public. Only use Decoder::new()"] #[deprecated = "Never should have been public. Only use Decoder::new()"]
pub fn init(self) -> TiffResult<Decoder<R>> { pub fn init(self) -> TiffResult<Decoder<R>> {
@@ -668,30 +594,32 @@ impl<R: Read + Seek> Decoder<R> {
/// If there is no further image in the TIFF file a format error is returned. /// 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. /// To determine whether there are more images call `TIFFDecoder::more_images` instead.
pub fn next_image(&mut self) -> TiffResult<()> { pub fn next_image(&mut self) -> TiffResult<()> {
self.ifd = Some(self.read_ifd()?);
self.width = self.get_tag_u32(Tag::ImageWidth)?;
self.height = self.get_tag_u32(Tag::ImageLength)?;
self.strip_decoder = None;
self.tile_attributes = None;
self.chunk_offsets.clear();
self.chunk_bytes.clear();
self.current_chunk = 0; self.current_chunk = 0;
self.photometric_interpretation = self self.image.ifd = Some(self.read_ifd()?);
self.image.width = self.get_tag_u32(Tag::ImageWidth)?;
self.image.height = self.get_tag_u32(Tag::ImageLength)?;
self.image.strip_decoder = None;
self.image.tile_attributes = None;
self.image.chunk_offsets.clear();
self.image.chunk_bytes.clear();
self.image.jpeg_tables = None;
self.image.photometric_interpretation = self
.find_tag_unsigned(Tag::PhotometricInterpretation)? .find_tag_unsigned(Tag::PhotometricInterpretation)?
.and_then(PhotometricInterpretation::from_u16) .and_then(PhotometricInterpretation::from_u16)
.ok_or(TiffUnsupportedError::UnknownInterpretation)?; .ok_or(TiffUnsupportedError::UnknownInterpretation)?;
// Try to parse both the compression method and the number, format, and bits of the included samples. // 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. // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images.
self.compression_method = match self.find_tag_unsigned(Tag::Compression)? { self.image.compression_method = match self.find_tag_unsigned(Tag::Compression)? {
Some(val) => CompressionMethod::from_u16(val) Some(val) => CompressionMethod::from_u16(val)
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?, .ok_or(TiffUnsupportedError::UnknownCompressionMethod)?,
None => CompressionMethod::None, None => CompressionMethod::None,
}; };
if self.compression_method == CompressionMethod::ModernJPEG { if self.image.compression_method == CompressionMethod::ModernJPEG {
self.jpeg_tables = self self.image.jpeg_tables = self
.find_tag(Tag::JPEGTables)? .find_tag(Tag::JPEGTables)?
.map(|_| { .map(|_| {
let vec = self.get_tag_u8_vec(Tag::JPEGTables)?; let vec = self.get_tag_u8_vec(Tag::JPEGTables)?;
@@ -705,9 +633,9 @@ impl<R: Read + Seek> Decoder<R> {
.transpose()?; .transpose()?;
} }
self.samples = self.find_tag_unsigned(Tag::SamplesPerPixel)?.unwrap_or(1); self.image.samples = self.find_tag_unsigned(Tag::SamplesPerPixel)?.unwrap_or(1);
self.sample_format = match self.find_tag_unsigned_vec(Tag::SampleFormat)? { self.image.sample_format = match self.find_tag_unsigned_vec(Tag::SampleFormat)? {
Some(vals) => { Some(vals) => {
let sample_format: Vec<_> = vals let sample_format: Vec<_> = vals
.into_iter() .into_iter()
@@ -717,7 +645,7 @@ impl<R: Read + Seek> Decoder<R> {
// TODO: for now, only homogenous formats across samples are supported. // TODO: for now, only homogenous formats across samples are supported.
if !sample_format.windows(2).all(|s| s[0] == s[1]) { if !sample_format.windows(2).all(|s| s[0] == s[1]) {
return Err(TiffUnsupportedError::UnsupportedSampleFormat( return Err(TiffUnsupportedError::UnsupportedSampleFormat(
self.sample_format.clone(), self.image.sample_format.clone(),
) )
.into()); .into());
} }
@@ -727,14 +655,14 @@ impl<R: Read + Seek> Decoder<R> {
None => vec![SampleFormat::Uint], None => vec![SampleFormat::Uint],
}; };
self.bits_per_sample = match self.samples { self.image.bits_per_sample = match self.image.samples {
1 | 3 | 4 => self 1 | 3 | 4 => self
.find_tag_unsigned_vec(Tag::BitsPerSample)? .find_tag_unsigned_vec(Tag::BitsPerSample)?
.unwrap_or_else(|| vec![1]), .unwrap_or_else(|| vec![1]),
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()), _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.image.samples).into()),
}; };
self.predictor = self self.image.predictor = self
.find_tag_unsigned(Tag::Predictor)? .find_tag_unsigned(Tag::Predictor)?
.map(|p| { .map(|p| {
Predictor::from_u16(p) Predictor::from_u16(p)
@@ -743,7 +671,7 @@ impl<R: Read + Seek> Decoder<R> {
.transpose()? .transpose()?
.unwrap_or(Predictor::None); .unwrap_or(Predictor::None);
let ifd = self.ifd.as_ref().unwrap(); let ifd = self.image.ifd.as_ref().unwrap();
match ( match (
ifd.contains_key(&Tag::StripByteCounts), ifd.contains_key(&Tag::StripByteCounts),
ifd.contains_key(&Tag::StripOffsets), ifd.contains_key(&Tag::StripOffsets),
@@ -751,18 +679,18 @@ impl<R: Read + Seek> Decoder<R> {
ifd.contains_key(&Tag::TileOffsets), ifd.contains_key(&Tag::TileOffsets),
) { ) {
(true, true, false, false) => { (true, true, false, false) => {
self.chunk_type = ChunkType::Strip; self.image.chunk_type = ChunkType::Strip;
self.chunk_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?; self.image.chunk_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?;
self.chunk_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?; self.image.chunk_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?;
let rows_per_strip = self let rows_per_strip = self
.find_tag(Tag::RowsPerStrip)? .find_tag(Tag::RowsPerStrip)?
.unwrap_or(ifd::Value::Unsigned(self.height)) .unwrap_or(ifd::Value::Unsigned(self.image.height))
.into_u32()?; .into_u32()?;
self.strip_decoder = Some(StripDecodeState { rows_per_strip }); self.image.strip_decoder = Some(StripDecodeState { rows_per_strip });
} }
(false, false, true, true) => { (false, false, true, true) => {
self.chunk_type = ChunkType::Tile; self.image.chunk_type = ChunkType::Tile;
let tile_width = usize::try_from(self.get_tag_u32(Tag::TileWidth)?)?; let tile_width = usize::try_from(self.get_tag_u32(Tag::TileWidth)?)?;
let tile_length = usize::try_from(self.get_tag_u32(Tag::TileLength)?)?; let tile_length = usize::try_from(self.get_tag_u32(Tag::TileLength)?)?;
@@ -773,19 +701,19 @@ impl<R: Read + Seek> Decoder<R> {
return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into()); return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into());
} }
self.tile_attributes = Some(TileAttributes { self.image.tile_attributes = Some(TileAttributes {
image_width: usize::try_from(self.width)?, image_width: usize::try_from(self.image.width)?,
image_height: usize::try_from(self.height)?, image_height: usize::try_from(self.image.height)?,
samples_per_pixel: self.bits_per_sample.len(), samples_per_pixel: self.image.bits_per_sample.len(),
tile_width, tile_width,
tile_length, tile_length,
}); });
self.chunk_offsets = self.get_tag_u64_vec(Tag::TileOffsets)?; self.image.chunk_offsets = self.get_tag_u64_vec(Tag::TileOffsets)?;
self.chunk_bytes = self.get_tag_u64_vec(Tag::TileByteCounts)?; self.image.chunk_bytes = self.get_tag_u64_vec(Tag::TileByteCounts)?;
let tile = self.tile_attributes.as_ref().unwrap(); let tile = self.image.tile_attributes.as_ref().unwrap();
if self.chunk_offsets.len() != self.chunk_bytes.len() if self.image.chunk_offsets.len() != self.image.chunk_bytes.len()
|| self.chunk_offsets.len() != tile.tiles_down() * tile.tiles_across() || self.image.chunk_offsets.len() != tile.tiles_down() * tile.tiles_across()
{ {
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
@@ -984,7 +912,7 @@ impl<R: Read + Seek> Decoder<R> {
/// Tries to retrieve a tag. /// Tries to retrieve a tag.
/// Return `Ok(None)` if the tag is not present. /// Return `Ok(None)` if the tag is not present.
pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> { pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> {
let entry = match self.ifd.as_ref().unwrap().get(&tag) { let entry = match self.image().ifd.as_ref().unwrap().get(&tag) {
None => return Ok(None), None => return Ok(None),
Some(entry) => entry.clone(), Some(entry) => entry.clone(),
}; };
@@ -1199,10 +1127,11 @@ impl<R: Read + Seek> Decoder<R> {
// Construct necessary reader to perform decompression. // Construct necessary reader to perform decompression.
self.goto_offset_u64(offset)?; self.goto_offset_u64(offset)?;
let byte_order = self.reader.byte_order; let byte_order = self.reader.byte_order;
let compression_method = self.image().compression_method;
let reader = Self::create_reader( let reader = Self::create_reader(
&mut self.reader, &mut self.reader,
self.compression_method, compression_method,
length, length,
buffer.len(), buffer.len(),
buffer.byte_len(), buffer.byte_len(),
@@ -1235,7 +1164,7 @@ impl<R: Read + Seek> Decoder<R> {
Self::fix_endianness(&mut buffer, byte_order); Self::fix_endianness(&mut buffer, byte_order);
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { if self.image().photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
Self::invert_colors(&mut buffer, color_type); Self::invert_colors(&mut buffer, color_type);
} }
@@ -1255,20 +1184,22 @@ impl<R: Read + Seek> Decoder<R> {
let byte_len = buffer.byte_len(); let byte_len = buffer.byte_len();
let byte_order = self.reader.byte_order; let byte_order = self.reader.byte_order;
let tile_attrs = self.tile_attributes.as_mut().unwrap(); let tile_attrs = self.image().tile_attributes.as_ref().unwrap();
let (padding_right, padding_down) = tile_attrs.get_padding(tile); let (padding_right, padding_down) = tile_attrs.get_padding(tile);
let tile_samples = tile_attrs.tile_samples(); let tile_samples = tile_attrs.tile_samples();
let tile_length = tile_attrs.tile_length; let tile_length = tile_attrs.tile_length;
let row_samples = tile_attrs.row_samples(); let row_samples = tile_attrs.row_samples();
let padding_right_samples = padding_right * self.bits_per_sample.len(); let padding_right_samples = padding_right * self.image().bits_per_sample.len();
self.goto_offset_u64(offset)?; self.goto_offset_u64(offset)?;
let line_samples = output_width * self.bits_per_sample.len(); let line_samples = output_width * self.image().bits_per_sample.len();
let compression_method = self.image().compression_method;
let photometric_interpretation = self.image().photometric_interpretation;
let mut reader = Self::create_reader( let mut reader = Self::create_reader(
&mut self.reader, &mut self.reader,
self.compression_method, compression_method,
compressed_length, compressed_length,
tile_samples, tile_samples,
byte_len, byte_len,
@@ -1303,7 +1234,7 @@ impl<R: Read + Seek> Decoder<R> {
Self::fix_endianness(&mut buffer.subrange(row_start..row_end), byte_order); Self::fix_endianness(&mut buffer.subrange(row_start..row_end), byte_order);
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
Self::invert_colors(&mut buffer.subrange(row_start..row_end), color_type); Self::invert_colors(&mut buffer.subrange(row_start..row_end), color_type);
} }
} }
@@ -1346,10 +1277,10 @@ impl<R: Read + Seek> Decoder<R> {
} }
fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> { fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> {
if expected != self.chunk_type { if expected != self.image().chunk_type {
return Err(TiffError::UsageError(UsageError::InvalidChunkType( return Err(TiffError::UsageError(UsageError::InvalidChunkType(
expected, expected,
self.chunk_type, self.image().chunk_type,
))); )));
} }
@@ -1358,20 +1289,20 @@ impl<R: Read + Seek> Decoder<R> {
/// The chunk type (Strips / Tiles) of the image /// The chunk type (Strips / Tiles) of the image
pub fn get_chunk_type(&self) -> ChunkType { pub fn get_chunk_type(&self) -> ChunkType {
self.chunk_type self.image().chunk_type
} }
/// Number of strips in image /// Number of strips in image
pub fn strip_count(&mut self) -> TiffResult<u32> { pub fn strip_count(&mut self) -> TiffResult<u32> {
self.check_chunk_type(ChunkType::Strip)?; self.check_chunk_type(ChunkType::Strip)?;
let rows_per_strip = self.strip_decoder.as_ref().unwrap().rows_per_strip; let rows_per_strip = self.image().strip_decoder.as_ref().unwrap().rows_per_strip;
if rows_per_strip == 0 { if rows_per_strip == 0 {
return Ok(0); return Ok(0);
} }
// rows_per_strip - 1 can never fail since we know it's at least 1 // rows_per_strip - 1 can never fail since we know it's at least 1
let height = match self.height.checked_add(rows_per_strip - 1) { let height = match self.image().height.checked_add(rows_per_strip - 1) {
Some(h) => h, Some(h) => h,
None => return Err(TiffError::IntSizeError), None => return Err(TiffError::IntSizeError),
}; };
@@ -1382,7 +1313,7 @@ impl<R: Read + Seek> Decoder<R> {
/// Number of tiles in image /// Number of tiles in image
pub fn tile_count(&mut self) -> TiffResult<u32> { pub fn tile_count(&mut self) -> TiffResult<u32> {
self.check_chunk_type(ChunkType::Tile)?; self.check_chunk_type(ChunkType::Tile)?;
Ok(u32::try_from(self.chunk_offsets.len())?) Ok(u32::try_from(self.image().chunk_offsets.len())?)
} }
pub fn read_jpeg(&mut self) -> TiffResult<DecodingResult> { pub fn read_jpeg(&mut self) -> TiffResult<DecodingResult> {
@@ -1416,12 +1347,15 @@ impl<R: Read + Seek> Decoder<R> {
self.goto_offset(*offset)?; self.goto_offset(*offset)?;
let length = bytes[idx]; let length = bytes[idx];
if self.jpeg_tables.is_some() && length < 2 { if self.image().jpeg_tables.is_some() && length < 2 {
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InvalidTagValueType(Tag::JPEGTables), TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
)); ));
} }
let jpeg_reader = JpegReader::new(&mut self.reader, length, &self.jpeg_tables)?; // TODO: avoid this clone
let jpeg_tables = self.image().jpeg_tables.clone();
let jpeg_reader = JpegReader::new(&mut self.reader, length, &jpeg_tables)?;
let mut decoder = jpeg::Decoder::new(jpeg_reader); let mut decoder = jpeg::Decoder::new(jpeg_reader);
match decoder.decode() { match decoder.decode() {
@@ -1449,16 +1383,25 @@ impl<R: Read + Seek> Decoder<R> {
pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> { pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> {
self.check_chunk_type(ChunkType::Strip)?; self.check_chunk_type(ChunkType::Strip)?;
let index = self.current_chunk; let index = self.current_chunk;
let offset = *self.chunk_offsets.get(index).ok_or(TiffError::FormatError( let offset = *self
.image()
.chunk_offsets
.get(index)
.ok_or(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
))?; ))?;
let byte_count = *self.chunk_bytes.get(index).ok_or(TiffError::FormatError( let byte_count = *self
.image()
.chunk_bytes
.get(index)
.ok_or(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
))?; ))?;
let rows_per_strip = usize::try_from(self.strip_decoder.as_ref().unwrap().rows_per_strip)?; let rows_per_strip =
usize::try_from(self.image().strip_decoder.as_ref().unwrap().rows_per_strip)?;
let sized_width = usize::try_from(self.width)?; let sized_width = usize::try_from(self.image().width)?;
let sized_height = usize::try_from(self.height)?; let sized_height = usize::try_from(self.image().height)?;
let strip_height_without_padding = index let strip_height_without_padding = index
.checked_mul(rows_per_strip) .checked_mul(rows_per_strip)
@@ -1470,7 +1413,7 @@ impl<R: Read + Seek> Decoder<R> {
let buffer_size = sized_width let buffer_size = sized_width
.checked_mul(strip_height) .checked_mul(strip_height)
.and_then(|x| x.checked_mul(self.bits_per_sample.len())) .and_then(|x| x.checked_mul(self.image().bits_per_sample.len()))
.ok_or(TiffError::LimitsExceeded)?; .ok_or(TiffError::LimitsExceeded)?;
if buffer.len() < buffer_size { if buffer.len() < buffer_size {
@@ -1482,16 +1425,13 @@ impl<R: Read + Seek> Decoder<R> {
self.expand_strip(buffer.subrange(0..buffer_size), offset, byte_count)?; self.expand_strip(buffer.subrange(0..buffer_size), offset, byte_count)?;
self.current_chunk += 1; self.current_chunk += 1;
if u32::try_from(index)? == self.strip_count()? { match self.image().predictor {
self.strip_decoder = None;
}
match self.predictor {
Predictor::None => {} Predictor::None => {}
Predictor::Horizontal => { Predictor::Horizontal => {
rev_hpredict( rev_hpredict(
buffer.copy(), buffer.copy(),
(self.width, u32::try_from(strip_height)?), (self.image().width, u32::try_from(strip_height)?),
usize::try_from(self.width)?, usize::try_from(self.image().width)?,
self.colortype()?, self.colortype()?,
)?; )?;
} }
@@ -1506,15 +1446,24 @@ impl<R: Read + Seek> Decoder<R> {
tile: usize, tile: usize,
output_width: usize, output_width: usize,
) -> TiffResult<()> { ) -> TiffResult<()> {
let file_offset = *self.chunk_offsets.get(tile).ok_or(TiffError::FormatError( let file_offset = *self
.image()
.chunk_offsets
.get(tile)
.ok_or(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
))?; ))?;
let compressed_bytes = *self.chunk_bytes.get(tile).ok_or(TiffError::FormatError( let compressed_bytes =
*self
.image()
.chunk_bytes
.get(tile)
.ok_or(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
))?; ))?;
let tile_attrs = self.tile_attributes.as_ref().unwrap(); let tile_attrs = self.image().tile_attributes.as_ref().unwrap();
let tile_width = tile_attrs.tile_width; let tile_width = tile_attrs.tile_width;
let tile_length = tile_attrs.tile_length; let tile_length = tile_attrs.tile_length;
@@ -1528,7 +1477,7 @@ impl<R: Read + Seek> Decoder<R> {
output_width, output_width,
)?; )?;
match self.predictor { match self.image().predictor {
Predictor::None => {} Predictor::None => {}
Predictor::Horizontal => { Predictor::Horizontal => {
rev_hpredict( rev_hpredict(
@@ -1549,14 +1498,25 @@ impl<R: Read + Seek> Decoder<R> {
fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> { fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> {
let buffer_size = match width let buffer_size = match width
.checked_mul(height) .checked_mul(height)
.and_then(|x| x.checked_mul(self.bits_per_sample.len())) .and_then(|x| x.checked_mul(self.image().bits_per_sample.len()))
{ {
Some(s) => s, Some(s) => s,
None => return Err(TiffError::LimitsExceeded), None => return Err(TiffError::LimitsExceeded),
}; };
let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8); let max_sample_bits = self
match self.sample_format.first().unwrap_or(&SampleFormat::Uint) { .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 { SampleFormat::Uint => match max_sample_bits {
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits), 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 <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
@@ -1593,14 +1553,15 @@ impl<R: Read + Seek> Decoder<R> {
self.check_chunk_type(ChunkType::Strip)?; self.check_chunk_type(ChunkType::Strip)?;
let index = self.current_chunk; let index = self.current_chunk;
let rows_per_strip = usize::try_from(self.strip_decoder.as_ref().unwrap().rows_per_strip)?; let rows_per_strip =
usize::try_from(self.image().strip_decoder.as_ref().unwrap().rows_per_strip)?;
let strip_height = cmp::min( let strip_height = cmp::min(
rows_per_strip, rows_per_strip,
usize::try_from(self.height)? - index * rows_per_strip, usize::try_from(self.image().height)? - index * rows_per_strip,
); );
let mut result = self.result_buffer(usize::try_from(self.width)?, strip_height)?; let mut result = self.result_buffer(usize::try_from(self.image().width)?, strip_height)?;
self.read_strip_to_buffer(result.as_buffer(0))?; self.read_strip_to_buffer(result.as_buffer(0))?;
Ok(result) Ok(result)
@@ -1612,7 +1573,7 @@ impl<R: Read + Seek> Decoder<R> {
let tile = self.current_chunk; let tile = self.current_chunk;
let tile_attrs = self.tile_attributes.as_ref().unwrap(); let tile_attrs = self.image().tile_attributes.as_ref().unwrap();
let (padding_right, padding_down) = tile_attrs.get_padding(tile); let (padding_right, padding_down) = tile_attrs.get_padding(tile);
let tile_width = tile_attrs.tile_width - padding_right; let tile_width = tile_attrs.tile_width - padding_right;
@@ -1628,11 +1589,16 @@ impl<R: Read + Seek> Decoder<R> {
} }
fn read_tiled_image(&mut self) -> TiffResult<DecodingResult> { fn read_tiled_image(&mut self) -> TiffResult<DecodingResult> {
let width = usize::try_from(self.width)?; let width = usize::try_from(self.image().width)?;
let mut result = self.result_buffer(width, usize::try_from(self.height)?)?; let mut result = self.result_buffer(width, usize::try_from(self.image().height)?)?;
for tile in 0..self.chunk_offsets.len() { for tile in 0..self.image().chunk_offsets.len() {
let buffer_offset = self.tile_attributes.as_ref().unwrap().get_offset(tile); let buffer_offset = self
.image()
.tile_attributes
.as_ref()
.unwrap()
.get_offset(tile);
self.read_tile_to_buffer(&mut result.as_buffer(buffer_offset), tile, width)?; self.read_tile_to_buffer(&mut result.as_buffer(buffer_offset), tile, width)?;
} }
@@ -1640,18 +1606,21 @@ impl<R: Read + Seek> Decoder<R> {
} }
fn read_stripped_image(&mut self) -> TiffResult<DecodingResult> { fn read_stripped_image(&mut self) -> TiffResult<DecodingResult> {
let rows_per_strip = usize::try_from(self.strip_decoder.as_ref().unwrap().rows_per_strip)?; let rows_per_strip =
usize::try_from(self.image().strip_decoder.as_ref().unwrap().rows_per_strip)?;
let samples_per_strip = match usize::try_from(self.width)? let samples_per_strip = match usize::try_from(self.image().width)?
.checked_mul(rows_per_strip) .checked_mul(rows_per_strip)
.and_then(|x| x.checked_mul(self.bits_per_sample.len())) .and_then(|x| x.checked_mul(self.image().bits_per_sample.len()))
{ {
Some(s) => s, Some(s) => s,
None => return Err(TiffError::LimitsExceeded), None => return Err(TiffError::LimitsExceeded),
}; };
let mut result = let mut result = self.result_buffer(
self.result_buffer(usize::try_from(self.width)?, usize::try_from(self.height)?)?; usize::try_from(self.image().width)?,
usize::try_from(self.image().height)?,
)?;
for i in 0..usize::try_from(self.strip_count()?)? { for i in 0..usize::try_from(self.strip_count()?)? {
let r = result.as_buffer(samples_per_strip * i); let r = result.as_buffer(samples_per_strip * i);
@@ -1662,7 +1631,7 @@ impl<R: Read + Seek> Decoder<R> {
/// Decodes the entire image and return it as a Vector /// Decodes the entire image and return it as a Vector
pub fn read_image(&mut self) -> TiffResult<DecodingResult> { pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
let result = match (self.chunk_type, self.compression_method) { let result = match (self.image().chunk_type, self.image().compression_method) {
(_, CompressionMethod::ModernJPEG) => self.read_jpeg()?, (_, CompressionMethod::ModernJPEG) => self.read_jpeg()?,
(ChunkType::Strip, _) => self.read_stripped_image()?, (ChunkType::Strip, _) => self.read_stripped_image()?,
(ChunkType::Tile, _) => self.read_tiled_image()?, (ChunkType::Tile, _) => self.read_tiled_image()?,