//! Decoding and Encoding of TIFF Images //! //! TIFF (Tagged Image File Format) is a versatile image format that supports //! lossless and lossy compression. //! //! # Related Links //! * - 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 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>, } impl TiffDecoder where R: Read + Seek, { /// Create a new TiffDecoder. pub fn new(r: R) -> Result, 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::(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>` pub struct TiffReader(Cursor>, PhantomData); impl Read for TiffReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { 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 { type Reader = TiffReader; fn dimensions(&self) -> (u32, u32) { self.dimensions } fn color_type(&self) -> ColorType { self.color_type } fn icc_profile(&mut self) -> Option> { 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 { 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, } // 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 . ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic( format!("{:?}", err), ))) }) } impl TiffEncoder { /// Create a new encoder that writes its output to `w` pub fn new(w: W) -> TiffEncoder { 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::(width, height, data) } ColorType::Rgb8 => { encoder.write_image::(width, height, data) } ColorType::Rgba8 => { encoder.write_image::(width, height, data) } ColorType::L16 => encoder.write_image::( width, height, u8_slice_as_u16(data)?, ), ColorType::Rgb16 => encoder.write_image::( width, height, u8_slice_as_u16(data)?, ), ColorType::Rgba16 => encoder.write_image::( 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 ImageEncoder for TiffEncoder { fn write_image( self, buf: &[u8], width: u32, height: u32, color_type: ColorType, ) -> ImageResult<()> { self.encode(buf, width, height, color_type) } }