1//! Decoding and Encoding of TIFF Images
2//!
3//! TIFF (Tagged Image File Format) is a versatile image format that supports
4//! lossless and lossy compression.
5//!
6//! # Related Links
7//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification
8
9extern crate tiff;
10
11use std::io::{self, Cursor, Read, Seek, Write};
12use std::marker::PhantomData;
13use std::mem;
14
15use crate::color::{ColorType, ExtendedColorType};
16use crate::error::{
17 DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
18 ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
19};
20use crate::image::{ImageDecoder, ImageEncoder, ImageFormat};
21use crate::utils;
22
23/// Decoder for TIFF images.
24pub struct TiffDecoder<R>
25where
26 R: Read + Seek,
27{
28 dimensions: (u32, u32),
29 color_type: ColorType,
30 original_color_type: ExtendedColorType,
31
32 // We only use an Option here so we can call with_limits on the decoder without moving.
33 inner: Option<tiff::decoder::Decoder<R>>,
34}
35
36impl<R> TiffDecoder<R>
37where
38 R: Read + Seek,
39{
40 /// Create a new TiffDecoder.
41 pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> {
42 let mut inner = tiff::decoder::Decoder::new(r).map_err(ImageError::from_tiff_decode)?;
43
44 let dimensions = inner.dimensions().map_err(ImageError::from_tiff_decode)?;
45 let tiff_color_type = inner.colortype().map_err(ImageError::from_tiff_decode)?;
46 match inner.find_tag_unsigned_vec::<u16>(tiff::tags::Tag::SampleFormat) {
47 Ok(Some(sample_formats)) => {
48 for format in sample_formats {
49 check_sample_format(format)?;
50 }
51 }
52 Ok(None) => { /* assume UInt format */ }
53 Err(other) => return Err(ImageError::from_tiff_decode(other)),
54 };
55
56 let color_type = match tiff_color_type {
57 tiff::ColorType::Gray(8) => ColorType::L8,
58 tiff::ColorType::Gray(16) => ColorType::L16,
59 tiff::ColorType::GrayA(8) => ColorType::La8,
60 tiff::ColorType::GrayA(16) => ColorType::La16,
61 tiff::ColorType::RGB(8) => ColorType::Rgb8,
62 tiff::ColorType::RGB(16) => ColorType::Rgb16,
63 tiff::ColorType::RGBA(8) => ColorType::Rgba8,
64 tiff::ColorType::RGBA(16) => ColorType::Rgba16,
65 tiff::ColorType::CMYK(8) => ColorType::Rgb8,
66
67 tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => {
68 return Err(err_unknown_color_type(n))
69 }
70 tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n.saturating_mul(2))),
71 tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n.saturating_mul(3))),
72 tiff::ColorType::YCbCr(n) => return Err(err_unknown_color_type(n.saturating_mul(3))),
73 tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => {
74 return Err(err_unknown_color_type(n.saturating_mul(4)))
75 }
76 };
77
78 let original_color_type = match tiff_color_type {
79 tiff::ColorType::CMYK(8) => ExtendedColorType::Cmyk8,
80 _ => color_type.into(),
81 };
82
83 Ok(TiffDecoder {
84 dimensions,
85 color_type,
86 original_color_type,
87 inner: Some(inner),
88 })
89 }
90
91 // The buffer can be larger for CMYK than the RGB output
92 fn total_bytes_buffer(&self) -> u64 {
93 let dimensions = self.dimensions();
94 let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
95 let bytes_per_pixel = if self.original_color_type == ExtendedColorType::Cmyk8 {
96 16
97 } else {
98 u64::from(self.color_type().bytes_per_pixel())
99 };
100 total_pixels.saturating_mul(bytes_per_pixel)
101 }
102}
103
104fn check_sample_format(sample_format: u16) -> Result<(), ImageError> {
105 match tiff::tags::SampleFormat::from_u16(val:sample_format) {
106 Some(tiff::tags::SampleFormat::Uint) => Ok(()),
107 Some(other: SampleFormat) => Err(ImageError::Unsupported(
108 UnsupportedError::from_format_and_kind(
109 format:ImageFormat::Tiff.into(),
110 kind:UnsupportedErrorKind::GenericFeature(format!(
111 "Unhandled TIFF sample format {:?}",
112 other
113 )),
114 ),
115 )),
116 None => Err(ImageError::Decoding(DecodingError::from_format_hint(
117 format:ImageFormat::Tiff.into(),
118 ))),
119 }
120}
121
122fn err_unknown_color_type(value: u8) -> ImageError {
123 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
124 format:ImageFormat::Tiff.into(),
125 kind:UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
126 ))
127}
128
129impl ImageError {
130 fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
131 match err {
132 tiff::TiffError::IoError(err) => ImageError::IoError(err),
133 err @ tiff::TiffError::FormatError(_)
134 | err @ tiff::TiffError::IntSizeError
135 | err @ tiff::TiffError::UsageError(_) => {
136 ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
137 }
138 tiff::TiffError::UnsupportedError(desc) => {
139 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
140 ImageFormat::Tiff.into(),
141 UnsupportedErrorKind::GenericFeature(desc.to_string()),
142 ))
143 }
144 tiff::TiffError::LimitsExceeded => {
145 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
146 }
147 }
148 }
149
150 fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
151 match err {
152 tiff::TiffError::IoError(err) => ImageError::IoError(err),
153 err @ tiff::TiffError::FormatError(_)
154 | err @ tiff::TiffError::IntSizeError
155 | err @ tiff::TiffError::UsageError(_) => {
156 ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
157 }
158 tiff::TiffError::UnsupportedError(desc) => {
159 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
160 ImageFormat::Tiff.into(),
161 UnsupportedErrorKind::GenericFeature(desc.to_string()),
162 ))
163 }
164 tiff::TiffError::LimitsExceeded => {
165 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
166 }
167 }
168 }
169}
170
171/// Wrapper struct around a `Cursor<Vec<u8>>`
172pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
173impl<R> Read for TiffReader<R> {
174 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
175 self.0.read(buf)
176 }
177 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
178 if self.0.position() == 0 && buf.is_empty() {
179 mem::swap(x:buf, self.0.get_mut());
180 Ok(buf.len())
181 } else {
182 self.0.read_to_end(buf)
183 }
184 }
185}
186
187impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TiffDecoder<R> {
188 type Reader = TiffReader<R>;
189
190 fn dimensions(&self) -> (u32, u32) {
191 self.dimensions
192 }
193
194 fn color_type(&self) -> ColorType {
195 self.color_type
196 }
197
198 fn original_color_type(&self) -> ExtendedColorType {
199 self.original_color_type
200 }
201
202 fn icc_profile(&mut self) -> Option<Vec<u8>> {
203 if let Some(decoder) = &mut self.inner {
204 decoder.get_tag_u8_vec(tiff::tags::Tag::Unknown(34675)).ok()
205 } else {
206 None
207 }
208 }
209
210 fn set_limits(&mut self, limits: crate::io::Limits) -> ImageResult<()> {
211 limits.check_support(&crate::io::LimitSupport::default())?;
212
213 let (width, height) = self.dimensions();
214 limits.check_dimensions(width, height)?;
215
216 let max_alloc = limits.max_alloc.unwrap_or(u64::MAX);
217 let max_intermediate_alloc = max_alloc.saturating_sub(self.total_bytes_buffer());
218
219 let mut tiff_limits: tiff::decoder::Limits = Default::default();
220 tiff_limits.decoding_buffer_size =
221 usize::try_from(max_alloc - max_intermediate_alloc).unwrap_or(usize::MAX);
222 tiff_limits.intermediate_buffer_size =
223 usize::try_from(max_intermediate_alloc).unwrap_or(usize::MAX);
224 tiff_limits.ifd_value_size = tiff_limits.intermediate_buffer_size;
225 self.inner = Some(self.inner.take().unwrap().with_limits(tiff_limits));
226
227 Ok(())
228 }
229
230 fn into_reader(self) -> ImageResult<Self::Reader> {
231 let buf = match self
232 .inner
233 .unwrap()
234 .read_image()
235 .map_err(ImageError::from_tiff_decode)?
236 {
237 tiff::decoder::DecodingResult::U8(v) => v,
238 tiff::decoder::DecodingResult::U16(v) => utils::vec_copy_to_u8(&v),
239 tiff::decoder::DecodingResult::U32(v) => utils::vec_copy_to_u8(&v),
240 tiff::decoder::DecodingResult::U64(v) => utils::vec_copy_to_u8(&v),
241 tiff::decoder::DecodingResult::I8(v) => utils::vec_copy_to_u8(&v),
242 tiff::decoder::DecodingResult::I16(v) => utils::vec_copy_to_u8(&v),
243 tiff::decoder::DecodingResult::I32(v) => utils::vec_copy_to_u8(&v),
244 tiff::decoder::DecodingResult::I64(v) => utils::vec_copy_to_u8(&v),
245 tiff::decoder::DecodingResult::F32(v) => utils::vec_copy_to_u8(&v),
246 tiff::decoder::DecodingResult::F64(v) => utils::vec_copy_to_u8(&v),
247 };
248
249 Ok(TiffReader(Cursor::new(buf), PhantomData))
250 }
251
252 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
253 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
254 match self
255 .inner
256 .unwrap()
257 .read_image()
258 .map_err(ImageError::from_tiff_decode)?
259 {
260 tiff::decoder::DecodingResult::U8(v)
261 if self.original_color_type == ExtendedColorType::Cmyk8 =>
262 {
263 let mut out_cur = Cursor::new(buf);
264 for cmyk in v.chunks_exact(4) {
265 out_cur.write_all(&cmyk_to_rgb(cmyk))?;
266 }
267 }
268 tiff::decoder::DecodingResult::U8(v) => {
269 buf.copy_from_slice(&v);
270 }
271 tiff::decoder::DecodingResult::U16(v) => {
272 buf.copy_from_slice(bytemuck::cast_slice(&v));
273 }
274 tiff::decoder::DecodingResult::U32(v) => {
275 buf.copy_from_slice(bytemuck::cast_slice(&v));
276 }
277 tiff::decoder::DecodingResult::U64(v) => {
278 buf.copy_from_slice(bytemuck::cast_slice(&v));
279 }
280 tiff::decoder::DecodingResult::I8(v) => {
281 buf.copy_from_slice(bytemuck::cast_slice(&v));
282 }
283 tiff::decoder::DecodingResult::I16(v) => {
284 buf.copy_from_slice(bytemuck::cast_slice(&v));
285 }
286 tiff::decoder::DecodingResult::I32(v) => {
287 buf.copy_from_slice(bytemuck::cast_slice(&v));
288 }
289 tiff::decoder::DecodingResult::I64(v) => {
290 buf.copy_from_slice(bytemuck::cast_slice(&v));
291 }
292 tiff::decoder::DecodingResult::F32(v) => {
293 buf.copy_from_slice(bytemuck::cast_slice(&v));
294 }
295 tiff::decoder::DecodingResult::F64(v) => {
296 buf.copy_from_slice(bytemuck::cast_slice(&v));
297 }
298 }
299 Ok(())
300 }
301}
302
303/// Encoder for tiff images
304pub struct TiffEncoder<W> {
305 w: W,
306}
307
308fn cmyk_to_rgb(cmyk: &[u8]) -> [u8; 3] {
309 let c: f32 = cmyk[0] as f32;
310 let m: f32 = cmyk[1] as f32;
311 let y: f32 = cmyk[2] as f32;
312 let kf: f32 = 1. - cmyk[3] as f32 / 255.;
313 [
314 ((255. - c) * kf) as u8,
315 ((255. - m) * kf) as u8,
316 ((255. - y) * kf) as u8,
317 ]
318}
319
320// Utility to simplify and deduplicate error handling during 16-bit encoding.
321fn u8_slice_as_u16(buf: &[u8]) -> ImageResult<&[u16]> {
322 bytemuck::try_cast_slice(buf).map_err(|err: PodCastError| {
323 // If the buffer is not aligned or the correct length for a u16 slice, err.
324 //
325 // `bytemuck::PodCastError` of bytemuck-1.2.0 does not implement
326 // `Error` and `Display` trait.
327 // See <https://github.com/Lokathor/bytemuck/issues/22>.
328 ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic(
329 format!("{:?}", err),
330 )))
331 })
332}
333
334impl<W: Write + Seek> TiffEncoder<W> {
335 /// Create a new encoder that writes its output to `w`
336 pub fn new(w: W) -> TiffEncoder<W> {
337 TiffEncoder { w }
338 }
339
340 /// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
341 ///
342 /// 16-bit types assume the buffer is native endian.
343 ///
344 /// # Panics
345 ///
346 /// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`.
347 #[track_caller]
348 pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
349 let expected_buffer_len =
350 (width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64);
351 assert_eq!(
352 expected_buffer_len,
353 data.len() as u64,
354 "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
355 data.len(),
356 );
357
358 let mut encoder =
359 tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
360 match color {
361 ColorType::L8 => {
362 encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data)
363 }
364 ColorType::Rgb8 => {
365 encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data)
366 }
367 ColorType::Rgba8 => {
368 encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data)
369 }
370 ColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(
371 width,
372 height,
373 u8_slice_as_u16(data)?,
374 ),
375 ColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(
376 width,
377 height,
378 u8_slice_as_u16(data)?,
379 ),
380 ColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(
381 width,
382 height,
383 u8_slice_as_u16(data)?,
384 ),
385 _ => {
386 return Err(ImageError::Unsupported(
387 UnsupportedError::from_format_and_kind(
388 ImageFormat::Tiff.into(),
389 UnsupportedErrorKind::Color(color.into()),
390 ),
391 ))
392 }
393 }
394 .map_err(ImageError::from_tiff_encode)?;
395
396 Ok(())
397 }
398}
399
400impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
401 #[track_caller]
402 fn write_image(
403 self,
404 buf: &[u8],
405 width: u32,
406 height: u32,
407 color_type: ColorType,
408 ) -> ImageResult<()> {
409 self.encode(data:buf, width, height, color_type)
410 }
411}
412