use std::{collections::HashMap, sync::Arc};
use crate::{
ImgiiOptions,
conversion::{image_data::ImageData, render_char_to_png::str_to_png},
error::{FontError, ImageError, ImgiiError, ParseError},
};
use super::super::render_char_to_png::{ColoredStr, str_to_transparent_png};
use ab_glyph::FontRef;
use regex::Regex;
#[derive(Clone, Debug)]
pub(crate) struct Imgii2dImage {
pub(crate) image_2d: Vec<Arc<ImageData>>,
pub(crate) width: usize,
pub(crate) height: usize,
}
pub(crate) fn render_ascii_generic(
imgii_options: &ImgiiOptions,
ascii_text: String,
) -> Result<Imgii2dImage, ImgiiError> {
let font = FontRef::try_from_slice(imgii_options.font().as_slice())
.map_err(|_| FontError::FontLoad {
font_name: String::from(imgii_options.font_name()),
})?;
let mut image_2d_vec = Vec::new();
let (mut width, height) = (0, ascii_text.lines().count());
let mut rendered_images: HashMap<ColoredStr, Arc<ImageData>> = HashMap::new();
let transparent_png = Arc::from(str_to_transparent_png(imgii_options));
for (i, line) in ascii_text.lines().enumerate() {
let pattern_str = concat!('\u{1b}', r"\[38;2;([0-9]+);([0-9]+);([0-9]+)m(.)");
let re = Regex::new(pattern_str)?;
let mut line_width = 0;
for (_full_str, [r, g, b, the_str]) in re.captures_iter(line).map(|c| c.extract()) {
let red = r.parse::<u8>().map_err(|err| ParseError::ParseColor {
value_name: String::from("red"),
the_str: String::from(the_str),
err,
})?;
let green = g.parse::<u8>().map_err(|err| ParseError::ParseColor {
value_name: String::from("green"),
the_str: String::from(the_str),
err,
})?;
let blue = b.parse::<u8>().map_err(|err| ParseError::ParseColor {
value_name: String::from("blue"),
the_str: String::from(the_str),
err,
})?;
let generated_png = {
if the_str.trim().is_empty() {
transparent_png.clone()
} else {
let colored = ColoredStr {
red,
green,
blue,
string: String::from(the_str),
};
let rendered_img = rendered_images.get(&colored);
match rendered_img {
Some(rendered_img) => rendered_img.clone(),
None => {
let image_data = Arc::from(str_to_png(&colored, &font, imgii_options));
let result = rendered_images.insert(colored, image_data.clone());
match result {
None => image_data,
Some(colored) => {
return Err(ImageError::Render {
reason: format!(
"the image ({colored:?}) should not exist already in the hash map",
),
}.into());
}
}
}
}
}
};
line_width += 1;
image_2d_vec.push(generated_png);
}
if i == 0 {
width = line_width;
image_2d_vec.reserve(width * height);
} else {
if width != line_width {
return Err(ImageError::Render {
reason: format!(
"width {} is not equal to the current line width {}",
width, line_width
),
}
.into());
}
}
}
if width * height != image_2d_vec.len() {
return Err(ImageError::Render {
reason: format!(
"expected length of the 2d vector was {} but got {}",
width * height,
image_2d_vec.len()
),
}
.into());
}
Ok(Imgii2dImage {
image_2d: image_2d_vec,
width,
height,
})
}