1use std::any::TypeId;
2
3use crate::materialize::{copy_band_values_into_slice, BandMaterializer};
4use lerc_core::{BandLayout, BandSetInfo, BlobInfo, Error, PixelData, Result};
5#[cfg(feature = "ndarray")]
6use ndarray::{ArrayD, IxDyn};
7
8#[cfg(feature = "ndarray")]
9use crate::allocation::{checked_mul, default_vec, vec_with_capacity};
10
11#[derive(Debug, Clone, PartialEq)]
13pub struct Decoded {
14 pub info: BlobInfo,
16 pub pixels: PixelData,
18 pub mask: Option<Vec<u8>>,
20}
21
22impl Decoded {
23 #[cfg(feature = "ndarray")]
24 pub fn to_ndarray<T: NdArrayElement>(&self) -> Result<ArrayD<T>> {
29 self.pixels.clone().into_ndarray(&self.info.ndarray_shape())
30 }
31
32 #[cfg(feature = "ndarray")]
33 pub fn into_ndarray<T: NdArrayElement>(self) -> Result<ArrayD<T>> {
38 let shape = self.info.ndarray_shape();
39 self.pixels.into_ndarray(&shape)
40 }
41
42 #[cfg(feature = "ndarray")]
43 pub fn into_mask_ndarray(self) -> Result<Option<ArrayD<u8>>> {
48 let shape = self.info.mask_ndarray_shape();
49 self.mask
50 .map(|mask| {
51 ArrayD::from_shape_vec(IxDyn(&shape), mask).map_err(|err| {
52 Error::invalid_blob(format!("failed to build ndarray from decoded mask: {err}"))
53 })
54 })
55 .transpose()
56 }
57
58 #[cfg(feature = "ndarray")]
59 pub fn mask_ndarray(&self) -> Result<Option<ArrayD<u8>>> {
64 let shape = self.info.mask_ndarray_shape();
65 self.mask
66 .as_ref()
67 .map(|mask| {
68 ArrayD::from_shape_vec(IxDyn(&shape), mask.clone()).map_err(|err| {
69 Error::invalid_blob(format!("failed to build ndarray from decoded mask: {err}"))
70 })
71 })
72 .transpose()
73 }
74}
75
76#[derive(Debug, Clone, PartialEq)]
78pub struct DecodedF64 {
79 pub info: BlobInfo,
81 pub pixels: Vec<f64>,
83 pub mask: Option<Vec<u8>>,
85}
86
87impl DecodedF64 {
88 #[cfg(feature = "ndarray")]
89 pub fn to_ndarray(&self) -> Result<ArrayD<f64>> {
94 ArrayD::from_shape_vec(IxDyn(&self.info.ndarray_shape()), self.pixels.clone()).map_err(
95 |err| {
96 Error::invalid_blob(format!(
97 "failed to build ndarray from decoded pixels: {err}"
98 ))
99 },
100 )
101 }
102
103 #[cfg(feature = "ndarray")]
104 pub fn into_ndarray(self) -> Result<ArrayD<f64>> {
109 ArrayD::from_shape_vec(IxDyn(&self.info.ndarray_shape()), self.pixels).map_err(|err| {
110 Error::invalid_blob(format!(
111 "failed to build ndarray from decoded pixels: {err}"
112 ))
113 })
114 }
115
116 #[cfg(feature = "ndarray")]
117 pub fn into_mask_ndarray(self) -> Result<Option<ArrayD<u8>>> {
122 let shape = self.info.mask_ndarray_shape();
123 self.mask
124 .map(|mask| {
125 ArrayD::from_shape_vec(IxDyn(&shape), mask).map_err(|err| {
126 Error::invalid_blob(format!("failed to build ndarray from decoded mask: {err}"))
127 })
128 })
129 .transpose()
130 }
131
132 #[cfg(feature = "ndarray")]
133 pub fn mask_ndarray(&self) -> Result<Option<ArrayD<u8>>> {
138 let shape = self.info.mask_ndarray_shape();
139 self.mask
140 .as_ref()
141 .map(|mask| {
142 ArrayD::from_shape_vec(IxDyn(&shape), mask.clone()).map_err(|err| {
143 Error::invalid_blob(format!("failed to build ndarray from decoded mask: {err}"))
144 })
145 })
146 .transpose()
147 }
148}
149
150#[derive(Debug, Clone, PartialEq)]
152pub struct DecodedBandSet {
153 info: BandSetInfo,
155 bands: Vec<PixelData>,
157 band_masks: Vec<Option<Vec<u8>>>,
159}
160
161impl DecodedBandSet {
162 pub(crate) fn new(
163 info: BandSetInfo,
164 bands: Vec<PixelData>,
165 band_masks: Vec<Option<Vec<u8>>>,
166 ) -> Result<Self> {
167 validate_band_set_parts(&info, &bands, &band_masks)?;
168 Ok(Self {
169 info,
170 bands,
171 band_masks,
172 })
173 }
174
175 pub fn info(&self) -> &BandSetInfo {
177 &self.info
178 }
179
180 pub fn bands(&self) -> &[PixelData] {
182 &self.bands
183 }
184
185 pub fn band_masks(&self) -> &[Option<Vec<u8>>] {
187 &self.band_masks
188 }
189
190 pub fn into_parts(self) -> (BandSetInfo, Vec<PixelData>, Vec<Option<Vec<u8>>>) {
192 (self.info, self.bands, self.band_masks)
193 }
194
195 #[cfg(feature = "ndarray")]
196 pub fn to_ndarray<T: BandElement>(&self) -> Result<ArrayD<T>> {
201 self.clone().into_ndarray()
202 }
203
204 #[cfg(feature = "ndarray")]
205 pub fn into_ndarray<T: BandElement>(self) -> Result<ArrayD<T>> {
210 self.into_ndarray_with_layout(BandLayout::Interleaved)
211 }
212
213 #[cfg(feature = "ndarray")]
214 pub fn into_ndarray_with_layout<T: BandElement>(self, layout: BandLayout) -> Result<ArrayD<T>> {
219 let shape = self.info.ndarray_shape_for_layout(layout);
220 let values = self.into_vec_with_layout(layout)?;
221 ArrayD::from_shape_vec(IxDyn(&shape), values).map_err(|err| {
222 Error::invalid_blob(format!(
223 "failed to build ndarray from decoded band set: {err}"
224 ))
225 })
226 }
227
228 pub fn into_vec_with_layout<T: BandElement>(self, layout: BandLayout) -> Result<Vec<T>> {
233 if self.bands.len() == 1 {
234 let band = self
235 .bands
236 .into_iter()
237 .next()
238 .ok_or(Error::Internal("single-band decode lost its only band"))?;
239 return T::from_pixel_data(band);
240 }
241
242 let mut materializer = BandMaterializer::new(
243 self.info.first().pixel_count()?,
244 self.info.depth() as usize,
245 self.info.band_count(),
246 layout,
247 )?;
248 for (band_index, band) in self.bands.into_iter().enumerate() {
249 copy_pixel_data_into_materializer(&mut materializer, band_index, band)?;
250 }
251 materializer.finish()
252 }
253
254 pub fn copy_into_slice<T: BandElement>(self, layout: BandLayout, out: &mut [T]) -> Result<()> {
259 let pixel_count = self.info.first().pixel_count()?;
260 let depth = self.info.depth() as usize;
261 let band_count = self.info.band_count();
262 let expected_len = self.info.value_count()?;
263 if out.len() != expected_len {
264 return Err(Error::InvalidArgument(
265 "output slice length does not match decoded band set length",
266 ));
267 }
268
269 for (band_index, band) in self.bands.into_iter().enumerate() {
270 copy_pixel_data_into_layout_slice(
271 out,
272 band_index,
273 pixel_count,
274 depth,
275 band_count,
276 layout,
277 band,
278 )?;
279 }
280 Ok(())
281 }
282
283 #[cfg(feature = "ndarray")]
284 pub fn into_band_mask_ndarray(self) -> Result<Option<ArrayD<u8>>> {
289 band_masks_into_ndarray(self.info, self.band_masks)
290 }
291
292 #[cfg(feature = "ndarray")]
293 pub fn band_mask_ndarray(&self) -> Result<Option<ArrayD<u8>>> {
298 band_masks_into_ndarray(self.info.clone(), self.band_masks.clone())
299 }
300}
301
302fn validate_band_set_parts(
303 info: &BandSetInfo,
304 bands: &[PixelData],
305 band_masks: &[Option<Vec<u8>>],
306) -> Result<()> {
307 if bands.len() != info.band_count() || band_masks.len() != info.band_count() {
308 return Err(Error::invalid_blob(
309 "decoded band and mask counts do not match band-set metadata",
310 ));
311 }
312 for ((band_info, band), mask) in info.bands().iter().zip(bands).zip(band_masks) {
313 if band.data_type() != band_info.data_type {
314 return Err(Error::invalid_blob(
315 "decoded band type does not match its metadata",
316 ));
317 }
318 if band.len() != band_info.sample_count()? {
319 return Err(Error::invalid_blob(
320 "decoded band length does not match its metadata",
321 ));
322 }
323 match mask {
324 Some(mask) => {
325 if mask.len() != band_info.pixel_count()? {
326 return Err(Error::invalid_blob(
327 "decoded band mask length does not match its metadata",
328 ));
329 }
330 let valid_pixel_count = mask.iter().filter(|&&value| value != 0).count();
331 if valid_pixel_count != band_info.valid_pixel_count as usize {
332 return Err(Error::invalid_blob(
333 "decoded band mask count does not match its metadata",
334 ));
335 }
336 }
337 None if band_info.valid_pixel_count as usize != band_info.pixel_count()? => {
338 return Err(Error::invalid_blob(
339 "decoded band is missing the mask required by its metadata",
340 ));
341 }
342 None => {}
343 }
344 }
345 Ok(())
346}
347
348#[cfg(feature = "ndarray")]
349pub(crate) fn band_masks_into_ndarray(
350 info: BandSetInfo,
351 band_masks: Vec<Option<Vec<u8>>>,
352) -> Result<Option<ArrayD<u8>>> {
353 if band_masks.iter().all(Option::is_none) {
354 return Ok(None);
355 }
356
357 let pixel_count = info.first().pixel_count()?;
358 let band_count = info.band_count();
359 let shape = info.band_mask_ndarray_shape();
360
361 if band_count == 1 {
362 let mask = band_masks
363 .into_iter()
364 .next()
365 .flatten()
366 .map(Ok)
367 .unwrap_or_else(|| default_vec(pixel_count, "decoded band mask"))?;
368 return ArrayD::from_shape_vec(IxDyn(&shape), mask)
369 .map(Some)
370 .map_err(|err| {
371 Error::invalid_blob(format!("failed to build ndarray from decoded mask: {err}"))
372 });
373 }
374
375 let merged_len = checked_mul(pixel_count, band_count, "decoded band mask length")?;
376 let mut merged = vec_with_capacity(merged_len, "decoded band mask")?;
377 for pixel in 0..pixel_count {
378 for band_mask in &band_masks {
379 merged.push(band_mask.as_ref().map(|mask| mask[pixel]).unwrap_or(1));
380 }
381 }
382
383 ArrayD::from_shape_vec(IxDyn(&shape), merged)
384 .map(Some)
385 .map_err(|err| {
386 Error::invalid_blob(format!(
387 "failed to build ndarray from decoded band mask: {err}"
388 ))
389 })
390}
391
392trait SupportedElementValue: Copy + 'static + IntoF64 {
393 const KIND: BandElementKind;
394}
395
396macro_rules! match_pixel_data_values {
397 ($band:expr, |$values:ident| $body:expr) => {
398 match $band {
399 PixelData::I8($values) => $body,
400 PixelData::U8($values) => $body,
401 PixelData::I16($values) => $body,
402 PixelData::U16($values) => $body,
403 PixelData::I32($values) => $body,
404 PixelData::U32($values) => $body,
405 PixelData::F32($values) => $body,
406 PixelData::F64($values) => $body,
407 }
408 };
409}
410
411fn copy_pixel_data_into_materializer<T: BandElement>(
412 materializer: &mut BandMaterializer<T>,
413 band_index: usize,
414 band: PixelData,
415) -> Result<()> {
416 match_pixel_data_values!(band, |values| {
417 copy_typed_values_into_materializer(materializer, band_index, &values)
418 })
419}
420
421fn copy_typed_values_into_materializer<T: BandElement, U: SupportedElementValue>(
422 materializer: &mut BandMaterializer<T>,
423 band_index: usize,
424 values: &[U],
425) -> Result<()> {
426 if T::KIND == U::KIND {
427 let typed = unsafe { cast_slice::<U, T>(values) };
431 return materializer.copy_band(band_index, typed);
432 }
433 if T::KIND == BandElementKind::F64 {
434 return materializer.copy_band_with(band_index, |index| {
435 unsafe_cast::<T, f64>(values[index].into_f64())
437 });
438 }
439 Err(Error::invalid_blob(format!(
440 "cannot decode {} pixels into ndarray<{}>",
441 data_type_name::<U>(),
442 std::any::type_name::<T>()
443 .rsplit("::")
444 .next()
445 .unwrap_or("unknown"),
446 )))
447}
448
449fn copy_pixel_data_into_layout_slice<T: BandElement>(
450 out: &mut [T],
451 band_index: usize,
452 pixel_count: usize,
453 depth: usize,
454 band_count: usize,
455 layout: BandLayout,
456 band: PixelData,
457) -> Result<()> {
458 match_pixel_data_values!(band, |values| {
459 copy_typed_values_into_layout_slice(
460 out,
461 band_index,
462 pixel_count,
463 depth,
464 band_count,
465 layout,
466 &values,
467 )
468 })
469}
470
471fn copy_typed_values_into_layout_slice<T: BandElement, U: SupportedElementValue>(
472 out: &mut [T],
473 band_index: usize,
474 pixel_count: usize,
475 depth: usize,
476 band_count: usize,
477 layout: BandLayout,
478 values: &[U],
479) -> Result<()> {
480 if T::KIND == U::KIND {
481 let typed = unsafe { cast_slice::<U, T>(values) };
485 return copy_band_values_into_slice(
486 out,
487 typed,
488 pixel_count,
489 depth,
490 band_index,
491 band_count,
492 layout,
493 );
494 }
495 if T::KIND == BandElementKind::F64 {
496 let band_len = pixel_count
497 .checked_mul(depth.max(1))
498 .ok_or(Error::SizeOverflow("decoded band length"))?;
499 if values.len() != band_len {
500 return Err(Error::invalid_blob(
501 "decoded band length does not match its metadata",
502 ));
503 }
504 for (value_index, value) in values.iter().copied().enumerate() {
505 let out_index = match layout {
506 BandLayout::Interleaved => {
507 if depth <= 1 {
508 value_index * band_count + band_index
509 } else {
510 let pixel = value_index / depth;
511 let sample = value_index % depth;
512 (pixel * band_count + band_index) * depth + sample
513 }
514 }
515 BandLayout::Bsq => band_index * band_len + value_index,
516 };
517 out[out_index] = unsafe_cast::<T, f64>(value.into_f64());
519 }
520 return Ok(());
521 }
522 Err(Error::invalid_blob(format!(
523 "cannot decode {} pixels into ndarray<{}>",
524 data_type_name::<U>(),
525 std::any::type_name::<T>()
526 .rsplit("::")
527 .next()
528 .unwrap_or("unknown"),
529 )))
530}
531
532unsafe fn cast_slice<U, T>(values: &[U]) -> &[T] {
533 debug_assert_eq!(std::mem::size_of::<U>(), std::mem::size_of::<T>());
534 debug_assert_eq!(std::mem::align_of::<U>(), std::mem::align_of::<T>());
535 unsafe { &*(values as *const [U] as *const [T]) }
537}
538
539fn unsafe_cast<T, U: Copy>(value: U) -> T {
540 debug_assert_eq!(std::mem::size_of::<U>(), std::mem::size_of::<T>());
541 debug_assert_eq!(std::mem::align_of::<U>(), std::mem::align_of::<T>());
542 unsafe { std::mem::transmute_copy(&value) }
545}
546
547trait IntoF64 {
548 fn into_f64(self) -> f64;
549}
550
551impl IntoF64 for i8 {
552 fn into_f64(self) -> f64 {
553 self as f64
554 }
555}
556impl IntoF64 for u8 {
557 fn into_f64(self) -> f64 {
558 self as f64
559 }
560}
561impl IntoF64 for i16 {
562 fn into_f64(self) -> f64 {
563 self as f64
564 }
565}
566impl IntoF64 for u16 {
567 fn into_f64(self) -> f64 {
568 self as f64
569 }
570}
571impl IntoF64 for i32 {
572 fn into_f64(self) -> f64 {
573 self as f64
574 }
575}
576impl IntoF64 for u32 {
577 fn into_f64(self) -> f64 {
578 self as f64
579 }
580}
581impl IntoF64 for f32 {
582 fn into_f64(self) -> f64 {
583 self as f64
584 }
585}
586impl IntoF64 for f64 {
587 fn into_f64(self) -> f64 {
588 self
589 }
590}
591
592fn data_type_name<T: 'static>() -> &'static str {
593 if TypeId::of::<T>() == TypeId::of::<i8>() {
594 "i8"
595 } else if TypeId::of::<T>() == TypeId::of::<u8>() {
596 "u8"
597 } else if TypeId::of::<T>() == TypeId::of::<i16>() {
598 "i16"
599 } else if TypeId::of::<T>() == TypeId::of::<u16>() {
600 "u16"
601 } else if TypeId::of::<T>() == TypeId::of::<i32>() {
602 "i32"
603 } else if TypeId::of::<T>() == TypeId::of::<u32>() {
604 "u32"
605 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
606 "f32"
607 } else if TypeId::of::<T>() == TypeId::of::<f64>() {
608 "f64"
609 } else {
610 "unknown"
611 }
612}
613
614mod private {
615 pub trait Sealed {}
616
617 impl Sealed for i8 {}
618 impl Sealed for u8 {}
619 impl Sealed for i16 {}
620 impl Sealed for u16 {}
621 impl Sealed for i32 {}
622 impl Sealed for u32 {}
623 impl Sealed for f32 {}
624 impl Sealed for f64 {}
625}
626
627#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
629pub enum BandElementKind {
630 I8,
632 U8,
634 I16,
636 U16,
638 I32,
640 U32,
642 F32,
644 F64,
646}
647
648pub trait BandElement: private::Sealed + Copy + Default + Send + Sync + 'static {
650 const KIND: BandElementKind;
652 fn from_pixel_data(pixels: PixelData) -> Result<Vec<Self>>;
654}
655
656#[cfg(feature = "ndarray")]
657pub trait NdArrayElement: BandElement {}
659
660macro_rules! impl_exact_band_element {
661 ($ty:ty, $variant:ident, $name:literal) => {
662 impl BandElement for $ty {
663 const KIND: BandElementKind = BandElementKind::$variant;
664
665 fn from_pixel_data(pixels: PixelData) -> Result<Vec<Self>> {
666 match pixels {
667 PixelData::$variant(values) => Ok(values),
668 other => Err(Error::invalid_blob(format!(
669 "cannot decode {} pixels into ndarray<{}>",
670 other.data_type().name(),
671 $name
672 ))),
673 }
674 }
675 }
676 };
677}
678
679impl_exact_band_element!(i8, I8, "i8");
680impl_exact_band_element!(u8, U8, "u8");
681impl_exact_band_element!(i16, I16, "i16");
682impl_exact_band_element!(u16, U16, "u16");
683impl_exact_band_element!(i32, I32, "i32");
684impl_exact_band_element!(u32, U32, "u32");
685impl_exact_band_element!(f32, F32, "f32");
686
687impl BandElement for f64 {
688 const KIND: BandElementKind = BandElementKind::F64;
689
690 fn from_pixel_data(pixels: PixelData) -> Result<Vec<Self>> {
691 Ok(pixels.to_f64())
692 }
693}
694
695#[cfg(feature = "ndarray")]
696macro_rules! impl_ndarray_element {
697 ($($ty:ty),+ $(,)?) => { $(impl NdArrayElement for $ty {})+ };
698}
699
700#[cfg(feature = "ndarray")]
701impl_ndarray_element!(i8, u8, i16, u16, i32, u32, f32, f64);
702
703macro_rules! impl_band_element {
704 ($ty:ty, $kind:ident) => {
705 impl SupportedElementValue for $ty {
706 const KIND: BandElementKind = BandElementKind::$kind;
707 }
708 };
709}
710
711impl_band_element!(i8, I8);
712impl_band_element!(u8, U8);
713impl_band_element!(i16, I16);
714impl_band_element!(u16, U16);
715impl_band_element!(i32, I32);
716impl_band_element!(u32, U32);
717impl_band_element!(f32, F32);
718impl_band_element!(f64, F64);
719
720#[cfg(feature = "ndarray")]
721trait PixelDataExt {
722 fn into_ndarray<T: NdArrayElement>(self, shape: &[usize]) -> Result<ArrayD<T>>;
723}
724
725#[cfg(feature = "ndarray")]
726impl PixelDataExt for PixelData {
727 fn into_ndarray<T: NdArrayElement>(self, shape: &[usize]) -> Result<ArrayD<T>> {
728 ArrayD::from_shape_vec(IxDyn(shape), T::from_pixel_data(self)?).map_err(|err| {
729 Error::invalid_blob(format!(
730 "failed to build ndarray from decoded pixels: {err}"
731 ))
732 })
733 }
734}