Skip to main content

lerc_core/
raster.rs

1use crate::error::{Error, Result};
2use crate::types::{BandLayout, DataType, PixelData};
3
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct RasterView<'a, T: Sample> {
6    width: u32,
7    height: u32,
8    depth: u32,
9    data: &'a [T],
10}
11
12impl<'a, T: Sample> RasterView<'a, T> {
13    pub fn new(width: u32, height: u32, depth: u32, data: &'a [T]) -> Result<Self> {
14        let expected_len = sample_count_from_dims(width, height, depth)?;
15        if data.len() != expected_len {
16            return Err(Error::InvalidArgument(format!(
17                "raster slice length {} does not match width={width}, height={height}, depth={depth}",
18                data.len()
19            )));
20        }
21        Ok(Self {
22            width,
23            height,
24            depth,
25            data,
26        })
27    }
28
29    pub fn width(self) -> u32 {
30        self.width
31    }
32
33    pub fn height(self) -> u32 {
34        self.height
35    }
36
37    pub fn depth(self) -> u32 {
38        self.depth
39    }
40
41    pub fn data(self) -> &'a [T] {
42        self.data
43    }
44
45    pub fn data_type(self) -> DataType {
46        T::data_type()
47    }
48
49    pub fn pixel_count(self) -> Result<usize> {
50        pixel_count_from_dims(self.width, self.height)
51    }
52
53    pub fn sample_count(self) -> Result<usize> {
54        sample_count_from_dims(self.width, self.height, self.depth)
55    }
56
57    pub fn sample(self, pixel: usize, dim: usize) -> T {
58        self.data[sample_index(pixel, self.depth as usize, dim)]
59    }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq)]
63pub struct BandSetView<'a, T: Sample> {
64    width: u32,
65    height: u32,
66    depth: u32,
67    band_count: usize,
68    layout: BandLayout,
69    data: &'a [T],
70}
71
72impl<'a, T: Sample> BandSetView<'a, T> {
73    pub fn new(
74        width: u32,
75        height: u32,
76        depth: u32,
77        band_count: usize,
78        layout: BandLayout,
79        data: &'a [T],
80    ) -> Result<Self> {
81        if band_count == 0 {
82            return Err(Error::InvalidArgument(
83                "band_count must be greater than zero".into(),
84            ));
85        }
86
87        let band_sample_count = sample_count_from_dims(width, height, depth)?;
88        let expected_len = band_sample_count
89            .checked_mul(band_count)
90            .ok_or_else(|| Error::InvalidArgument("band set size overflows usize".into()))?;
91        if data.len() != expected_len {
92            return Err(Error::InvalidArgument(format!(
93                "band set slice length {} does not match width={width}, height={height}, depth={depth}, band_count={band_count}",
94                data.len()
95            )));
96        }
97
98        Ok(Self {
99            width,
100            height,
101            depth,
102            band_count,
103            layout,
104            data,
105        })
106    }
107
108    pub fn width(self) -> u32 {
109        self.width
110    }
111
112    pub fn height(self) -> u32 {
113        self.height
114    }
115
116    pub fn depth(self) -> u32 {
117        self.depth
118    }
119
120    pub fn band_count(self) -> usize {
121        self.band_count
122    }
123
124    pub fn layout(self) -> BandLayout {
125        self.layout
126    }
127
128    pub fn data(self) -> &'a [T] {
129        self.data
130    }
131
132    pub fn data_type(self) -> DataType {
133        T::data_type()
134    }
135
136    pub fn pixel_count(self) -> Result<usize> {
137        pixel_count_from_dims(self.width, self.height)
138    }
139
140    pub fn band_sample_count(self) -> Result<usize> {
141        sample_count_from_dims(self.width, self.height, self.depth)
142    }
143
144    pub fn value_count(self) -> Result<usize> {
145        self.band_sample_count()?
146            .checked_mul(self.band_count)
147            .ok_or_else(|| Error::InvalidArgument("band set size overflows usize".into()))
148    }
149
150    pub fn sample(self, band: usize, pixel: usize, dim: usize) -> T {
151        let depth = self.depth as usize;
152        let pixel_count = self.width as usize * self.height as usize;
153        let band_stride = pixel_count * depth;
154        let index = match self.layout {
155            BandLayout::Interleaved => (pixel * self.band_count + band) * depth + dim,
156            BandLayout::Bsq => band * band_stride + pixel * depth + dim,
157        };
158        self.data[index]
159    }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct MaskView<'a> {
164    width: u32,
165    height: u32,
166    data: &'a [u8],
167}
168
169impl<'a> MaskView<'a> {
170    pub fn new(width: u32, height: u32, data: &'a [u8]) -> Result<Self> {
171        let expected_len = pixel_count_from_dims(width, height)?;
172        if data.len() != expected_len {
173            return Err(Error::InvalidArgument(format!(
174                "mask slice length {} does not match width={width}, height={height}",
175                data.len()
176            )));
177        }
178        Ok(Self {
179            width,
180            height,
181            data,
182        })
183    }
184
185    pub fn width(self) -> u32 {
186        self.width
187    }
188
189    pub fn height(self) -> u32 {
190        self.height
191    }
192
193    pub fn data(self) -> &'a [u8] {
194        self.data
195    }
196
197    pub fn pixel_count(self) -> Result<usize> {
198        pixel_count_from_dims(self.width, self.height)
199    }
200
201    pub fn valid_count(self) -> usize {
202        self.data.iter().filter(|&&value| value != 0).count()
203    }
204}
205
206pub trait Sample: Copy + Default + private::Sealed + 'static {
207    fn data_type() -> DataType;
208    fn from_f64(value: f64) -> Self;
209    fn to_f64(self) -> f64;
210    fn read_vec(bytes: &[u8]) -> Result<Vec<Self>>;
211    fn into_pixel_data(values: Vec<Self>) -> PixelData;
212    fn append_le_bytes(self, out: &mut Vec<u8>);
213}
214
215macro_rules! impl_sample {
216    ($ty:ty, $size:expr, $variant:ident) => {
217        impl Sample for $ty {
218            fn data_type() -> DataType {
219                DataType::$variant
220            }
221
222            fn from_f64(value: f64) -> Self {
223                value as $ty
224            }
225
226            fn to_f64(self) -> f64 {
227                self as f64
228            }
229
230            fn read_vec(bytes: &[u8]) -> Result<Vec<Self>> {
231                let chunks = bytes.chunks_exact($size);
232                if !chunks.remainder().is_empty() {
233                    return Err(Error::InvalidBlob(
234                        "typed value payload length is not aligned to its data type".into(),
235                    ));
236                }
237                Ok(chunks
238                    .map(|chunk| <$ty>::from_le_bytes(chunk.try_into().unwrap()))
239                    .collect())
240            }
241
242            fn into_pixel_data(values: Vec<Self>) -> PixelData {
243                PixelData::$variant(values)
244            }
245
246            fn append_le_bytes(self, out: &mut Vec<u8>) {
247                out.extend_from_slice(&self.to_le_bytes());
248            }
249        }
250    };
251}
252
253impl Sample for u8 {
254    fn data_type() -> DataType {
255        DataType::U8
256    }
257
258    fn from_f64(value: f64) -> Self {
259        value as u8
260    }
261
262    fn to_f64(self) -> f64 {
263        self as f64
264    }
265
266    fn read_vec(bytes: &[u8]) -> Result<Vec<Self>> {
267        Ok(bytes.to_vec())
268    }
269
270    fn into_pixel_data(values: Vec<Self>) -> PixelData {
271        PixelData::U8(values)
272    }
273
274    fn append_le_bytes(self, out: &mut Vec<u8>) {
275        out.push(self);
276    }
277}
278
279impl Sample for i8 {
280    fn data_type() -> DataType {
281        DataType::I8
282    }
283
284    fn from_f64(value: f64) -> Self {
285        value as i8
286    }
287
288    fn to_f64(self) -> f64 {
289        self as f64
290    }
291
292    fn read_vec(bytes: &[u8]) -> Result<Vec<Self>> {
293        Ok(bytes
294            .iter()
295            .map(|&byte| i8::from_le_bytes([byte]))
296            .collect())
297    }
298
299    fn into_pixel_data(values: Vec<Self>) -> PixelData {
300        PixelData::I8(values)
301    }
302
303    fn append_le_bytes(self, out: &mut Vec<u8>) {
304        out.push(self as u8);
305    }
306}
307
308impl_sample!(i16, 2, I16);
309impl_sample!(u16, 2, U16);
310impl_sample!(i32, 4, I32);
311impl_sample!(u32, 4, U32);
312impl_sample!(f32, 4, F32);
313impl_sample!(f64, 8, F64);
314
315pub fn pixel_count_from_dims(width: u32, height: u32) -> Result<usize> {
316    let width = usize::try_from(width)
317        .map_err(|_| Error::InvalidArgument("width does not fit in usize".into()))?;
318    let height = usize::try_from(height)
319        .map_err(|_| Error::InvalidArgument("height does not fit in usize".into()))?;
320    width
321        .checked_mul(height)
322        .ok_or_else(|| Error::InvalidArgument("pixel count overflows usize".into()))
323}
324
325pub fn sample_count_from_dims(width: u32, height: u32, depth: u32) -> Result<usize> {
326    if depth == 0 {
327        return Err(Error::InvalidArgument(
328            "depth must be greater than zero".into(),
329        ));
330    }
331    pixel_count_from_dims(width, height)?
332        .checked_mul(depth as usize)
333        .ok_or_else(|| Error::InvalidArgument("sample count overflows usize".into()))
334}
335
336pub fn read_values_as<T: Sample>(bytes: &[u8], source_type: DataType) -> Result<Vec<T>> {
337    if source_type == T::data_type() {
338        return T::read_vec(bytes);
339    }
340
341    read_typed_values(bytes, source_type)
342        .map(|values| values.into_iter().map(T::from_f64).collect())
343}
344
345pub fn coerce_f64_to_data_type(value: f64, data_type: DataType) -> f64 {
346    match data_type {
347        DataType::I8 => (value as i8) as f64,
348        DataType::U8 => (value as u8) as f64,
349        DataType::I16 => (value as i16) as f64,
350        DataType::U16 => (value as u16) as f64,
351        DataType::I32 => (value as i32) as f64,
352        DataType::U32 => (value as u32) as f64,
353        DataType::F32 => (value as f32) as f64,
354        DataType::F64 => value,
355    }
356}
357
358pub fn output_value<T: Sample>(value: f64, source_type: DataType) -> T {
359    if T::data_type() == DataType::F64 && source_type != DataType::F64 {
360        T::from_f64(coerce_f64_to_data_type(value, source_type))
361    } else {
362        T::from_f64(value)
363    }
364}
365
366pub fn read_scalar(bytes: &[u8], data_type: DataType) -> Result<f64> {
367    Ok(match data_type {
368        DataType::I8 => i8::from_le_bytes([bytes[0]]) as f64,
369        DataType::U8 => bytes[0] as f64,
370        DataType::I16 => i16::from_le_bytes(bytes.try_into().unwrap()) as f64,
371        DataType::U16 => u16::from_le_bytes(bytes.try_into().unwrap()) as f64,
372        DataType::I32 => i32::from_le_bytes(bytes.try_into().unwrap()) as f64,
373        DataType::U32 => u32::from_le_bytes(bytes.try_into().unwrap()) as f64,
374        DataType::F32 => f32::from_le_bytes(bytes.try_into().unwrap()) as f64,
375        DataType::F64 => f64::from_le_bytes(bytes.try_into().unwrap()),
376    })
377}
378
379pub fn read_typed_values(bytes: &[u8], data_type: DataType) -> Result<Vec<f64>> {
380    let mut out = Vec::with_capacity(bytes.len() / data_type.byte_len());
381    for chunk in bytes.chunks_exact(data_type.byte_len()) {
382        out.push(read_scalar(chunk, data_type)?);
383    }
384    if bytes.len() % data_type.byte_len() != 0 {
385        return Err(Error::InvalidBlob(
386            "typed value payload length is not aligned to its data type".into(),
387        ));
388    }
389    Ok(out)
390}
391
392pub fn append_value_as(out: &mut Vec<u8>, value: f64, data_type: DataType) {
393    match data_type {
394        DataType::I8 => out.push((value as i8) as u8),
395        DataType::U8 => out.push(value as u8),
396        DataType::I16 => out.extend_from_slice(&(value as i16).to_le_bytes()),
397        DataType::U16 => out.extend_from_slice(&(value as u16).to_le_bytes()),
398        DataType::I32 => out.extend_from_slice(&(value as i32).to_le_bytes()),
399        DataType::U32 => out.extend_from_slice(&(value as u32).to_le_bytes()),
400        DataType::F32 => out.extend_from_slice(&(value as f32).to_le_bytes()),
401        DataType::F64 => out.extend_from_slice(&value.to_le_bytes()),
402    }
403}
404
405pub fn count_valid_in_block(
406    mask: &[u8],
407    width: usize,
408    x0: usize,
409    y0: usize,
410    block_width: usize,
411    block_height: usize,
412) -> usize {
413    let mut count = 0usize;
414    for row in 0..block_height {
415        let row_offset = (y0 + row) * width + x0;
416        for col in 0..block_width {
417            count += usize::from(mask[row_offset + col] != 0);
418        }
419    }
420    count
421}
422
423pub fn bits_required(max_index: usize) -> u8 {
424    let mut bits = 0u8;
425    let mut value = max_index;
426    while value > 0 {
427        bits += 1;
428        value >>= 1;
429    }
430    bits
431}
432
433pub fn words_from_padded(bytes: &[u8]) -> Vec<u32> {
434    if bytes.is_empty() {
435        return Vec::new();
436    }
437    let mut padded = vec![0u8; bytes.len().div_ceil(4) * 4];
438    padded[..bytes.len()].copy_from_slice(bytes);
439    padded
440        .chunks_exact(4)
441        .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
442        .collect()
443}
444
445pub fn fletcher32(bytes: &[u8]) -> u32 {
446    let mut sum1 = 0xffffu32;
447    let mut sum2 = 0xffffu32;
448    let mut words = bytes.len() / 2;
449    let mut index = 0usize;
450
451    while words > 0 {
452        let chunk = words.min(359);
453        words -= chunk;
454        for _ in 0..chunk {
455            sum1 += (bytes[index] as u32) << 8;
456            index += 1;
457            sum2 += sum1 + bytes[index] as u32;
458            sum1 += bytes[index] as u32;
459            index += 1;
460        }
461        sum1 = (sum1 & 0xffff) + (sum1 >> 16);
462        sum2 = (sum2 & 0xffff) + (sum2 >> 16);
463    }
464
465    if bytes.len() & 1 != 0 {
466        sum1 += (bytes[index] as u32) << 8;
467        sum2 += sum1;
468    }
469
470    sum1 = (sum1 & 0xffff) + (sum1 >> 16);
471    sum2 = (sum2 & 0xffff) + (sum2 >> 16);
472    (sum2 << 16) | (sum1 & 0xffff)
473}
474
475pub fn sample_index(pixel: usize, depth: usize, dim: usize) -> usize {
476    pixel * depth + dim
477}
478
479mod private {
480    pub trait Sealed {}
481
482    impl Sealed for i8 {}
483    impl Sealed for u8 {}
484    impl Sealed for i16 {}
485    impl Sealed for u16 {}
486    impl Sealed for i32 {}
487    impl Sealed for u32 {}
488    impl Sealed for f32 {}
489    impl Sealed for f64 {}
490}