Skip to content

Instantly share code, notes, and snippets.

@karno
Last active November 6, 2018 16:44
Show Gist options
  • Save karno/636e673589ede837b4645f8ce07d60fa to your computer and use it in GitHub Desktop.
Save karno/636e673589ede837b4645f8ce07d60fa to your computer and use it in GitHub Desktop.
bmp2gray
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