use embedded_graphics::prelude::*;
use crate::{
color_table::ColorTable,
parser::{le_u16, le_u32, take2, take_slice},
try_const, ParseError,
};
mod dib_header;
use dib_header::DibHeader;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[non_exhaustive]
pub enum Bpp {
Bits1,
Bits4,
Bits8,
Bits16,
Bits24,
Bits32,
}
impl Bpp {
const fn new(value: u16) -> Result<Self, ParseError> {
Ok(match value {
1 => Self::Bits1,
4 => Self::Bits4,
8 => Self::Bits8,
16 => Self::Bits16,
24 => Self::Bits24,
32 => Self::Bits32,
_ => return Err(ParseError::UnsupportedBpp(value)),
})
}
const fn parse(input: &[u8]) -> Result<(&[u8], Self), ParseError> {
let (input, value) = try_const!(le_u16(input));
Ok((input, try_const!(Self::new(value))))
}
pub const fn bits(self) -> u16 {
match self {
Self::Bits1 => 1,
Self::Bits4 => 4,
Self::Bits8 => 8,
Self::Bits16 => 16,
Self::Bits24 => 24,
Self::Bits32 => 32,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[non_exhaustive]
pub enum RowOrder {
BottomUp,
TopDown,
}
impl Default for RowOrder {
fn default() -> Self {
Self::BottomUp
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Header {
pub file_size: u32,
pub image_data_start: usize,
pub image_size: Size,
pub bpp: Bpp,
pub image_data_len: u32,
pub channel_masks: Option<ChannelMasks>,
pub row_order: RowOrder,
pub compression_method: CompressionMethod,
}
impl Header {
pub(crate) const fn parse(
input: &[u8],
) -> Result<(&[u8], (Header, Option<ColorTable<'_>>)), ParseError> {
let (input, magic) = try_const!(take2(input));
if !matches!(&magic, b"BM") {
return Err(ParseError::InvalidFileSignature(magic));
}
let (input, file_size) = try_const!(le_u32(input));
let (input, _reserved_1) = try_const!(le_u16(input));
let (input, _reserved_2) = try_const!(le_u16(input));
let (input, image_data_start) = try_const!(le_u32(input));
let (input, dib_header) = try_const!(DibHeader::parse(input));
let (input, color_table) = if dib_header.color_table_num_entries > 0 {
let (input, table) = try_const!(take_slice(
input,
dib_header.color_table_num_entries as usize * 4
));
(input, Some(ColorTable::new(table)))
} else {
(input, None)
};
Ok((
input,
(
Header {
file_size,
image_data_start: image_data_start as usize,
image_size: dib_header.image_size,
image_data_len: dib_header.image_data_len,
bpp: dib_header.bpp,
channel_masks: dib_header.channel_masks,
row_order: dib_header.row_order,
compression_method: dib_header.compression,
},
color_table,
),
))
}
pub(crate) const fn bytes_per_row(&self) -> usize {
let bits_per_row = self.image_size.width as usize * self.bpp.bits() as usize;
(bits_per_row + 31) / 32 * (32 / 8)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct ChannelMasks {
pub red: u32,
pub green: u32,
pub blue: u32,
pub alpha: u32,
}
impl ChannelMasks {
pub const RGB555: Self = Self {
red: 0b11111_00000_00000,
green: 0b00000_11111_00000,
blue: 0b00000_00000_11111,
alpha: 0,
};
pub const RGB565: Self = Self {
red: 0b11111_000000_00000,
green: 0b00000_111111_00000,
blue: 0b00000_000000_11111,
alpha: 0,
};
pub const RGB888: Self = Self {
red: 0xFF0000,
green: 0x00FF00,
blue: 0x0000FF,
alpha: 0,
};
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum CompressionMethod {
Rgb,
Bitfields,
Rle8,
Rle4,
}
impl CompressionMethod {
const fn new(value: u32) -> Result<Self, ParseError> {
Ok(match value {
0 => Self::Rgb,
1 => Self::Rle8,
2 => Self::Rle4,
3 => Self::Bitfields,
_ => return Err(ParseError::UnsupportedCompressionMethod(value)),
})
}
const fn parse(input: &[u8]) -> Result<(&[u8], Self), ParseError> {
let (input, value) = try_const!(le_u32(input));
Ok((input, try_const!(Self::new(value))))
}
}