#![deny(missing_docs)]
use std::fmt::{Display, Formatter};
use std::io::Error;
mod bit;
mod decode;
mod encode;
const B: u8 = 66;
const M: u8 = 77;
const COLOR_PALLET_SIZE: u32 = 2 * 4;
const HEADER_SIZE: u32 = 2 + 12 + 40 + COLOR_PALLET_SIZE;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Bmp {
data: Vec<bool>,
width: usize,
}
#[derive(Debug)]
pub enum BmpError {
Generic,
Content,
Header,
Data,
}
impl Display for BmpError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for BmpError {}
#[derive(Debug)]
struct BmpHeader {
height: u32,
width: u32,
}
impl Bmp {
pub fn new(data: Vec<bool>, width: usize) -> Result<Bmp, BmpError> {
if data.is_empty() || width == 0 || data.len() % width != 0 {
Err(BmpError::Data)
} else {
Ok(Bmp { data, width })
}
}
pub fn height(&self) -> usize {
self.data.len() / self.width
}
pub fn width(&self) -> usize {
self.width
}
pub fn pixel(&self, i: usize, j: usize) -> bool {
let h = self.height() - i - 1;
self.data[h * self.width + j]
}
pub fn get(&self, x: usize, y: usize) -> bool {
self.data[x * self.width + y]
}
pub fn mul(&self, mul: usize) -> Bmp {
let mut data = vec![];
for i in 0..self.height() {
let mut row = vec![];
for j in 0..self.width {
for _ in 0..mul {
row.push(self.get(i, j));
}
}
for _ in 0..mul {
data.extend(row.clone());
}
}
let width = self.width * mul;
Bmp { data, width }
}
pub fn div(&self, div: usize) -> Result<Bmp, BmpError> {
let new_width = self.width / div;
if div <= 1 || new_width == 0 {
return Err(BmpError::Generic);
}
let mut new_data = vec![];
for (i, chunk) in self.data.chunks(div).enumerate() {
let row = i / new_width;
if chunk.iter().all(|e| chunk[0] == *e) {
if row % div == 0 {
new_data.push(chunk[0]);
}
} else {
return Err(BmpError::Generic);
}
}
Bmp::new(new_data, new_width)
}
fn div_with_greater_possible(&self, greater_start: usize) -> Bmp {
for i in (2..greater_start).rev() {
if let Ok(bmp) = self.div(i) {
return bmp;
}
}
self.clone()
}
pub fn normalize(&self) -> Bmp {
self.remove_white_border().div_with_greater_possible(10)
}
pub fn add_white_border(&self, border_size: usize) -> Bmp {
let width = self.width + border_size * 2;
let mut data = vec![];
data.extend(vec![false; width * border_size]);
for vec in self.data.chunks(self.width) {
data.extend(vec![false; border_size]);
data.extend(vec);
data.extend(vec![false; border_size]);
}
data.extend(vec![false; width * border_size]);
Bmp { data, width }
}
pub fn remove_white_border(&self) -> Bmp {
let mut cur = self.clone();
loop {
match cur.remove_one_white_border() {
Ok(bmp) => cur = bmp,
Err(_) => return cur,
}
}
}
fn remove_one_white_border(&self) -> Result<Bmp, BmpError> {
if self.width > 2
&& self
.data
.iter()
.enumerate()
.filter(|(i, _)| self.is_border(*i))
.all(|(_, e)| !*e)
{
let data: Vec<bool> = self
.data
.iter()
.enumerate()
.filter_map(|(i, e)| if self.is_border(i) { None } else { Some(*e) })
.collect();
Ok(Bmp::new(data, self.width - 2).unwrap())
} else {
Err(BmpError::Generic)
}
}
fn is_border(&self, i: usize) -> bool {
let max_h = self.height();
let max_w = self.width;
let h = i / max_w;
let w = i % max_w;
h == 0 || h == max_h - 1 || w == 0 || w == max_w - 1
}
}
impl From<std::io::Error> for BmpError {
fn from(_: Error) -> Self {
BmpError::Generic
}
}
impl BmpHeader {
fn bytes_per_row(&self) -> u32 {
(self.width + 7) / 8
}
fn padding(&self) -> u32 {
(4 - self.bytes_per_row() % 4) % 4
}
}
#[cfg(test)]
mod test {
use crate::*;
use rand::Rng;
use std::fs::File;
use std::io::Cursor;
#[test]
fn test_data_matrix() {
assert!(Bmp::new(vec![], 1).is_err());
assert!(Bmp::new(vec![true], 0).is_err());
assert!(Bmp::new(vec![true], 1).is_ok());
assert!(Bmp::new(vec![true], 2).is_err());
assert!(Bmp::new(vec![true, false], 2).is_ok());
assert!(Bmp::new(vec![true, false], 1).is_ok());
assert!(Bmp::new(vec![true, false, true], 1).is_ok());
assert!(Bmp::new(vec![true, false, true], 2).is_err());
}
#[test]
fn test_padding() {
let mut header = BmpHeader {
height: 0,
width: 0,
};
assert_eq!(header.padding(), 0);
header.width = 1;
assert_eq!(header.padding(), 3);
header.width = 9;
assert_eq!(header.padding(), 2);
header.width = 17;
assert_eq!(header.padding(), 1);
header.width = 25;
assert_eq!(header.padding(), 0);
}
#[test]
fn test_bytes_per_row() {
let mut header = BmpHeader {
height: 0,
width: 0,
};
assert_eq!(header.bytes_per_row(), 0);
header.width = 1;
assert_eq!(header.bytes_per_row(), 1);
header.width = 8;
assert_eq!(header.bytes_per_row(), 1);
header.width = 9;
assert_eq!(header.bytes_per_row(), 2);
}
#[test]
fn test_mul() {
let data = Bmp {
data: vec![false, true, false, true],
width: 2,
};
let data_bigger = Bmp {
data: vec![
false, false, true, true, false, false, true, true, false, false, true, true,
false, false, true, true,
],
width: 4,
};
assert_eq!(data.mul(2), data_bigger);
let data = Bmp {
data: vec![false, false, false, true],
width: 2,
};
let data_bigger = Bmp {
data: vec![
false, false, false, false, false, false, false, false, false, false, true, true,
false, false, true, true,
],
width: 4,
};
assert_eq!(data.mul(2), data_bigger);
}
#[test]
fn test_div() {
let data = Bmp {
data: vec![false, false, true, true, false, false, true, true],
width: 4,
};
let expected = Bmp {
data: vec![false, true],
width: 2,
};
assert_eq!(expected, data.div(2).unwrap());
}
#[test]
fn test_mul_div() {
let expected = random_bmp();
let mul = expected.mul(3);
let div = mul.div(3).unwrap();
assert_eq!(expected, div);
}
#[test]
fn test_add_white_border() {
let data = Bmp {
data: vec![false],
width: 1,
};
let data_bigger = Bmp {
data: vec![false; 25],
width: 5,
};
assert_eq!(data.add_white_border(2), data_bigger);
}
#[test]
fn test_bmp() {
let data_test1 = Bmp {
data: vec![false, true, true, false],
width: 2,
};
let bytes_test1 = Bmp::read(&mut File::open("test_bmp/test1.bmp").unwrap()).unwrap();
assert_eq!(data_test1, bytes_test1);
let bmp_test2 = data_test1.mul(3).add_white_border(12);
bmp_test2
.write(File::create("test_bmp/test2.bmp").unwrap())
.unwrap();
let bytes_test2 = Bmp::read(&mut File::open("test_bmp/test2.bmp").unwrap()).unwrap();
assert_eq!(bmp_test2, bytes_test2);
}
#[test]
fn test_monochrome_image() {
let expected = Bmp {
data: vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1,
1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
.iter()
.map(|e| *e != 0)
.collect(),
width: 18,
};
let bmp = Bmp::read(File::open("test_bmp/monochrome_image.bmp").unwrap()).unwrap();
assert_eq!(expected, bmp);
}
#[test]
fn test_rt() {
let expected = random_bmp();
let mut cursor = Cursor::new(vec![]);
expected.write(&mut cursor).unwrap();
cursor.set_position(0);
let bmp = Bmp::read(&mut cursor).unwrap();
assert_eq!(expected, bmp);
}
#[test]
fn test_get_and_pixel() {
let file = File::open("test_bmp/test1.bmp").unwrap();
let bmp = Bmp::read(file).unwrap();
assert!(!bmp.get(0, 0), "lower-left is not dark");
assert!(bmp.pixel(0, 0), "upper-left is not white");
}
#[test]
fn test_is_border() {
let bmp = Bmp {
data: vec![false; 9],
width: 3,
};
for i in 0..9 {
assert_eq!(bmp.is_border(i), i != 4);
}
let bmp = Bmp {
data: vec![false; 16],
width: 4,
};
for i in 0..16 {
assert_eq!(bmp.is_border(i), ![5, 6, 9, 10].contains(&i));
}
}
#[test]
fn test_remove_white_border() {
let bmp = Bmp {
data: vec![false; 25],
width: 5,
};
let expected = Bmp {
data: vec![false; 1],
width: 1,
};
assert_eq!(expected, bmp.remove_white_border());
}
#[test]
fn test_div_with_greater_possible() {
let bmp = random_bmp();
let mul = bmp.mul(4);
let div = mul.div_with_greater_possible(10);
assert_eq!(div, bmp);
}
#[test]
fn test_normalize() {
let bmp = Bmp::read(File::open("test_bmp/qr_not_normalized.bmp").unwrap()).unwrap();
let bmp_normalized = Bmp::read(File::open("test_bmp/qr_normalized.bmp").unwrap()).unwrap();
assert_eq!(bmp.normalize(), bmp_normalized);
}
fn random_bmp() -> Bmp {
let mut rng = rand::thread_rng();
let width = rng.gen_range(1, 20);
let height = rng.gen_range(1, 20);
let data: Vec<bool> = (0..width * height).map(|_| rng.gen()).collect();
Bmp::new(data, width).unwrap()
}
}