1use std::any::TypeId;
2
3use lerc_band_materialize::{
4 copy_band_values_into_slice, BandLayout as MaterializeLayout, BandMaterializer,
5};
6use lerc_core::{BandLayout, BandSetInfo, BlobInfo, Error, PixelData, Result};
7use ndarray::{ArrayD, IxDyn};
8
9use crate::allocation::{checked_mul, default_vec, vec_with_capacity};
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct Decoded {
13 pub info: BlobInfo,
14 pub pixels: PixelData,
15 pub mask: Option<Vec<u8>>,
16}
17
18impl Decoded {
19 pub fn into_ndarray<T: NdArrayElement>(self) -> Result<ArrayD<T>> {
20 let shape = self.info.ndarray_shape();
21 self.pixels.into_ndarray(&shape)
22 }
23
24 pub fn into_mask_ndarray(self) -> Result<Option<ArrayD<u8>>> {
25 let shape = self.info.mask_ndarray_shape();
26 self.mask
27 .map(|mask| {
28 ArrayD::from_shape_vec(IxDyn(&shape), mask).map_err(|err| {
29 Error::InvalidBlob(format!("failed to build ndarray from decoded mask: {err}"))
30 })
31 })
32 .transpose()
33 }
34}
35
36#[derive(Debug, Clone, PartialEq)]
37pub struct DecodedF64 {
38 pub info: BlobInfo,
39 pub pixels: Vec<f64>,
40 pub mask: Option<Vec<u8>>,
41}
42
43impl DecodedF64 {
44 pub fn into_ndarray(self) -> Result<ArrayD<f64>> {
45 ArrayD::from_shape_vec(IxDyn(&self.info.ndarray_shape()), self.pixels).map_err(|err| {
46 Error::InvalidBlob(format!(
47 "failed to build ndarray from decoded pixels: {err}"
48 ))
49 })
50 }
51
52 pub fn into_mask_ndarray(self) -> Result<Option<ArrayD<u8>>> {
53 let shape = self.info.mask_ndarray_shape();
54 self.mask
55 .map(|mask| {
56 ArrayD::from_shape_vec(IxDyn(&shape), mask).map_err(|err| {
57 Error::InvalidBlob(format!("failed to build ndarray from decoded mask: {err}"))
58 })
59 })
60 .transpose()
61 }
62}
63
64#[derive(Debug, Clone, PartialEq)]
65pub struct DecodedBandSet {
66 pub info: BandSetInfo,
67 pub bands: Vec<PixelData>,
68 pub band_masks: Vec<Option<Vec<u8>>>,
69}
70
71impl DecodedBandSet {
72 pub fn into_ndarray<T: BandElement>(self) -> Result<ArrayD<T>> {
73 self.into_ndarray_with_layout(BandLayout::Interleaved)
74 }
75
76 pub fn into_ndarray_with_layout<T: BandElement>(self, layout: BandLayout) -> Result<ArrayD<T>> {
77 let shape = self.info.ndarray_shape_for_layout(layout);
78 let values = self.into_vec_with_layout(layout)?;
79 ArrayD::from_shape_vec(IxDyn(&shape), values).map_err(|err| {
80 Error::InvalidBlob(format!(
81 "failed to build ndarray from decoded band set: {err}"
82 ))
83 })
84 }
85
86 pub fn into_vec_with_layout<T: BandElement>(self, layout: BandLayout) -> Result<Vec<T>> {
87 if self.bands.len() == 1 {
88 return T::from_pixel_data(self.bands.into_iter().next().unwrap());
89 }
90
91 let mut materializer = BandMaterializer::new(
92 self.info.bands[0].pixel_count()?,
93 self.info.depth() as usize,
94 self.info.band_count(),
95 materialize_layout(layout),
96 )
97 .map_err(materialize_error)?;
98 for (band_index, band) in self.bands.into_iter().enumerate() {
99 copy_pixel_data_into_materializer(&mut materializer, band_index, band)?;
100 }
101 materializer.finish().map_err(materialize_error)
102 }
103
104 pub fn copy_into_slice<T: BandElement>(self, layout: BandLayout, out: &mut [T]) -> Result<()> {
105 let pixel_count = self.info.bands[0].pixel_count()?;
106 let depth = self.info.depth() as usize;
107 let band_count = self.info.band_count();
108 let expected_len = self.info.value_count()?;
109 if out.len() != expected_len {
110 return Err(Error::InvalidBlob(format!(
111 "output slice length {} does not match decoded band set length {}",
112 out.len(),
113 expected_len
114 )));
115 }
116
117 for (band_index, band) in self.bands.into_iter().enumerate() {
118 copy_pixel_data_into_layout_slice(
119 out,
120 band_index,
121 pixel_count,
122 depth,
123 band_count,
124 layout,
125 band,
126 )?;
127 }
128 Ok(())
129 }
130
131 pub fn into_band_mask_ndarray(self) -> Result<Option<ArrayD<u8>>> {
132 into_band_mask_ndarray(self.info, self.band_masks)
133 }
134}
135
136pub fn into_band_mask_ndarray(
137 info: BandSetInfo,
138 band_masks: Vec<Option<Vec<u8>>>,
139) -> Result<Option<ArrayD<u8>>> {
140 if band_masks.iter().all(Option::is_none) {
141 return Ok(None);
142 }
143
144 let pixel_count = info.bands[0].pixel_count()?;
145 let band_count = info.band_count();
146 let shape = info.band_mask_ndarray_shape();
147
148 if band_count == 1 {
149 let mask = band_masks
150 .into_iter()
151 .next()
152 .flatten()
153 .map(Ok)
154 .unwrap_or_else(|| default_vec(pixel_count, "decoded band mask"))?;
155 return ArrayD::from_shape_vec(IxDyn(&shape), mask)
156 .map(Some)
157 .map_err(|err| {
158 Error::InvalidBlob(format!("failed to build ndarray from decoded mask: {err}"))
159 });
160 }
161
162 let merged_len = checked_mul(pixel_count, band_count, "decoded band mask length")?;
163 let mut merged = vec_with_capacity(merged_len, "decoded band mask")?;
164 for pixel in 0..pixel_count {
165 for band_mask in &band_masks {
166 merged.push(band_mask.as_ref().map(|mask| mask[pixel]).unwrap_or(1));
167 }
168 }
169
170 ArrayD::from_shape_vec(IxDyn(&shape), merged)
171 .map(Some)
172 .map_err(|err| {
173 Error::InvalidBlob(format!(
174 "failed to build ndarray from decoded band mask: {err}"
175 ))
176 })
177}
178
179trait SupportedElementValue: Copy + 'static + IntoF64 {
180 const KIND: BandElementKind;
181}
182
183macro_rules! match_pixel_data_values {
184 ($band:expr, |$values:ident| $body:expr) => {
185 match $band {
186 PixelData::I8($values) => $body,
187 PixelData::U8($values) => $body,
188 PixelData::I16($values) => $body,
189 PixelData::U16($values) => $body,
190 PixelData::I32($values) => $body,
191 PixelData::U32($values) => $body,
192 PixelData::F32($values) => $body,
193 PixelData::F64($values) => $body,
194 }
195 };
196}
197
198fn copy_pixel_data_into_materializer<T: BandElement>(
199 materializer: &mut BandMaterializer<T>,
200 band_index: usize,
201 band: PixelData,
202) -> Result<()> {
203 match_pixel_data_values!(band, |values| {
204 copy_typed_values_into_materializer(materializer, band_index, &values)
205 })
206}
207
208fn copy_typed_values_into_materializer<T: BandElement, U: SupportedElementValue>(
209 materializer: &mut BandMaterializer<T>,
210 band_index: usize,
211 values: &[U],
212) -> Result<()> {
213 if T::KIND == U::KIND {
214 let typed = unsafe { cast_slice::<U, T>(values) };
218 return materializer
219 .copy_band(band_index, typed)
220 .map_err(materialize_error);
221 }
222 if T::KIND == BandElementKind::F64 {
223 return materializer
224 .copy_band_with(band_index, |index| {
225 unsafe_cast::<T, f64>(values[index].into_f64())
227 })
228 .map_err(materialize_error);
229 }
230 Err(Error::InvalidBlob(format!(
231 "cannot decode {} pixels into ndarray<{}>",
232 data_type_name::<U>(),
233 std::any::type_name::<T>()
234 .rsplit("::")
235 .next()
236 .unwrap_or("unknown"),
237 )))
238}
239
240fn copy_pixel_data_into_layout_slice<T: BandElement>(
241 out: &mut [T],
242 band_index: usize,
243 pixel_count: usize,
244 depth: usize,
245 band_count: usize,
246 layout: BandLayout,
247 band: PixelData,
248) -> Result<()> {
249 match_pixel_data_values!(band, |values| {
250 copy_typed_values_into_layout_slice(
251 out,
252 band_index,
253 pixel_count,
254 depth,
255 band_count,
256 layout,
257 &values,
258 )
259 })
260}
261
262fn copy_typed_values_into_layout_slice<T: BandElement, U: SupportedElementValue>(
263 out: &mut [T],
264 band_index: usize,
265 pixel_count: usize,
266 depth: usize,
267 band_count: usize,
268 layout: BandLayout,
269 values: &[U],
270) -> Result<()> {
271 if T::KIND == U::KIND {
272 let typed = unsafe { cast_slice::<U, T>(values) };
276 return copy_band_values_into_slice(
277 out,
278 typed,
279 pixel_count,
280 depth,
281 band_index,
282 band_count,
283 materialize_layout(layout),
284 )
285 .map_err(materialize_error);
286 }
287 if T::KIND == BandElementKind::F64 {
288 let band_len = pixel_count
289 .checked_mul(depth.max(1))
290 .ok_or_else(|| Error::InvalidBlob("decoded band length overflows usize".into()))?;
291 if values.len() != band_len {
292 return Err(Error::InvalidBlob(
293 "decoded band length does not match its metadata".into(),
294 ));
295 }
296 for (value_index, value) in values.iter().copied().enumerate() {
297 let out_index = match layout {
298 BandLayout::Interleaved => {
299 if depth <= 1 {
300 value_index * band_count + band_index
301 } else {
302 let pixel = value_index / depth;
303 let sample = value_index % depth;
304 (pixel * band_count + band_index) * depth + sample
305 }
306 }
307 BandLayout::Bsq => band_index * band_len + value_index,
308 };
309 out[out_index] = unsafe_cast::<T, f64>(value.into_f64());
311 }
312 return Ok(());
313 }
314 Err(Error::InvalidBlob(format!(
315 "cannot decode {} pixels into ndarray<{}>",
316 data_type_name::<U>(),
317 std::any::type_name::<T>()
318 .rsplit("::")
319 .next()
320 .unwrap_or("unknown"),
321 )))
322}
323
324unsafe fn cast_slice<U, T>(values: &[U]) -> &[T] {
325 unsafe { &*(values as *const [U] as *const [T]) }
327}
328
329fn unsafe_cast<T, U: Copy>(value: U) -> T {
330 unsafe { std::mem::transmute_copy(&value) }
333}
334
335trait IntoF64 {
336 fn into_f64(self) -> f64;
337}
338
339impl IntoF64 for i8 {
340 fn into_f64(self) -> f64 {
341 self as f64
342 }
343}
344impl IntoF64 for u8 {
345 fn into_f64(self) -> f64 {
346 self as f64
347 }
348}
349impl IntoF64 for i16 {
350 fn into_f64(self) -> f64 {
351 self as f64
352 }
353}
354impl IntoF64 for u16 {
355 fn into_f64(self) -> f64 {
356 self as f64
357 }
358}
359impl IntoF64 for i32 {
360 fn into_f64(self) -> f64 {
361 self as f64
362 }
363}
364impl IntoF64 for u32 {
365 fn into_f64(self) -> f64 {
366 self as f64
367 }
368}
369impl IntoF64 for f32 {
370 fn into_f64(self) -> f64 {
371 self as f64
372 }
373}
374impl IntoF64 for f64 {
375 fn into_f64(self) -> f64 {
376 self
377 }
378}
379
380fn data_type_name<T: 'static>() -> &'static str {
381 if TypeId::of::<T>() == TypeId::of::<i8>() {
382 "i8"
383 } else if TypeId::of::<T>() == TypeId::of::<u8>() {
384 "u8"
385 } else if TypeId::of::<T>() == TypeId::of::<i16>() {
386 "i16"
387 } else if TypeId::of::<T>() == TypeId::of::<u16>() {
388 "u16"
389 } else if TypeId::of::<T>() == TypeId::of::<i32>() {
390 "i32"
391 } else if TypeId::of::<T>() == TypeId::of::<u32>() {
392 "u32"
393 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
394 "f32"
395 } else if TypeId::of::<T>() == TypeId::of::<f64>() {
396 "f64"
397 } else {
398 "unknown"
399 }
400}
401
402pub trait NdArrayElement: Sized + Clone {
403 fn from_pixel_data(pixels: PixelData) -> Result<Vec<Self>>;
404}
405
406mod private {
407 pub trait Sealed {}
408
409 impl Sealed for i8 {}
410 impl Sealed for u8 {}
411 impl Sealed for i16 {}
412 impl Sealed for u16 {}
413 impl Sealed for i32 {}
414 impl Sealed for u32 {}
415 impl Sealed for f32 {}
416 impl Sealed for f64 {}
417}
418
419#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
420pub enum BandElementKind {
421 I8,
422 U8,
423 I16,
424 U16,
425 I32,
426 U32,
427 F32,
428 F64,
429}
430
431pub trait BandElement: NdArrayElement + private::Sealed + Copy + Default + 'static {
432 const KIND: BandElementKind;
433}
434
435macro_rules! impl_exact_ndarray_element {
436 ($ty:ty, $variant:ident, $name:literal) => {
437 impl NdArrayElement for $ty {
438 fn from_pixel_data(pixels: PixelData) -> Result<Vec<Self>> {
439 match pixels {
440 PixelData::$variant(values) => Ok(values),
441 other => Err(Error::InvalidBlob(format!(
442 "cannot decode {} pixels into ndarray<{}>",
443 other.data_type().name(),
444 $name
445 ))),
446 }
447 }
448 }
449 };
450}
451
452impl_exact_ndarray_element!(i8, I8, "i8");
453impl_exact_ndarray_element!(u8, U8, "u8");
454impl_exact_ndarray_element!(i16, I16, "i16");
455impl_exact_ndarray_element!(u16, U16, "u16");
456impl_exact_ndarray_element!(i32, I32, "i32");
457impl_exact_ndarray_element!(u32, U32, "u32");
458impl_exact_ndarray_element!(f32, F32, "f32");
459
460impl NdArrayElement for f64 {
461 fn from_pixel_data(pixels: PixelData) -> Result<Vec<Self>> {
462 Ok(pixels.to_f64())
463 }
464}
465
466macro_rules! impl_band_element {
467 ($ty:ty, $kind:ident) => {
468 impl BandElement for $ty {
469 const KIND: BandElementKind = BandElementKind::$kind;
470 }
471
472 impl SupportedElementValue for $ty {
473 const KIND: BandElementKind = BandElementKind::$kind;
474 }
475 };
476}
477
478impl_band_element!(i8, I8);
479impl_band_element!(u8, U8);
480impl_band_element!(i16, I16);
481impl_band_element!(u16, U16);
482impl_band_element!(i32, I32);
483impl_band_element!(u32, U32);
484impl_band_element!(f32, F32);
485impl_band_element!(f64, F64);
486
487fn materialize_layout(layout: BandLayout) -> MaterializeLayout {
488 match layout {
489 BandLayout::Interleaved => MaterializeLayout::Interleaved,
490 BandLayout::Bsq => MaterializeLayout::Bsq,
491 }
492}
493
494fn materialize_error(err: lerc_band_materialize::MaterializeError) -> Error {
495 Error::InvalidBlob(err.to_string())
496}
497
498trait PixelDataExt {
499 fn into_ndarray<T: NdArrayElement>(self, shape: &[usize]) -> Result<ArrayD<T>>;
500}
501
502impl PixelDataExt for PixelData {
503 fn into_ndarray<T: NdArrayElement>(self, shape: &[usize]) -> Result<ArrayD<T>> {
504 ArrayD::from_shape_vec(IxDyn(shape), T::from_pixel_data(self)?).map_err(|err| {
505 Error::InvalidBlob(format!(
506 "failed to build ndarray from decoded pixels: {err}"
507 ))
508 })
509 }
510}