diff --git a/Cargo.toml b/Cargo.toml index 2da53e5..ad506e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "binimage" -version = "0.1.0" +version = "0.1.1" authors = ["teaearlgraycold "] repository = "https://github.com/teaearlgraycold/binimage" license = "MIT" diff --git a/README.md b/README.md index 7bf5444..9d8f092 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,25 @@ Build with cargo: `cargo build --release` ``` Usage: - binimage [--width=] - binimage [--height=] + binimage [--width=] [--bitdepth=] + binimage [--height=] [--bitdepth=] binimage (-h | --help) Options: - -h --help Show this screen. - --width= Specify output image width (default is sqrt of the file size). - --height= + -h --help Show this screen. + --width= Specify output image width. Default is sqrt of the file size. + --height= Specify output image height. Default is sqrt of the file size. + --bitdepth= Number of bits per pixel. Default is 24. Less than 12 is grayscale. + Valid values: 1, 2, 4, 8, 12, 24 ``` -### Example +### Examples + +Running `binimage` on an Atari ROM produces interesting results. This is a +cropped output image. + +![binimage ran on MarioBros](examples/mario_bros.png) This is the image produced when `binimage` is ran on its own binary. -![binimage ran on itself](example.png) +![binimage ran on itself](examples/binimage.png) diff --git a/example.png b/examples/binimage.png similarity index 100% rename from example.png rename to examples/binimage.png diff --git a/examples/mario_bros.png b/examples/mario_bros.png new file mode 100644 index 0000000..a64177f Binary files /dev/null and b/examples/mario_bros.png differ diff --git a/src/main.rs b/src/main.rs index b69b2d1..0371d94 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,21 +8,25 @@ use std::io::Read; use std::fs::File; use std::path::Path; +use image::ColorType; use docopt::Docopt; +const BITS_PER_BYTE: f32 = 8.0; const USAGE: &'static str = " binimage Create an image from the binary data of a file. Usage: - binimage [--width=] - binimage [--height=] + binimage [--width=] [--bitdepth=] + binimage [--height=] [--bitdepth=] binimage (-h | --help) Options: - -h --help Show this screen. - --width= Specify output image width (default is sqrt of the file size). - --height= + -h --help Show this screen. + --width= Specify output image width. Default is sqrt of the file size. + --height= Specify output image height. Default is sqrt of the file size. + --bitdepth= Number of bits per pixel. Default is 24. Less than 12 is grayscale. + Valid values: 1, 2, 4, 8, 12, 24 "; #[derive(Debug, Deserialize)] @@ -30,7 +34,8 @@ struct Args { arg_input: String, arg_output: String, flag_width: u32, - flag_height: u32 + flag_height: u32, + flag_bitdepth: u8 } fn main() { @@ -41,10 +46,30 @@ fn main() { render_file(args); } +fn colortype_from_bitdepth(bitdepth: u8) -> ColorType { + match bitdepth { + 0 => image::RGB(8), + 1 => image::Gray(1), + 2 => image::Gray(2), + 4 => image::Gray(4), + 8 => image::Gray(8), + 12 => image::RGB(4), + 24 => image::RGB(8), + _ => panic!("Invalid bit-depth: {}", bitdepth) + } +} + +fn bits_per_pixel(c: ColorType) -> u32 { + match c { + ColorType::Gray(n) => n as u32, + ColorType::RGB(n) => 3 * n as u32, + _ => panic!("Unsupported ColorType") + } +} + // The shape to give an image from its file size -fn image_shape(buffer_size: usize, arg_width: u32, arg_height: u32) -> (u32, u32) { - let arg_size = (arg_width, arg_height); - let num_pixels = (buffer_size as f32) / 3.0; +fn image_shape(buffer_size: usize, arg_size: (u32, u32), colortype: ColorType) -> (u32, u32) { + let num_pixels = ((buffer_size as f32) * BITS_PER_BYTE / bits_per_pixel(colortype) as f32).ceil(); if arg_size.0 > num_pixels as u32 || arg_size.1 > num_pixels as u32 { panic!("Neither height nor width can be greater than the file size / 3."); @@ -69,9 +94,10 @@ fn image_shape(buffer_size: usize, arg_width: u32, arg_height: u32) -> (u32, u32 } // The number of additional bytes necessary to match the buffer size and image size (in pixels) -fn bytes_to_add(buffer_size: usize, dims: (u32, u32)) -> u32 { - let bytes_required = dims.0 * dims.1 * 3; // 3 bytes per pixel - return bytes_required - (buffer_size as u32); +fn bytes_to_add(buffer_size: usize, dims: (u32, u32), colortype: ColorType) -> u32 { + let bits_required = dims.0 * dims.1 * bits_per_pixel(colortype); + let bytes_required = (bits_required as f32 / BITS_PER_BYTE).ceil() as u32; + return bytes_required - buffer_size as u32; } fn render_file(args: Args) { @@ -90,8 +116,10 @@ fn render_file(args: Args) { Err(why) => panic!("Couldn't read {}: {}", input_path.display(), why.description()) }; - let dims = image_shape(size, args.flag_width, args.flag_height); - let size_diff = bytes_to_add(size, dims); + let colortype = colortype_from_bitdepth(args.flag_bitdepth); + let arg_size = (args.flag_width, args.flag_height); + let dims = image_shape(size, arg_size, colortype); + let size_diff = bytes_to_add(size, dims, colortype); // Add any extra bytes onto the end as black pixels for _ in 0..size_diff { @@ -99,5 +127,5 @@ fn render_file(args: Args) { } // Write image - image::save_buffer(&output_path, &buffer, dims.0, dims.1, image::RGB(8)).unwrap(); + image::save_buffer(&output_path, &buffer, dims.0, dims.1, colortype).unwrap(); }