image/codecs/
tiff.rs

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, BufRead, 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::metadata::Orientation;
22
23/// Decoder for TIFF images.
24pub struct TiffDecoder<R>
25where
26    R: BufRead + 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: BufRead + 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(sample_format) {
106        Some(tiff::tags::SampleFormat::Uint) => Ok(()),
107        Some(other) => Err(ImageError::Unsupported(
108            UnsupportedError::from_format_and_kind(
109                ImageFormat::Tiff.into(),
110                UnsupportedErrorKind::GenericFeature(format!(
111                    "Unhandled TIFF sample format {other:?}"
112                )),
113            ),
114        )),
115        None => Err(ImageError::Decoding(DecodingError::from_format_hint(
116            ImageFormat::Tiff.into(),
117        ))),
118    }
119}
120
121fn err_unknown_color_type(value: u8) -> ImageError {
122    ImageError::Unsupported(UnsupportedError::from_format_and_kind(
123        ImageFormat::Tiff.into(),
124        UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
125    ))
126}
127
128impl ImageError {
129    fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
130        match err {
131            tiff::TiffError::IoError(err) => ImageError::IoError(err),
132            err @ (tiff::TiffError::FormatError(_)
133            | tiff::TiffError::IntSizeError
134            | tiff::TiffError::UsageError(_)) => {
135                ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
136            }
137            tiff::TiffError::UnsupportedError(desc) => {
138                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
139                    ImageFormat::Tiff.into(),
140                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
141                ))
142            }
143            tiff::TiffError::LimitsExceeded => {
144                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
145            }
146        }
147    }
148
149    fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
150        match err {
151            tiff::TiffError::IoError(err) => ImageError::IoError(err),
152            err @ (tiff::TiffError::FormatError(_)
153            | tiff::TiffError::IntSizeError
154            | tiff::TiffError::UsageError(_)) => {
155                ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
156            }
157            tiff::TiffError::UnsupportedError(desc) => {
158                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
159                    ImageFormat::Tiff.into(),
160                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
161                ))
162            }
163            tiff::TiffError::LimitsExceeded => {
164                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
165            }
166        }
167    }
168}
169
170/// Wrapper struct around a `Cursor<Vec<u8>>`
171#[allow(dead_code)]
172#[deprecated]
173pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
174#[allow(deprecated)]
175impl<R> Read for TiffReader<R> {
176    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
177        self.0.read(buf)
178    }
179
180    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
181        if self.0.position() == 0 && buf.is_empty() {
182            mem::swap(buf, self.0.get_mut());
183            Ok(buf.len())
184        } else {
185            self.0.read_to_end(buf)
186        }
187    }
188}
189
190impl<R: BufRead + Seek> ImageDecoder for TiffDecoder<R> {
191    fn dimensions(&self) -> (u32, u32) {
192        self.dimensions
193    }
194
195    fn color_type(&self) -> ColorType {
196        self.color_type
197    }
198
199    fn original_color_type(&self) -> ExtendedColorType {
200        self.original_color_type
201    }
202
203    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
204        if let Some(decoder) = &mut self.inner {
205            Ok(decoder.get_tag_u8_vec(tiff::tags::Tag::Unknown(34675)).ok())
206        } else {
207            Ok(None)
208        }
209    }
210
211    fn orientation(&mut self) -> ImageResult<Orientation> {
212        if let Some(decoder) = &mut self.inner {
213            Ok(decoder
214                .find_tag(tiff::tags::Tag::Orientation)
215                .map_err(ImageError::from_tiff_decode)?
216                .and_then(|v| Orientation::from_exif(v.into_u16().ok()?.min(255) as u8))
217                .unwrap_or(Orientation::NoTransforms))
218        } else {
219            Ok(Orientation::NoTransforms)
220        }
221    }
222
223    fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
224        limits.check_support(&crate::LimitSupport::default())?;
225
226        let (width, height) = self.dimensions();
227        limits.check_dimensions(width, height)?;
228
229        let max_alloc = limits.max_alloc.unwrap_or(u64::MAX);
230        let max_intermediate_alloc = max_alloc.saturating_sub(self.total_bytes_buffer());
231
232        let mut tiff_limits: tiff::decoder::Limits = Default::default();
233        tiff_limits.decoding_buffer_size =
234            usize::try_from(max_alloc - max_intermediate_alloc).unwrap_or(usize::MAX);
235        tiff_limits.intermediate_buffer_size =
236            usize::try_from(max_intermediate_alloc).unwrap_or(usize::MAX);
237        tiff_limits.ifd_value_size = tiff_limits.intermediate_buffer_size;
238        self.inner = Some(self.inner.take().unwrap().with_limits(tiff_limits));
239
240        Ok(())
241    }
242
243    fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
244        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
245        match self
246            .inner
247            .unwrap()
248            .read_image()
249            .map_err(ImageError::from_tiff_decode)?
250        {
251            tiff::decoder::DecodingResult::U8(v)
252                if self.original_color_type == ExtendedColorType::Cmyk8 =>
253            {
254                let mut out_cur = Cursor::new(buf);
255                for cmyk in v.chunks_exact(4) {
256                    out_cur.write_all(&cmyk_to_rgb(cmyk))?;
257                }
258            }
259            tiff::decoder::DecodingResult::U8(v) => {
260                buf.copy_from_slice(&v);
261            }
262            tiff::decoder::DecodingResult::U16(v) => {
263                buf.copy_from_slice(bytemuck::cast_slice(&v));
264            }
265            tiff::decoder::DecodingResult::U32(v) => {
266                buf.copy_from_slice(bytemuck::cast_slice(&v));
267            }
268            tiff::decoder::DecodingResult::U64(v) => {
269                buf.copy_from_slice(bytemuck::cast_slice(&v));
270            }
271            tiff::decoder::DecodingResult::I8(v) => {
272                buf.copy_from_slice(bytemuck::cast_slice(&v));
273            }
274            tiff::decoder::DecodingResult::I16(v) => {
275                buf.copy_from_slice(bytemuck::cast_slice(&v));
276            }
277            tiff::decoder::DecodingResult::I32(v) => {
278                buf.copy_from_slice(bytemuck::cast_slice(&v));
279            }
280            tiff::decoder::DecodingResult::I64(v) => {
281                buf.copy_from_slice(bytemuck::cast_slice(&v));
282            }
283            tiff::decoder::DecodingResult::F32(v) => {
284                buf.copy_from_slice(bytemuck::cast_slice(&v));
285            }
286            tiff::decoder::DecodingResult::F64(v) => {
287                buf.copy_from_slice(bytemuck::cast_slice(&v));
288            }
289        }
290        Ok(())
291    }
292
293    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
294        (*self).read_image(buf)
295    }
296}
297
298/// Encoder for tiff images
299pub struct TiffEncoder<W> {
300    w: W,
301}
302
303fn cmyk_to_rgb(cmyk: &[u8]) -> [u8; 3] {
304    let c = f32::from(cmyk[0]);
305    let m = f32::from(cmyk[1]);
306    let y = f32::from(cmyk[2]);
307    let kf = 1. - f32::from(cmyk[3]) / 255.;
308    [
309        ((255. - c) * kf) as u8,
310        ((255. - m) * kf) as u8,
311        ((255. - y) * kf) as u8,
312    ]
313}
314
315// Utility to simplify and deduplicate error handling during 16-bit encoding.
316fn u8_slice_as_u16(buf: &[u8]) -> ImageResult<&[u16]> {
317    bytemuck::try_cast_slice(buf).map_err(|err| {
318        // If the buffer is not aligned or the correct length for a u16 slice, err.
319        //
320        // `bytemuck::PodCastError` of bytemuck-1.2.0 does not implement
321        // `Error` and `Display` trait.
322        // See <https://github.com/Lokathor/bytemuck/issues/22>.
323        ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic(
324            format!("{err:?}"),
325        )))
326    })
327}
328
329impl<W: Write + Seek> TiffEncoder<W> {
330    /// Create a new encoder that writes its output to `w`
331    pub fn new(w: W) -> TiffEncoder<W> {
332        TiffEncoder { w }
333    }
334
335    /// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
336    ///
337    /// 16-bit types assume the buffer is native endian.
338    ///
339    /// # Panics
340    ///
341    /// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`.
342    #[track_caller]
343    pub fn encode(
344        self,
345        buf: &[u8],
346        width: u32,
347        height: u32,
348        color_type: ExtendedColorType,
349    ) -> ImageResult<()> {
350        let expected_buffer_len = color_type.buffer_size(width, height);
351        assert_eq!(
352            expected_buffer_len,
353            buf.len() as u64,
354            "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
355            buf.len(),
356        );
357
358        let mut encoder =
359            tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
360        match color_type {
361            ExtendedColorType::L8 => {
362                encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, buf)
363            }
364            ExtendedColorType::Rgb8 => {
365                encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, buf)
366            }
367            ExtendedColorType::Rgba8 => {
368                encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, buf)
369            }
370            ExtendedColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(
371                width,
372                height,
373                u8_slice_as_u16(buf)?,
374            ),
375            ExtendedColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(
376                width,
377                height,
378                u8_slice_as_u16(buf)?,
379            ),
380            ExtendedColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(
381                width,
382                height,
383                u8_slice_as_u16(buf)?,
384            ),
385            _ => {
386                return Err(ImageError::Unsupported(
387                    UnsupportedError::from_format_and_kind(
388                        ImageFormat::Tiff.into(),
389                        UnsupportedErrorKind::Color(color_type),
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: ExtendedColorType,
408    ) -> ImageResult<()> {
409        self.encode(buf, width, height, color_type)
410    }
411}