Merge pull request #375 from image-rs/image-as-option
Make decoder's `Image` an option
This commit is contained in:
2 files changed
+182
-131
No files matched your search
+11
-26
@@ -155,32 +155,6 @@ pub(crate) struct ReadoutLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub(crate) fn no_image() -> Image {
|
|
||||||
Image {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
bits_per_sample: 1,
|
|
||||||
samples: 1,
|
|
||||||
extra_samples: vec![],
|
|
||||||
photometric_samples: 1,
|
|
||||||
sample_format: SampleFormat::Uint,
|
|
||||||
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
|
||||||
compression_method: CompressionMethod::None,
|
|
||||||
jpeg_tables: None,
|
|
||||||
predictor: Predictor::None,
|
|
||||||
chunk_type: ChunkType::Strip,
|
|
||||||
planar_config: PlanarConfiguration::Chunky,
|
|
||||||
strip_decoder: None,
|
|
||||||
tile_attributes: None,
|
|
||||||
chunk_offsets: Vec::new(),
|
|
||||||
chunk_bytes: Vec::new(),
|
|
||||||
chroma_subsampling: (2, 2),
|
|
||||||
fill_order: 1,
|
|
||||||
color_map: None,
|
|
||||||
decompression_to_host_endian: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_reader<R: Read + Seek>(
|
pub fn from_reader<R: Read + Seek>(
|
||||||
decoder: &mut ValueReader<R>,
|
decoder: &mut ValueReader<R>,
|
||||||
ifd: &Directory,
|
ifd: &Directory,
|
||||||
@@ -528,6 +502,17 @@ impl Image {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> {
|
||||||
|
if expected != self.chunk_type {
|
||||||
|
return Err(TiffError::UsageError(UsageError::InvalidChunkType(
|
||||||
|
expected,
|
||||||
|
self.chunk_type,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn colortype(&self) -> TiffResult<ColorType> {
|
pub(crate) fn colortype(&self) -> TiffResult<ColorType> {
|
||||||
let is_alpha_extra_samples = matches!(
|
let is_alpha_extra_samples = matches!(
|
||||||
self.extra_samples.as_slice(),
|
self.extra_samples.as_slice(),
|
||||||
|
|||||||
+171
-105
@@ -253,7 +253,7 @@ pub struct Decoder<R> {
|
|||||||
ifd_offsets: Vec<IfdPointer>,
|
ifd_offsets: Vec<IfdPointer>,
|
||||||
/// Map from the ifd into the `ifd_offsets` ordered list.
|
/// Map from the ifd into the `ifd_offsets` ordered list.
|
||||||
seen_ifds: cycles::IfdCycles,
|
seen_ifds: cycles::IfdCycles,
|
||||||
image: Image,
|
image: Option<Image>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the information needed to read and interpret byte slices from the underlying file, i.e. to
|
/// All the information needed to read and interpret byte slices from the underlying file, i.e. to
|
||||||
@@ -626,7 +626,7 @@ impl TiffHeader {
|
|||||||
dir
|
dir
|
||||||
},
|
},
|
||||||
seen_ifds: cycles::IfdCycles::new(),
|
seen_ifds: cycles::IfdCycles::new(),
|
||||||
image: Image::no_image(),
|
image: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -709,6 +709,22 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: this tuple would be a good candidate for a proxy, like IfdDecoder. That has been
|
||||||
|
// requested in some form or another in a number of issues.
|
||||||
|
#[expect(clippy::unnecessary_unwrap)] // Lifetimes would overlap due to return.
|
||||||
|
fn ensure_image_and_reader(&mut self) -> TiffResult<(&mut Image, &mut ValueReader<R>)> {
|
||||||
|
if self.image.is_some() {
|
||||||
|
return Ok((self.image.as_mut().unwrap(), &mut self.value_reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = Image::from_reader(&mut self.value_reader, &self.directory)?;
|
||||||
|
Ok((self.image.insert(image), &mut self.value_reader))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_image(&mut self) -> TiffResult<&mut Image> {
|
||||||
|
Ok(self.ensure_image_and_reader()?.0)
|
||||||
|
}
|
||||||
|
|
||||||
/// The offset of the directory representing the current image.
|
/// The offset of the directory representing the current image.
|
||||||
pub fn ifd_pointer(&mut self) -> Option<IfdPointer> {
|
pub fn ifd_pointer(&mut self) -> Option<IfdPointer> {
|
||||||
self.current_ifd_pointer
|
self.current_ifd_pointer
|
||||||
@@ -732,9 +748,15 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
///
|
///
|
||||||
/// Error guarantee: the current directory and [`Self::ifd_pointer`] is not modified when an
|
/// Error guarantee: the current directory and [`Self::ifd_pointer`] is not modified when an
|
||||||
/// error is returned.
|
/// error is returned.
|
||||||
|
///
|
||||||
|
/// The next directory image attributes are *not* parsed on a successful seek, i.e. it need not be
|
||||||
|
/// semantically valid as an image (incoherent sample depths, unsupported planar configuration,
|
||||||
|
/// missing required tags, etc.) but [`Self::read_image`] may not work. Calling methods
|
||||||
|
/// interacting with it as an image after this will first attempt to parse the directory
|
||||||
|
/// instead.
|
||||||
pub fn seek_to_directory(&mut self, ifd_index: usize) -> TiffResult<()> {
|
pub fn seek_to_directory(&mut self, ifd_index: usize) -> TiffResult<()> {
|
||||||
let (offset, directory) = self.find_nth_ifd(ifd_index)?;
|
let (offset, directory) = self.find_nth_ifd(ifd_index)?;
|
||||||
self.image = Image::no_image();
|
self.image = None;
|
||||||
self.current_ifd_pointer = Some(offset);
|
self.current_ifd_pointer = Some(offset);
|
||||||
self.directory = directory;
|
self.directory = directory;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -748,7 +770,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// error is returned.
|
/// error is returned.
|
||||||
pub fn seek_to_image(&mut self, ifd_index: usize) -> TiffResult<()> {
|
pub fn seek_to_image(&mut self, ifd_index: usize) -> TiffResult<()> {
|
||||||
let (offset, directory) = self.find_nth_ifd(ifd_index)?;
|
let (offset, directory) = self.find_nth_ifd(ifd_index)?;
|
||||||
self.image = Image::from_reader(&mut self.value_reader, &directory)?;
|
self.image = Some(Image::from_reader(&mut self.value_reader, &directory)?);
|
||||||
self.current_ifd_pointer = Some(offset);
|
self.current_ifd_pointer = Some(offset);
|
||||||
self.directory = directory;
|
self.directory = directory;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -811,15 +833,21 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
/// Reads the next IFD.
|
/// Reads the next IFD.
|
||||||
///
|
///
|
||||||
/// If there is no further image in the TIFF file a format error is returned.
|
/// If there is no further IFD 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 [`Self::more_images`] instead.
|
||||||
|
///
|
||||||
|
/// The next directory image attributes are *not* parsed on a successful seek, i.e. it need not be
|
||||||
|
/// semantically valid as an image (incoherent sample depths, unsupported planar configuration,
|
||||||
|
/// missing required tags, etc.) but [`Self::read_image`] may not work. Calling methods
|
||||||
|
/// interacting with it as an image after this will first attempt to parse the directory
|
||||||
|
/// instead.
|
||||||
///
|
///
|
||||||
/// Error guarantee: the current directory and [`Self::ifd_pointer`] is not modified when an error is
|
/// Error guarantee: the current directory and [`Self::ifd_pointer`] is not modified when an error is
|
||||||
/// returned.
|
/// returned.
|
||||||
pub fn next_directory(&mut self) -> TiffResult<()> {
|
pub fn next_directory(&mut self) -> TiffResult<()> {
|
||||||
let next_ifd = self.directory.next();
|
let next_ifd = self.directory.next();
|
||||||
let (offset, directory) = self.read_chained_ifd(self.current_ifd_pointer, next_ifd)?;
|
let (offset, directory) = self.read_chained_ifd(self.current_ifd_pointer, next_ifd)?;
|
||||||
self.image = Image::no_image();
|
self.image = None;
|
||||||
self.current_ifd_pointer = Some(offset);
|
self.current_ifd_pointer = Some(offset);
|
||||||
self.directory = directory;
|
self.directory = directory;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -828,42 +856,19 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// Reads in the next image.
|
/// Reads in the next image.
|
||||||
///
|
///
|
||||||
/// 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 [`Self::more_images`] instead.
|
||||||
///
|
///
|
||||||
/// Error guarantee: the current directory and [`Self::ifd_pointer`] is not modified when an error is
|
/// Error guarantee: the current directory and [`Self::ifd_pointer`] is not modified when an error is
|
||||||
/// returned.
|
/// returned.
|
||||||
pub fn next_image(&mut self) -> TiffResult<()> {
|
pub fn next_image(&mut self) -> TiffResult<()> {
|
||||||
let next_ifd = self.directory.next();
|
let next_ifd = self.directory.next();
|
||||||
let (offset, directory) = self.read_chained_ifd(self.current_ifd_pointer, next_ifd)?;
|
let (offset, directory) = self.read_chained_ifd(self.current_ifd_pointer, next_ifd)?;
|
||||||
self.image = Image::from_reader(&mut self.value_reader, &directory)?;
|
self.image = Some(Image::from_reader(&mut self.value_reader, &directory)?);
|
||||||
self.current_ifd_pointer = Some(offset);
|
self.current_ifd_pointer = Some(offset);
|
||||||
self.directory = directory;
|
self.directory = directory;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a tag-entry map from a known offset.
|
|
||||||
///
|
|
||||||
/// A TIFF [`Directory`], aka. image file directory aka. IFD, refers to a map from
|
|
||||||
/// tags–identified by a `u16`–to a typed vector of elements. It is encoded as a list
|
|
||||||
/// of ascending tag values with the offset and type of their corresponding values. The
|
|
||||||
/// semantic interpretations of a tag and its type requirements depend on the context of the
|
|
||||||
/// directory. The main image directories, those iterated over by the `Decoder` after
|
|
||||||
/// construction, are represented by [`Tag`] and [`ifd::Value`]. Other forms are EXIF and GPS
|
|
||||||
/// data as well as thumbnail Sub-IFD representations associated with each image file.
|
|
||||||
///
|
|
||||||
/// This method allows the decoding of a directory from an arbitrary offset in the image file
|
|
||||||
/// with no specific semantic interpretation. Such an offset is usually found as the value of
|
|
||||||
/// a tag, e.g. [`Tag::SubIfd`], [`Tag::ExifDirectory`], [`Tag::GpsDirectory`] and recovered
|
|
||||||
/// from the associated value by [`ifd::Value::into_ifd_pointer`].
|
|
||||||
///
|
|
||||||
/// The library will not verify whether the offset overlaps any other directory or would form a
|
|
||||||
/// cycle with any other directory when calling this method. This will modify the position of
|
|
||||||
/// the reader, i.e. continuing with direct reads at a later point will require going back with
|
|
||||||
/// [`Self::goto_offset`].
|
|
||||||
pub fn read_directory(&mut self, ptr: IfdPointer) -> TiffResult<Directory> {
|
|
||||||
self.value_reader.read_directory(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepare reading values for tags of a given directory.
|
/// Prepare reading values for tags of a given directory.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@@ -899,6 +904,17 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Utility to create a reader like [`Self::read_entry`], from the current IFD.
|
||||||
|
///
|
||||||
|
/// Returns an `Ok(None)` if the tag is not found in the current directory. See
|
||||||
|
/// [`Self::read_entry´] of which this is a simple wrapper.
|
||||||
|
pub fn read_tag(&mut self, tag: Tag) -> TiffResult<Option<EntryBytesReader<'_, R>>> {
|
||||||
|
match self.current_ifd().find_entry(tag) {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(entry) => self.read_entry(entry).map(Some),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if there is at least one more image available.
|
/// Returns `true` if there is at least one more image available.
|
||||||
pub fn more_images(&self) -> bool {
|
pub fn more_images(&self) -> bool {
|
||||||
self.directory.next().is_some()
|
self.directory.next().is_some()
|
||||||
@@ -1039,20 +1055,13 @@ impl<R: Read> Decoder<R> {
|
|||||||
/// demanding them from all types as `dyn` would. There is also no `M×N` instantiation problem
|
/// demanding them from all types as `dyn` would. There is also no `M×N` instantiation problem
|
||||||
/// with this method: you get one type per reader type and monomorphic methods from the traits
|
/// with this method: you get one type per reader type and monomorphic methods from the traits
|
||||||
/// it implements.
|
/// it implements.
|
||||||
pub fn read_entry(&mut self, entry: ifd::Entry) -> TiffResult<EntryBytesReader<'_, R>> {
|
pub fn read_entry(&mut self, entry: ifd::Entry) -> TiffResult<EntryBytesReader<'_, R>>
|
||||||
|
where
|
||||||
|
R: Seek,
|
||||||
|
{
|
||||||
EntryBytesReader::from_entry(&mut self.value_reader, entry)
|
EntryBytesReader::from_entry(&mut self.value_reader, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility to create a reader like [`Self::read_entry`], from the current IFD.
|
|
||||||
///
|
|
||||||
/// Returns an `Ok(None)` if the tag is not found in the current directory.
|
|
||||||
pub fn read_tag(&mut self, tag: Tag) -> TiffResult<Option<EntryBytesReader<'_, R>>> {
|
|
||||||
match self.current_ifd().find_entry(tag) {
|
|
||||||
None => Ok(None),
|
|
||||||
Some(entry) => self.read_entry(entry).map(Some),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a TIFF IFA offset/value field
|
/// Reads a TIFF IFA offset/value field
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
|
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
|
||||||
@@ -1081,23 +1090,29 @@ impl<R: Read> Decoder<R> {
|
|||||||
|
|
||||||
/// <span id=header-image>Methods that interact with the current directory as an image.</span>
|
/// <span id=header-image>Methods that interact with the current directory as an image.</span>
|
||||||
impl<R: Read + Seek> Decoder<R> {
|
impl<R: Read + Seek> Decoder<R> {
|
||||||
fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> {
|
/// Return the current IFD's image size.
|
||||||
if expected != self.image.chunk_type {
|
///
|
||||||
return Err(TiffError::UsageError(UsageError::InvalidChunkType(
|
/// Returns an error if the current directory could not be parsed as an image. This can occur
|
||||||
expected,
|
/// if [`Self::next_directory`] or [`Self::seek_to_directory`] was used to advance the decoder
|
||||||
self.image.chunk_type,
|
/// to the current directory which implies the IFD will be validated as an image on-demand by
|
||||||
)));
|
/// this method.
|
||||||
}
|
///
|
||||||
|
/// [`Self::image_buffer_layout`] provides further information on reading the image data and
|
||||||
Ok(())
|
/// can be used alternatively to verify further that the image can be read.
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> {
|
pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> {
|
||||||
Ok((self.image.width, self.image.height))
|
let image = self.ensure_image()?;
|
||||||
|
Ok((image.width, image.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the color type resulting from reading the image (in the current configuration).
|
||||||
|
///
|
||||||
|
/// Returns an error if the current directory could not be parsed as an image. This can occur
|
||||||
|
/// if [`Self::next_directory`] or [`Self::seek_to_directory`] was used to advance the decoder
|
||||||
|
/// to the current directory which implies the IFD will be validated as an image on-demand by
|
||||||
|
/// this method.
|
||||||
pub fn colortype(&mut self) -> TiffResult<ColorType> {
|
pub fn colortype(&mut self) -> TiffResult<ColorType> {
|
||||||
self.image.colortype()
|
let image = self.ensure_image()?;
|
||||||
|
image.colortype()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the color map (palette) for the current image, if it uses
|
/// Returns the color map (palette) for the current image, if it uses
|
||||||
@@ -1107,32 +1122,63 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// laid out as: all red values, then all green values, then all blue values.
|
/// laid out as: all red values, then all green values, then all blue values.
|
||||||
/// Each value is in the range `0..=65535` regardless of the actual bit depth.
|
/// Each value is in the range `0..=65535` regardless of the actual bit depth.
|
||||||
pub fn color_map(&self) -> Option<&[u16]> {
|
pub fn color_map(&self) -> Option<&[u16]> {
|
||||||
self.image.color_map.as_deref()
|
self.image.as_ref().and_then(|img| img.color_map.as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a tag-entry map from a known offset.
|
||||||
|
///
|
||||||
|
/// A TIFF [`Directory`], aka. image file directory aka. IFD, refers to a map from
|
||||||
|
/// tags–identified by a `u16`–to a typed vector of elements. It is encoded as a list
|
||||||
|
/// of ascending tag values with the offset and type of their corresponding values. The
|
||||||
|
/// semantic interpretations of a tag and its type requirements depend on the context of the
|
||||||
|
/// directory. The main image directories, those iterated over by the `Decoder` after
|
||||||
|
/// construction, are represented by [`Tag`] and [`ifd::Value`]. Other forms are EXIF and GPS
|
||||||
|
/// data as well as thumbnail Sub-IFD representations associated with each image file.
|
||||||
|
///
|
||||||
|
/// This method allows the decoding of a directory from an arbitrary offset in the image file
|
||||||
|
/// with no specific semantic interpretation. Such an offset is usually found as the value of
|
||||||
|
/// a tag, e.g. [`Tag::SubIfd`], [`Tag::ExifDirectory`], [`Tag::GpsDirectory`] and recovered
|
||||||
|
/// from the associated value by [`ifd::Value::into_ifd_pointer`].
|
||||||
|
///
|
||||||
|
/// The library will not verify whether the offset overlaps any other directory or would form a
|
||||||
|
/// cycle with any other directory when calling this method. This will modify the position of
|
||||||
|
/// the reader, i.e. continuing with direct reads at a later point will require going back with
|
||||||
|
/// [`Self::goto_offset`].
|
||||||
|
pub fn read_directory(&mut self, ptr: IfdPointer) -> TiffResult<Directory> {
|
||||||
|
self.value_reader.read_directory(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The chunk type (Strips / Tiles) of the image
|
/// The chunk type (Strips / Tiles) of the image
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method panics if the directory was read with [`Self::next_directory`] or
|
||||||
|
/// [`Self::seek_to_directory`] and [`Self::dimensions`] has not yet succeeded. (Other
|
||||||
|
/// image-related methods with signature (`fn(&mut self) -> TiffResult<_>`) can be substituted
|
||||||
|
/// but use that one by default).
|
||||||
pub fn get_chunk_type(&self) -> ChunkType {
|
pub fn get_chunk_type(&self) -> ChunkType {
|
||||||
self.image.chunk_type
|
self.image.as_ref().unwrap().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)?;
|
let image = self.ensure_image()?;
|
||||||
let rows_per_strip = self.image.strip_decoder.as_ref().unwrap().rows_per_strip;
|
image.check_chunk_type(ChunkType::Strip)?;
|
||||||
|
let rows_per_strip = 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.image.height.checked_add(rows_per_strip - 1) {
|
let height = match image.height.checked_add(rows_per_strip - 1) {
|
||||||
Some(h) => h,
|
Some(h) => h,
|
||||||
None => return Err(TiffError::IntSizeError),
|
None => return Err(TiffError::IntSizeError),
|
||||||
};
|
};
|
||||||
|
|
||||||
let strips = match self.image.planar_config {
|
let strips = match image.planar_config {
|
||||||
PlanarConfiguration::Chunky => height / rows_per_strip,
|
PlanarConfiguration::Chunky => height / rows_per_strip,
|
||||||
PlanarConfiguration::Planar => height / rows_per_strip * self.image.samples as u32,
|
PlanarConfiguration::Planar => height / rows_per_strip * image.samples as u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(strips)
|
Ok(strips)
|
||||||
@@ -1140,8 +1186,9 @@ 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)?;
|
let image = self.ensure_image()?;
|
||||||
Ok(u32::try_from(self.image.chunk_offsets.len())?)
|
image.check_chunk_type(ChunkType::Tile)?;
|
||||||
|
Ok(u32::try_from(image.chunk_offsets.len())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_chunk_to_bytes(
|
fn read_chunk_to_bytes(
|
||||||
@@ -1150,12 +1197,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
chunk_index: u32,
|
chunk_index: u32,
|
||||||
layout: &image::ReadoutLayout,
|
layout: &image::ReadoutLayout,
|
||||||
) -> TiffResult<()> {
|
) -> TiffResult<()> {
|
||||||
let offset = self.image.chunk_file_range(chunk_index)?.0;
|
let (image, value_reader) = self.ensure_image_and_reader()?;
|
||||||
self.goto_offset_u64(offset)?;
|
let offset = image.chunk_file_range(chunk_index)?.0;
|
||||||
|
value_reader.reader.goto_offset(offset)?;
|
||||||
self.image
|
image.expand_chunk(value_reader, buffer, layout, chunk_index)?;
|
||||||
.expand_chunk(&mut self.value_reader, buffer, layout, chunk_index)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1170,8 +1215,9 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
chunk_index: u32,
|
chunk_index: u32,
|
||||||
) -> TiffResult<BufferLayoutPreference> {
|
) -> TiffResult<BufferLayoutPreference> {
|
||||||
let data_dims = self.image.chunk_data_dimensions(chunk_index)?;
|
let image = self.ensure_image()?;
|
||||||
let readout = self.image.readout_for_size(data_dims.0, data_dims.1)?;
|
let data_dims = image.chunk_data_dimensions(chunk_index)?;
|
||||||
|
let readout = image.readout_for_size(data_dims.0, data_dims.1)?;
|
||||||
|
|
||||||
let extent = readout.result_extent_for_planes(0..1)?;
|
let extent = readout.result_extent_for_planes(0..1)?;
|
||||||
let sample_type = extent.sample_type();
|
let sample_type = extent.sample_type();
|
||||||
@@ -1186,7 +1232,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
planes: 1,
|
planes: 1,
|
||||||
plane_stride,
|
plane_stride,
|
||||||
complete_len: layout.size(),
|
complete_len: layout.size(),
|
||||||
sample_format: self.image.sample_format,
|
sample_format: image.sample_format,
|
||||||
sample_type: Some(sample_type),
|
sample_type: Some(sample_type),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1203,17 +1249,15 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
code_unit: TiffCodingUnit,
|
code_unit: TiffCodingUnit,
|
||||||
) -> TiffResult<BufferLayoutPreference> {
|
) -> TiffResult<BufferLayoutPreference> {
|
||||||
match self.image.planar_config {
|
let image = self.ensure_image()?;
|
||||||
|
|
||||||
|
match image.planar_config {
|
||||||
PlanarConfiguration::Chunky => return self.image_chunk_buffer_layout(code_unit.0),
|
PlanarConfiguration::Chunky => return self.image_chunk_buffer_layout(code_unit.0),
|
||||||
PlanarConfiguration::Planar => {}
|
PlanarConfiguration::Planar => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (width, height) = self.image.chunk_data_dimensions(code_unit.0)?;
|
let (width, height) = image.chunk_data_dimensions(code_unit.0)?;
|
||||||
|
let layout = image.readout_for_size(width, height)?.to_plane_layout()?;
|
||||||
let layout = self
|
|
||||||
.image
|
|
||||||
.readout_for_size(width, height)?
|
|
||||||
.to_plane_layout()?;
|
|
||||||
|
|
||||||
if code_unit.0 >= layout.readout.chunks_per_plane {
|
if code_unit.0 >= layout.readout.chunks_per_plane {
|
||||||
return Err(TiffError::UsageError(UsageError::InvalidCodingUnit(
|
return Err(TiffError::UsageError(UsageError::InvalidCodingUnit(
|
||||||
@@ -1229,9 +1273,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
///
|
///
|
||||||
/// Note that for planar images each chunk contains only one sample of the underlying data.
|
/// Note that for planar images each chunk contains only one sample of the underlying data.
|
||||||
pub fn read_chunk(&mut self, chunk_index: u32) -> TiffResult<DecodingSampleBuffer> {
|
pub fn read_chunk(&mut self, chunk_index: u32) -> TiffResult<DecodingSampleBuffer> {
|
||||||
let (width, height) = self.image.chunk_data_dimensions(chunk_index)?;
|
let image = self.ensure_image()?;
|
||||||
|
|
||||||
let readout = self.image.readout_for_size(width, height)?;
|
let (width, height) = image.chunk_data_dimensions(chunk_index)?;
|
||||||
|
let readout = image.readout_for_size(width, height)?;
|
||||||
|
|
||||||
let mut result = readout
|
let mut result = readout
|
||||||
.result_extent_for_planes(0..1)?
|
.result_extent_for_planes(0..1)?
|
||||||
@@ -1250,9 +1295,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
///
|
///
|
||||||
/// Note that for planar images each chunk contains only one sample of the underlying data.
|
/// Note that for planar images each chunk contains only one sample of the underlying data.
|
||||||
pub fn read_chunk_bytes(&mut self, chunk_index: u32, buffer: &mut [u8]) -> TiffResult<()> {
|
pub fn read_chunk_bytes(&mut self, chunk_index: u32, buffer: &mut [u8]) -> TiffResult<()> {
|
||||||
let (width, height) = self.image.chunk_data_dimensions(chunk_index)?;
|
let image = self.ensure_image()?;
|
||||||
|
|
||||||
let layout = self.image.readout_for_size(width, height)?;
|
let (width, height) = image.chunk_data_dimensions(chunk_index)?;
|
||||||
|
let layout = image.readout_for_size(width, height)?;
|
||||||
layout.assert_min_layout(buffer)?;
|
layout.assert_min_layout(buffer)?;
|
||||||
|
|
||||||
self.read_chunk_to_bytes(buffer, chunk_index, &layout)?;
|
self.read_chunk_to_bytes(buffer, chunk_index, &layout)?;
|
||||||
@@ -1273,9 +1319,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
chunk_index: u32,
|
chunk_index: u32,
|
||||||
output_width: usize,
|
output_width: usize,
|
||||||
) -> TiffResult<()> {
|
) -> TiffResult<()> {
|
||||||
let (width, height) = self.image.chunk_data_dimensions(chunk_index)?;
|
let image = self.ensure_image()?;
|
||||||
|
|
||||||
let mut layout = self.image.readout_for_size(width, height)?;
|
let (width, height) = image.chunk_data_dimensions(chunk_index)?;
|
||||||
|
let mut layout = image.readout_for_size(width, height)?;
|
||||||
layout.set_row_stride(output_width)?;
|
layout.set_row_stride(output_width)?;
|
||||||
|
|
||||||
let extent = layout.result_extent_for_planes(0..1)?;
|
let extent = layout.result_extent_for_planes(0..1)?;
|
||||||
@@ -1305,8 +1352,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
slice: TiffCodingUnit,
|
slice: TiffCodingUnit,
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
) -> TiffResult<()> {
|
) -> TiffResult<()> {
|
||||||
let (width, height) = self.image.chunk_data_dimensions(slice.0)?;
|
let (image, value_reader) = self.ensure_image_and_reader()?;
|
||||||
let readout = self.image.readout_for_size(width, height)?;
|
|
||||||
|
let (width, height) = image.chunk_data_dimensions(slice.0)?;
|
||||||
|
let readout = image.readout_for_size(width, height)?;
|
||||||
|
|
||||||
let ref layout @ image::PlaneLayout {
|
let ref layout @ image::PlaneLayout {
|
||||||
ref plane_offsets,
|
ref plane_offsets,
|
||||||
@@ -1328,14 +1377,9 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
for (idx, &plane_offset) in plane_offsets[..used_plane_offsets].iter().enumerate() {
|
for (idx, &plane_offset) in plane_offsets[..used_plane_offsets].iter().enumerate() {
|
||||||
let chunk = slice.0 + idx as u32 * readout.chunks_per_plane;
|
let chunk = slice.0 + idx as u32 * readout.chunks_per_plane;
|
||||||
self.goto_offset_u64(self.image.chunk_offsets[chunk as usize])?;
|
let offset = image.chunk_offsets[chunk as usize];
|
||||||
|
value_reader.reader.goto_offset(offset)?;
|
||||||
self.image.expand_chunk(
|
image.expand_chunk(value_reader, &mut buffer[plane_offset..], readout, chunk)?;
|
||||||
&mut self.value_reader,
|
|
||||||
&mut buffer[plane_offset..],
|
|
||||||
readout,
|
|
||||||
chunk,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1343,14 +1387,32 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
/// Returns the default chunk size for the current image. Any given chunk in the image is at most as large as
|
/// 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`.
|
/// the value returned here. For the size of the data (chunk minus padding), use `chunk_data_dimensions`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method panics if the directory was read with [`Self::next_directory`] or
|
||||||
|
/// [`Self::seek_to_directory`] and [`Self::dimensions`] has not yet succeeded. (Other
|
||||||
|
/// image-related methods with signature (`fn(&mut self) -> TiffResult<_>`) can be substituted
|
||||||
|
/// but use that one by default).
|
||||||
pub fn chunk_dimensions(&self) -> (u32, u32) {
|
pub fn chunk_dimensions(&self) -> (u32, u32) {
|
||||||
self.image.chunk_dimensions().unwrap()
|
self.image.as_ref().unwrap().chunk_dimensions().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the size of the data in the chunk with the specified index. This is the default size of the chunk,
|
/// Returns the size of the data in the chunk with the specified index. This is the default size of the chunk,
|
||||||
/// minus any padding.
|
/// minus any padding.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method panics if the directory was read with [`Self::next_directory`] or
|
||||||
|
/// [`Self::seek_to_directory`] and [`Self::dimensions`] has not yet succeeded. (Other
|
||||||
|
/// image-related methods with signature (`fn(&mut self) -> TiffResult<_>`) can be substituted
|
||||||
|
/// but use that one to be sure).
|
||||||
|
///
|
||||||
|
/// This method panics if the index is out-of-range.
|
||||||
pub fn chunk_data_dimensions(&self, chunk_index: u32) -> (u32, u32) {
|
pub fn chunk_data_dimensions(&self, chunk_index: u32) -> (u32, u32) {
|
||||||
self.image
|
self.image
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
.chunk_data_dimensions(chunk_index)
|
.chunk_data_dimensions(chunk_index)
|
||||||
.expect("invalid chunk_index")
|
.expect("invalid chunk_index")
|
||||||
}
|
}
|
||||||
@@ -1369,7 +1431,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// indicate the layout needed to read the first data plane. This will be fixed in a future
|
/// indicate the layout needed to read the first data plane. This will be fixed in a future
|
||||||
/// major version of `tiff`.
|
/// major version of `tiff`.
|
||||||
pub fn image_buffer_layout(&mut self) -> TiffResult<BufferLayoutPreference> {
|
pub fn image_buffer_layout(&mut self) -> TiffResult<BufferLayoutPreference> {
|
||||||
let layout = self.image.readout_for_image()?.to_plane_layout()?;
|
let image = self.ensure_image()?;
|
||||||
|
let layout = image.readout_for_image()?.to_plane_layout()?;
|
||||||
Ok(BufferLayoutPreference::from_planes(&layout))
|
Ok(BufferLayoutPreference::from_planes(&layout))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1393,7 +1456,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// replaced, to ensure that existing code will not run into unexpectedly large allocations
|
/// replaced, to ensure that existing code will not run into unexpectedly large allocations
|
||||||
/// that will error on limits instead.
|
/// that will error on limits instead.
|
||||||
pub fn read_image(&mut self) -> TiffResult<DecodingSampleBuffer> {
|
pub fn read_image(&mut self) -> TiffResult<DecodingSampleBuffer> {
|
||||||
let readout = self.image.readout_for_image()?;
|
let image = self.ensure_image()?;
|
||||||
|
let readout = image.readout_for_image()?;
|
||||||
|
|
||||||
let mut result = readout
|
let mut result = readout
|
||||||
.result_extent_for_planes(0..1)?
|
.result_extent_for_planes(0..1)?
|
||||||
@@ -1455,7 +1519,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
result: &mut DecodingSampleBuffer,
|
result: &mut DecodingSampleBuffer,
|
||||||
) -> TiffResult<BufferLayoutPreference> {
|
) -> TiffResult<BufferLayoutPreference> {
|
||||||
let readout = self.image.readout_for_image()?;
|
let readout = self.ensure_image()?.readout_for_image()?;
|
||||||
let planes = readout.to_plane_layout()?;
|
let planes = readout.to_plane_layout()?;
|
||||||
|
|
||||||
let num_planes = if planes.total_bytes <= self.value_reader.limits.decoding_buffer_size {
|
let num_planes = if planes.total_bytes <= self.value_reader.limits.decoding_buffer_size {
|
||||||
@@ -1486,7 +1550,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// Returns an error if the buffer fits less than one plane. In particular, for non-planar
|
/// Returns an error if the buffer fits less than one plane. In particular, for non-planar
|
||||||
/// images returns an error if the buffer does not fit the required size.
|
/// images returns an error if the buffer does not fit the required size.
|
||||||
pub fn read_image_bytes(&mut self, buffer: &mut [u8]) -> TiffResult<()> {
|
pub fn read_image_bytes(&mut self, buffer: &mut [u8]) -> TiffResult<()> {
|
||||||
let readout = self.image.readout_for_image()?;
|
let (image, value_reader) = self.ensure_image_and_reader()?;
|
||||||
|
let readout = image.readout_for_image()?;
|
||||||
|
|
||||||
let ref layout @ image::PlaneLayout {
|
let ref layout @ image::PlaneLayout {
|
||||||
ref plane_offsets,
|
ref plane_offsets,
|
||||||
@@ -1510,10 +1575,11 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
for (idx, &plane_offset) in plane_offsets[..used_plane_offsets].iter().enumerate() {
|
for (idx, &plane_offset) in plane_offsets[..used_plane_offsets].iter().enumerate() {
|
||||||
let chunk = chunk + idx as u32 * readout.chunks_per_plane;
|
let chunk = chunk + idx as u32 * readout.chunks_per_plane;
|
||||||
self.goto_offset_u64(self.image.chunk_offsets[chunk as usize])?;
|
let offset = image.chunk_offsets[chunk as usize];
|
||||||
|
value_reader.reader.goto_offset(offset)?;
|
||||||
|
|
||||||
self.image.expand_chunk(
|
image.expand_chunk(
|
||||||
&mut self.value_reader,
|
value_reader,
|
||||||
&mut buffer[plane_offset..][buffer_offset..],
|
&mut buffer[plane_offset..][buffer_offset..],
|
||||||
readout,
|
readout,
|
||||||
chunk,
|
chunk,
|
||||||
|
|||||||
Reference in new issue
Block a user