use std::{fs::File, io::BufReader};
use crate::{
conversion::converters::generic_converter::{Imgii2dImage, render_ascii_generic},
error::ImgiiError,
options::{ImgiiOptions, RasciiOptions},
};
use image::{AnimationDecoder, Delay, DynamicImage, codecs::gif::GifDecoder};
use rascii_art_img::render_image_to;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
#[derive(Debug, Clone)]
pub(crate) struct FrameMetadata {
left: u32,
top: u32,
delay: Delay,
}
#[derive(Debug, Clone)]
pub(crate) struct NonRenderedFramePart {
image_ascii: String,
frame_metadata: FrameMetadata,
}
#[derive(Debug, Clone)]
pub(crate) struct RenderedFramePart {
image_data: Imgii2dImage,
frame_metadata: FrameMetadata,
}
impl FrameMetadata {
#[must_use]
pub(crate) fn new(left: u32, top: u32, delay: Delay) -> Self {
Self { left, top, delay }
}
#[must_use]
pub(crate) fn left(&self) -> u32 {
self.left
}
#[must_use]
pub(crate) fn top(&self) -> u32 {
self.top
}
#[must_use]
pub(crate) fn delay(&self) -> Delay {
self.delay
}
}
impl RenderedFramePart {
#[must_use]
#[allow(dead_code)]
pub(crate) fn new(image_data: Imgii2dImage, frame_metadata: FrameMetadata) -> Self {
Self {
image_data,
frame_metadata,
}
}
#[must_use]
#[allow(dead_code)]
pub(crate) fn image_data(&self) -> &Imgii2dImage {
&self.image_data
}
#[must_use]
#[allow(dead_code)]
pub(crate) fn frame_metadata(&self) -> &FrameMetadata {
&self.frame_metadata
}
#[must_use]
#[allow(dead_code)]
pub(crate) fn into_frame_data(self) -> (Imgii2dImage, FrameMetadata) {
(self.image_data, self.frame_metadata)
}
}
impl NonRenderedFramePart {
#[must_use]
pub(crate) fn new(image_ascii: String, frame_metadata: FrameMetadata) -> Self {
Self {
image_ascii,
frame_metadata,
}
}
}
pub(crate) fn read_gif_as_deconstructed_ascii(
input_file_name: &str,
rascii_options: &RasciiOptions,
) -> Result<Vec<Option<NonRenderedFramePart>>, ImgiiError> {
let deconstructed_gif = read_deconstructed_gif(input_file_name)?;
Ok(deconstructed_gif
.into_par_iter()
.map(|(image, deconstructed_frame)| {
let mut ascii_text = String::new();
if render_image_to(&image, &mut ascii_text, rascii_options).is_err() {
None
} else {
Some(NonRenderedFramePart::new(ascii_text, deconstructed_frame))
}
})
.collect())
}
pub(crate) fn read_as_deconstructed_rendered_gif_vec(
input_file_name: &str,
imgii_options: &ImgiiOptions,
) -> Result<Vec<Option<RenderedFramePart>>, ImgiiError> {
let ascii_text =
read_gif_as_deconstructed_ascii(input_file_name, imgii_options.rascii_options())?;
Ok(ascii_text
.into_par_iter()
.filter_map(|frame| frame) .map(|frame_part| {
let rendered_image_res = render_ascii_generic(imgii_options, frame_part.image_ascii);
match rendered_image_res {
Ok(rendered_image) => Some(RenderedFramePart::new(
rendered_image,
frame_part.frame_metadata,
)),
Err(err) => {
log::warn!("A frame was detected with an error ({err})");
None
}
}
})
.collect())
}
pub(crate) fn read_deconstructed_gif(
input_file_name: &str,
) -> Result<Vec<(DynamicImage, FrameMetadata)>, ImgiiError> {
let file_in = BufReader::new(File::open(input_file_name)?);
let decoder = match GifDecoder::new(file_in) {
Ok(decoder) => decoder,
Err(err) => {
let err = anyhow::Error::new(err);
return Err(err.into());
}
};
let frames = match decoder.into_frames().collect_frames() {
Ok(frames) => frames,
Err(err) => {
let err = anyhow::Error::new(err);
return Err(err.into());
}
};
let ret = frames
.into_iter()
.map(|frame| {
let left = frame.left();
let top = frame.top();
let delay = frame.delay();
(
frame.into_buffer().into(),
FrameMetadata::new(left, top, delay),
)
})
.collect();
Ok(ret)
}