use image::DynamicImage;
use ratatui::style::{Color, Style};
use ratatui::text::{Line, Span};
pub struct ShapeArt;
impl ShapeArt {
pub fn to_colored_ascii(img: &DynamicImage, width: u32, height: u32) -> Vec<Line<'static>> {
let sharpened = img.unsharpen(1.0, 15);
let scout_w = width * 2;
let scout_h = height * 4;
let scout_img =
sharpened.resize_exact(scout_w, scout_h, image::imageops::FilterType::Triangle);
let scout_rgb = scout_img.to_rgb8();
let scout_luma = scout_img.to_luma8();
let mut lines = Vec::with_capacity(height as usize);
for y_cell in 0..height {
let mut spans = Vec::with_capacity(width as usize);
for x_cell in 0..width {
let top_color = Self::sample_with_feature_bias(
&scout_rgb,
&scout_luma,
x_cell * 2,
y_cell * 4,
2,
2,
);
let bottom_color = Self::sample_with_feature_bias(
&scout_rgb,
&scout_luma,
x_cell * 2,
y_cell * 4 + 2,
2,
2,
);
spans.push(Span::styled(
"▄",
Style::default().fg(bottom_color).bg(top_color),
));
}
lines.push(Line::from(spans));
}
lines
}
fn sample_with_feature_bias(
rgb: &image::RgbImage,
luma: &image::GrayImage,
start_x: u32,
start_y: u32,
width: u32,
height: u32,
) -> Color {
let mut sum_r = 0u32;
let mut sum_g = 0u32;
let mut sum_b = 0u32;
let mut min_luma = 255u8;
let mut max_luma = 0u8;
let mut min_idx = (0, 0);
let mut max_idx = (0, 0);
let count = width * height;
for py in 0..height {
for px in 0..width {
let x = start_x + px;
let y = start_y + py;
let l = luma.get_pixel(x, y)[0];
let c = rgb.get_pixel(x, y);
sum_r += u32::from(c[0]);
sum_g += u32::from(c[1]);
sum_b += u32::from(c[2]);
if l < min_luma {
min_luma = l;
min_idx = (x, y);
}
if l > max_luma {
max_luma = l;
max_idx = (x, y);
}
}
}
let avg_luma = (sum_r + sum_g + sum_b) / (3 * count);
let contrast = max_luma.saturating_sub(min_luma);
if contrast > 40 {
if avg_luma > 100 && (avg_luma as i32 - i32::from(min_luma)) > 30 {
let c = rgb.get_pixel(min_idx.0, min_idx.1);
return Color::Rgb(c[0], c[1], c[2]);
}
if avg_luma < 100 && (i32::from(max_luma) - avg_luma as i32) > 30 {
let c = rgb.get_pixel(max_idx.0, max_idx.1);
return Color::Rgb(c[0], c[1], c[2]);
}
}
Color::Rgb(
(sum_r / count) as u8,
(sum_g / count) as u8,
(sum_b / count) as u8,
)
}
}