use simple_parse::*;
use std::io::{Read, Write};
#[derive(SpRead, SpWrite)]
#[sp(endian = "little")] pub struct BmpHeader {
#[sp(validate = "validate_magic_header")] pub magic: u16,
pub size: u32,
reserved1: u16,
reserved2: u16,
pixel_offset: u32,
#[sp(var_size)] dib: DIBHeader,
#[sp(
reader="readall_at_offset, pixel_offset, size", // Read the rest of the buffer into pixels
writer="writeall_at_offset, pixel_offset" // Write pixels for the rest of the buffer
)]
pixels: Vec<u32>,
}
use std::fmt;
impl fmt::Debug for BmpHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BmpHeader")
.field("magic", &self.magic)
.field("size", &self.size)
.field("pixel_offset", &self.pixel_offset)
.field("dib", &self.dib)
.field("pixels", &self.pixels.len())
.finish()
}
}
#[derive(Debug, SpRead, SpWrite)]
#[sp(id_type = "u32", endian = "little")] pub enum DIBHeader {
#[sp(id = 40)]
BitmapInfo {
width: i32,
height: i32,
planes: u16,
bit_count: u16,
compression: u32,
size: u32,
horizontal_res: i32,
vertical_res: i32,
clr_used: u32,
important: u32,
#[sp(
reader="BitmapCompression::read, compression", // self.compression_info = BitmapCompression::read(&compression, ...)?;
writer="BitmapCompression::write" // BitmapCompression::write(this: &BitmapCompression, ...)
)]
compression_info: BitmapCompression,
#[sp(
reader="parse_color_table, clr_used, compression_info, bit_count", // parse_color_table(clr_used: &u32, comp_info: &BitCompression, )
writer="write_color_table"
)]
color_palette: Vec<RgbQuad>,
},
}
#[derive(Debug)]
pub enum BitmapCompression {
None,
BitFields {
red: u32,
green: u32,
blue: u32,
},
AlphaBitFields {
alpha: u32,
red: u32,
green: u32,
blue: u32,
},
}
#[derive(Debug, SpRead, SpWrite)]
pub struct RgbQuad {
blue: u8,
green: u8,
red: u8,
reserved: u8,
}
impl BitmapCompression {
fn read<R: Read + ?Sized>(
compression_bitmask: &u32,
src: &mut R,
ctx: &mut SpCtx,
) -> Result<Self, SpError> {
const BI_RGB: u32 = 0;
const BI_BITFIELDS: u32 = 3;
const BI_ALPHABITFIELDS: u32 = 6;
match *compression_bitmask {
BI_RGB => Ok(Self::None),
BI_BITFIELDS => {
let red = u32::inner_from_reader(src, ctx)?;
let green = u32::inner_from_reader(src, ctx)?;
let blue = u32::inner_from_reader(src, ctx)?;
Ok(Self::BitFields { red, green, blue })
}
BI_ALPHABITFIELDS => {
let mut tmp = [0u8, 16];
validate_reader_exact(&mut tmp, src)?;
let mut checked_bytes = tmp.as_mut_ptr();
let alpha: u32;
let red: u32;
let green: u32;
let blue: u32;
unsafe {
alpha = u32::inner_from_reader_unchecked(checked_bytes, src, ctx)?;
checked_bytes = checked_bytes.add(4);
red = u32::inner_from_reader_unchecked(checked_bytes, src, ctx)?;
checked_bytes = checked_bytes.add(4);
green = u32::inner_from_reader_unchecked(checked_bytes, src, ctx)?;
checked_bytes = checked_bytes.add(4);
blue = u32::inner_from_reader_unchecked(checked_bytes, src, ctx)?;
}
Ok(Self::AlphaBitFields {
alpha,
red,
green,
blue,
})
}
_ => Err(SpError::InvalidBytes),
}
}
fn write<W: Write + ?Sized>(
this: &BitmapCompression,
ctx: &mut SpCtx,
dst: &mut W,
) -> Result<usize, SpError> {
match this {
&Self::None => Ok(0),
&Self::AlphaBitFields {
alpha,
red,
green,
blue,
} => {
alpha.inner_to_writer(ctx, dst)?;
red.inner_to_writer(ctx, dst)?;
green.inner_to_writer(ctx, dst)?;
blue.inner_to_writer(ctx, dst)?;
Ok(16)
}
&Self::BitFields { red, green, blue } => {
red.inner_to_writer(ctx, dst)?;
green.inner_to_writer(ctx, dst)?;
blue.inner_to_writer(ctx, dst)?;
Ok(12)
}
}
}
}
fn validate_magic_header(magic: &u16, ctx: &mut SpCtx) -> Result<(), SpError> {
println!("Validating magic bmp header !!");
if !ctx.is_reading {
return Ok(());
}
if *magic != 0x4D42 {
Err(SpError::InvalidBytes)
} else {
Ok(())
}
}
fn parse_color_table<R: Read + ?Sized>(
clr_used: &u32,
compression: &BitmapCompression,
bit_count: &u16,
src: &mut R,
ctx: &mut SpCtx,
) -> Result<Vec<RgbQuad>, SpError> {
if let BitmapCompression::None = compression {
return Ok(Vec::new());
}
let mut res = Vec::new();
match *bit_count {
1 | 2 => {
for _i in 0..(2u8).pow(*bit_count as _) {
res.push(RgbQuad::inner_from_reader(src, ctx)?);
}
}
4 | 8 | 16 | 24 | 32 => {
if *clr_used > (2u32).pow(*bit_count as _) {
return Err(SpError::InvalidBytes);
}
for _i in 0..*clr_used {
res.push(RgbQuad::inner_from_reader(src, ctx)?);
}
}
_ => return Err(SpError::InvalidBytes),
}
Ok(res)
}
fn write_color_table<W: Write + ?Sized>(
this: &Vec<RgbQuad>,
ctx: &mut SpCtx,
dst: &mut W,
) -> Result<usize, SpError> {
let mut size_written = 0;
for color in this.iter() {
size_written += color.inner_to_writer(ctx, dst)?;
}
Ok(size_written)
}