Last active
November 6, 2018 16:44
-
-
Save karno/636e673589ede837b4645f8ce07d60fa to your computer and use it in GitHub Desktop.
bmp2gray
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::env; | |
use std::fs::File; | |
use std::io::prelude::*; | |
use std::io::{BufReader, BufWriter, Read, Result, SeekFrom, Write}; | |
use std::mem::transmute; | |
trait ToInt<T> { | |
fn to_int(&self) -> T; | |
} | |
trait ToUInt<T> { | |
fn to_uint(&self) -> T; | |
} | |
trait ToByteArray { | |
fn to_bytearray(&self) -> Vec<u8>; | |
} | |
impl ToUInt<u8> for [u8; 1] { | |
fn to_uint(&self) -> u8 { | |
self[0] | |
} | |
} | |
impl ToInt<i8> for [u8; 1] { | |
fn to_int(&self) -> i8 { | |
unsafe { transmute::<[u8; 1], i8>(*self) }.to_le() | |
} | |
} | |
impl ToByteArray for u8 { | |
fn to_bytearray(&self) -> Vec<u8> { | |
vec![*self; 1] | |
} | |
} | |
impl ToByteArray for i8 { | |
fn to_bytearray(&self) -> Vec<u8> { | |
unsafe { transmute::<i8, [u8; 1]>(self.to_le()) }.to_vec() | |
} | |
} | |
impl ToUInt<u16> for [u8; 2] { | |
fn to_uint(&self) -> u16 { | |
unsafe { transmute::<[u8; 2], u16>(*self) }.to_le() | |
} | |
} | |
impl ToInt<i16> for [u8; 2] { | |
fn to_int(&self) -> i16 { | |
unsafe { transmute::<[u8; 2], i16>(*self) }.to_le() | |
} | |
} | |
impl ToByteArray for u16 { | |
fn to_bytearray(&self) -> Vec<u8> { | |
unsafe { transmute::<u16, [u8; 2]>(self.to_le()) }.to_vec() | |
} | |
} | |
impl ToByteArray for i16 { | |
fn to_bytearray(&self) -> Vec<u8> { | |
unsafe { transmute::<i16, [u8; 2]>(self.to_le()) }.to_vec() | |
} | |
} | |
impl ToUInt<u32> for [u8; 4] { | |
fn to_uint(&self) -> u32 { | |
unsafe { transmute::<[u8; 4], u32>(*self) }.to_le() | |
} | |
} | |
impl ToInt<i32> for [u8; 4] { | |
fn to_int(&self) -> i32 { | |
unsafe { transmute::<[u8; 4], i32>(*self) }.to_le() | |
} | |
} | |
impl ToByteArray for u32 { | |
fn to_bytearray(&self) -> Vec<u8> { | |
unsafe { transmute::<u32, [u8; 4]>(self.to_le()) }.to_vec() | |
} | |
} | |
impl ToByteArray for i32 { | |
fn to_bytearray(&self) -> Vec<u8> { | |
unsafe { transmute::<i32, [u8; 4]>(self.to_le()) }.to_vec() | |
} | |
} | |
trait IntValueReader { | |
fn read_i8(&mut self) -> Result<i8>; | |
fn read_i16(&mut self) -> Result<i16>; | |
fn read_i32(&mut self) -> Result<i32>; | |
fn read_u8(&mut self) -> Result<u8>; | |
fn read_u16(&mut self) -> Result<u16>; | |
fn read_u32(&mut self) -> Result<u32>; | |
} | |
impl<T: Read> IntValueReader for T { | |
fn read_i8(&mut self) -> Result<i8> { | |
let mut buf = [0u8; 1]; | |
self.read_exact(&mut buf)?; | |
Ok(buf.to_int()) | |
} | |
fn read_i16(&mut self) -> Result<i16> { | |
let mut buf = [0u8; 2]; | |
self.read_exact(&mut buf)?; | |
Ok(buf.to_int()) | |
} | |
fn read_i32(&mut self) -> Result<i32> { | |
let mut buf = [0u8; 4]; | |
self.read_exact(&mut buf)?; | |
Ok(buf.to_int()) | |
} | |
fn read_u8(&mut self) -> Result<u8> { | |
let mut buf = [0u8; 1]; | |
self.read_exact(&mut buf)?; | |
Ok(buf.to_uint()) | |
} | |
fn read_u16(&mut self) -> Result<u16> { | |
let mut buf = [0u8; 2]; | |
self.read_exact(&mut buf)?; | |
Ok(buf.to_uint()) | |
} | |
fn read_u32(&mut self) -> Result<u32> { | |
let mut buf = [0u8; 4]; | |
self.read_exact(&mut buf)?; | |
Ok(buf.to_uint()) | |
} | |
} | |
trait IntValueWriter { | |
fn write_int<T: ToByteArray>(&mut self, value: &T) -> Result<()>; | |
} | |
impl<T: Write> IntValueWriter for T { | |
fn write_int<V: ToByteArray>(&mut self, value: &V) -> Result<()> { | |
self.write_all(&value.to_bytearray())?; | |
Ok(()) | |
} | |
} | |
trait Grayable { | |
fn to_grayscale(&self) -> Self; | |
} | |
struct Pixel { | |
r: u8, | |
g: u8, | |
b: u8, | |
} | |
impl Pixel { | |
fn read(reader: &mut Read, bit_size: u16, len: u32) -> Result<Vec<Pixel>> { | |
assert!(bit_size == 24 || bit_size == 32); | |
let mut buf = [0u8; 3]; | |
let mut skip_buf = [0u8; 1]; | |
let mut ret = Vec::<Pixel>::new(); | |
for _ in 0..len { | |
reader.read_exact(&mut buf)?; | |
ret.push(Pixel { | |
b: buf[0], | |
g: buf[1], | |
r: buf[2], | |
}); | |
if bit_size == 32 { | |
reader.read_exact(&mut skip_buf)?; | |
} | |
} | |
Ok(ret) | |
} | |
fn write(&self, mut writer: &mut Write, bit_size: u16) -> Result<()> { | |
assert!(bit_size == 24 || bit_size == 32); | |
writer.write_int(&self.b)?; | |
writer.write_int(&self.g)?; | |
writer.write_int(&self.r)?; | |
if bit_size == 32 { | |
writer.write_int(&0u8)?; | |
} | |
Ok(()) | |
} | |
} | |
impl Grayable for Pixel { | |
fn to_grayscale(&self) -> Pixel { | |
let r: f32 = self.r as f32; | |
let g: f32 = self.g as f32; | |
let b: f32 = self.b as f32; | |
let y: u8 = (0.2126 * r + 0.7152 * g + 0.0722 * b) as u8; | |
return Pixel { r: y, g: y, b: y }; | |
} | |
} | |
struct BitmapImage { | |
width: u32, | |
height: u32, | |
pixels: Vec<Pixel>, | |
} | |
struct BitmapFileHeader { | |
fsize: u32, | |
offset: u32, | |
} | |
impl BitmapImage { | |
pub fn read(path: &str) -> Result<BitmapImage> { | |
let mut f = File::open(path).unwrap(); | |
let file_header = BitmapFileHeader::read(&mut f)?; | |
// read headers (already read the ) | |
let info_header = BitmapInfoHeader::read(&mut f)?; | |
let pixel_len = info_header.width * info_header.height; | |
// begin reading pixels | |
f.seek(SeekFrom::Start(file_header.offset as u64))?; | |
println!( | |
"bits of color: {:?} - must be 24 or 32.", | |
info_header.color_bit | |
); | |
let pixels = Pixel::read( | |
&mut BufReader::new(f), | |
info_header.color_bit, | |
pixel_len as u32, | |
)?; | |
Ok(BitmapImage { | |
width: info_header.width as u32, | |
height: info_header.height as u32, | |
pixels: pixels, | |
}) | |
} | |
pub fn write(&self, path: &str) -> Result<()> { | |
let mut f = BufWriter::new(File::create(path).unwrap()); | |
// write 24bit bitmap | |
let fsize = (14 + 40 + (24 * self.width * self.height)) as u32; | |
let offset = (14 + 40) as u32; | |
// write bitmap file header | |
f.write_all("BM".as_bytes())?; | |
f.write_int(&fsize)?; | |
f.write_all(&[0u8; 4])?; // write reserved area | |
f.write_int(&offset)?; | |
// write information header | |
f.write_int(&40u32)?; | |
f.write_int(&(self.width as i32))?; | |
f.write_int(&(self.height as i32))?; | |
f.write_int(&1u16)?; // plane | |
f.write_int(&24u16)?; // color bits | |
f.write_int(&0u32)?; // compression | |
f.write_int(&0u32)?; // image size, 0 could be valid value | |
f.write_int(&0i32)?; // ppm_horz | |
f.write_int(&0i32)?; // ppm_vert | |
f.write_int(&0u32)?; // num of pallettes | |
f.write_int(&0u32)?; // num of important colors | |
// write pixels | |
for p in &self.pixels { | |
p.write(&mut f, 24)?; | |
} | |
// write file | |
Ok(()) | |
} | |
} | |
impl Grayable for BitmapImage { | |
fn to_grayscale(&self) -> BitmapImage { | |
let mut a = Vec::new(); | |
for p in &self.pixels { | |
a.push(p.to_grayscale()); | |
} | |
return BitmapImage { | |
width: self.width, | |
height: self.height, | |
pixels: a, | |
}; | |
} | |
} | |
impl BitmapFileHeader { | |
fn read<T: Read>(reader: &mut T) -> Result<BitmapFileHeader> { | |
// read and validate file header | |
let mut ftype = [0u8; 2]; | |
reader.read_exact(&mut ftype)?; | |
assert_eq!(ftype, "BM".as_bytes()); | |
// read file size | |
let fsize = reader.read_u32()?; | |
// skip reserved area | |
reader.read_u32()?; | |
// read offset | |
let offset = reader.read_u32()?; | |
return Ok(BitmapFileHeader { | |
fsize: fsize, | |
offset: offset, | |
}); | |
} | |
} | |
enum BitmapInfoHeaderFormat { | |
Windows, | |
OS2, | |
} | |
struct BitmapInfoHeader { | |
format: BitmapInfoHeaderFormat, | |
width: i32, | |
height: i32, | |
color_bit: u16, | |
} | |
impl BitmapInfoHeader { | |
fn read<T: Read>(reader: &mut T) -> Result<BitmapInfoHeader> { | |
let header_size = reader.read_u32()?; | |
match header_size { | |
16 => { | |
let width = reader.read_i16()? as i32; | |
let height = reader.read_i16()? as i32; | |
assert_eq!(reader.read_u16()?, 1u16); //plane | |
let color_bit = reader.read_u16()?; | |
Ok(BitmapInfoHeader { | |
format: BitmapInfoHeaderFormat::OS2, | |
width: width, | |
height: height, | |
color_bit: color_bit, | |
}) | |
} | |
40 => { | |
let width = reader.read_i32()?; | |
let height = reader.read_i32()?; | |
assert_eq!(reader.read_u16()?, 1u16); // plane | |
let color_bit = reader.read_u16()?; | |
reader.read_u32()?; // compression | |
reader.read_u32()?; // data_size | |
reader.read_i32()?; // horizontal pixel per meter | |
reader.read_i32()?; // vertical pixel per meter | |
reader.read_u32()?; // num of pallettes | |
reader.read_u32()?; // num of important colors | |
Ok(BitmapInfoHeader { | |
format: BitmapInfoHeaderFormat::Windows, | |
width: width, | |
height: height, | |
color_bit: color_bit, | |
}) | |
} | |
_ => Err(std::io::Error::new( | |
std::io::ErrorKind::InvalidData, | |
"Unknown header size", | |
)), | |
} | |
} | |
} | |
struct BitmapPalette { | |
b: u8, | |
g: u8, | |
r: u8, | |
} | |
impl BitmapPalette { | |
fn read(reader: &mut BufReader<File>, bit_size: u16, len: u32) -> Result<Vec<BitmapPalette>> { | |
assert!(bit_size == 24 || bit_size == 32); | |
let mut buf = [0u8; 3]; | |
let mut skip_buf = [0u8; 1]; | |
let mut ret = Vec::<BitmapPalette>::new(); | |
for _ in 0..len { | |
reader.read_exact(&mut buf)?; | |
ret.push(BitmapPalette { | |
b: buf[0], | |
g: buf[1], | |
r: buf[2], | |
}); | |
if bit_size == 32 { | |
reader.read_exact(&mut skip_buf)?; | |
} | |
} | |
Ok(ret) | |
} | |
} | |
fn main() { | |
let args: Vec<String> = env::args().collect(); | |
if args.len() < 3 { | |
println!("usage: {:?} [BITMAP_IMAGE] [OUTPUT_FILE]", args[0]); | |
return; | |
} | |
let img = BitmapImage::read(&args[1]).unwrap(); | |
let grayed = img.to_grayscale(); | |
grayed.write(&args[2]).unwrap(); | |
println!("finished.!"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment