Clone this, get cargo
and run cargo build --release
.
Then ./target/release/imgview /path/to/image
will render images ✨
Created
June 5, 2017 18:09
-
-
Save goto-bus-stop/34b6b2fa510c2254a55c1578a91a20ce to your computer and use it in GitHub Desktop.
imgview
This file contains 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
[package] | |
name = "imgview" | |
version = "0.1.0" | |
[[bin]] | |
name = "imgview" | |
path = "main.rs" | |
[dependencies] | |
clap = "2.24.2" | |
image = "0.13.0" | |
term = "0.4.5" | |
term_size = "0.3.0" |
This file contains 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
pub struct Color { | |
pub r: u8, | |
pub g: u8, | |
pub b: u8, | |
} | |
pub const COLORS: [Color; 256] = [ | |
Color { r: 0x00, g: 0x00, b: 0x00 }, | |
Color { r: 0x80, g: 0x00, b: 0x00 }, | |
Color { r: 0x00, g: 0x80, b: 0x00 }, | |
Color { r: 0x80, g: 0x80, b: 0x00 }, | |
Color { r: 0x00, g: 0x00, b: 0x80 }, | |
Color { r: 0x80, g: 0x00, b: 0x80 }, | |
Color { r: 0x00, g: 0x80, b: 0x80 }, | |
Color { r: 0xc0, g: 0xc0, b: 0xc0 }, | |
Color { r: 0x80, g: 0x80, b: 0x80 }, | |
Color { r: 0xff, g: 0x00, b: 0x00 }, | |
Color { r: 0x00, g: 0xff, b: 0x00 }, | |
Color { r: 0xff, g: 0xff, b: 0x00 }, | |
Color { r: 0x00, g: 0x00, b: 0xff }, | |
Color { r: 0xff, g: 0x00, b: 0xff }, | |
Color { r: 0x00, g: 0xff, b: 0xff }, | |
Color { r: 0xff, g: 0xff, b: 0xff }, | |
Color { r: 0x00, g: 0x00, b: 0x00 }, | |
Color { r: 0x00, g: 0x00, b: 0x5f }, | |
Color { r: 0x00, g: 0x00, b: 0x87 }, | |
Color { r: 0x00, g: 0x00, b: 0xaf }, | |
Color { r: 0x00, g: 0x00, b: 0xd7 }, | |
Color { r: 0x00, g: 0x00, b: 0xff }, | |
Color { r: 0x00, g: 0x5f, b: 0x00 }, | |
Color { r: 0x00, g: 0x5f, b: 0x5f }, | |
Color { r: 0x00, g: 0x5f, b: 0x87 }, | |
Color { r: 0x00, g: 0x5f, b: 0xaf }, | |
Color { r: 0x00, g: 0x5f, b: 0xd7 }, | |
Color { r: 0x00, g: 0x5f, b: 0xff }, | |
Color { r: 0x00, g: 0x87, b: 0x00 }, | |
Color { r: 0x00, g: 0x87, b: 0x5f }, | |
Color { r: 0x00, g: 0x87, b: 0x87 }, | |
Color { r: 0x00, g: 0x87, b: 0xaf }, | |
Color { r: 0x00, g: 0x87, b: 0xd7 }, | |
Color { r: 0x00, g: 0x87, b: 0xff }, | |
Color { r: 0x00, g: 0xaf, b: 0x00 }, | |
Color { r: 0x00, g: 0xaf, b: 0x5f }, | |
Color { r: 0x00, g: 0xaf, b: 0x87 }, | |
Color { r: 0x00, g: 0xaf, b: 0xaf }, | |
Color { r: 0x00, g: 0xaf, b: 0xd7 }, | |
Color { r: 0x00, g: 0xaf, b: 0xff }, | |
Color { r: 0x00, g: 0xd7, b: 0x00 }, | |
Color { r: 0x00, g: 0xd7, b: 0x5f }, | |
Color { r: 0x00, g: 0xd7, b: 0x87 }, | |
Color { r: 0x00, g: 0xd7, b: 0xaf }, | |
Color { r: 0x00, g: 0xd7, b: 0xd7 }, | |
Color { r: 0x00, g: 0xd7, b: 0xff }, | |
Color { r: 0x00, g: 0xff, b: 0x00 }, | |
Color { r: 0x00, g: 0xff, b: 0x5f }, | |
Color { r: 0x00, g: 0xff, b: 0x87 }, | |
Color { r: 0x00, g: 0xff, b: 0xaf }, | |
Color { r: 0x00, g: 0xff, b: 0xd7 }, | |
Color { r: 0x00, g: 0xff, b: 0xff }, | |
Color { r: 0x5f, g: 0x00, b: 0x00 }, | |
Color { r: 0x5f, g: 0x00, b: 0x5f }, | |
Color { r: 0x5f, g: 0x00, b: 0x87 }, | |
Color { r: 0x5f, g: 0x00, b: 0xaf }, | |
Color { r: 0x5f, g: 0x00, b: 0xd7 }, | |
Color { r: 0x5f, g: 0x00, b: 0xff }, | |
Color { r: 0x5f, g: 0x5f, b: 0x00 }, | |
Color { r: 0x5f, g: 0x5f, b: 0x5f }, | |
Color { r: 0x5f, g: 0x5f, b: 0x87 }, | |
Color { r: 0x5f, g: 0x5f, b: 0xaf }, | |
Color { r: 0x5f, g: 0x5f, b: 0xd7 }, | |
Color { r: 0x5f, g: 0x5f, b: 0xff }, | |
Color { r: 0x5f, g: 0x87, b: 0x00 }, | |
Color { r: 0x5f, g: 0x87, b: 0x5f }, | |
Color { r: 0x5f, g: 0x87, b: 0x87 }, | |
Color { r: 0x5f, g: 0x87, b: 0xaf }, | |
Color { r: 0x5f, g: 0x87, b: 0xd7 }, | |
Color { r: 0x5f, g: 0x87, b: 0xff }, | |
Color { r: 0x5f, g: 0xaf, b: 0x00 }, | |
Color { r: 0x5f, g: 0xaf, b: 0x5f }, | |
Color { r: 0x5f, g: 0xaf, b: 0x87 }, | |
Color { r: 0x5f, g: 0xaf, b: 0xaf }, | |
Color { r: 0x5f, g: 0xaf, b: 0xd7 }, | |
Color { r: 0x5f, g: 0xaf, b: 0xff }, | |
Color { r: 0x5f, g: 0xd7, b: 0x00 }, | |
Color { r: 0x5f, g: 0xd7, b: 0x5f }, | |
Color { r: 0x5f, g: 0xd7, b: 0x87 }, | |
Color { r: 0x5f, g: 0xd7, b: 0xaf }, | |
Color { r: 0x5f, g: 0xd7, b: 0xd7 }, | |
Color { r: 0x5f, g: 0xd7, b: 0xff }, | |
Color { r: 0x5f, g: 0xff, b: 0x00 }, | |
Color { r: 0x5f, g: 0xff, b: 0x5f }, | |
Color { r: 0x5f, g: 0xff, b: 0x87 }, | |
Color { r: 0x5f, g: 0xff, b: 0xaf }, | |
Color { r: 0x5f, g: 0xff, b: 0xd7 }, | |
Color { r: 0x5f, g: 0xff, b: 0xff }, | |
Color { r: 0x87, g: 0x00, b: 0x00 }, | |
Color { r: 0x87, g: 0x00, b: 0x5f }, | |
Color { r: 0x87, g: 0x00, b: 0x87 }, | |
Color { r: 0x87, g: 0x00, b: 0xaf }, | |
Color { r: 0x87, g: 0x00, b: 0xd7 }, | |
Color { r: 0x87, g: 0x00, b: 0xff }, | |
Color { r: 0x87, g: 0x5f, b: 0x00 }, | |
Color { r: 0x87, g: 0x5f, b: 0x5f }, | |
Color { r: 0x87, g: 0x5f, b: 0x87 }, | |
Color { r: 0x87, g: 0x5f, b: 0xaf }, | |
Color { r: 0x87, g: 0x5f, b: 0xd7 }, | |
Color { r: 0x87, g: 0x5f, b: 0xff }, | |
Color { r: 0x87, g: 0x87, b: 0x00 }, | |
Color { r: 0x87, g: 0x87, b: 0x5f }, | |
Color { r: 0x87, g: 0x87, b: 0x87 }, | |
Color { r: 0x87, g: 0x87, b: 0xaf }, | |
Color { r: 0x87, g: 0x87, b: 0xd7 }, | |
Color { r: 0x87, g: 0x87, b: 0xff }, | |
Color { r: 0x87, g: 0xaf, b: 0x00 }, | |
Color { r: 0x87, g: 0xaf, b: 0x5f }, | |
Color { r: 0x87, g: 0xaf, b: 0x87 }, | |
Color { r: 0x87, g: 0xaf, b: 0xaf }, | |
Color { r: 0x87, g: 0xaf, b: 0xd7 }, | |
Color { r: 0x87, g: 0xaf, b: 0xff }, | |
Color { r: 0x87, g: 0xd7, b: 0x00 }, | |
Color { r: 0x87, g: 0xd7, b: 0x5f }, | |
Color { r: 0x87, g: 0xd7, b: 0x87 }, | |
Color { r: 0x87, g: 0xd7, b: 0xaf }, | |
Color { r: 0x87, g: 0xd7, b: 0xd7 }, | |
Color { r: 0x87, g: 0xd7, b: 0xff }, | |
Color { r: 0x87, g: 0xff, b: 0x00 }, | |
Color { r: 0x87, g: 0xff, b: 0x5f }, | |
Color { r: 0x87, g: 0xff, b: 0x87 }, | |
Color { r: 0x87, g: 0xff, b: 0xaf }, | |
Color { r: 0x87, g: 0xff, b: 0xd7 }, | |
Color { r: 0x87, g: 0xff, b: 0xff }, | |
Color { r: 0xaf, g: 0x00, b: 0x00 }, | |
Color { r: 0xaf, g: 0x00, b: 0x5f }, | |
Color { r: 0xaf, g: 0x00, b: 0x87 }, | |
Color { r: 0xaf, g: 0x00, b: 0xaf }, | |
Color { r: 0xaf, g: 0x00, b: 0xd7 }, | |
Color { r: 0xaf, g: 0x00, b: 0xff }, | |
Color { r: 0xaf, g: 0x5f, b: 0x00 }, | |
Color { r: 0xaf, g: 0x5f, b: 0x5f }, | |
Color { r: 0xaf, g: 0x5f, b: 0x87 }, | |
Color { r: 0xaf, g: 0x5f, b: 0xaf }, | |
Color { r: 0xaf, g: 0x5f, b: 0xd7 }, | |
Color { r: 0xaf, g: 0x5f, b: 0xff }, | |
Color { r: 0xaf, g: 0x87, b: 0x00 }, | |
Color { r: 0xaf, g: 0x87, b: 0x5f }, | |
Color { r: 0xaf, g: 0x87, b: 0x87 }, | |
Color { r: 0xaf, g: 0x87, b: 0xaf }, | |
Color { r: 0xaf, g: 0x87, b: 0xd7 }, | |
Color { r: 0xaf, g: 0x87, b: 0xff }, | |
Color { r: 0xaf, g: 0xaf, b: 0x00 }, | |
Color { r: 0xaf, g: 0xaf, b: 0x5f }, | |
Color { r: 0xaf, g: 0xaf, b: 0x87 }, | |
Color { r: 0xaf, g: 0xaf, b: 0xaf }, | |
Color { r: 0xaf, g: 0xaf, b: 0xd7 }, | |
Color { r: 0xaf, g: 0xaf, b: 0xff }, | |
Color { r: 0xaf, g: 0xd7, b: 0x00 }, | |
Color { r: 0xaf, g: 0xd7, b: 0x5f }, | |
Color { r: 0xaf, g: 0xd7, b: 0x87 }, | |
Color { r: 0xaf, g: 0xd7, b: 0xaf }, | |
Color { r: 0xaf, g: 0xd7, b: 0xd7 }, | |
Color { r: 0xaf, g: 0xd7, b: 0xff }, | |
Color { r: 0xaf, g: 0xff, b: 0x00 }, | |
Color { r: 0xaf, g: 0xff, b: 0x5f }, | |
Color { r: 0xaf, g: 0xff, b: 0x87 }, | |
Color { r: 0xaf, g: 0xff, b: 0xaf }, | |
Color { r: 0xaf, g: 0xff, b: 0xd7 }, | |
Color { r: 0xaf, g: 0xff, b: 0xff }, | |
Color { r: 0xd7, g: 0x00, b: 0x00 }, | |
Color { r: 0xd7, g: 0x00, b: 0x5f }, | |
Color { r: 0xd7, g: 0x00, b: 0x87 }, | |
Color { r: 0xd7, g: 0x00, b: 0xaf }, | |
Color { r: 0xd7, g: 0x00, b: 0xd7 }, | |
Color { r: 0xd7, g: 0x00, b: 0xff }, | |
Color { r: 0xd7, g: 0x5f, b: 0x00 }, | |
Color { r: 0xd7, g: 0x5f, b: 0x5f }, | |
Color { r: 0xd7, g: 0x5f, b: 0x87 }, | |
Color { r: 0xd7, g: 0x5f, b: 0xaf }, | |
Color { r: 0xd7, g: 0x5f, b: 0xd7 }, | |
Color { r: 0xd7, g: 0x5f, b: 0xff }, | |
Color { r: 0xd7, g: 0x87, b: 0x00 }, | |
Color { r: 0xd7, g: 0x87, b: 0x5f }, | |
Color { r: 0xd7, g: 0x87, b: 0x87 }, | |
Color { r: 0xd7, g: 0x87, b: 0xaf }, | |
Color { r: 0xd7, g: 0x87, b: 0xd7 }, | |
Color { r: 0xd7, g: 0x87, b: 0xff }, | |
Color { r: 0xd7, g: 0xaf, b: 0x00 }, | |
Color { r: 0xd7, g: 0xaf, b: 0x5f }, | |
Color { r: 0xd7, g: 0xaf, b: 0x87 }, | |
Color { r: 0xd7, g: 0xaf, b: 0xaf }, | |
Color { r: 0xd7, g: 0xaf, b: 0xd7 }, | |
Color { r: 0xd7, g: 0xaf, b: 0xff }, | |
Color { r: 0xd7, g: 0xd7, b: 0x00 }, | |
Color { r: 0xd7, g: 0xd7, b: 0x5f }, | |
Color { r: 0xd7, g: 0xd7, b: 0x87 }, | |
Color { r: 0xd7, g: 0xd7, b: 0xaf }, | |
Color { r: 0xd7, g: 0xd7, b: 0xd7 }, | |
Color { r: 0xd7, g: 0xd7, b: 0xff }, | |
Color { r: 0xd7, g: 0xff, b: 0x00 }, | |
Color { r: 0xd7, g: 0xff, b: 0x5f }, | |
Color { r: 0xd7, g: 0xff, b: 0x87 }, | |
Color { r: 0xd7, g: 0xff, b: 0xaf }, | |
Color { r: 0xd7, g: 0xff, b: 0xd7 }, | |
Color { r: 0xd7, g: 0xff, b: 0xff }, | |
Color { r: 0xff, g: 0x00, b: 0x00 }, | |
Color { r: 0xff, g: 0x00, b: 0x5f }, | |
Color { r: 0xff, g: 0x00, b: 0x87 }, | |
Color { r: 0xff, g: 0x00, b: 0xaf }, | |
Color { r: 0xff, g: 0x00, b: 0xd7 }, | |
Color { r: 0xff, g: 0x00, b: 0xff }, | |
Color { r: 0xff, g: 0x5f, b: 0x00 }, | |
Color { r: 0xff, g: 0x5f, b: 0x5f }, | |
Color { r: 0xff, g: 0x5f, b: 0x87 }, | |
Color { r: 0xff, g: 0x5f, b: 0xaf }, | |
Color { r: 0xff, g: 0x5f, b: 0xd7 }, | |
Color { r: 0xff, g: 0x5f, b: 0xff }, | |
Color { r: 0xff, g: 0x87, b: 0x00 }, | |
Color { r: 0xff, g: 0x87, b: 0x5f }, | |
Color { r: 0xff, g: 0x87, b: 0x87 }, | |
Color { r: 0xff, g: 0x87, b: 0xaf }, | |
Color { r: 0xff, g: 0x87, b: 0xd7 }, | |
Color { r: 0xff, g: 0x87, b: 0xff }, | |
Color { r: 0xff, g: 0xaf, b: 0x00 }, | |
Color { r: 0xff, g: 0xaf, b: 0x5f }, | |
Color { r: 0xff, g: 0xaf, b: 0x87 }, | |
Color { r: 0xff, g: 0xaf, b: 0xaf }, | |
Color { r: 0xff, g: 0xaf, b: 0xd7 }, | |
Color { r: 0xff, g: 0xaf, b: 0xff }, | |
Color { r: 0xff, g: 0xd7, b: 0x00 }, | |
Color { r: 0xff, g: 0xd7, b: 0x5f }, | |
Color { r: 0xff, g: 0xd7, b: 0x87 }, | |
Color { r: 0xff, g: 0xd7, b: 0xaf }, | |
Color { r: 0xff, g: 0xd7, b: 0xd7 }, | |
Color { r: 0xff, g: 0xd7, b: 0xff }, | |
Color { r: 0xff, g: 0xff, b: 0x00 }, | |
Color { r: 0xff, g: 0xff, b: 0x5f }, | |
Color { r: 0xff, g: 0xff, b: 0x87 }, | |
Color { r: 0xff, g: 0xff, b: 0xaf }, | |
Color { r: 0xff, g: 0xff, b: 0xd7 }, | |
Color { r: 0xff, g: 0xff, b: 0xff }, | |
Color { r: 0x08, g: 0x08, b: 0x08 }, | |
Color { r: 0x12, g: 0x12, b: 0x12 }, | |
Color { r: 0x1c, g: 0x1c, b: 0x1c }, | |
Color { r: 0x26, g: 0x26, b: 0x26 }, | |
Color { r: 0x30, g: 0x30, b: 0x30 }, | |
Color { r: 0x3a, g: 0x3a, b: 0x3a }, | |
Color { r: 0x44, g: 0x44, b: 0x44 }, | |
Color { r: 0x4e, g: 0x4e, b: 0x4e }, | |
Color { r: 0x58, g: 0x58, b: 0x58 }, | |
Color { r: 0x60, g: 0x60, b: 0x60 }, | |
Color { r: 0x66, g: 0x66, b: 0x66 }, | |
Color { r: 0x76, g: 0x76, b: 0x76 }, | |
Color { r: 0x80, g: 0x80, b: 0x80 }, | |
Color { r: 0x8a, g: 0x8a, b: 0x8a }, | |
Color { r: 0x94, g: 0x94, b: 0x94 }, | |
Color { r: 0x9e, g: 0x9e, b: 0x9e }, | |
Color { r: 0xa8, g: 0xa8, b: 0xa8 }, | |
Color { r: 0xb2, g: 0xb2, b: 0xb2 }, | |
Color { r: 0xbc, g: 0xbc, b: 0xbc }, | |
Color { r: 0xc6, g: 0xc6, b: 0xc6 }, | |
Color { r: 0xd0, g: 0xd0, b: 0xd0 }, | |
Color { r: 0xda, g: 0xda, b: 0xda }, | |
Color { r: 0xe4, g: 0xe4, b: 0xe4 }, | |
Color { r: 0xee, g: 0xee, b: 0xee }, | |
]; |
This file contains 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
extern crate clap; | |
extern crate image; | |
extern crate term; | |
extern crate term_size; | |
mod colors; | |
use std::fs::File; | |
use std::io::{ | |
stdin, | |
Error, | |
Read, | |
Seek, | |
SeekFrom, | |
BufReader, | |
}; | |
use clap::{ | |
App, | |
Arg, | |
}; | |
use image::{ | |
load, | |
load_from_memory, | |
guess_format, | |
Rgba, | |
Pixel, | |
RgbaImage, | |
ImageError | |
}; | |
use colors::{Color, COLORS}; | |
fn read_image_from_file(mut file: File) -> Result<RgbaImage, ImageError> { | |
let mut magic_buf = [0 as u8; 512]; | |
file.read(&mut magic_buf)?; | |
file.seek(SeekFrom::Start(0))?; | |
let reader = BufReader::new(file); | |
let format = guess_format(&magic_buf)?; | |
load(reader, format).map(|image| image.to_rgba()) | |
} | |
fn read_image_from_stdin() -> Result<RgbaImage, ImageError> { | |
let mut buf = vec![]; | |
stdin().read_to_end(&mut buf).unwrap(); | |
load_from_memory(buf.as_slice()).map(|image| image.to_rgba()) | |
} | |
fn abs_sub(a: u8, b: u8) -> u32 { | |
(if a > b { | |
a - b | |
} else { | |
b - a | |
}) as u32 | |
} | |
fn get_distance(a: &Color, b: &Color) -> u32 { | |
abs_sub(a.r, b.r).pow(2) + abs_sub(a.g, b.g).pow(2) + abs_sub(a.b, b.b).pow(2) | |
} | |
fn get_closest_color(pixel: &Rgba<u8>) -> u16 { | |
let (r, g, b, _) = pixel.channels4(); | |
let mut best_distance = std::u32::MAX; | |
let mut best_color: u16 = 0; | |
for i in 0..256 { | |
let color = &COLORS[i as usize]; | |
let distance = get_distance(color, &Color { r: r, g: g, b: b }); | |
if distance < best_distance { | |
best_distance = distance; | |
best_color = i; | |
} | |
} | |
return best_color; | |
} | |
fn print_image(image: RgbaImage, width: u32) -> Result<(), Error> { | |
let mut t = term::stdout().unwrap(); | |
let (size_x, size_y) = image.dimensions(); | |
let step = (size_x / width) as u32; | |
for y in 0..(size_y / 2 / step) { | |
for x in 0..(size_x / step) { | |
let fgcolor = get_closest_color(image.get_pixel(x * step, y * 2 * step)); | |
let bgcolor = get_closest_color(image.get_pixel(x * step, (y * 2 + 1) * step)); | |
t.fg(fgcolor)?; | |
t.bg(bgcolor)?; | |
write!(t, "▀")?; | |
} | |
t.reset()?; | |
write!(t, "\n")?; | |
} | |
Ok(()) | |
} | |
fn get_default_width() -> u32 { | |
term_size::dimensions().map(|sizes| sizes.0 as u32) | |
.map_or(80, |width| if width > 80 { 80 } else { width }) | |
} | |
fn main() { | |
let matches = App::new("imgview") | |
.version("0.1.0") | |
.about("Render images to the terminal in 256 colours") | |
.arg(Arg::with_name("width") | |
.short("w") | |
.long("width") | |
.value_name("COLUMNS") | |
.takes_value(true)) | |
.arg(Arg::with_name("input") | |
.index(1)) | |
.get_matches(); | |
let width = u32::from_str_radix(matches.value_of("width").unwrap_or(""), 10) | |
.unwrap_or_else(|_| get_default_width()); | |
let file_path = matches.value_of("input").unwrap_or("-"); | |
let image = ( | |
if file_path != "-" { | |
match File::open(file_path) { | |
Ok(file) => read_image_from_file(file), | |
Err(err) => Err(ImageError::IoError(err)), | |
} | |
} else { | |
read_image_from_stdin() | |
} | |
).unwrap(); | |
print_image(image, width).unwrap(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment