diff options
author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/image/src/codecs/tiff.rs | |
parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/image/src/codecs/tiff.rs')
-rw-r--r-- | vendor/image/src/codecs/tiff.rs | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/vendor/image/src/codecs/tiff.rs b/vendor/image/src/codecs/tiff.rs new file mode 100644 index 0000000..7c33412 --- /dev/null +++ b/vendor/image/src/codecs/tiff.rs @@ -0,0 +1,353 @@ +//! Decoding and Encoding of TIFF Images +//! +//! TIFF (Tagged Image File Format) is a versatile image format that supports +//! lossless and lossy compression. +//! +//! # Related Links +//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification + +extern crate tiff; + +use std::convert::TryFrom; +use std::io::{self, Cursor, Read, Seek, Write}; +use std::marker::PhantomData; +use std::mem; + +use crate::color::{ColorType, ExtendedColorType}; +use crate::error::{ + DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind, + ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind, +}; +use crate::image::{ImageDecoder, ImageEncoder, ImageFormat}; +use crate::utils; + +/// Decoder for TIFF images. +pub struct TiffDecoder<R> +where + R: Read + Seek, +{ + dimensions: (u32, u32), + color_type: ColorType, + + // We only use an Option here so we can call with_limits on the decoder without moving. + inner: Option<tiff::decoder::Decoder<R>>, +} + +impl<R> TiffDecoder<R> +where + R: Read + Seek, +{ + /// Create a new TiffDecoder. + pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> { + let mut inner = tiff::decoder::Decoder::new(r).map_err(ImageError::from_tiff_decode)?; + + let dimensions = inner.dimensions().map_err(ImageError::from_tiff_decode)?; + let color_type = inner.colortype().map_err(ImageError::from_tiff_decode)?; + match inner.find_tag_unsigned_vec::<u16>(tiff::tags::Tag::SampleFormat) { + Ok(Some(sample_formats)) => { + for format in sample_formats { + check_sample_format(format)?; + } + } + Ok(None) => { /* assume UInt format */ } + Err(other) => return Err(ImageError::from_tiff_decode(other)), + }; + + let color_type = match color_type { + tiff::ColorType::Gray(8) => ColorType::L8, + tiff::ColorType::Gray(16) => ColorType::L16, + tiff::ColorType::GrayA(8) => ColorType::La8, + tiff::ColorType::GrayA(16) => ColorType::La16, + tiff::ColorType::RGB(8) => ColorType::Rgb8, + tiff::ColorType::RGB(16) => ColorType::Rgb16, + tiff::ColorType::RGBA(8) => ColorType::Rgba8, + tiff::ColorType::RGBA(16) => ColorType::Rgba16, + + tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => { + return Err(err_unknown_color_type(n)) + } + tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n.saturating_mul(2))), + tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n.saturating_mul(3))), + tiff::ColorType::YCbCr(n) => return Err(err_unknown_color_type(n.saturating_mul(3))), + tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => { + return Err(err_unknown_color_type(n.saturating_mul(4))) + } + }; + + Ok(TiffDecoder { + dimensions, + color_type, + inner: Some(inner), + }) + } +} + +fn check_sample_format(sample_format: u16) -> Result<(), ImageError> { + match tiff::tags::SampleFormat::from_u16(sample_format) { + Some(tiff::tags::SampleFormat::Uint) => Ok(()), + Some(other) => Err(ImageError::Unsupported( + UnsupportedError::from_format_and_kind( + ImageFormat::Tiff.into(), + UnsupportedErrorKind::GenericFeature(format!( + "Unhandled TIFF sample format {:?}", + other + )), + ), + )), + None => Err(ImageError::Decoding(DecodingError::from_format_hint( + ImageFormat::Tiff.into(), + ))), + } +} + +fn err_unknown_color_type(value: u8) -> ImageError { + ImageError::Unsupported(UnsupportedError::from_format_and_kind( + ImageFormat::Tiff.into(), + UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)), + )) +} + +impl ImageError { + fn from_tiff_decode(err: tiff::TiffError) -> ImageError { + match err { + tiff::TiffError::IoError(err) => ImageError::IoError(err), + err @ tiff::TiffError::FormatError(_) + | err @ tiff::TiffError::IntSizeError + | err @ tiff::TiffError::UsageError(_) => { + ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err)) + } + tiff::TiffError::UnsupportedError(desc) => { + ImageError::Unsupported(UnsupportedError::from_format_and_kind( + ImageFormat::Tiff.into(), + UnsupportedErrorKind::GenericFeature(desc.to_string()), + )) + } + tiff::TiffError::LimitsExceeded => { + ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory)) + } + } + } + + fn from_tiff_encode(err: tiff::TiffError) -> ImageError { + match err { + tiff::TiffError::IoError(err) => ImageError::IoError(err), + err @ tiff::TiffError::FormatError(_) + | err @ tiff::TiffError::IntSizeError + | err @ tiff::TiffError::UsageError(_) => { + ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err)) + } + tiff::TiffError::UnsupportedError(desc) => { + ImageError::Unsupported(UnsupportedError::from_format_and_kind( + ImageFormat::Tiff.into(), + UnsupportedErrorKind::GenericFeature(desc.to_string()), + )) + } + tiff::TiffError::LimitsExceeded => { + ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory)) + } + } + } +} + +/// Wrapper struct around a `Cursor<Vec<u8>>` +pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>); +impl<R> Read for TiffReader<R> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + if self.0.position() == 0 && buf.is_empty() { + mem::swap(buf, self.0.get_mut()); + Ok(buf.len()) + } else { + self.0.read_to_end(buf) + } + } +} + +impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TiffDecoder<R> { + type Reader = TiffReader<R>; + + fn dimensions(&self) -> (u32, u32) { + self.dimensions + } + + fn color_type(&self) -> ColorType { + self.color_type + } + + fn icc_profile(&mut self) -> Option<Vec<u8>> { + if let Some(decoder) = &mut self.inner { + decoder.get_tag_u8_vec(tiff::tags::Tag::Unknown(34675)).ok() + } else { + None + } + } + + fn set_limits(&mut self, limits: crate::io::Limits) -> ImageResult<()> { + limits.check_support(&crate::io::LimitSupport::default())?; + + let (width, height) = self.dimensions(); + limits.check_dimensions(width, height)?; + + let max_alloc = limits.max_alloc.unwrap_or(u64::MAX); + let max_intermediate_alloc = max_alloc.saturating_sub(self.total_bytes()); + + let mut tiff_limits: tiff::decoder::Limits = Default::default(); + tiff_limits.decoding_buffer_size = + usize::try_from(max_alloc - max_intermediate_alloc).unwrap_or(usize::MAX); + tiff_limits.intermediate_buffer_size = + usize::try_from(max_intermediate_alloc).unwrap_or(usize::MAX); + tiff_limits.ifd_value_size = tiff_limits.intermediate_buffer_size; + self.inner = Some(self.inner.take().unwrap().with_limits(tiff_limits)); + + Ok(()) + } + + fn into_reader(self) -> ImageResult<Self::Reader> { + let buf = match self + .inner + .unwrap() + .read_image() + .map_err(ImageError::from_tiff_decode)? + { + tiff::decoder::DecodingResult::U8(v) => v, + tiff::decoder::DecodingResult::U16(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::U32(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::U64(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::I8(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::I16(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::I32(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::I64(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::F32(v) => utils::vec_copy_to_u8(&v), + tiff::decoder::DecodingResult::F64(v) => utils::vec_copy_to_u8(&v), + }; + + Ok(TiffReader(Cursor::new(buf), PhantomData)) + } + + fn read_image(self, buf: &mut [u8]) -> ImageResult<()> { + assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes())); + match self + .inner + .unwrap() + .read_image() + .map_err(ImageError::from_tiff_decode)? + { + tiff::decoder::DecodingResult::U8(v) => { + buf.copy_from_slice(&v); + } + tiff::decoder::DecodingResult::U16(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::U32(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::U64(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::I8(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::I16(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::I32(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::I64(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::F32(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + tiff::decoder::DecodingResult::F64(v) => { + buf.copy_from_slice(bytemuck::cast_slice(&v)); + } + } + Ok(()) + } +} + +/// Encoder for tiff images +pub struct TiffEncoder<W> { + w: W, +} + +// Utility to simplify and deduplicate error handling during 16-bit encoding. +fn u8_slice_as_u16(buf: &[u8]) -> ImageResult<&[u16]> { + bytemuck::try_cast_slice(buf).map_err(|err| { + // If the buffer is not aligned or the correct length for a u16 slice, err. + // + // `bytemuck::PodCastError` of bytemuck-1.2.0 does not implement + // `Error` and `Display` trait. + // See <https://github.com/Lokathor/bytemuck/issues/22>. + ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic( + format!("{:?}", err), + ))) + }) +} + +impl<W: Write + Seek> TiffEncoder<W> { + /// Create a new encoder that writes its output to `w` + pub fn new(w: W) -> TiffEncoder<W> { + TiffEncoder { w } + } + + /// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`. + /// + /// 16-bit types assume the buffer is native endian. + pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> { + let mut encoder = + tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?; + match color { + ColorType::L8 => { + encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data) + } + ColorType::Rgb8 => { + encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data) + } + ColorType::Rgba8 => { + encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data) + } + ColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>( + width, + height, + u8_slice_as_u16(data)?, + ), + ColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>( + width, + height, + u8_slice_as_u16(data)?, + ), + ColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>( + width, + height, + u8_slice_as_u16(data)?, + ), + _ => { + return Err(ImageError::Unsupported( + UnsupportedError::from_format_and_kind( + ImageFormat::Tiff.into(), + UnsupportedErrorKind::Color(color.into()), + ), + )) + } + } + .map_err(ImageError::from_tiff_encode)?; + + Ok(()) + } +} + +impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> { + fn write_image( + self, + buf: &[u8], + width: u32, + height: u32, + color_type: ColorType, + ) -> ImageResult<()> { + self.encode(buf, width, height, color_type) + } +} |