fix: add lenient mode for tiff image decoder
This commit is contained in:
1 parent
279851a9a9
commit
0309349e1b
2 files changed
+40
-10
No files matched your search
+12
-6
@@ -97,6 +97,7 @@ pub(crate) struct Image {
|
|||||||
/// run on samples directly and thus skip the outer byte order entirely, except potentially
|
/// run on samples directly and thus skip the outer byte order entirely, except potentially
|
||||||
/// they have interior byte order treatment.
|
/// they have interior byte order treatment.
|
||||||
pub decompression_to_host_endian: bool,
|
pub decompression_to_host_endian: bool,
|
||||||
|
pub lenient: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes how to read a tile-aligned portion of the image.
|
/// Describes how to read a tile-aligned portion of the image.
|
||||||
@@ -155,9 +156,10 @@ pub(crate) struct ReadoutLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub fn from_reader<R: Read + Seek>(
|
pub fn from_reader_with_options<R: Read + Seek>(
|
||||||
decoder: &mut ValueReader<R>,
|
decoder: &mut ValueReader<R>,
|
||||||
ifd: &Directory,
|
ifd: &Directory,
|
||||||
|
lenient: bool,
|
||||||
) -> TiffResult<Image> {
|
) -> TiffResult<Image> {
|
||||||
let mut tag_reader = TagReader { decoder, ifd };
|
let mut tag_reader = TagReader { decoder, ifd };
|
||||||
|
|
||||||
@@ -500,6 +502,7 @@ impl Image {
|
|||||||
compression_method,
|
compression_method,
|
||||||
CompressionMethod::SgiLog | CompressionMethod::SgiLog24
|
CompressionMethod::SgiLog | CompressionMethod::SgiLog24
|
||||||
),
|
),
|
||||||
|
lenient,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,10 +518,13 @@ impl Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 = match self.extra_samples.as_slice().get(0) {
|
||||||
self.extra_samples.as_slice(),
|
Some(ExtraSamples::AssociatedAlpha) | Some(ExtraSamples::UnassociatedAlpha) => true,
|
||||||
[ExtraSamples::AssociatedAlpha, ..] | [ExtraSamples::UnassociatedAlpha, ..]
|
// In lenient mode, keep unspecified alpha interpretation logic compatible with libtiff
|
||||||
);
|
// See Issue #341
|
||||||
|
Some(ExtraSamples::Unspecified) if self.lenient && self.samples > 3 => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
match self.photometric_interpretation {
|
match self.photometric_interpretation {
|
||||||
PhotometricInterpretation::RGB => match self.photometric_samples {
|
PhotometricInterpretation::RGB => match self.photometric_samples {
|
||||||
@@ -1192,7 +1198,7 @@ impl Image {
|
|||||||
super::fix_endianness_and_predict(
|
super::fix_endianness_and_predict(
|
||||||
out_row,
|
out_row,
|
||||||
color_type.bit_depth(),
|
color_type.bit_depth(),
|
||||||
samples,
|
data_samples,
|
||||||
ByteOrder::native(),
|
ByteOrder::native(),
|
||||||
predictor,
|
predictor,
|
||||||
scratch,
|
scratch,
|
||||||
|
|||||||
+28
-4
@@ -227,6 +227,7 @@ pub struct TiffHeader {
|
|||||||
pub byte_order: ByteOrder,
|
pub byte_order: ByteOrder,
|
||||||
pub variant: TiffVariant,
|
pub variant: TiffVariant,
|
||||||
pub first_ifd: IfdPointer,
|
pub first_ifd: IfdPointer,
|
||||||
|
lenient: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The representation of a TIFF decoder
|
/// The representation of a TIFF decoder
|
||||||
@@ -257,6 +258,8 @@ pub struct Decoder<R> {
|
|||||||
/// Reusable scratch buffer for the floating-point predictor. Stored here so that the
|
/// Reusable scratch buffer for the floating-point predictor. Stored here so that the
|
||||||
/// allocation persists across strips/tiles and images, avoiding per-row heap allocation.
|
/// allocation persists across strips/tiles and images, avoiding per-row heap allocation.
|
||||||
scratch: Vec<u8>,
|
scratch: Vec<u8>,
|
||||||
|
/// Leniency flag that allows libtiff-like TIFF color interpretation behavior
|
||||||
|
lenient: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -674,7 +677,11 @@ fn fix_endianness(buf: &mut [u8], byte_order: ByteOrder, bit_depth: u8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TiffHeader {
|
impl TiffHeader {
|
||||||
pub fn parse<R: Read + Seek>(mut r: R) -> TiffResult<Self> {
|
pub fn parse<R: Read + Seek>(r: R) -> TiffResult<Self> {
|
||||||
|
Self::parse_with_options(r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_with_options<R: Read + Seek>(mut r: R, lenient: bool) -> TiffResult<Self> {
|
||||||
let mut endianess = Vec::with_capacity(2);
|
let mut endianess = Vec::with_capacity(2);
|
||||||
(&mut r).take(2).read_to_end(&mut endianess)?;
|
(&mut r).take(2).read_to_end(&mut endianess)?;
|
||||||
let byte_order = match &*endianess {
|
let byte_order = match &*endianess {
|
||||||
@@ -727,6 +734,7 @@ impl TiffHeader {
|
|||||||
TiffVariant::Standard
|
TiffVariant::Standard
|
||||||
},
|
},
|
||||||
first_ifd: next_ifd,
|
first_ifd: next_ifd,
|
||||||
|
lenient,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -750,6 +758,7 @@ impl TiffHeader {
|
|||||||
seen_ifds: cycles::IfdCycles::new(),
|
seen_ifds: cycles::IfdCycles::new(),
|
||||||
image: None,
|
image: None,
|
||||||
scratch: Vec::new(),
|
scratch: Vec::new(),
|
||||||
|
lenient: self.lenient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -792,6 +801,11 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(header.open(r))
|
Ok(header.open(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_with_options(mut r: R, lenient: bool) -> TiffResult<Decoder<R>> {
|
||||||
|
let header = TiffHeader::parse_with_options(&mut r, lenient)?;
|
||||||
|
Ok(header.open(r))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a decoder and attempt to parse the first image directory.
|
/// Create a decoder and attempt to parse the first image directory.
|
||||||
///
|
///
|
||||||
/// To open a file without parsing any directory yet (for instance to retain a [`Decoder`] on
|
/// To open a file without parsing any directory yet (for instance to retain a [`Decoder`] on
|
||||||
@@ -846,7 +860,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = Image::from_reader(&mut self.value_reader, &self.directory)?;
|
let image =
|
||||||
|
Image::from_reader_with_options(&mut self.value_reader, &self.directory, self.lenient)?;
|
||||||
Ok((
|
Ok((
|
||||||
self.image.insert(image),
|
self.image.insert(image),
|
||||||
&mut self.value_reader,
|
&mut self.value_reader,
|
||||||
@@ -982,6 +997,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
current_ifd_pointer: self.current_ifd_pointer,
|
current_ifd_pointer: self.current_ifd_pointer,
|
||||||
image: core::mem::take(&mut self.image),
|
image: core::mem::take(&mut self.image),
|
||||||
scratch: core::mem::take(&mut self.scratch),
|
scratch: core::mem::take(&mut self.scratch),
|
||||||
|
lenient: self.lenient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1030,7 +1046,11 @@ 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 = Some(Image::from_reader(&mut self.value_reader, &directory)?);
|
self.image = Some(Image::from_reader_with_options(
|
||||||
|
&mut self.value_reader,
|
||||||
|
&directory,
|
||||||
|
self.lenient,
|
||||||
|
)?);
|
||||||
self.current_ifd_pointer = Some(offset);
|
self.current_ifd_pointer = Some(offset);
|
||||||
self.directory = directory;
|
self.directory = directory;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1123,7 +1143,11 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
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 = Some(Image::from_reader(&mut self.value_reader, &directory)?);
|
self.image = Some(Image::from_reader_with_options(
|
||||||
|
&mut self.value_reader,
|
||||||
|
&directory,
|
||||||
|
self.lenient,
|
||||||
|
)?);
|
||||||
self.current_ifd_pointer = Some(offset);
|
self.current_ifd_pointer = Some(offset);
|
||||||
self.directory = directory;
|
self.directory = directory;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in new issue
Block a user