gistools/readers/geotiff/
image_util.rs

1use crate::parsers::Buffer;
2use alloc::{vec, vec::Vec};
3use core::iter::Sum;
4use half::f16;
5
6/// An array type
7#[derive(Debug, Default, Clone, PartialEq)]
8pub enum GTiffDataType {
9    /// Unsigned 8-bit
10    #[default]
11    U8,
12    /// Unsigned 16-bit
13    U16,
14    /// Unsigned 32-bit
15    U32,
16    /// Signed 8-bit
17    I8,
18    /// Signed 16-bit
19    I16,
20    /// Signed 32-bit
21    I32,
22    /// 16-bit float
23    F16,
24    /// 32-bit float
25    F32,
26    /// 64-bit float
27    F64,
28}
29impl GTiffDataType {
30    /// Convert the data format and bits per sample to an array type
31    ///
32    /// ## Parameters
33    /// - `format`: the data format
34    /// - `bits_per_sample`: the bits per sample
35    ///
36    /// ## Returns
37    /// The array type constructor
38    pub fn to_type(format: u16, bits_per_sample: u16) -> GTiffDataType {
39        match format {
40            1 => {
41                // unsigned integer data
42                if bits_per_sample <= 8 {
43                    return GTiffDataType::U8;
44                } else if bits_per_sample <= 16 {
45                    return GTiffDataType::U16;
46                } else if bits_per_sample <= 32 {
47                    return GTiffDataType::U32;
48                }
49            }
50            2 => {
51                // twos complement signed integer data
52                if bits_per_sample == 8 {
53                    return GTiffDataType::I8;
54                } else if bits_per_sample == 16 {
55                    return GTiffDataType::I16;
56                } else if bits_per_sample == 32 {
57                    return GTiffDataType::I32;
58                }
59            }
60            3 => {
61                // floating point data
62                match bits_per_sample {
63                    16 => {
64                        return GTiffDataType::F16;
65                    }
66                    32 => {
67                        return GTiffDataType::F32;
68                    }
69                    64 => {
70                        return GTiffDataType::F64;
71                    }
72                    _ => {}
73                }
74            }
75            _ => {}
76        }
77        panic!("Unsupported data format/bits_per_sample");
78    }
79}
80
81/// Get the sum of a range of numbers
82///
83/// ## Parameters
84/// - `array`: An array of numbers
85/// - `start`: Start index
86/// - `end`: End index
87///
88/// ## Returns
89/// The sum
90pub fn sample_sum<T>(array: &[T], start: usize, end: usize) -> T
91where
92    T: Copy + Sum<T>,
93{
94    array[start..end].iter().copied().sum()
95}
96
97/// Check if the data needs normalization
98///
99/// ## Parameters
100/// - `format`: the data format
101/// - `bits_per_sample`: the bits per sample
102///
103/// ## Returns
104/// true if the data needs normalization
105pub fn needs_normalization(format: usize, bits_per_sample: usize) -> bool {
106    if (format == 1 || format == 2) && bits_per_sample <= 32 && bits_per_sample.is_multiple_of(8) {
107        false
108    } else {
109        !(format == 3 && (bits_per_sample == 16 || bits_per_sample == 32 || bits_per_sample == 64))
110    }
111}
112
113/// Normalize the array
114///
115/// ## Parameters
116/// - `in_buffer`: the input buffer
117/// - `format`: the data format
118/// - `planar_configuration`: the planar configuration
119/// - `samples_per_pixel`: the number of samples per pixel
120/// - `bits_per_sample`: the bits per sample
121/// - `tile_width`: the tile width
122/// - `tile_height`: the tile height
123///
124/// ## Returns
125/// The normalized array
126pub fn normalize_array(
127    in_buffer: Vec<u8>,
128    format: usize,
129    planar_configuration: usize,
130    samples_per_pixel: usize,
131    bits_per_sample: usize,
132    tile_width: usize,
133    tile_height: usize,
134) -> Vec<u8> {
135    // const inByteArray = new Uint8Array(in_buffer);
136    let mut view = Buffer::new(in_buffer);
137    let out_size = if planar_configuration == 2 {
138        tile_height * tile_width
139    } else {
140        tile_height * tile_width * samples_per_pixel
141    };
142    let samples_to_transfer = if planar_configuration == 2 { 1 } else { samples_per_pixel };
143    // let out_array = RasterData::from_size(format, bits_per_sample, out_size); // (just create f64 of out_size)
144    let mut out_array: Vec<u8> = vec![0; out_size];
145    // let mut pixel = 0;
146
147    let bit_mask = (1_usize << bits_per_sample) - 1;
148
149    if format == 1 {
150        // unsigned integer
151        // translation of https://github.com/OSGeo/gdal/blob/master/gdal/frmts/gtiff/geotiff.cpp#L7337
152        let pixel_bit_skip = if planar_configuration == 1 {
153            samples_per_pixel * bits_per_sample
154        } else {
155            bits_per_sample
156        };
157
158        // Bits per line rounds up to next byte boundary.
159        let mut bits_per_line = tile_width * pixel_bit_skip;
160        if (bits_per_line & 7) != 0 {
161            bits_per_line = (bits_per_line + 7) & !7;
162        }
163
164        for y in 0..tile_height {
165            let line_bit_offset = y * bits_per_line;
166            for x in 0..tile_width {
167                let pixel_bit_offset = line_bit_offset + x * samples_to_transfer * bits_per_sample;
168                for i in 0..samples_to_transfer {
169                    let bit_offset = pixel_bit_offset + i * bits_per_sample;
170                    let out_index = (y * tile_width + x) * samples_to_transfer + i;
171
172                    // let byte_offset = Math.floor(bit_offset / 8);
173                    let byte_offset = bit_offset / 8;
174                    let inner_bit_offset = bit_offset % 8;
175                    if inner_bit_offset + bits_per_sample <= 8 {
176                        out_array[out_index] = ((view.get_u8_at(byte_offset) as usize
177                            >> (8 - bits_per_sample - inner_bit_offset))
178                            & bit_mask) as u8;
179                    } else if inner_bit_offset + bits_per_sample <= 16 {
180                        let val = ((view.get_u16_at(byte_offset) as usize
181                            >> (16 - bits_per_sample - inner_bit_offset))
182                            & bit_mask) as u16;
183                        // add the two bytes
184                        out_array[out_index * 2] = (val >> 8) as u8;
185                        out_array[out_index * 2 + 1] = val as u8;
186                    } else if inner_bit_offset + bits_per_sample <= 24 {
187                        let raw = ((view.get_u16_at(byte_offset) as usize) << 8)
188                            | view.get_u8_at(byte_offset + 2) as usize;
189                        let val =
190                            ((raw >> (24 - bits_per_sample - inner_bit_offset)) & bit_mask) as u32;
191                        out_array[out_index * 3] = (val >> 16) as u8;
192                        out_array[out_index * 3 + 1] = (val >> 8) as u8;
193                        out_array[out_index * 3 + 2] = val as u8;
194                    } else {
195                        let val = ((view.get_u32_at(byte_offset) as usize
196                            >> (32 - bits_per_sample - inner_bit_offset))
197                            & bit_mask) as u32;
198                        out_array[out_index * 4] = (val >> 24) as u8;
199                        out_array[out_index * 4 + 1] = (val >> 16) as u8;
200                        out_array[out_index * 4 + 2] = (val >> 8) as u8;
201                        out_array[out_index * 4 + 3] = val as u8;
202                    }
203
204                    // let outWord = 0;
205                    // for (let bit = 0; bit < bits_per_sample; ++bit) {
206                    //   if (inByteArray[bit_offset >> 3]
207                    //     & (0x80 >> (bit_offset & 7))) {
208                    //     outWord |= (1 << (bits_per_sample - 1 - bit));
209                    //   }
210                    //   ++bit_offset;
211                    // }
212
213                    // out_array[out_index] = outWord;
214                    // out_array[pixel] = outWord;
215                    // pixel += 1;
216                }
217                // bit_offset = bit_offset + pixel_bit_skip - bits_per_sample;
218            }
219        }
220    } else if format == 3 {
221        // floating point - Float16 is handled elsewhere
222        // normalize 16/24 bit floats to 32 bit floats in the array
223        // if bits_per_sample == 16 {
224        //     let mut byte = 0;
225        //     let mut out_index = 0;
226        //     while byte < in_buffer.len() {
227        //         out_array[out_index] = view.get_f16(byte) as f64;
228        //         byte += 2;
229        //         out_index += 1;
230        //     }
231        // }
232    }
233
234    out_array
235}
236
237/// Returns the reader for a sample
238///
239/// ## Parameters
240/// - `bits_per_sample`: the bits per sample
241/// - `format`: the format type
242///
243/// ## Returns
244/// A function to read each sample value
245pub fn get_reader_for_sample(
246    bits_per_sample: u16,
247    format: u16,
248) -> fn(buffer: &[u8], offset: usize, little_endian: bool) -> f64 {
249    match format {
250        0 | 1 => {
251            // unsigned integer data
252            if bits_per_sample <= 8 {
253                return |buffer: &[u8], offset: usize, _little_endian: bool| -> f64 {
254                    buffer[offset] as f64
255                    // buffer.get(offset).map(|v| *v as f64).unwrap_or(0.0)
256                };
257            } else if bits_per_sample <= 16 {
258                return |buffer: &[u8], offset: usize, little_endian: bool| -> f64 {
259                    let u16_offset = offset / 2;
260                    let bytes = [buffer[u16_offset], buffer[u16_offset + 1]];
261                    let value = if little_endian {
262                        u16::from_le_bytes(bytes)
263                    } else {
264                        u16::from_be_bytes(bytes)
265                    };
266                    value as f64
267                };
268            } else if bits_per_sample <= 32 {
269                return |buffer: &[u8], offset: usize, little_endian: bool| -> f64 {
270                    let start = offset / 4;
271                    let bytes: [u8; 4] = buffer[start..start + 4].try_into().unwrap();
272                    let value = if little_endian {
273                        u32::from_le_bytes(bytes)
274                    } else {
275                        u32::from_be_bytes(bytes)
276                    };
277                    value as f64
278                };
279            }
280        }
281        2 => {
282            // twos complement signed integer data
283            if bits_per_sample <= 8 {
284                return |buffer: &[u8], offset: usize, _little_endian: bool| -> f64 {
285                    (buffer[offset] as i8) as f64
286                };
287            } else if bits_per_sample <= 16 {
288                return |buffer: &[u8], offset: usize, little_endian: bool| -> f64 {
289                    let i16_offset = offset / 2;
290                    let bytes = [buffer[i16_offset], buffer[i16_offset + 1]];
291                    (if little_endian {
292                        i16::from_le_bytes(bytes)
293                    } else {
294                        i16::from_be_bytes(bytes)
295                    }) as f64
296                };
297            } else if bits_per_sample <= 32 {
298                return |buffer: &[u8], offset: usize, little_endian: bool| -> f64 {
299                    let start = offset / 4;
300                    let bytes: [u8; 4] = buffer[start..start + 4].try_into().unwrap();
301                    (if little_endian {
302                        i32::from_le_bytes(bytes)
303                    } else {
304                        i32::from_be_bytes(bytes)
305                    }) as f64
306                };
307            }
308        }
309        3 => {
310            if bits_per_sample <= 16 {
311                return |buffer: &[u8], offset: usize, little_endian: bool| -> f64 {
312                    let f16_offset = offset / 2;
313                    let bytes = [buffer[f16_offset], buffer[f16_offset + 1]];
314                    let value = if little_endian {
315                        f16::from_le_bytes(bytes)
316                    } else {
317                        f16::from_be_bytes(bytes)
318                    };
319                    value.to_f64()
320                };
321            } else if bits_per_sample <= 32 {
322                return |buffer: &[u8], offset: usize, little_endian: bool| -> f64 {
323                    let start = offset / 4;
324                    let bytes: [u8; 4] = buffer[start..start + 4].try_into().unwrap();
325                    (if little_endian {
326                        f32::from_le_bytes(bytes)
327                    } else {
328                        f32::from_be_bytes(bytes)
329                    }) as f64
330                };
331            } else if bits_per_sample <= 64 {
332                return |buffer: &[u8], offset: usize, little_endian: bool| -> f64 {
333                    let start = offset / 8;
334                    let bytes: [u8; 8] = buffer[start..start + 8].try_into().unwrap();
335                    if little_endian {
336                        f64::from_le_bytes(bytes)
337                    } else {
338                        f64::from_be_bytes(bytes)
339                    }
340                };
341            }
342        }
343        _ => {}
344    }
345    panic!("Unsupported data format/bits_per_sample: {format} / {bits_per_sample}");
346}