Skip to main content

async_tiff/
data_type.rs

1use crate::tags::SampleFormat;
2
3/// Supported numeric data types for array elements.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum DataType {
6    /// Boolean mask data.
7    Bool,
8    /// Unsigned 8-bit integer.
9    UInt8,
10    /// Unsigned 16-bit integer.
11    UInt16,
12    /// Unsigned 32-bit integer.
13    UInt32,
14    /// Unsigned 64-bit integer.
15    UInt64,
16    /// Signed 8-bit integer.
17    Int8,
18    /// Signed 16-bit integer.
19    Int16,
20    /// Signed 32-bit integer.
21    Int32,
22    /// Signed 64-bit integer.
23    Int64,
24    /// 32-bit floating point.
25    Float32,
26    /// 64-bit floating point.
27    Float64,
28}
29
30impl DataType {
31    /// The size in bytes of this data type.
32    ///
33    /// ```
34    /// use async_tiff::DataType;
35    ///
36    /// assert_eq!(DataType::Bool.size(), 1);
37    /// assert_eq!(DataType::UInt8.size(), 1);
38    /// assert_eq!(DataType::Int16.size(), 2);
39    /// assert_eq!(DataType::Float32.size(), 4);
40    /// assert_eq!(DataType::Float64.size(), 8);
41    /// ```
42    pub fn size(&self) -> usize {
43        match self {
44            DataType::Bool | DataType::UInt8 | DataType::Int8 => 1,
45            DataType::UInt16 | DataType::Int16 => 2,
46            DataType::UInt32 | DataType::Int32 | DataType::Float32 => 4,
47            DataType::UInt64 | DataType::Int64 | DataType::Float64 => 8,
48        }
49    }
50
51    /// Parse a DataType from TIFF IFD tags.
52    ///
53    /// Returns `None` if the combination of sample format and bits per sample
54    /// is not supported, or if the values are inconsistent across samples.
55    ///
56    /// # Arguments
57    /// * `sample_format` - The SampleFormat values from the TIFF IFD
58    /// * `bits_per_sample` - The BitsPerSample values from the TIFF IFD
59    pub(crate) fn from_tags(
60        sample_format: &[SampleFormat],
61        bits_per_sample: &[u16],
62    ) -> Option<Self> {
63        // All samples must have the same format and bit depth
64        let first_format = sample_format.first()?;
65        let first_bits = bits_per_sample.first()?;
66
67        // Check that all samples have consistent format and bit depth
68        if !sample_format.iter().all(|f| f == first_format) {
69            return None;
70        }
71        if !bits_per_sample.iter().all(|b| b == first_bits) {
72            return None;
73        }
74
75        match (first_format, first_bits) {
76            (SampleFormat::Uint, 1) => Some(DataType::Bool),
77            (SampleFormat::Uint, 8) => Some(DataType::UInt8),
78            (SampleFormat::Uint, 16) => Some(DataType::UInt16),
79            (SampleFormat::Uint, 32) => Some(DataType::UInt32),
80            (SampleFormat::Uint, 64) => Some(DataType::UInt64),
81            (SampleFormat::Int, 8) => Some(DataType::Int8),
82            (SampleFormat::Int, 16) => Some(DataType::Int16),
83            (SampleFormat::Int, 32) => Some(DataType::Int32),
84            (SampleFormat::Int, 64) => Some(DataType::Int64),
85            (SampleFormat::Float, 32) => Some(DataType::Float32),
86            (SampleFormat::Float, 64) => Some(DataType::Float64),
87            // Unsupported combinations (e.g., Void, Unknown, or unusual bit depths)
88            _ => None,
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_from_tags_uint_types() {
99        assert_eq!(
100            DataType::from_tags(&[SampleFormat::Uint], &[1]),
101            Some(DataType::Bool),
102            "Uint 1-bit should be Bool"
103        );
104        assert_eq!(
105            DataType::from_tags(&[SampleFormat::Uint], &[8]),
106            Some(DataType::UInt8),
107            "Uint 8-bit should be UInt8"
108        );
109        assert_eq!(
110            DataType::from_tags(&[SampleFormat::Uint], &[16]),
111            Some(DataType::UInt16),
112            "Uint 16-bit should be UInt16"
113        );
114        assert_eq!(
115            DataType::from_tags(&[SampleFormat::Uint], &[32]),
116            Some(DataType::UInt32),
117            "Uint 32-bit should be UInt32"
118        );
119        assert_eq!(
120            DataType::from_tags(&[SampleFormat::Uint], &[64]),
121            Some(DataType::UInt64),
122            "Uint 64-bit should be UInt64"
123        );
124    }
125
126    #[test]
127    fn test_from_tags_int_types() {
128        assert_eq!(
129            DataType::from_tags(&[SampleFormat::Int], &[8]),
130            Some(DataType::Int8),
131            "Int 8-bit should be Int8"
132        );
133        assert_eq!(
134            DataType::from_tags(&[SampleFormat::Int], &[16]),
135            Some(DataType::Int16),
136            "Int 16-bit should be Int16"
137        );
138        assert_eq!(
139            DataType::from_tags(&[SampleFormat::Int], &[32]),
140            Some(DataType::Int32),
141            "Int 32-bit should be Int32"
142        );
143        assert_eq!(
144            DataType::from_tags(&[SampleFormat::Int], &[64]),
145            Some(DataType::Int64),
146            "Int 64-bit should be Int64"
147        );
148    }
149
150    #[test]
151    fn test_from_tags_float_types() {
152        assert_eq!(
153            DataType::from_tags(&[SampleFormat::Float], &[32]),
154            Some(DataType::Float32),
155            "Float 32-bit should be Float32"
156        );
157        assert_eq!(
158            DataType::from_tags(&[SampleFormat::Float], &[64]),
159            Some(DataType::Float64),
160            "Float 64-bit should be Float64"
161        );
162    }
163
164    #[test]
165    fn test_from_tags_rgb_consistent() {
166        // RGB image with 3 samples, all UInt8
167        assert_eq!(
168            DataType::from_tags(
169                &[SampleFormat::Uint, SampleFormat::Uint, SampleFormat::Uint],
170                &[8, 8, 8]
171            ),
172            Some(DataType::UInt8),
173            "RGB with consistent UInt8 should succeed"
174        );
175
176        // RGB image with 3 samples, all UInt16
177        assert_eq!(
178            DataType::from_tags(
179                &[SampleFormat::Uint, SampleFormat::Uint, SampleFormat::Uint],
180                &[16, 16, 16]
181            ),
182            Some(DataType::UInt16),
183            "RGB with consistent UInt16 should succeed"
184        );
185    }
186
187    #[test]
188    fn test_from_tags_inconsistent_format() {
189        // Mixed formats should return None
190        assert_eq!(
191            DataType::from_tags(&[SampleFormat::Uint, SampleFormat::Int], &[8, 8]),
192            None,
193            "Inconsistent sample formats should return None"
194        );
195    }
196
197    #[test]
198    fn test_from_tags_inconsistent_bits() {
199        // Mixed bit depths should return None
200        assert_eq!(
201            DataType::from_tags(&[SampleFormat::Uint, SampleFormat::Uint], &[8, 16]),
202            None,
203            "Inconsistent bit depths should return None"
204        );
205    }
206
207    #[test]
208    fn test_from_tags_empty_arrays() {
209        assert_eq!(
210            DataType::from_tags(&[], &[]),
211            None,
212            "Empty arrays should return None"
213        );
214    }
215
216    #[test]
217    fn test_from_tags_unsupported_bit_depth() {
218        // Unsupported bit depth
219        assert_eq!(
220            DataType::from_tags(&[SampleFormat::Uint], &[12]),
221            None,
222            "Unsupported bit depth (12) should return None"
223        );
224        assert_eq!(
225            DataType::from_tags(&[SampleFormat::Uint], &[24]),
226            None,
227            "Unsupported bit depth (24) should return None"
228        );
229    }
230
231    #[test]
232    fn test_from_tags_unsupported_format() {
233        // Void format is not supported
234        assert_eq!(
235            DataType::from_tags(&[SampleFormat::Void], &[8]),
236            None,
237            "Void format should return None"
238        );
239
240        // Unknown format should also return None
241        assert_eq!(
242            DataType::from_tags(&[SampleFormat::Unknown(99)], &[8]),
243            None,
244            "Unknown format should return None"
245        );
246    }
247}