use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum PixelType {
U8,
U16,
U32,
I8,
I16,
I32,
F32,
F64,
Rgb8,
Rgba8,
}
impl PixelType {
pub fn size_bytes(self) -> usize {
match self {
Self::U8 | Self::I8 => 1,
Self::U16 | Self::I16 => 2,
Self::U32 | Self::I32 | Self::F32 => 4,
Self::F64 => 8,
Self::Rgb8 => 3,
Self::Rgba8 => 4,
#[allow(unreachable_patterns)]
_ => 0,
}
}
pub fn is_float(self) -> bool {
matches!(self, Self::F32 | Self::F64)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BandDescriptor {
pub name: Option<String>,
pub dtype: PixelType,
pub nodata: Option<f64>,
pub unit: Option<String>,
pub scale: Option<f64>,
pub offset: Option<f64>,
}
impl BandDescriptor {
pub fn new(name: Option<String>, dtype: PixelType) -> Self {
Self {
name,
dtype,
nodata: None,
unit: None,
scale: None,
offset: None,
}
}
pub fn with_nodata(mut self, nodata: f64) -> Self {
self.nodata = Some(nodata);
self
}
}
#[derive(Clone, PartialEq)]
pub struct Band {
pub descriptor: BandDescriptor,
pub data: Vec<u8>,
}
impl Band {
pub fn new(descriptor: BandDescriptor, data: Vec<u8>) -> Self {
Self { descriptor, data }
}
pub fn pixel_count(&self) -> usize {
let bpp = self.descriptor.dtype.size_bytes();
self.data.len().checked_div(bpp).unwrap_or(0)
}
}
impl fmt::Debug for Band {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Band")
.field("descriptor", &self.descriptor)
.field("data_bytes", &self.data.len())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pixel_type_sizes() {
assert_eq!(PixelType::U8.size_bytes(), 1);
assert_eq!(PixelType::U16.size_bytes(), 2);
assert_eq!(PixelType::F32.size_bytes(), 4);
assert_eq!(PixelType::F64.size_bytes(), 8);
assert_eq!(PixelType::Rgb8.size_bytes(), 3);
assert_eq!(PixelType::Rgba8.size_bytes(), 4);
}
#[test]
fn pixel_type_float_classification() {
assert!(PixelType::F32.is_float());
assert!(PixelType::F64.is_float());
assert!(!PixelType::U8.is_float());
assert!(!PixelType::Rgb8.is_float());
}
#[test]
fn band_pixel_count_matches_data_size() {
let desc = BandDescriptor::new(Some("red".into()), PixelType::U8);
let band = Band::new(desc, vec![0u8; 256 * 256]);
assert_eq!(band.pixel_count(), 256 * 256);
}
#[test]
fn band_descriptor_builder() {
let desc =
BandDescriptor::new(Some("elevation".into()), PixelType::F32).with_nodata(-9999.0);
assert_eq!(desc.nodata, Some(-9999.0));
assert!(desc.dtype.is_float());
}
}