Skip to main content

tiff_core/
layout.rs

1use thiserror::Error;
2
3/// Errors produced while computing derived raster layout sizes.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
5pub enum LayoutError {
6    #[error("{0} overflows usize")]
7    SizeOverflow(&'static str),
8}
9
10/// Raster layout information normalized from TIFF tags.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct RasterLayout {
13    pub width: usize,
14    pub height: usize,
15    pub samples_per_pixel: usize,
16    pub bits_per_sample: u16,
17    pub bytes_per_sample: usize,
18    pub sample_format: u16,
19    pub planar_configuration: u16,
20    pub predictor: u16,
21}
22
23impl RasterLayout {
24    pub fn checked_pixel_stride_bytes(&self) -> Result<usize, LayoutError> {
25        self.samples_per_pixel
26            .checked_mul(self.bytes_per_sample)
27            .ok_or(LayoutError::SizeOverflow("pixel stride byte count"))
28    }
29
30    pub fn pixel_stride_bytes(&self) -> usize {
31        self.checked_pixel_stride_bytes().unwrap_or(usize::MAX)
32    }
33
34    pub fn checked_row_bytes_for_width(&self, width: usize) -> Result<usize, LayoutError> {
35        width
36            .checked_mul(self.checked_pixel_stride_bytes()?)
37            .ok_or(LayoutError::SizeOverflow("row byte count"))
38    }
39
40    pub fn checked_packed_row_bytes_for_width(&self, width: usize) -> Result<usize, LayoutError> {
41        width
42            .checked_mul(self.samples_per_pixel)
43            .and_then(|samples| samples.checked_mul(self.bits_per_sample as usize))
44            .map(|bits| bits.div_ceil(8))
45            .ok_or(LayoutError::SizeOverflow("packed row byte count"))
46    }
47
48    pub fn packed_row_bytes_for_width(&self, width: usize) -> usize {
49        self.checked_packed_row_bytes_for_width(width)
50            .unwrap_or(usize::MAX)
51    }
52
53    pub fn checked_row_bytes(&self) -> Result<usize, LayoutError> {
54        self.checked_row_bytes_for_width(self.width)
55    }
56
57    pub fn row_bytes(&self) -> usize {
58        self.checked_row_bytes().unwrap_or(usize::MAX)
59    }
60
61    pub fn checked_packed_row_bytes(&self) -> Result<usize, LayoutError> {
62        self.checked_packed_row_bytes_for_width(self.width)
63    }
64
65    pub fn packed_row_bytes(&self) -> usize {
66        self.checked_packed_row_bytes().unwrap_or(usize::MAX)
67    }
68
69    pub fn checked_sample_plane_row_bytes_for_width(
70        &self,
71        width: usize,
72    ) -> Result<usize, LayoutError> {
73        width
74            .checked_mul(self.bytes_per_sample)
75            .ok_or(LayoutError::SizeOverflow("sample plane row byte count"))
76    }
77
78    pub fn checked_packed_sample_plane_row_bytes_for_width(
79        &self,
80        width: usize,
81    ) -> Result<usize, LayoutError> {
82        width
83            .checked_mul(self.bits_per_sample as usize)
84            .map(|bits| bits.div_ceil(8))
85            .ok_or(LayoutError::SizeOverflow(
86                "packed sample plane row byte count",
87            ))
88    }
89
90    pub fn packed_sample_plane_row_bytes_for_width(&self, width: usize) -> usize {
91        self.checked_packed_sample_plane_row_bytes_for_width(width)
92            .unwrap_or(usize::MAX)
93    }
94
95    pub fn checked_sample_plane_row_bytes(&self) -> Result<usize, LayoutError> {
96        self.checked_sample_plane_row_bytes_for_width(self.width)
97    }
98
99    pub fn sample_plane_row_bytes(&self) -> usize {
100        self.checked_sample_plane_row_bytes().unwrap_or(usize::MAX)
101    }
102
103    pub fn checked_packed_sample_plane_row_bytes(&self) -> Result<usize, LayoutError> {
104        self.checked_packed_sample_plane_row_bytes_for_width(self.width)
105    }
106
107    pub fn packed_sample_plane_row_bytes(&self) -> usize {
108        self.checked_packed_sample_plane_row_bytes()
109            .unwrap_or(usize::MAX)
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::{LayoutError, RasterLayout};
116
117    fn layout(width: usize, samples_per_pixel: usize, bits_per_sample: u16) -> RasterLayout {
118        RasterLayout {
119            width,
120            height: 1,
121            samples_per_pixel,
122            bits_per_sample,
123            bytes_per_sample: usize::from(bits_per_sample.div_ceil(8)),
124            sample_format: 1,
125            planar_configuration: 1,
126            predictor: 1,
127        }
128    }
129
130    #[test]
131    fn checked_layout_helpers_return_expected_byte_counts() {
132        let layout = layout(4, 3, 16);
133
134        assert_eq!(layout.checked_pixel_stride_bytes().unwrap(), 6);
135        assert_eq!(layout.checked_row_bytes().unwrap(), 24);
136        assert_eq!(layout.checked_sample_plane_row_bytes().unwrap(), 8);
137        assert_eq!(layout.checked_packed_row_bytes().unwrap(), 24);
138        assert_eq!(layout.checked_packed_sample_plane_row_bytes().unwrap(), 8);
139    }
140
141    #[test]
142    fn checked_layout_helpers_reject_usize_overflow() {
143        let layout = RasterLayout {
144            width: usize::MAX,
145            height: 1,
146            samples_per_pixel: usize::MAX,
147            bits_per_sample: 16,
148            bytes_per_sample: 2,
149            sample_format: 1,
150            planar_configuration: 1,
151            predictor: 1,
152        };
153
154        assert!(matches!(
155            layout.checked_pixel_stride_bytes(),
156            Err(LayoutError::SizeOverflow(_))
157        ));
158        assert!(matches!(
159            layout.checked_row_bytes(),
160            Err(LayoutError::SizeOverflow(_))
161        ));
162        assert!(matches!(
163            layout.checked_sample_plane_row_bytes(),
164            Err(LayoutError::SizeOverflow(_))
165        ));
166        assert!(matches!(
167            layout.checked_packed_row_bytes(),
168            Err(LayoutError::SizeOverflow(_))
169        ));
170    }
171}