use deku::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
#[deku(endian = "little", id_type = "u8", ctx = "endian: deku::ctx::Endian")]
pub enum DecodeType {
#[deku(id = 1)]
None,
#[deku(id = 2)]
Default,
#[deku(id = 3)]
Lossless,
#[deku(id = 4)]
Palette,
#[deku(id_pat = "_")]
Unknown { tag: u8 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
#[deku(endian = "little", id_type = "u8", ctx = "endian: deku::ctx::Endian")]
pub enum PixelFormat {
#[deku(id = 1)]
G8,
#[deku(id = 2)]
GA88,
#[deku(id = 3)]
Rgb888,
#[deku(id = 4)]
Rgba8888,
#[deku(id_pat = "_")]
Unknown { tag: u8 },
}
impl PixelFormat {
pub fn bytes_per_pixel(self) -> usize {
match self {
Self::G8 => 1,
Self::GA88 => 2,
Self::Rgb888 => 3,
Self::Rgba8888 => 4,
Self::Unknown { tag } => tag as usize,
}
}
pub fn has_alpha(self) -> bool {
matches!(self, Self::GA88 | Self::Rgba8888)
}
pub fn is_color(self) -> bool {
matches!(self, Self::Rgb888 | Self::Rgba8888)
}
pub fn split_stream_components(self) -> usize {
if self.is_color() { 3 } else { 1 }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
#[deku(endian = "little", id_type = "u8", ctx = "endian: deku::ctx::Endian")]
pub enum Predictor {
#[deku(id = 0)]
None,
#[deku(id = 1)]
Paeth,
#[deku(id = 2)]
Left,
#[deku(id = 3)]
Up,
#[deku(id = 4)]
Mean,
#[deku(id_pat = "_")]
Unknown { tag: u8 },
}
#[derive(Debug, Clone, DekuRead)]
#[deku(magic = b"dmp2", endian = "little")]
pub struct Deepmap2Header {
pub decode_type: DecodeType,
pub version: u8,
pub predictor_type: u8,
pub pixel_format: PixelFormat,
pub width: u16,
pub height: u16,
#[deku(cond = "matches!(decode_type, DecodeType::Palette)")]
pub palette_size: Option<u16>,
#[deku(cond = "matches!(decode_type, DecodeType::Palette)")]
pub palette_type: Option<u16>,
#[deku(count = "palette_size.unwrap_or(0) as usize")]
pub palette: Vec<u32>,
}
impl Deepmap2Header {
pub fn header_size(&self) -> usize {
if matches!(self.decode_type, DecodeType::Palette) {
16 + self.palette_size.unwrap_or(0) as usize * 4
} else {
12
}
}
pub fn chroma_scale(&self) -> u8 {
if self.version != 0 { 1 } else { 0 }
}
pub fn pixel_count(&self) -> usize {
self.width as usize * self.height as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_header(bytes: &[u8]) -> Deepmap2Header {
let mut cursor = std::io::Cursor::new(bytes);
let mut reader = Reader::new(&mut cursor);
Deepmap2Header::from_reader_with_ctx(&mut reader, ()).unwrap()
}
#[test]
fn parse_known_header_default_type() {
let bytes: &[u8] = &[
0x64, 0x6d, 0x70, 0x32, 0x02, 0x01, 0x0a, 0x02, 0x64, 0x00, 0x90, 0x01, ];
let header = parse_header(bytes);
assert_eq!(header.decode_type, DecodeType::Default);
assert_eq!(header.version, 1);
assert_eq!(header.predictor_type, 10);
assert_eq!(header.pixel_format, PixelFormat::GA88);
assert_eq!(header.width, 100);
assert_eq!(header.height, 400);
assert_eq!(header.header_size(), 12);
assert_eq!(header.chroma_scale(), 1);
}
#[test]
fn parse_known_header_lossless_type() {
let bytes: &[u8] = &[
0x64, 0x6d, 0x70, 0x32, 0x03, 0x01, 0x0a, 0x02, 0x1e, 0x00, 0x32, 0x00, ];
let header = parse_header(bytes);
assert_eq!(header.decode_type, DecodeType::Lossless);
assert_eq!(header.width, 30);
assert_eq!(header.height, 50);
assert_eq!(header.header_size(), 12);
}
#[test]
fn parse_unknown_decode_type_returns_unknown_variant() {
let bytes: &[u8] = &[
0x64, 0x6d, 0x70, 0x32, 0xFF, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, ];
let header = parse_header(bytes);
assert!(matches!(
header.decode_type,
DecodeType::Unknown { tag: 0xFF }
));
}
#[test]
fn header_size_palette_type() {
let mut bytes = vec![
0x64, 0x6d, 0x70, 0x32, 0x04, 0x01, 0x0a, 0x04, 0x10, 0x00, 0x10, 0x00, 0x02, 0x00, 0x04, 0x00, ];
bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0xFF]); bytes.extend_from_slice(&[0xFF, 0x00, 0x00, 0xFF]); let header = parse_header(&bytes);
assert_eq!(header.decode_type, DecodeType::Palette);
assert_eq!(header.palette_size, Some(2));
assert_eq!(header.palette_type, Some(4));
assert_eq!(header.palette.len(), 2);
assert_eq!(header.header_size(), 24);
}
}