1use std::io::{self, BufRead, Cursor, Read, Seek, Write};
9use std::marker::PhantomData;
10use std::mem;
11
12use tiff::decoder::{Decoder, DecodingResult};
13use tiff::tags::Tag;
14
15use crate::color::{ColorType, ExtendedColorType};
16use crate::error::{
17 DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
18 ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
19};
20use crate::metadata::Orientation;
21use crate::{utils, ImageDecoder, ImageEncoder, ImageFormat};
22
23const TAG_XML_PACKET: Tag = Tag::Unknown(700);
24
25pub struct TiffDecoder<R>
27where
28 R: BufRead + Seek,
29{
30 dimensions: (u32, u32),
31 color_type: ColorType,
32 original_color_type: ExtendedColorType,
33
34 inner: Option<Decoder<R>>,
36 buffer: DecodingResult,
37}
38
39impl<R> TiffDecoder<R>
40where
41 R: BufRead + Seek,
42{
43 pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> {
45 let mut inner = Decoder::new(r).map_err(ImageError::from_tiff_decode)?;
46
47 let dimensions = inner.dimensions().map_err(ImageError::from_tiff_decode)?;
48 let tiff_color_type = inner.colortype().map_err(ImageError::from_tiff_decode)?;
49
50 match inner.find_tag_unsigned_vec::<u16>(Tag::SampleFormat) {
51 Ok(Some(sample_formats)) => {
52 for format in sample_formats {
53 check_sample_format(format, tiff_color_type)?;
54 }
55 }
56 Ok(None) => { }
57 Err(other) => return Err(ImageError::from_tiff_decode(other)),
58 }
59
60 let color_type = match tiff_color_type {
61 tiff::ColorType::Gray(1) => ColorType::L8,
62 tiff::ColorType::Gray(8) => ColorType::L8,
63 tiff::ColorType::Gray(16) => ColorType::L16,
64 tiff::ColorType::GrayA(8) => ColorType::La8,
65 tiff::ColorType::GrayA(16) => ColorType::La16,
66 tiff::ColorType::RGB(8) => ColorType::Rgb8,
67 tiff::ColorType::RGB(16) => ColorType::Rgb16,
68 tiff::ColorType::RGBA(8) => ColorType::Rgba8,
69 tiff::ColorType::RGBA(16) => ColorType::Rgba16,
70 tiff::ColorType::CMYK(8) => ColorType::Rgb8,
71 tiff::ColorType::CMYK(16) => ColorType::Rgb16,
72 tiff::ColorType::RGB(32) => ColorType::Rgb32F,
73 tiff::ColorType::RGBA(32) => ColorType::Rgba32F,
74
75 tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => {
76 return Err(err_unknown_color_type(n))
77 }
78 tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n.saturating_mul(2))),
79 tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n.saturating_mul(3))),
80 tiff::ColorType::YCbCr(n) => return Err(err_unknown_color_type(n.saturating_mul(3))),
81 tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => {
82 return Err(err_unknown_color_type(n.saturating_mul(4)))
83 }
84 tiff::ColorType::Multiband {
85 bit_depth,
86 num_samples,
87 } => {
88 return Err(err_unknown_color_type(
89 bit_depth.saturating_mul(num_samples.min(255) as u8),
90 ))
91 }
92 _ => return Err(err_unknown_color_type(0)),
93 };
94
95 let original_color_type = match tiff_color_type {
96 tiff::ColorType::Gray(1) => ExtendedColorType::L1,
97 tiff::ColorType::CMYK(8) => ExtendedColorType::Cmyk8,
98 tiff::ColorType::CMYK(16) => ExtendedColorType::Cmyk16,
99 _ => color_type.into(),
100 };
101
102 Ok(TiffDecoder {
103 dimensions,
104 color_type,
105 original_color_type,
106 inner: Some(inner),
107 buffer: DecodingResult::U8(vec![]),
108 })
109 }
110
111 fn total_bytes_buffer(&self) -> u64 {
113 let dimensions = self.dimensions();
114 let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
115
116 let bytes_per_pixel = match self.original_color_type {
117 ExtendedColorType::Cmyk8 => 4,
118 ExtendedColorType::Cmyk16 => 8,
119 _ => u64::from(self.color_type().bytes_per_pixel()),
120 };
121 total_pixels.saturating_mul(bytes_per_pixel)
122 }
123
124 fn interleave_planes(
126 &mut self,
127 layout: tiff::decoder::BufferLayoutPreference,
128 output: &mut [u8],
129 ) -> ImageResult<()> {
130 if self.original_color_type != self.color_type.into() {
131 return Err(ImageError::Unsupported(
132 UnsupportedError::from_format_and_kind(
133 ImageFormat::Tiff.into(),
134 UnsupportedErrorKind::GenericFeature(
135 "Planar TIFF with CMYK color type is not supported".to_string(),
136 ),
137 ),
138 ));
139 }
140
141 let plane_stride = layout.plane_stride.map_or(0, |n| n.get());
159 let bytes = self.buffer.as_buffer(0);
160
161 let planes = bytes
162 .as_bytes()
163 .chunks_exact(plane_stride)
164 .collect::<Vec<_>>();
165
166 if planes.len() < usize::from(self.color_type.channel_count()) {
169 return Err(ImageError::Decoding(DecodingError::new(
170 ImageFormat::Tiff.into(),
171 "Not enough planes read from TIFF image".to_string(),
172 )));
173 }
174
175 utils::interleave_planes(
176 output,
177 self.color_type,
178 &planes[..usize::from(self.color_type.channel_count())],
179 );
180
181 Ok(())
182 }
183}
184
185fn check_sample_format(sample_format: u16, color_type: tiff::ColorType) -> Result<(), ImageError> {
186 use tiff::{tags::SampleFormat, ColorType};
187 let num_bits = match color_type {
188 ColorType::CMYK(k) => k,
189 ColorType::Gray(k) => k,
190 ColorType::RGB(k) => k,
191 ColorType::RGBA(k) => k,
192 ColorType::GrayA(k) => k,
193 ColorType::Palette(k) | ColorType::YCbCr(k) => {
194 return Err(ImageError::Unsupported(
195 UnsupportedError::from_format_and_kind(
196 ImageFormat::Tiff.into(),
197 UnsupportedErrorKind::GenericFeature(format!(
198 "Unhandled TIFF color type {color_type:?} for {k} bits",
199 )),
200 ),
201 ))
202 }
203 _ => {
204 return Err(ImageError::Unsupported(
205 UnsupportedError::from_format_and_kind(
206 ImageFormat::Tiff.into(),
207 UnsupportedErrorKind::GenericFeature(format!(
208 "Unhandled TIFF color type {color_type:?}",
209 )),
210 ),
211 ))
212 }
213 };
214
215 match SampleFormat::from_u16(sample_format) {
216 Some(SampleFormat::Uint) if num_bits <= 16 => Ok(()),
217 Some(SampleFormat::IEEEFP) if num_bits == 32 => Ok(()),
218 _ => Err(ImageError::Unsupported(
219 UnsupportedError::from_format_and_kind(
220 ImageFormat::Tiff.into(),
221 UnsupportedErrorKind::GenericFeature(format!(
222 "Unhandled TIFF sample format {sample_format:?} for {num_bits} bits",
223 )),
224 ),
225 )),
226 }
227}
228
229fn err_unknown_color_type(value: u8) -> ImageError {
230 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
231 ImageFormat::Tiff.into(),
232 UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
233 ))
234}
235
236impl ImageError {
237 fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
238 match err {
239 tiff::TiffError::IoError(err) => ImageError::IoError(err),
240 err @ (tiff::TiffError::FormatError(_)
241 | tiff::TiffError::IntSizeError
242 | tiff::TiffError::UsageError(_)) => {
243 ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
244 }
245 tiff::TiffError::UnsupportedError(desc) => {
246 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
247 ImageFormat::Tiff.into(),
248 UnsupportedErrorKind::GenericFeature(desc.to_string()),
249 ))
250 }
251 tiff::TiffError::LimitsExceeded => {
252 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
253 }
254 }
255 }
256
257 fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
258 match err {
259 tiff::TiffError::IoError(err) => ImageError::IoError(err),
260 err @ (tiff::TiffError::FormatError(_)
261 | tiff::TiffError::IntSizeError
262 | tiff::TiffError::UsageError(_)) => {
263 ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
264 }
265 tiff::TiffError::UnsupportedError(desc) => {
266 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
267 ImageFormat::Tiff.into(),
268 UnsupportedErrorKind::GenericFeature(desc.to_string()),
269 ))
270 }
271 tiff::TiffError::LimitsExceeded => {
272 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
273 }
274 }
275 }
276}
277
278#[allow(dead_code)]
280#[deprecated]
281pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
282#[allow(deprecated)]
283impl<R> Read for TiffReader<R> {
284 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
285 self.0.read(buf)
286 }
287
288 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
289 if self.0.position() == 0 && buf.is_empty() {
290 mem::swap(buf, self.0.get_mut());
291 Ok(buf.len())
292 } else {
293 self.0.read_to_end(buf)
294 }
295 }
296}
297
298impl<R: BufRead + Seek> ImageDecoder for TiffDecoder<R> {
299 fn dimensions(&self) -> (u32, u32) {
300 self.dimensions
301 }
302
303 fn color_type(&self) -> ColorType {
304 self.color_type
305 }
306
307 fn original_color_type(&self) -> ExtendedColorType {
308 self.original_color_type
309 }
310
311 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
312 if let Some(decoder) = &mut self.inner {
313 Ok(decoder.get_tag_u8_vec(Tag::IccProfile).ok())
314 } else {
315 Ok(None)
316 }
317 }
318
319 fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
320 let Some(decoder) = &mut self.inner else {
321 return Ok(None);
322 };
323
324 let value = match decoder.get_tag(TAG_XML_PACKET) {
325 Ok(value) => value,
326 Err(tiff::TiffError::FormatError(tiff::TiffFormatError::RequiredTagNotFound(_))) => {
327 return Ok(None);
328 }
329 Err(err) => return Err(ImageError::from_tiff_decode(err)),
330 };
331 value
332 .into_u8_vec()
333 .map(Some)
334 .map_err(ImageError::from_tiff_decode)
335 }
336
337 fn orientation(&mut self) -> ImageResult<Orientation> {
338 if let Some(decoder) = &mut self.inner {
339 Ok(decoder
340 .find_tag(Tag::Orientation)
341 .map_err(ImageError::from_tiff_decode)?
342 .and_then(|v| Orientation::from_exif(v.into_u16().ok()?.min(255) as u8))
343 .unwrap_or(Orientation::NoTransforms))
344 } else {
345 Ok(Orientation::NoTransforms)
346 }
347 }
348
349 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
350 limits.check_support(&crate::LimitSupport::default())?;
351
352 let (width, height) = self.dimensions();
353 limits.check_dimensions(width, height)?;
354
355 let max_alloc = limits.max_alloc.unwrap_or(u64::MAX);
356 let max_intermediate_alloc = max_alloc.saturating_sub(self.total_bytes_buffer());
357
358 let mut tiff_limits: tiff::decoder::Limits = Default::default();
359 tiff_limits.decoding_buffer_size =
360 usize::try_from(max_alloc - max_intermediate_alloc).unwrap_or(usize::MAX);
361 tiff_limits.intermediate_buffer_size =
362 usize::try_from(max_intermediate_alloc).unwrap_or(usize::MAX);
363 tiff_limits.ifd_value_size = tiff_limits.intermediate_buffer_size;
364 self.inner = Some(self.inner.take().unwrap().with_limits(tiff_limits));
365
366 Ok(())
367 }
368
369 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
370 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
371
372 let layout = self
373 .inner
374 .as_mut()
375 .unwrap()
376 .read_image_to_buffer(&mut self.buffer)
377 .map_err(ImageError::from_tiff_decode)?;
378
379 if self.buffer.as_buffer(0).as_bytes().len() < layout.complete_len {
381 return Err(ImageError::Limits(LimitError::from_kind(
382 LimitErrorKind::InsufficientMemory,
383 )));
384 }
385
386 if layout.planes > 1 {
387 return self.interleave_planes(layout, buf);
390 }
391
392 match self.buffer {
393 DecodingResult::U8(v) if self.original_color_type == ExtendedColorType::Cmyk8 => {
394 let mut out_cur = Cursor::new(buf);
395 for cmyk in v.as_chunks::<4>().0 {
396 out_cur.write_all(&cmyk_to_rgb(cmyk))?;
397 }
398 }
399 DecodingResult::U16(v) if self.original_color_type == ExtendedColorType::Cmyk16 => {
400 let mut out_cur = Cursor::new(buf);
401 for cmyk in v.as_chunks::<4>().0 {
402 out_cur.write_all(bytemuck::cast_slice(&cmyk_to_rgb16(cmyk)))?;
403 }
404 }
405 DecodingResult::U8(v) if self.original_color_type == ExtendedColorType::L1 => {
406 let width = self.dimensions.0;
407 let row_bytes = width.div_ceil(8);
408
409 for (in_row, out_row) in v
410 .chunks_exact(row_bytes as usize)
411 .zip(buf.chunks_exact_mut(width as usize))
412 {
413 out_row.copy_from_slice(&utils::expand_bits(1, width, in_row));
414 }
415 }
416 DecodingResult::U8(v) => {
417 buf.copy_from_slice(&v);
418 }
419 DecodingResult::U16(v) => {
420 buf.copy_from_slice(bytemuck::cast_slice(&v));
421 }
422 DecodingResult::U32(v) => {
423 buf.copy_from_slice(bytemuck::cast_slice(&v));
424 }
425 DecodingResult::U64(v) => {
426 buf.copy_from_slice(bytemuck::cast_slice(&v));
427 }
428 DecodingResult::I8(v) => {
429 buf.copy_from_slice(bytemuck::cast_slice(&v));
430 }
431 DecodingResult::I16(v) => {
432 buf.copy_from_slice(bytemuck::cast_slice(&v));
433 }
434 DecodingResult::I32(v) => {
435 buf.copy_from_slice(bytemuck::cast_slice(&v));
436 }
437 DecodingResult::I64(v) => {
438 buf.copy_from_slice(bytemuck::cast_slice(&v));
439 }
440 DecodingResult::F32(v) => {
441 buf.copy_from_slice(bytemuck::cast_slice(&v));
442 }
443 DecodingResult::F64(v) => {
444 buf.copy_from_slice(bytemuck::cast_slice(&v));
445 }
446 DecodingResult::F16(_) => unreachable!(),
447 }
448
449 Ok(())
450 }
451
452 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
453 (*self).read_image(buf)
454 }
455}
456
457pub struct TiffEncoder<W> {
459 w: W,
460 icc: Option<Vec<u8>>,
461}
462
463fn cmyk_to_rgb(cmyk: &[u8; 4]) -> [u8; 3] {
464 let c = f32::from(cmyk[0]);
465 let m = f32::from(cmyk[1]);
466 let y = f32::from(cmyk[2]);
467 let kf = 1. - f32::from(cmyk[3]) / 255.;
468 [
469 ((255. - c) * kf) as u8,
470 ((255. - m) * kf) as u8,
471 ((255. - y) * kf) as u8,
472 ]
473}
474
475fn cmyk_to_rgb16(cmyk: &[u16; 4]) -> [u16; 3] {
476 let c = f32::from(cmyk[0]);
477 let m = f32::from(cmyk[1]);
478 let y = f32::from(cmyk[2]);
479 let kf = 1. - f32::from(cmyk[3]) / 65535.;
480 [
481 ((65535. - c) * kf) as u16,
482 ((65535. - m) * kf) as u16,
483 ((65535. - y) * kf) as u16,
484 ]
485}
486
487fn u8_slice_as_pod<P: bytemuck::Pod>(buf: &[u8]) -> ImageResult<std::borrow::Cow<'_, [P]>> {
489 bytemuck::try_cast_slice(buf)
490 .map(std::borrow::Cow::Borrowed)
491 .or_else(|err| {
492 match err {
493 bytemuck::PodCastError::TargetAlignmentGreaterAndInputNotAligned => {
494 let vec = bytemuck::allocation::pod_collect_to_vec(buf);
498 Ok(std::borrow::Cow::Owned(vec))
499 }
500 _ => {
502 Err(ImageError::Parameter(ParameterError::from_kind(
506 ParameterErrorKind::Generic(format!(
507 "Casting samples to their representation failed: {err:?}",
508 )),
509 )))
510 }
511 }
512 })
513}
514
515impl<W: Write + Seek> TiffEncoder<W> {
516 pub fn new(w: W) -> TiffEncoder<W> {
518 TiffEncoder { w, icc: None }
519 }
520
521 fn write_tiff<C: tiff::encoder::colortype::ColorType<Inner: bytemuck::Pod>>(
523 self,
524 width: u32,
525 height: u32,
526 data: &[u8],
527 ) -> ImageResult<()>
528 where
529 [C::Inner]: tiff::encoder::TiffValue,
530 {
531 let mut encoder =
532 tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
533 let data = u8_slice_as_pod::<C::Inner>(data)?;
534 let mut img_encoder = encoder
535 .new_image::<C>(width, height)
536 .map_err(ImageError::from_tiff_encode)?;
537 if let Some(icc_profile) = self.icc {
538 let ifd_encoder = img_encoder.encoder(); ifd_encoder
545 .write_tag(Tag::IccProfile, icc_profile.as_slice())
546 .map_err(ImageError::from_tiff_encode)?;
547 }
548 img_encoder
549 .write_data(&data)
550 .map_err(ImageError::from_tiff_encode)
551 }
552
553 #[track_caller]
555 #[deprecated = "Use the `write_image` method from the `ImageEncoder` trait directly."]
556 pub fn encode(
557 self,
558 buf: &[u8],
559 width: u32,
560 height: u32,
561 color_type: ExtendedColorType,
562 ) -> ImageResult<()> {
563 self.write_image(buf, width, height, color_type)
565 }
566}
567
568impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
569 #[track_caller]
577 fn write_image(
578 self,
579 buf: &[u8],
580 width: u32,
581 height: u32,
582 color_type: ExtendedColorType,
583 ) -> ImageResult<()> {
584 use tiff::encoder::colortype::{
585 Gray16, Gray8, RGB32Float, RGBA32Float, RGB16, RGB8, RGBA16, RGBA8,
586 };
587 let expected_buffer_len = color_type.buffer_size(width, height);
588 assert_eq!(
589 expected_buffer_len,
590 buf.len() as u64,
591 "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
592 buf.len(),
593 );
594 match color_type {
595 ExtendedColorType::L8 => self.write_tiff::<Gray8>(width, height, buf),
596 ExtendedColorType::Rgb8 => self.write_tiff::<RGB8>(width, height, buf),
597 ExtendedColorType::Rgba8 => self.write_tiff::<RGBA8>(width, height, buf),
598 ExtendedColorType::L16 => self.write_tiff::<Gray16>(width, height, buf),
599 ExtendedColorType::Rgb16 => self.write_tiff::<RGB16>(width, height, buf),
600 ExtendedColorType::Rgba16 => self.write_tiff::<RGBA16>(width, height, buf),
601 ExtendedColorType::Rgb32F => self.write_tiff::<RGB32Float>(width, height, buf),
602 ExtendedColorType::Rgba32F => self.write_tiff::<RGBA32Float>(width, height, buf),
603 _ => Err(ImageError::Unsupported(
604 UnsupportedError::from_format_and_kind(
605 ImageFormat::Tiff.into(),
606 UnsupportedErrorKind::Color(color_type),
607 ),
608 )),
609 }
610 }
611
612 fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
613 self.icc = Some(icc_profile);
614 Ok(())
615 }
616}