1use thiserror::Error;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
5pub enum LayoutError {
6 #[error("{0} overflows usize")]
7 SizeOverflow(&'static str),
8}
9
10#[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}