Skip to main content

async_tiff/
tile.rs

1use bytes::Bytes;
2
3use crate::array::Array;
4use crate::decoder::DecoderRegistry;
5use crate::error::{AsyncTiffResult, TiffError, TiffUnsupportedError};
6use crate::predictor::{fix_endianness, unpredict_float, unpredict_hdiff, PredictorInfo};
7use crate::tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor};
8use crate::DataType;
9
10/// A TIFF Tile response.
11///
12/// This contains the required information to decode the tile. Decoding is separated from fetching
13/// so that sync and async operations can be separated and non-blocking.
14///
15/// This is returned by `fetch_tile`.
16///
17/// A strip of a stripped tiff is an image-width, rows-per-strip tile.
18#[derive(Debug, Clone)]
19pub struct Tile {
20    pub(crate) x: usize,
21    pub(crate) y: usize,
22    pub(crate) data_type: Option<DataType>,
23    pub(crate) samples_per_pixel: u16,
24    pub(crate) width: u32,
25    pub(crate) height: u32,
26    pub(crate) planar_configuration: PlanarConfiguration,
27    pub(crate) predictor: Predictor,
28    pub(crate) predictor_info: PredictorInfo,
29    pub(crate) compressed_bytes: Bytes,
30    pub(crate) compression_method: CompressionMethod,
31    pub(crate) photometric_interpretation: PhotometricInterpretation,
32    pub(crate) jpeg_tables: Option<Bytes>,
33}
34
35impl Tile {
36    /// The column index of this tile.
37    pub fn x(&self) -> usize {
38        self.x
39    }
40
41    /// The row index of this tile.
42    pub fn y(&self) -> usize {
43        self.y
44    }
45
46    /// Access the compressed bytes underlying this tile.
47    ///
48    /// Note that [`Bytes`] is reference-counted, so it is very cheap to clone if needed.
49    pub fn compressed_bytes(&self) -> &Bytes {
50        &self.compressed_bytes
51    }
52
53    /// Access the compression tag representing this tile.
54    pub fn compression_method(&self) -> CompressionMethod {
55        self.compression_method
56    }
57
58    /// Access the photometric interpretation tag representing this tile.
59    pub fn photometric_interpretation(&self) -> PhotometricInterpretation {
60        self.photometric_interpretation
61    }
62
63    /// Access the JPEG Tables, if any, from the IFD producing this tile.
64    ///
65    /// Note that [`Bytes`] is reference-counted, so it is very cheap to clone if needed.
66    pub fn jpeg_tables(&self) -> Option<&Bytes> {
67        self.jpeg_tables.as_ref()
68    }
69
70    /// Decode this tile to an [`Array`].
71    ///
72    /// Decoding is separate from data fetching so that sync and async operations do not block the
73    /// same runtime.
74    pub fn decode(self, decoder_registry: &DecoderRegistry) -> AsyncTiffResult<Array> {
75        let decoder = decoder_registry
76            .as_ref()
77            .get(&self.compression_method)
78            .ok_or(TiffError::UnsupportedError(
79                TiffUnsupportedError::UnsupportedCompressionMethod(self.compression_method),
80            ))?;
81
82        let mut decoded_tile = decoder.decode_tile(
83            self.compressed_bytes.clone(),
84            self.photometric_interpretation,
85            self.jpeg_tables.as_deref(),
86        )?;
87
88        let decoded = match self.predictor {
89            Predictor::None => {
90                fix_endianness(
91                    &mut decoded_tile,
92                    self.predictor_info.endianness(),
93                    self.predictor_info.bits_per_sample(),
94                );
95                Ok(decoded_tile)
96            }
97            Predictor::Horizontal => {
98                unpredict_hdiff(decoded_tile, &self.predictor_info, self.x as _)
99            }
100            Predictor::FloatingPoint => {
101                unpredict_float(decoded_tile, &self.predictor_info, self.x as _, self.y as _)
102            }
103        }?;
104
105        let shape = infer_shape(
106            self.planar_configuration,
107            self.width as _,
108            self.height as _,
109            self.samples_per_pixel as _,
110        );
111        Array::try_new(decoded, shape, self.data_type)
112    }
113}
114
115fn infer_shape(
116    planar_configuration: PlanarConfiguration,
117    width: usize,
118    height: usize,
119    samples_per_pixel: usize,
120) -> [usize; 3] {
121    match planar_configuration {
122        PlanarConfiguration::Chunky => [height, width, samples_per_pixel],
123        PlanarConfiguration::Planar => [samples_per_pixel, height, width],
124    }
125}