use self::super::util::{ANSI_BG_COLOUR_ESCAPES, ANSI_RESET_ATTRIBUTES, ANSI_COLOUR_ESCAPES, JPEG_MAGIC, BMP_MAGIC, ICO_MAGIC, GIF_MAGIC, PNG_MAGIC,
closest_colour, bg_colours_for};
use image::{self, GenericImageView, DynamicImage, ImageFormat, Pixel};
use std::io::{BufReader, Write, Read};
use image::imageops::FilterType;
use self::super::Error;
use std::path::PathBuf;
use std::ops::Index;
use std::fs::File;
mod no_ansi;
pub use self::no_ansi::write_no_ansi;
pub fn guess_format(file: &(String, PathBuf)) -> Result<ImageFormat, Error> {
file.1
.extension()
.and_then(|ext| match &ext.to_str().unwrap().to_lowercase()[..] {
"png" => Some(Ok(ImageFormat::Png)),
"jpg" | "jpeg" | "jpe" | "jif" | "jfif" | "jfi" => Some(Ok(ImageFormat::Jpeg)),
"gif" => Some(Ok(ImageFormat::Gif)),
"webp" => Some(Ok(ImageFormat::WebP)),
"ppm" => Some(Ok(ImageFormat::Pnm)),
"tiff" | "tif" => Some(Ok(ImageFormat::Tiff)),
"tga" => Some(Ok(ImageFormat::Tga)),
"bmp" | "dib" => Some(Ok(ImageFormat::Bmp)),
"ico" => Some(Ok(ImageFormat::Ico)),
"hdr" => Some(Ok(ImageFormat::Hdr)),
_ => None,
})
.unwrap_or_else(|| {
let mut buf = [0; 32];
let read = File::open(&file.1).map_err(|_| Error::OpeningImageFailed(file.0.clone()))?.read(&mut buf).unwrap();
let buf = &buf[..read];
if buf.len() >= PNG_MAGIC.len() && &buf[..PNG_MAGIC.len()] == PNG_MAGIC {
Ok(ImageFormat::Png)
} else if buf.len() >= JPEG_MAGIC.len() && &buf[..JPEG_MAGIC.len()] == JPEG_MAGIC {
Ok(ImageFormat::Jpeg)
} else if buf.len() >= GIF_MAGIC.len() && &buf[..GIF_MAGIC.len()] == GIF_MAGIC {
Ok(ImageFormat::Gif)
} else if buf.len() >= BMP_MAGIC.len() && &buf[..BMP_MAGIC.len()] == BMP_MAGIC {
Ok(ImageFormat::Bmp)
} else if buf.len() >= ICO_MAGIC.len() && &buf[..ICO_MAGIC.len()] == ICO_MAGIC {
Ok(ImageFormat::Ico)
} else {
Err(Error::GuessingFormatFailed(file.0.clone()))
}
})
}
pub fn load_image(file: &(String, PathBuf), format: ImageFormat) -> Result<DynamicImage, Error> {
Ok(image::load(BufReader::new(File::open(&file.1).map_err(|_| Error::OpeningImageFailed(file.0.clone()))?),
format)
.unwrap())
}
pub fn image_resized_size(size: (u32, u32), term_size: (u32, u32), preserve_aspect: bool) -> (u32, u32) {
if !preserve_aspect {
return (term_size.0, term_size.1 * 2);
}
let nwidth = term_size.0;
let nheight = term_size.1 * 2;
let (width, height) = size;
let ratio = width as f32 / height as f32;
let nratio = nwidth as f32 / nheight as f32;
let scale = if nratio > ratio {
nheight as f32 / height as f32
} else {
nwidth as f32 / width as f32
};
((width as f32 * scale) as u32, (height as f32 * scale) as u32)
}
pub fn resize_image(img: &DynamicImage, size: (u32, u32)) -> DynamicImage {
img.resize_exact(size.0, size.1, FilterType::Nearest)
}
pub fn create_colourtable<C: Index<usize, Output = u8>>(img: &DynamicImage, upper_colours: &[C], lower_colours: &[C]) -> Vec<Vec<(usize, usize)>> {
let (width, height) = img.dimensions();
let term_h = height / 2;
(0..term_h)
.map(|y| {
let upper_y = y * 2;
let lower_y = upper_y + 1;
(0..width)
.map(|x| (closest_colour(img.get_pixel(x, upper_y).to_rgb(), upper_colours), closest_colour(img.get_pixel(x, lower_y).to_rgb(), lower_colours)))
.collect()
})
.collect()
}
pub fn write_ansi<W: Write, C: Index<usize, Output = u8>>(out: &mut W, img: &DynamicImage, foreground_colours: &[C]) {
for line in create_colourtable(img, foreground_colours, &bg_colours_for(foreground_colours)) {
for (upper_clr, lower_clr) in line {
write!(out,
"{}{}\u{2580}", ANSI_COLOUR_ESCAPES[upper_clr],
ANSI_BG_COLOUR_ESCAPES[lower_clr])
.unwrap();
}
writeln!(out, "{}", ANSI_RESET_ATTRIBUTES).unwrap();
}
}
pub fn write_ansi_truecolor<W: Write>(out: &mut W, img: &DynamicImage) {
let (width, height) = img.dimensions();
let term_h = height / 2;
for y in 0..term_h {
let upper_y = y * 2;
let lower_y = upper_y + 1;
for x in 0..width {
let upper_pixel = img.get_pixel(x, upper_y).to_rgb();
let lower_pixel = img.get_pixel(x, lower_y).to_rgb();
write!(out,
"\x1B[38;2;{};{};{}m\
\x1B[48;2;{};{};{}m\u{2580}", upper_pixel[0],
upper_pixel[1],
upper_pixel[2],
lower_pixel[0],
lower_pixel[1],
lower_pixel[2])
.unwrap();
}
writeln!(out, "{}", ANSI_RESET_ATTRIBUTES).unwrap();
}
}