#![allow(dead_code, unused)]
use crate::ansi::{AnsiImage, AnsiImageResult, Ansinator};
use crate::error::AnsiImageError;
use ansinator_ascii_font::AsciiFont;
use image::{DynamicImage, GenericImageView, RgbImage, GrayImage};
use std::default::Default;
use ansi_term::Color;
#[derive(Debug, Clone, Copy)]
pub enum AsciiColor {
Truecolor,
Terminalcolor,
Fixed,
}
impl Default for AsciiColor {
fn default() -> Self {
Self::Fixed
}
}
#[derive(Debug, Clone, Copy)]
pub enum AsciiMode {
Gradient,
Pattern,
}
impl Default for AsciiMode {
fn default() -> Self {
Self::Pattern
}
}
pub type AnsiAscii = AnsiImage<AsciiMode, AsciiColor>;
impl AnsiAscii {
pub fn true_color(&self) -> Self {
Self { color: AsciiColor::Truecolor, .. *self}
}
pub fn terminal_color(&self) -> Self {
Self { color: AsciiColor::Terminalcolor, .. *self}
}
fn set_foreground(&self, foreground: (u8,u8,u8) ) -> Self {
Self{ has_foreground: true, foreground, color: AsciiColor::Fixed, .. *self}
}
fn set_background(&self, background: (u8,u8,u8) ) -> Self {
Self{ has_background: true, background, color: AsciiColor::Fixed, .. *self}
}
pub fn gradient(&self) -> Self {
Self { mode: AsciiMode::Gradient, scale: (1,1), .. *self}
}
pub fn pattern(&self) -> Self {
Self { mode: AsciiMode::Pattern, scale: (5,7), .. *self}
}
fn get_color(&self, r: u8, g:u8, b:u8) -> ansi_term::Style {
match self.color {
AsciiColor::Truecolor => {
Color::RGB(r,g,b).normal()
},
AsciiColor::Terminalcolor => {
let index = ansinator_terminal_colors::TermColor::from(r, g, b)
.index;
Color::Fixed(index).normal()
},
AsciiColor::Fixed => {
match (self.has_foreground, self.has_background) {
(false, false) => {
ansi_term::Style::new()
},
(false, true) => {
let (br, bg, bb) = self.background;
Color::RGB(0,0,0).on(Color::RGB(br,bg,bb))
},
(true, false) => {
let (r, g, b) = self.foreground;
Color::RGB(r,g,b).normal()
},
(true, true) => {
let (r, g, b) = self.foreground;
let (br, bg, bb) = self.background;
Color::RGB(r,g,b).on(Color::RGB(br,bg,bb))
},
}
},
}
}
pub fn get_style(&self, r:u8, g:u8, b:u8) -> ansi_term::Style {
let mut style = self.get_color(r,g,b);
if self.bold {
style = style.bold()
}
if self.blink {
style = style.blink()
}
if self.underline {
style = style.underline()
}
style
}
pub fn convert(&self, image_path: &str, char_set: &str) -> Result<AnsiImageResult, AnsiImageError>{
let image = match image::open(image_path) {
Ok(image) => image,
Err(e) => return Err(AnsiImageError::ImageError(e)),
};
let size = self.size_aspect_ratio(image.dimensions());
let image = image.adjust_contrast(self.contrast)
.brighten(self.brighten);
let mut image = self.image_resize_with_scale(&image);
if self.invert {
image.invert();
}
let luma = image.to_luma8();
let rgb = image.resize_exact(size.0, size.1, self.filter)
.to_rgb8();
assert_eq!(rgb.width() * self.scale.0, luma.width());
assert_eq!(rgb.height() * self.scale.1, luma.height());
let res =
match self.mode {
AsciiMode::Gradient => {
let char_set = char_set.chars()
.collect::<Vec<char>>();
self.ascii_gradient(rgb, luma, &char_set)
},
AsciiMode::Pattern => {
let mut ascii_font_set = char_set.chars()
.map(|c| AsciiFont::from(c))
.collect::<Vec<AsciiFont>>();
ascii_font_set.sort_unstable();
ascii_font_set.dedup();
self.ascii_pattern(rgb, luma, &ascii_font_set)
},
};
Ok(res)
}
fn ascii_pattern<'b>(&self, rgb: RgbImage, luma: GrayImage, font_set: &Vec<AsciiFont>) -> AnsiImageResult<'b> {
let mut ansi = AnsiImageResult{ data: vec![] };
let mut style = self.get_style(0,0,0);
let style_normal = ansi_term::Style::new();
let width = rgb.width();
let height = rgb.height();
for y in (0..height) {
for x in (0..width) {
let rgb_pixel = rgb.get_pixel(x+0,y+0);
let r = rgb_pixel[0];
let g = rgb_pixel[1];
let b = rgb_pixel[2];
style = self.get_style(r,g,b);
let ch = window_analysis(&luma, x, y, &font_set)
.to_string();
ansi.data.push(style.paint(ch));
}
ansi.data.push(style_normal.paint("\n"));
}
ansi
}
fn ascii_gradient<'b>(&self, rgb: RgbImage, luma: GrayImage, char_set: &Vec<char>) -> AnsiImageResult<'b> {
let mut ansi = AnsiImageResult{ data: vec![] };
let mut style = self.get_style(0,0,0);
let style_normal = ansi_term::Style::new();
let width = rgb.width();
let height = rgb.height();
for y in (0..height) {
for x in (0..width) {
let ch = luma_mapping(&luma, x, y, &char_set)
.to_string();
ansi.data.push(style.paint(ch));
}
ansi.data.push(style_normal.paint("\n"));
}
ansi
}
}
fn window_analysis(win: &GrayImage, x:u32, y:u32, font_set: &Vec<AsciiFont>) -> char {
let mut font = AsciiFont::default();
for j in 0..7 {
for i in 0..5 {
let index = j*5 + i;
font.data[index] = win.get_pixel(5*x + i as u32, 7*y + j as u32)[0];
}
}
ansinator_ascii_font::minimize(&font, &font_set)
}
fn luma_mapping(luma: &GrayImage, x:u32, y:u32, char_set: &Vec<char>) -> char {
let p = luma.get_pixel(x+0,y+0)[0];
let len = char_set.len();
let index = p as usize * (len - 1) / 255;
char_set[ index ]
}
#[cfg(test)]
mod tests {
use super::*;
fn setup_image_size() -> (u32, u32) {
return (120,50)
}
fn setup_path() -> String {
"../images/pic5.jpg".to_string()
}
#[test]
fn test_gradient_truecolor() {
let (w,h) = setup_image_size();
let image_path = setup_path();
let ascii = AnsiAscii::new()
.bold()
.underline()
.true_color()
.gradient()
.size(w, h);
println!("{:?}", ascii);
let result = ascii.convert(&image_path, "012345789")
.unwrap();
result.print();
result.save("../ascii_gradient_truecolor.txt");
}
#[test]
fn test_gradient_terminalcolor() {
let (w,h) = setup_image_size();
let image_path = setup_path();
let ascii = AnsiAscii::new()
.bold()
.underline()
.terminal_color()
.gradient()
.size(w, h);
println!("{:?}", ascii);
let result = ascii.convert(&image_path, "012345789")
.unwrap();
result.print();
result.save("../ascii_gradient_terminalcolor.txt");
}
#[test]
fn test_gradient_fixedcolor() {
let (w,h) = setup_image_size();
let image_path = setup_path();
let ascii = AnsiAscii::new()
.bold()
.underline()
.set_foreground((255,0,255))
.set_background((0,255,255))
.gradient()
.size(w, h);
println!("{:?}", ascii);
let result = ascii.convert(&image_path, "012345789")
.unwrap();
result.print();
result.save("../ascii_gradient_fixedcolor.txt");
}
#[test]
fn test_pattern_truecolor() {
let (w,h) = setup_image_size();
let image_path = setup_path();
let ascii = AnsiAscii::new()
.bold()
.underline()
.true_color()
.pattern()
.size(w, h);
println!("{:?}", ascii);
let result = ascii.convert(&image_path, "012345789")
.unwrap();
result.print();
result.save("../ascii_pattern_truecolor.txt");
}
#[test]
fn test_pattern_terminalcolor() {
let (w,h) = setup_image_size();
let image_path = setup_path();
let ascii = AnsiAscii::new()
.bold()
.underline()
.terminal_color()
.pattern()
.size(w, h);
println!("{:?}", ascii);
let result = ascii.convert(&image_path, "012345789")
.unwrap();
result.print();
result.save("../ascii_pattern_terminalcolor.txt");
}
#[test]
fn test_pattern_fixed() {
let (w,h) = setup_image_size();
let image_path = setup_path();
let ascii = AnsiAscii::new()
.bold()
.underline()
.set_foreground((150,50,200))
.set_background((50,255,155))
.pattern()
.size(w, h);
println!("{:?}", ascii);
let result = ascii.convert(&image_path, "012345789")
.unwrap();
result.print();
result.save("../ascii_pattern_terminalcolor.txt");
}
}