Skip to content

Instantly share code, notes, and snippets.

@goto-bus-stop
Created June 5, 2017 18:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goto-bus-stop/34b6b2fa510c2254a55c1578a91a20ce to your computer and use it in GitHub Desktop.
Save goto-bus-stop/34b6b2fa510c2254a55c1578a91a20ce to your computer and use it in GitHub Desktop.
imgview

Imgview

Clone this, get cargo and run cargo build --release. Then ./target/release/imgview /path/to/image will render images ✨

[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"
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 },
];
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