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