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}
37
38impl<R> TiffDecoder<R>
39where
40 R: BufRead + Seek,
41{
42 pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> {
44 let mut inner = Decoder::new(r).map_err(ImageError::from_tiff_decode)?;
45
46 let dimensions = inner.dimensions().map_err(ImageError::from_tiff_decode)?;
47 let tiff_color_type = inner.colortype().map_err(ImageError::from_tiff_decode)?;
48
49 match inner.find_tag_unsigned_vec::<u16>(Tag::SampleFormat) {
50 Ok(Some(sample_formats)) => {
51 for format in sample_formats {
52 check_sample_format(format, tiff_color_type)?;
53 }
54 }
55 Ok(None) => { }
56 Err(other) => return Err(ImageError::from_tiff_decode(other)),
57 }
58
59 let planar_config = inner
60 .find_tag(Tag::PlanarConfiguration)
61 .map(|res| res.and_then(|r| r.into_u16().ok()).unwrap_or_default())
62 .unwrap_or_default();
63
64 if planar_config > 1 {
66 Err(ImageError::Unsupported(
67 UnsupportedError::from_format_and_kind(
68 ImageFormat::Tiff.into(),
69 UnsupportedErrorKind::GenericFeature(String::from("PlanarConfiguration = 2")),
70 ),
71 ))?;
72 }
73
74 let color_type = match tiff_color_type {
75 tiff::ColorType::Gray(1) => ColorType::L8,
76 tiff::ColorType::Gray(8) => ColorType::L8,
77 tiff::ColorType::Gray(16) => ColorType::L16,
78 tiff::ColorType::GrayA(8) => ColorType::La8,
79 tiff::ColorType::GrayA(16) => ColorType::La16,
80 tiff::ColorType::RGB(8) => ColorType::Rgb8,
81 tiff::ColorType::RGB(16) => ColorType::Rgb16,
82 tiff::ColorType::RGBA(8) => ColorType::Rgba8,
83 tiff::ColorType::RGBA(16) => ColorType::Rgba16,
84 tiff::ColorType::CMYK(8) => ColorType::Rgb8,
85 tiff::ColorType::CMYK(16) => ColorType::Rgb16,
86 tiff::ColorType::RGB(32) => ColorType::Rgb32F,
87 tiff::ColorType::RGBA(32) => ColorType::Rgba32F,
88
89 tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => {
90 return Err(err_unknown_color_type(n))
91 }
92 tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n.saturating_mul(2))),
93 tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n.saturating_mul(3))),
94 tiff::ColorType::YCbCr(n) => return Err(err_unknown_color_type(n.saturating_mul(3))),
95 tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => {
96 return Err(err_unknown_color_type(n.saturating_mul(4)))
97 }
98 tiff::ColorType::Multiband {
99 bit_depth,
100 num_samples,
101 } => {
102 return Err(err_unknown_color_type(
103 bit_depth.saturating_mul(num_samples.min(255) as u8),
104 ))
105 }
106 _ => return Err(err_unknown_color_type(0)),
107 };
108
109 let original_color_type = match tiff_color_type {
110 tiff::ColorType::Gray(1) => ExtendedColorType::L1,
111 tiff::ColorType::CMYK(8) => ExtendedColorType::Cmyk8,
112 tiff::ColorType::CMYK(16) => ExtendedColorType::Cmyk16,
113 _ => color_type.into(),
114 };
115
116 Ok(TiffDecoder {
117 dimensions,
118 color_type,
119 original_color_type,
120 inner: Some(inner),
121 })
122 }
123
124 fn total_bytes_buffer(&self) -> u64 {
126 let dimensions = self.dimensions();
127 let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
128
129 let bytes_per_pixel = match self.original_color_type {
130 ExtendedColorType::Cmyk8 => 4,
131 ExtendedColorType::Cmyk16 => 8,
132 _ => u64::from(self.color_type().bytes_per_pixel()),
133 };
134 total_pixels.saturating_mul(bytes_per_pixel)
135 }
136}
137
138fn check_sample_format(sample_format: u16, color_type: tiff::ColorType) -> Result<(), ImageError> {
139 use tiff::{tags::SampleFormat, ColorType};
140 let num_bits = match color_type {
141 ColorType::CMYK(k) => k,
142 ColorType::Gray(k) => k,
143 ColorType::RGB(k) => k,
144 ColorType::RGBA(k) => k,
145 ColorType::GrayA(k) => k,
146 ColorType::Palette(k) | ColorType::YCbCr(k) => {
147 return Err(ImageError::Unsupported(
148 UnsupportedError::from_format_and_kind(
149 ImageFormat::Tiff.into(),
150 UnsupportedErrorKind::GenericFeature(format!(
151 "Unhandled TIFF color type {color_type:?} for {k} bits",
152 )),
153 ),
154 ))
155 }
156 _ => {
157 return Err(ImageError::Unsupported(
158 UnsupportedError::from_format_and_kind(
159 ImageFormat::Tiff.into(),
160 UnsupportedErrorKind::GenericFeature(format!(
161 "Unhandled TIFF color type {color_type:?}",
162 )),
163 ),
164 ))
165 }
166 };
167
168 match SampleFormat::from_u16(sample_format) {
169 Some(SampleFormat::Uint) if num_bits <= 16 => Ok(()),
170 Some(SampleFormat::IEEEFP) if num_bits == 32 => Ok(()),
171 _ => Err(ImageError::Unsupported(
172 UnsupportedError::from_format_and_kind(
173 ImageFormat::Tiff.into(),
174 UnsupportedErrorKind::GenericFeature(format!(
175 "Unhandled TIFF sample format {sample_format:?} for {num_bits} bits",
176 )),
177 ),
178 )),
179 }
180}
181
182fn err_unknown_color_type(value: u8) -> ImageError {
183 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
184 ImageFormat::Tiff.into(),
185 UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
186 ))
187}
188
189impl ImageError {
190 fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
191 match err {
192 tiff::TiffError::IoError(err) => ImageError::IoError(err),
193 err @ (tiff::TiffError::FormatError(_)
194 | tiff::TiffError::IntSizeError
195 | tiff::TiffError::UsageError(_)) => {
196 ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
197 }
198 tiff::TiffError::UnsupportedError(desc) => {
199 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
200 ImageFormat::Tiff.into(),
201 UnsupportedErrorKind::GenericFeature(desc.to_string()),
202 ))
203 }
204 tiff::TiffError::LimitsExceeded => {
205 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
206 }
207 }
208 }
209
210 fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
211 match err {
212 tiff::TiffError::IoError(err) => ImageError::IoError(err),
213 err @ (tiff::TiffError::FormatError(_)
214 | tiff::TiffError::IntSizeError
215 | tiff::TiffError::UsageError(_)) => {
216 ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
217 }
218 tiff::TiffError::UnsupportedError(desc) => {
219 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
220 ImageFormat::Tiff.into(),
221 UnsupportedErrorKind::GenericFeature(desc.to_string()),
222 ))
223 }
224 tiff::TiffError::LimitsExceeded => {
225 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
226 }
227 }
228 }
229}
230
231#[allow(dead_code)]
233#[deprecated]
234pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
235#[allow(deprecated)]
236impl<R> Read for TiffReader<R> {
237 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
238 self.0.read(buf)
239 }
240
241 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
242 if self.0.position() == 0 && buf.is_empty() {
243 mem::swap(buf, self.0.get_mut());
244 Ok(buf.len())
245 } else {
246 self.0.read_to_end(buf)
247 }
248 }
249}
250
251impl<R: BufRead + Seek> ImageDecoder for TiffDecoder<R> {
252 fn dimensions(&self) -> (u32, u32) {
253 self.dimensions
254 }
255
256 fn color_type(&self) -> ColorType {
257 self.color_type
258 }
259
260 fn original_color_type(&self) -> ExtendedColorType {
261 self.original_color_type
262 }
263
264 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
265 if let Some(decoder) = &mut self.inner {
266 Ok(decoder.get_tag_u8_vec(Tag::Unknown(34675)).ok())
267 } else {
268 Ok(None)
269 }
270 }
271
272 fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
273 let Some(decoder) = &mut self.inner else {
274 return Ok(None);
275 };
276
277 let value = match decoder.get_tag(TAG_XML_PACKET) {
278 Ok(value) => value,
279 Err(tiff::TiffError::FormatError(tiff::TiffFormatError::RequiredTagNotFound(_))) => {
280 return Ok(None);
281 }
282 Err(err) => return Err(ImageError::from_tiff_decode(err)),
283 };
284 value
285 .into_u8_vec()
286 .map(Some)
287 .map_err(ImageError::from_tiff_decode)
288 }
289
290 fn orientation(&mut self) -> ImageResult<Orientation> {
291 if let Some(decoder) = &mut self.inner {
292 Ok(decoder
293 .find_tag(Tag::Orientation)
294 .map_err(ImageError::from_tiff_decode)?
295 .and_then(|v| Orientation::from_exif(v.into_u16().ok()?.min(255) as u8))
296 .unwrap_or(Orientation::NoTransforms))
297 } else {
298 Ok(Orientation::NoTransforms)
299 }
300 }
301
302 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
303 limits.check_support(&crate::LimitSupport::default())?;
304
305 let (width, height) = self.dimensions();
306 limits.check_dimensions(width, height)?;
307
308 let max_alloc = limits.max_alloc.unwrap_or(u64::MAX);
309 let max_intermediate_alloc = max_alloc.saturating_sub(self.total_bytes_buffer());
310
311 let mut tiff_limits: tiff::decoder::Limits = Default::default();
312 tiff_limits.decoding_buffer_size =
313 usize::try_from(max_alloc - max_intermediate_alloc).unwrap_or(usize::MAX);
314 tiff_limits.intermediate_buffer_size =
315 usize::try_from(max_intermediate_alloc).unwrap_or(usize::MAX);
316 tiff_limits.ifd_value_size = tiff_limits.intermediate_buffer_size;
317 self.inner = Some(self.inner.take().unwrap().with_limits(tiff_limits));
318
319 Ok(())
320 }
321
322 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
323 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
324
325 match self
326 .inner
327 .unwrap()
328 .read_image()
329 .map_err(ImageError::from_tiff_decode)?
330 {
331 DecodingResult::U8(v) if self.original_color_type == ExtendedColorType::Cmyk8 => {
332 let mut out_cur = Cursor::new(buf);
333 for cmyk in v.chunks_exact(4) {
334 out_cur.write_all(&cmyk_to_rgb(cmyk))?;
335 }
336 }
337 DecodingResult::U16(v) if self.original_color_type == ExtendedColorType::Cmyk16 => {
338 let mut out_cur = Cursor::new(buf);
339 for cmyk in v.chunks_exact(4) {
340 out_cur.write_all(bytemuck::cast_slice(&cmyk_to_rgb16(cmyk)))?;
341 }
342 }
343 DecodingResult::U8(v) if self.original_color_type == ExtendedColorType::L1 => {
344 let width = self.dimensions.0;
345 let row_bytes = width.div_ceil(8);
346
347 for (in_row, out_row) in v
348 .chunks_exact(row_bytes as usize)
349 .zip(buf.chunks_exact_mut(width as usize))
350 {
351 out_row.copy_from_slice(&utils::expand_bits(1, width, in_row));
352 }
353 }
354 DecodingResult::U8(v) => {
355 buf.copy_from_slice(&v);
356 }
357 DecodingResult::U16(v) => {
358 buf.copy_from_slice(bytemuck::cast_slice(&v));
359 }
360 DecodingResult::U32(v) => {
361 buf.copy_from_slice(bytemuck::cast_slice(&v));
362 }
363 DecodingResult::U64(v) => {
364 buf.copy_from_slice(bytemuck::cast_slice(&v));
365 }
366 DecodingResult::I8(v) => {
367 buf.copy_from_slice(bytemuck::cast_slice(&v));
368 }
369 DecodingResult::I16(v) => {
370 buf.copy_from_slice(bytemuck::cast_slice(&v));
371 }
372 DecodingResult::I32(v) => {
373 buf.copy_from_slice(bytemuck::cast_slice(&v));
374 }
375 DecodingResult::I64(v) => {
376 buf.copy_from_slice(bytemuck::cast_slice(&v));
377 }
378 DecodingResult::F32(v) => {
379 buf.copy_from_slice(bytemuck::cast_slice(&v));
380 }
381 DecodingResult::F64(v) => {
382 buf.copy_from_slice(bytemuck::cast_slice(&v));
383 }
384 DecodingResult::F16(_) => unreachable!(),
385 }
386 Ok(())
387 }
388
389 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
390 (*self).read_image(buf)
391 }
392}
393
394pub struct TiffEncoder<W> {
396 w: W,
397}
398
399fn cmyk_to_rgb(cmyk: &[u8]) -> [u8; 3] {
400 let c = f32::from(cmyk[0]);
401 let m = f32::from(cmyk[1]);
402 let y = f32::from(cmyk[2]);
403 let kf = 1. - f32::from(cmyk[3]) / 255.;
404 [
405 ((255. - c) * kf) as u8,
406 ((255. - m) * kf) as u8,
407 ((255. - y) * kf) as u8,
408 ]
409}
410
411fn cmyk_to_rgb16(cmyk: &[u16]) -> [u16; 3] {
412 let c = f32::from(cmyk[0]);
413 let m = f32::from(cmyk[1]);
414 let y = f32::from(cmyk[2]);
415 let kf = 1. - f32::from(cmyk[3]) / 65535.;
416 [
417 ((65535. - c) * kf) as u16,
418 ((65535. - m) * kf) as u16,
419 ((65535. - y) * kf) as u16,
420 ]
421}
422
423fn u8_slice_as_pod<P: bytemuck::Pod>(buf: &[u8]) -> ImageResult<std::borrow::Cow<'_, [P]>> {
425 bytemuck::try_cast_slice(buf)
426 .map(std::borrow::Cow::Borrowed)
427 .or_else(|err| {
428 match err {
429 bytemuck::PodCastError::TargetAlignmentGreaterAndInputNotAligned => {
430 let vec = bytemuck::allocation::pod_collect_to_vec(buf);
434 Ok(std::borrow::Cow::Owned(vec))
435 }
436 _ => {
438 Err(ImageError::Parameter(ParameterError::from_kind(
442 ParameterErrorKind::Generic(format!(
443 "Casting samples to their representation failed: {err:?}",
444 )),
445 )))
446 }
447 }
448 })
449}
450
451impl<W: Write + Seek> TiffEncoder<W> {
452 pub fn new(w: W) -> TiffEncoder<W> {
454 TiffEncoder { w }
455 }
456
457 #[track_caller]
465 pub fn encode(
466 self,
467 buf: &[u8],
468 width: u32,
469 height: u32,
470 color_type: ExtendedColorType,
471 ) -> ImageResult<()> {
472 use tiff::encoder::colortype::{
473 Gray16, Gray8, RGB32Float, RGBA32Float, RGB16, RGB8, RGBA16, RGBA8,
474 };
475 let expected_buffer_len = color_type.buffer_size(width, height);
476 assert_eq!(
477 expected_buffer_len,
478 buf.len() as u64,
479 "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
480 buf.len(),
481 );
482 let mut encoder =
483 tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
484 match color_type {
485 ExtendedColorType::L8 => encoder.write_image::<Gray8>(width, height, buf),
486 ExtendedColorType::Rgb8 => encoder.write_image::<RGB8>(width, height, buf),
487 ExtendedColorType::Rgba8 => encoder.write_image::<RGBA8>(width, height, buf),
488 ExtendedColorType::L16 => {
489 encoder.write_image::<Gray16>(width, height, u8_slice_as_pod::<u16>(buf)?.as_ref())
490 }
491 ExtendedColorType::Rgb16 => {
492 encoder.write_image::<RGB16>(width, height, u8_slice_as_pod::<u16>(buf)?.as_ref())
493 }
494 ExtendedColorType::Rgba16 => {
495 encoder.write_image::<RGBA16>(width, height, u8_slice_as_pod::<u16>(buf)?.as_ref())
496 }
497 ExtendedColorType::Rgb32F => encoder.write_image::<RGB32Float>(
498 width,
499 height,
500 u8_slice_as_pod::<f32>(buf)?.as_ref(),
501 ),
502 ExtendedColorType::Rgba32F => encoder.write_image::<RGBA32Float>(
503 width,
504 height,
505 u8_slice_as_pod::<f32>(buf)?.as_ref(),
506 ),
507 _ => {
508 return Err(ImageError::Unsupported(
509 UnsupportedError::from_format_and_kind(
510 ImageFormat::Tiff.into(),
511 UnsupportedErrorKind::Color(color_type),
512 ),
513 ))
514 }
515 }
516 .map_err(ImageError::from_tiff_encode)?;
517
518 Ok(())
519 }
520}
521
522impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
523 #[track_caller]
524 fn write_image(
525 self,
526 buf: &[u8],
527 width: u32,
528 height: u32,
529 color_type: ExtendedColorType,
530 ) -> ImageResult<()> {
531 self.encode(buf, width, height, color_type)
532 }
533}