aboutsummaryrefslogtreecommitdiff
path: root/vendor/tiff/src/decoder/image.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/tiff/src/decoder/image.rs')
-rw-r--r--vendor/tiff/src/decoder/image.rs601
1 files changed, 0 insertions, 601 deletions
diff --git a/vendor/tiff/src/decoder/image.rs b/vendor/tiff/src/decoder/image.rs
deleted file mode 100644
index c037e31..0000000
--- a/vendor/tiff/src/decoder/image.rs
+++ /dev/null
@@ -1,601 +0,0 @@
-use super::ifd::{Directory, Value};
-use super::stream::{ByteOrder, DeflateReader, JpegReader, LZWReader, PackBitsReader};
-use super::tag_reader::TagReader;
-use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
-use super::{stream::SmartReader, ChunkType};
-use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag};
-use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
-use std::convert::{TryFrom, TryInto};
-use std::io::{self, Cursor, Read, Seek};
-use std::sync::Arc;
-
-#[derive(Debug)]
-pub(crate) struct StripDecodeState {
- pub rows_per_strip: u32,
-}
-
-#[derive(Debug)]
-/// Computed values useful for tile decoding
-pub(crate) struct TileAttributes {
- pub image_width: usize,
- pub image_height: usize,
-
- pub tile_width: usize,
- pub tile_length: usize,
-}
-
-impl TileAttributes {
- pub fn tiles_across(&self) -> usize {
- (self.image_width + self.tile_width - 1) / self.tile_width
- }
- pub fn tiles_down(&self) -> usize {
- (self.image_height + self.tile_length - 1) / self.tile_length
- }
- fn padding_right(&self) -> usize {
- (self.tile_width - self.image_width % self.tile_width) % self.tile_width
- }
- fn padding_down(&self) -> usize {
- (self.tile_length - self.image_height % self.tile_length) % self.tile_length
- }
- pub fn get_padding(&self, tile: usize) -> (usize, usize) {
- let row = tile / self.tiles_across();
- let column = tile % self.tiles_across();
-
- let padding_right = if column == self.tiles_across() - 1 {
- self.padding_right()
- } else {
- 0
- };
-
- let padding_down = if row == self.tiles_down() - 1 {
- self.padding_down()
- } else {
- 0
- };
-
- (padding_right, padding_down)
- }
-}
-
-#[derive(Debug)]
-pub(crate) struct Image {
- pub ifd: Option<Directory>,
- pub width: u32,
- pub height: u32,
- pub bits_per_sample: Vec<u8>,
- #[allow(unused)]
- pub samples: u8,
- pub sample_format: Vec<SampleFormat>,
- pub photometric_interpretation: PhotometricInterpretation,
- pub compression_method: CompressionMethod,
- pub predictor: Predictor,
- pub jpeg_tables: Option<Arc<Vec<u8>>>,
- pub chunk_type: ChunkType,
- pub strip_decoder: Option<StripDecodeState>,
- pub tile_attributes: Option<TileAttributes>,
- pub chunk_offsets: Vec<u64>,
- pub chunk_bytes: Vec<u64>,
-}
-
-impl Image {
- pub fn from_reader<R: Read + Seek>(
- reader: &mut SmartReader<R>,
- ifd: Directory,
- limits: &Limits,
- bigtiff: bool,
- ) -> TiffResult<Image> {
- let mut tag_reader = TagReader {
- reader,
- limits,
- ifd: &ifd,
- bigtiff,
- };
-
- let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?;
- let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?;
- if width == 0 || height == 0 {
- return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
- width, height,
- )));
- }
-
- let photometric_interpretation = tag_reader
- .find_tag(Tag::PhotometricInterpretation)?
- .map(Value::into_u16)
- .transpose()?
- .and_then(PhotometricInterpretation::from_u16)
- .ok_or(TiffUnsupportedError::UnknownInterpretation)?;
-
- // Try to parse both the compression method and the number, format, and bits of the included samples.
- // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images.
- let compression_method = match tag_reader.find_tag(Tag::Compression)? {
- Some(val) => CompressionMethod::from_u16(val.into_u16()?)
- .ok_or(TiffUnsupportedError::UnknownCompressionMethod)?,
- None => CompressionMethod::None,
- };
-
- let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG
- && ifd.contains_key(&Tag::JPEGTables)
- {
- let vec = tag_reader
- .find_tag(Tag::JPEGTables)?
- .unwrap()
- .into_u8_vec()?;
- if vec.len() < 2 {
- return Err(TiffError::FormatError(
- TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
- ));
- }
-
- Some(Arc::new(vec))
- } else {
- None
- };
-
- let samples = tag_reader
- .find_tag(Tag::SamplesPerPixel)?
- .map(Value::into_u16)
- .transpose()?
- .unwrap_or(1)
- .try_into()?;
-
- let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? {
- Some(vals) => {
- let sample_format: Vec<_> = vals
- .into_iter()
- .map(SampleFormat::from_u16_exhaustive)
- .collect();
-
- // TODO: for now, only homogenous formats across samples are supported.
- if !sample_format.windows(2).all(|s| s[0] == s[1]) {
- return Err(TiffUnsupportedError::UnsupportedSampleFormat(sample_format).into());
- }
-
- sample_format
- }
- None => vec![SampleFormat::Uint],
- };
-
- let bits_per_sample = match samples {
- 1 | 3 | 4 => tag_reader
- .find_tag_uint_vec(Tag::BitsPerSample)?
- .unwrap_or_else(|| vec![1]),
- _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(samples).into()),
- };
-
- let predictor = tag_reader
- .find_tag(Tag::Predictor)?
- .map(Value::into_u16)
- .transpose()?
- .map(|p| {
- Predictor::from_u16(p)
- .ok_or(TiffError::FormatError(TiffFormatError::UnknownPredictor(p)))
- })
- .transpose()?
- .unwrap_or(Predictor::None);
-
- let chunk_type;
- let chunk_offsets;
- let chunk_bytes;
- let strip_decoder;
- let tile_attributes;
- match (
- ifd.contains_key(&Tag::StripByteCounts),
- ifd.contains_key(&Tag::StripOffsets),
- ifd.contains_key(&Tag::TileByteCounts),
- ifd.contains_key(&Tag::TileOffsets),
- ) {
- (true, true, false, false) => {
- chunk_type = ChunkType::Strip;
-
- chunk_offsets = tag_reader
- .find_tag(Tag::StripOffsets)?
- .unwrap()
- .into_u64_vec()?;
- chunk_bytes = tag_reader
- .find_tag(Tag::StripByteCounts)?
- .unwrap()
- .into_u64_vec()?;
- let rows_per_strip = tag_reader
- .find_tag(Tag::RowsPerStrip)?
- .map(Value::into_u32)
- .transpose()?
- .unwrap_or(height);
- strip_decoder = Some(StripDecodeState { rows_per_strip });
- tile_attributes = None;
-
- if chunk_offsets.len() != chunk_bytes.len()
- || rows_per_strip == 0
- || u32::try_from(chunk_offsets.len())?
- != height.saturating_sub(1) / rows_per_strip + 1
- {
- return Err(TiffError::FormatError(
- TiffFormatError::InconsistentSizesEncountered,
- ));
- }
- }
- (false, false, true, true) => {
- chunk_type = ChunkType::Tile;
-
- let tile_width =
- usize::try_from(tag_reader.require_tag(Tag::TileWidth)?.into_u32()?)?;
- let tile_length =
- usize::try_from(tag_reader.require_tag(Tag::TileLength)?.into_u32()?)?;
-
- if tile_width == 0 {
- return Err(TiffFormatError::InvalidTagValueType(Tag::TileWidth).into());
- } else if tile_length == 0 {
- return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into());
- }
-
- strip_decoder = None;
- tile_attributes = Some(TileAttributes {
- image_width: usize::try_from(width)?,
- image_height: usize::try_from(height)?,
- tile_width,
- tile_length,
- });
- chunk_offsets = tag_reader
- .find_tag(Tag::TileOffsets)?
- .unwrap()
- .into_u64_vec()?;
- chunk_bytes = tag_reader
- .find_tag(Tag::TileByteCounts)?
- .unwrap()
- .into_u64_vec()?;
-
- let tile = tile_attributes.as_ref().unwrap();
- if chunk_offsets.len() != chunk_bytes.len()
- || chunk_offsets.len() != tile.tiles_down() * tile.tiles_across()
- {
- return Err(TiffError::FormatError(
- TiffFormatError::InconsistentSizesEncountered,
- ));
- }
- }
- (_, _, _, _) => {
- return Err(TiffError::FormatError(
- TiffFormatError::StripTileTagConflict,
- ))
- }
- };
-
- Ok(Image {
- ifd: Some(ifd),
- width,
- height,
- bits_per_sample,
- samples,
- sample_format,
- photometric_interpretation,
- compression_method,
- jpeg_tables,
- predictor,
- chunk_type,
- strip_decoder,
- tile_attributes,
- chunk_offsets,
- chunk_bytes,
- })
- }
-
- pub(crate) fn colortype(&self) -> TiffResult<ColorType> {
- match self.photometric_interpretation {
- PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
- [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
- [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
- // FIXME: We should _ignore_ other components. In particular:
- // > Beware of extra components. Some TIFF files may have more components per pixel
- // than you think. A Baseline TIFF reader must skip over them gracefully,using the
- // values of the SamplesPerPixel and BitsPerSample fields.
- // > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements.
- _ => Err(TiffError::UnsupportedError(
- TiffUnsupportedError::InterpretationWithBits(
- self.photometric_interpretation,
- self.bits_per_sample.clone(),
- ),
- )),
- },
- PhotometricInterpretation::CMYK => match self.bits_per_sample[..] {
- [c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
- _ => Err(TiffError::UnsupportedError(
- TiffUnsupportedError::InterpretationWithBits(
- self.photometric_interpretation,
- self.bits_per_sample.clone(),
- ),
- )),
- },
- PhotometricInterpretation::YCbCr => match self.bits_per_sample[..] {
- [y, cb, cr] if [y, y] == [cb, cr] => Ok(ColorType::YCbCr(y)),
- _ => Err(TiffError::UnsupportedError(
- TiffUnsupportedError::InterpretationWithBits(
- self.photometric_interpretation,
- self.bits_per_sample.clone(),
- ),
- )),
- },
- PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
- if self.bits_per_sample.len() == 1 =>
- {
- Ok(ColorType::Gray(self.bits_per_sample[0]))
- }
-
- // TODO: this is bad we should not fail at this point
- _ => Err(TiffError::UnsupportedError(
- TiffUnsupportedError::InterpretationWithBits(
- self.photometric_interpretation,
- self.bits_per_sample.clone(),
- ),
- )),
- }
- }
-
- fn create_reader<'r, R: 'r + Read>(
- reader: R,
- photometric_interpretation: PhotometricInterpretation,
- compression_method: CompressionMethod,
- compressed_length: u64,
- jpeg_tables: Option<Arc<Vec<u8>>>,
- ) -> TiffResult<Box<dyn Read + 'r>> {
- Ok(match compression_method {
- CompressionMethod::None => Box::new(reader),
- CompressionMethod::LZW => {
- Box::new(LZWReader::new(reader, usize::try_from(compressed_length)?))
- }
- CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)),
- CompressionMethod::Deflate | CompressionMethod::OldDeflate => {
- Box::new(DeflateReader::new(reader))
- }
- CompressionMethod::ModernJPEG => {
- if jpeg_tables.is_some() && compressed_length < 2 {
- return Err(TiffError::FormatError(
- TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
- ));
- }
-
- let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?;
- let mut decoder = jpeg::Decoder::new(jpeg_reader);
-
- match photometric_interpretation {
- PhotometricInterpretation::RGB => {
- decoder.set_color_transform(jpeg::ColorTransform::RGB)
- }
- PhotometricInterpretation::WhiteIsZero => {
- decoder.set_color_transform(jpeg::ColorTransform::None)
- }
- PhotometricInterpretation::BlackIsZero => {
- decoder.set_color_transform(jpeg::ColorTransform::None)
- }
- PhotometricInterpretation::TransparencyMask => {
- decoder.set_color_transform(jpeg::ColorTransform::None)
- }
- PhotometricInterpretation::CMYK => {
- decoder.set_color_transform(jpeg::ColorTransform::CMYK)
- }
- PhotometricInterpretation::YCbCr => {
- decoder.set_color_transform(jpeg::ColorTransform::YCbCr)
- }
- photometric_interpretation => {
- return Err(TiffError::UnsupportedError(
- TiffUnsupportedError::UnsupportedInterpretation(
- photometric_interpretation,
- ),
- ));
- }
- }
-
- let data = decoder.decode()?;
-
- Box::new(Cursor::new(data))
- }
- method => {
- return Err(TiffError::UnsupportedError(
- TiffUnsupportedError::UnsupportedCompressionMethod(method),
- ))
- }
- })
- }
-
- pub(crate) fn chunk_file_range(&self, chunk: u32) -> TiffResult<(u64, u64)> {
- let file_offset = self
- .chunk_offsets
- .get(chunk as usize)
- .ok_or(TiffError::FormatError(
- TiffFormatError::InconsistentSizesEncountered,
- ))?;
-
- let compressed_bytes =
- self.chunk_bytes
- .get(chunk as usize)
- .ok_or(TiffError::FormatError(
- TiffFormatError::InconsistentSizesEncountered,
- ))?;
-
- Ok((*file_offset, *compressed_bytes))
- }
-
- pub(crate) fn chunk_dimensions(&self) -> TiffResult<(u32, u32)> {
- match self.chunk_type {
- ChunkType::Strip => {
- let strip_attrs = self.strip_decoder.as_ref().unwrap();
- Ok((self.width, strip_attrs.rows_per_strip))
- }
- ChunkType::Tile => {
- let tile_attrs = self.tile_attributes.as_ref().unwrap();
- Ok((
- u32::try_from(tile_attrs.tile_width)?,
- u32::try_from(tile_attrs.tile_length)?,
- ))
- }
- }
- }
-
- pub(crate) fn chunk_data_dimensions(&self, chunk_index: u32) -> TiffResult<(u32, u32)> {
- let dims = self.chunk_dimensions()?;
-
- match self.chunk_type {
- ChunkType::Strip => {
- let strip_height_without_padding = chunk_index
- .checked_mul(dims.1)
- .and_then(|x| self.height.checked_sub(x))
- .ok_or(TiffError::UsageError(UsageError::InvalidChunkIndex(
- chunk_index,
- )))?;
-
- // Ignore potential vertical padding on the bottommost strip
- let strip_height = dims.1.min(strip_height_without_padding);
-
- Ok((dims.0, strip_height))
- }
- ChunkType::Tile => {
- let tile_attrs = self.tile_attributes.as_ref().unwrap();
- let (padding_right, padding_down) = tile_attrs.get_padding(chunk_index as usize);
-
- let tile_width = tile_attrs.tile_width - padding_right;
- let tile_length = tile_attrs.tile_length - padding_down;
-
- Ok((u32::try_from(tile_width)?, u32::try_from(tile_length)?))
- }
- }
- }
-
- pub(crate) fn expand_chunk(
- &self,
- reader: impl Read,
- mut buffer: DecodingBuffer,
- output_width: usize,
- byte_order: ByteOrder,
- chunk_index: u32,
- ) -> TiffResult<()> {
- // Validate that the provided buffer is of the expected type.
- let color_type = self.colortype()?;
- match (color_type, &buffer) {
- (ColorType::RGB(n), _)
- | (ColorType::RGBA(n), _)
- | (ColorType::CMYK(n), _)
- | (ColorType::YCbCr(n), _)
- | (ColorType::Gray(n), _)
- if usize::from(n) == buffer.byte_len() * 8 => {}
- (ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor {
- Predictor::None => {}
- Predictor::Horizontal => {
- return Err(TiffError::UnsupportedError(
- TiffUnsupportedError::HorizontalPredictor(color_type),
- ))
- }
- Predictor::FloatingPoint => {
- return Err(TiffError::UnsupportedError(
- TiffUnsupportedError::FloatingPointPredictor(color_type),
- ));
- }
- },
- (type_, _) => {
- return Err(TiffError::UnsupportedError(
- TiffUnsupportedError::UnsupportedColorType(type_),
- ))
- }
- }
-
- // Validate that the predictor is supported for the sample type.
- match (self.predictor, &buffer) {
- (Predictor::Horizontal, DecodingBuffer::F32(_))
- | (Predictor::Horizontal, DecodingBuffer::F64(_)) => {
- return Err(TiffError::UnsupportedError(
- TiffUnsupportedError::HorizontalPredictor(color_type),
- ));
- }
- (Predictor::FloatingPoint, DecodingBuffer::F32(_))
- | (Predictor::FloatingPoint, DecodingBuffer::F64(_)) => {}
- (Predictor::FloatingPoint, _) => {
- return Err(TiffError::UnsupportedError(
- TiffUnsupportedError::FloatingPointPredictor(color_type),
- ));
- }
- _ => {}
- }
-
- let compressed_bytes =
- self.chunk_bytes
- .get(chunk_index as usize)
- .ok_or(TiffError::FormatError(
- TiffFormatError::InconsistentSizesEncountered,
- ))?;
-
- let byte_len = buffer.byte_len();
- let compression_method = self.compression_method;
- let photometric_interpretation = self.photometric_interpretation;
- let predictor = self.predictor;
- let samples = self.bits_per_sample.len();
-
- let chunk_dims = self.chunk_dimensions()?;
- let data_dims = self.chunk_data_dimensions(chunk_index)?;
-
- let padding_right = chunk_dims.0 - data_dims.0;
-
- let jpeg_tables = self.jpeg_tables.clone();
- let mut reader = Self::create_reader(
- reader,
- photometric_interpretation,
- compression_method,
- *compressed_bytes,
- jpeg_tables,
- )?;
-
- if output_width == data_dims.0 as usize && padding_right == 0 {
- let total_samples = data_dims.0 as usize * data_dims.1 as usize * samples;
- let tile = &mut buffer.as_bytes_mut()[..total_samples * byte_len];
- reader.read_exact(tile)?;
-
- for row in 0..data_dims.1 as usize {
- let row_start = row as usize * output_width as usize * samples;
- let row_end = (row + 1) * output_width as usize * samples;
- let row = buffer.subrange(row_start..row_end);
- super::fix_endianness_and_predict(row, samples, byte_order, predictor);
- }
- if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
- super::invert_colors(&mut buffer.subrange(0..total_samples), color_type);
- }
- } else if padding_right > 0 && self.predictor == Predictor::FloatingPoint {
- // The floating point predictor shuffles the padding bytes into the encoded output, so
- // this case is handled specially when needed.
- let mut encoded = vec![0u8; chunk_dims.0 as usize * samples * byte_len];
-
- for row in 0..data_dims.1 as usize {
- let row_start = row * output_width as usize * samples;
- let row_end = row_start + data_dims.0 as usize * samples;
-
- reader.read_exact(&mut encoded)?;
- match buffer.subrange(row_start..row_end) {
- DecodingBuffer::F32(buf) => fp_predict_f32(&mut encoded, buf, samples),
- DecodingBuffer::F64(buf) => fp_predict_f64(&mut encoded, buf, samples),
- _ => unreachable!(),
- }
- if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
- super::invert_colors(&mut buffer.subrange(row_start..row_end), color_type);
- }
- }
- } else {
- for row in 0..data_dims.1 as usize {
- let row_start = row * output_width as usize * samples;
- let row_end = row_start + data_dims.0 as usize * samples;
-
- let row = &mut buffer.as_bytes_mut()[(row_start * byte_len)..(row_end * byte_len)];
- reader.read_exact(row)?;
-
- // Skip horizontal padding
- if padding_right > 0 {
- let len = u64::try_from(padding_right as usize * samples * byte_len)?;
- io::copy(&mut reader.by_ref().take(len), &mut io::sink())?;
- }
-
- let mut row = buffer.subrange(row_start..row_end);
- super::fix_endianness_and_predict(row.copy(), samples, byte_order, predictor);
- if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
- super::invert_colors(&mut row, color_type);
- }
- }
- }
-
- Ok(())
- }
-}