Skip to main content

lerc_core/
raster.rs

1use crate::error::{Error, Result};
2use crate::types::{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, Eq)]
63pub struct MaskView<'a> {
64    width: u32,
65    height: u32,
66    data: &'a [u8],
67}
68
69impl<'a> MaskView<'a> {
70    pub fn new(width: u32, height: u32, data: &'a [u8]) -> Result<Self> {
71        let expected_len = pixel_count_from_dims(width, height)?;
72        if data.len() != expected_len {
73            return Err(Error::InvalidArgument(format!(
74                "mask slice length {} does not match width={width}, height={height}",
75                data.len()
76            )));
77        }
78        Ok(Self {
79            width,
80            height,
81            data,
82        })
83    }
84
85    pub fn width(self) -> u32 {
86        self.width
87    }
88
89    pub fn height(self) -> u32 {
90        self.height
91    }
92
93    pub fn data(self) -> &'a [u8] {
94        self.data
95    }
96
97    pub fn pixel_count(self) -> Result<usize> {
98        pixel_count_from_dims(self.width, self.height)
99    }
100
101    pub fn valid_count(self) -> usize {
102        self.data.iter().filter(|&&value| value != 0).count()
103    }
104}
105
106pub trait Sample: Copy + Default + private::Sealed + 'static {
107    fn data_type() -> DataType;
108    fn from_f64(value: f64) -> Self;
109    fn to_f64(self) -> f64;
110    fn read_vec(bytes: &[u8]) -> Result<Vec<Self>>;
111    fn into_pixel_data(values: Vec<Self>) -> PixelData;
112    fn append_le_bytes(self, out: &mut Vec<u8>);
113}
114
115macro_rules! impl_sample {
116    ($ty:ty, $size:expr, $variant:ident) => {
117        impl Sample for $ty {
118            fn data_type() -> DataType {
119                DataType::$variant
120            }
121
122            fn from_f64(value: f64) -> Self {
123                value as $ty
124            }
125
126            fn to_f64(self) -> f64 {
127                self as f64
128            }
129
130            fn read_vec(bytes: &[u8]) -> Result<Vec<Self>> {
131                let chunks = bytes.chunks_exact($size);
132                if !chunks.remainder().is_empty() {
133                    return Err(Error::InvalidBlob(
134                        "typed value payload length is not aligned to its data type".into(),
135                    ));
136                }
137                Ok(chunks
138                    .map(|chunk| <$ty>::from_le_bytes(chunk.try_into().unwrap()))
139                    .collect())
140            }
141
142            fn into_pixel_data(values: Vec<Self>) -> PixelData {
143                PixelData::$variant(values)
144            }
145
146            fn append_le_bytes(self, out: &mut Vec<u8>) {
147                out.extend_from_slice(&self.to_le_bytes());
148            }
149        }
150    };
151}
152
153impl Sample for u8 {
154    fn data_type() -> DataType {
155        DataType::U8
156    }
157
158    fn from_f64(value: f64) -> Self {
159        value as u8
160    }
161
162    fn to_f64(self) -> f64 {
163        self as f64
164    }
165
166    fn read_vec(bytes: &[u8]) -> Result<Vec<Self>> {
167        Ok(bytes.to_vec())
168    }
169
170    fn into_pixel_data(values: Vec<Self>) -> PixelData {
171        PixelData::U8(values)
172    }
173
174    fn append_le_bytes(self, out: &mut Vec<u8>) {
175        out.push(self);
176    }
177}
178
179impl Sample for i8 {
180    fn data_type() -> DataType {
181        DataType::I8
182    }
183
184    fn from_f64(value: f64) -> Self {
185        value as i8
186    }
187
188    fn to_f64(self) -> f64 {
189        self as f64
190    }
191
192    fn read_vec(bytes: &[u8]) -> Result<Vec<Self>> {
193        Ok(bytes
194            .iter()
195            .map(|&byte| i8::from_le_bytes([byte]))
196            .collect())
197    }
198
199    fn into_pixel_data(values: Vec<Self>) -> PixelData {
200        PixelData::I8(values)
201    }
202
203    fn append_le_bytes(self, out: &mut Vec<u8>) {
204        out.push(self as u8);
205    }
206}
207
208impl_sample!(i16, 2, I16);
209impl_sample!(u16, 2, U16);
210impl_sample!(i32, 4, I32);
211impl_sample!(u32, 4, U32);
212impl_sample!(f32, 4, F32);
213impl_sample!(f64, 8, F64);
214
215pub fn pixel_count_from_dims(width: u32, height: u32) -> Result<usize> {
216    let width = usize::try_from(width)
217        .map_err(|_| Error::InvalidArgument("width does not fit in usize".into()))?;
218    let height = usize::try_from(height)
219        .map_err(|_| Error::InvalidArgument("height does not fit in usize".into()))?;
220    width
221        .checked_mul(height)
222        .ok_or_else(|| Error::InvalidArgument("pixel count overflows usize".into()))
223}
224
225pub fn sample_count_from_dims(width: u32, height: u32, depth: u32) -> Result<usize> {
226    if depth == 0 {
227        return Err(Error::InvalidArgument(
228            "depth must be greater than zero".into(),
229        ));
230    }
231    pixel_count_from_dims(width, height)?
232        .checked_mul(depth as usize)
233        .ok_or_else(|| Error::InvalidArgument("sample count overflows usize".into()))
234}
235
236pub fn read_values_as<T: Sample>(bytes: &[u8], source_type: DataType) -> Result<Vec<T>> {
237    if source_type == T::data_type() {
238        return T::read_vec(bytes);
239    }
240
241    read_typed_values(bytes, source_type)
242        .map(|values| values.into_iter().map(T::from_f64).collect())
243}
244
245pub fn coerce_f64_to_data_type(value: f64, data_type: DataType) -> f64 {
246    match data_type {
247        DataType::I8 => (value as i8) as f64,
248        DataType::U8 => (value as u8) as f64,
249        DataType::I16 => (value as i16) as f64,
250        DataType::U16 => (value as u16) as f64,
251        DataType::I32 => (value as i32) as f64,
252        DataType::U32 => (value as u32) as f64,
253        DataType::F32 => (value as f32) as f64,
254        DataType::F64 => value,
255    }
256}
257
258pub fn output_value<T: Sample>(value: f64, source_type: DataType) -> T {
259    if T::data_type() == DataType::F64 && source_type != DataType::F64 {
260        T::from_f64(coerce_f64_to_data_type(value, source_type))
261    } else {
262        T::from_f64(value)
263    }
264}
265
266pub fn read_scalar(bytes: &[u8], data_type: DataType) -> Result<f64> {
267    Ok(match data_type {
268        DataType::I8 => i8::from_le_bytes([bytes[0]]) as f64,
269        DataType::U8 => bytes[0] as f64,
270        DataType::I16 => i16::from_le_bytes(bytes.try_into().unwrap()) as f64,
271        DataType::U16 => u16::from_le_bytes(bytes.try_into().unwrap()) as f64,
272        DataType::I32 => i32::from_le_bytes(bytes.try_into().unwrap()) as f64,
273        DataType::U32 => u32::from_le_bytes(bytes.try_into().unwrap()) as f64,
274        DataType::F32 => f32::from_le_bytes(bytes.try_into().unwrap()) as f64,
275        DataType::F64 => f64::from_le_bytes(bytes.try_into().unwrap()),
276    })
277}
278
279pub fn read_typed_values(bytes: &[u8], data_type: DataType) -> Result<Vec<f64>> {
280    let mut out = Vec::with_capacity(bytes.len() / data_type.byte_len());
281    for chunk in bytes.chunks_exact(data_type.byte_len()) {
282        out.push(read_scalar(chunk, data_type)?);
283    }
284    if bytes.len() % data_type.byte_len() != 0 {
285        return Err(Error::InvalidBlob(
286            "typed value payload length is not aligned to its data type".into(),
287        ));
288    }
289    Ok(out)
290}
291
292pub fn append_value_as(out: &mut Vec<u8>, value: f64, data_type: DataType) {
293    match data_type {
294        DataType::I8 => out.push((value as i8) as u8),
295        DataType::U8 => out.push(value as u8),
296        DataType::I16 => out.extend_from_slice(&(value as i16).to_le_bytes()),
297        DataType::U16 => out.extend_from_slice(&(value as u16).to_le_bytes()),
298        DataType::I32 => out.extend_from_slice(&(value as i32).to_le_bytes()),
299        DataType::U32 => out.extend_from_slice(&(value as u32).to_le_bytes()),
300        DataType::F32 => out.extend_from_slice(&(value as f32).to_le_bytes()),
301        DataType::F64 => out.extend_from_slice(&value.to_le_bytes()),
302    }
303}
304
305pub fn count_valid_in_block(
306    mask: &[u8],
307    width: usize,
308    x0: usize,
309    y0: usize,
310    block_width: usize,
311    block_height: usize,
312) -> usize {
313    let mut count = 0usize;
314    for row in 0..block_height {
315        let row_offset = (y0 + row) * width + x0;
316        for col in 0..block_width {
317            count += usize::from(mask[row_offset + col] != 0);
318        }
319    }
320    count
321}
322
323pub fn bits_required(max_index: usize) -> u8 {
324    let mut bits = 0u8;
325    let mut value = max_index;
326    while value > 0 {
327        bits += 1;
328        value >>= 1;
329    }
330    bits
331}
332
333pub fn words_from_padded(bytes: &[u8]) -> Vec<u32> {
334    if bytes.is_empty() {
335        return Vec::new();
336    }
337    let mut padded = vec![0u8; bytes.len().div_ceil(4) * 4];
338    padded[..bytes.len()].copy_from_slice(bytes);
339    padded
340        .chunks_exact(4)
341        .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
342        .collect()
343}
344
345pub fn fletcher32(bytes: &[u8]) -> u32 {
346    let mut sum1 = 0xffffu32;
347    let mut sum2 = 0xffffu32;
348    let mut words = bytes.len() / 2;
349    let mut index = 0usize;
350
351    while words > 0 {
352        let chunk = words.min(359);
353        words -= chunk;
354        for _ in 0..chunk {
355            sum1 += (bytes[index] as u32) << 8;
356            index += 1;
357            sum2 += sum1 + bytes[index] as u32;
358            sum1 += bytes[index] as u32;
359            index += 1;
360        }
361        sum1 = (sum1 & 0xffff) + (sum1 >> 16);
362        sum2 = (sum2 & 0xffff) + (sum2 >> 16);
363    }
364
365    if bytes.len() & 1 != 0 {
366        sum1 += (bytes[index] as u32) << 8;
367        sum2 += sum1;
368    }
369
370    sum1 = (sum1 & 0xffff) + (sum1 >> 16);
371    sum2 = (sum2 & 0xffff) + (sum2 >> 16);
372    (sum2 << 16) | (sum1 & 0xffff)
373}
374
375pub fn sample_index(pixel: usize, depth: usize, dim: usize) -> usize {
376    pixel * depth + dim
377}
378
379mod private {
380    pub trait Sealed {}
381
382    impl Sealed for i8 {}
383    impl Sealed for u8 {}
384    impl Sealed for i16 {}
385    impl Sealed for u16 {}
386    impl Sealed for i32 {}
387    impl Sealed for u32 {}
388    impl Sealed for f32 {}
389    impl Sealed for f64 {}
390}