use {
super::Parameter,
crate::utils::ResizedImageDetails,
image::{DynamicImage, ImageReader},
indicatif::ProgressBar,
log::error,
std::{fs::File, io::Write, path::PathBuf},
webp::WebPMemory,
};
pub struct SingleProcessor {
params: Parameter,
image: Option<DynamicImage>,
progressbar: Option<ProgressBar>,
}
impl SingleProcessor {
pub fn new(
params: Parameter,
progressbar: Option<ProgressBar>,
) -> Result<Self, String> {
if !¶ms.input.is_file() {
return Err(format!(
"Given input ({}) is not a file!",
¶ms.input.to_str().unwrap()
));
}
Ok(Self {
params,
image: None,
progressbar,
})
}
fn load_image(&self) -> Result<DynamicImage, String> {
let img = ImageReader::open(&self.params.input);
let img = if let Err(msg) = img {
return Err(msg.to_string());
} else {
img.unwrap()
};
let img = match img.decode() {
Ok(i) => i,
Err(msg) => return Err(msg.to_string()),
};
Ok(img)
}
fn encode_image(&self, img: &DynamicImage) -> Result<WebPMemory, String> {
let encoder = webp::Encoder::from_image(img);
if let Err(msg) = &encoder {
return Err(msg.to_string());
}
Ok(encoder
.unwrap()
.encode(self.params.webp_parameter.quality as f32))
}
pub fn run(&mut self) -> Result<(), String> {
if let Some(pb) = &self.progressbar {
let fname = self
.params
.input
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string();
pb.set_prefix(fname);
pb.set_message("Loading image...");
}
self.image = Some(self.load_image().unwrap());
let output_file_name = match self.get_output_file_name() {
Ok(v) => v,
Err(msg) => return Err(msg.to_string()),
};
let output_file_name = &self.params.output_dir.join(output_file_name);
if let Some(pb) = &self.progressbar {
pb.set_message("Encoding...");
}
let encoded_img = self.encode_image(self.image.as_ref().unwrap())?;
if let Some(pb) = &self.progressbar {
pb.set_message("Saving...");
}
{
let mut buf = File::create(output_file_name).unwrap();
if let Err(msg) = buf.write_all(&encoded_img) {
error!("{}", msg);
};
}
if let Some(pb) = &self.progressbar {
pb.set_message("...done!");
pb.inc(1);
}
match ResizedImageDetails::from(
&self.params.input,
self.params.scaled_images_count,
) {
Ok(v) => {
self.run_resize_images(v)?;
}
Err(msg) => {
if let Some(pb) = &self.progressbar {
pb.finish_with_message(format!("Error: {}", &msg));
}
error!("{}", msg);
return Err(msg);
}
};
if let Some(pb) = &self.progressbar {
pb.finish_with_message("Done!");
}
Ok(())
}
fn run_resize_images(
&self,
details: Vec<ResizedImageDetails>,
) -> Result<(), String> {
for detail in details.iter().rev() {
if let Some(pb) = &self.progressbar {
pb.set_message(format!(
"Resizing to {}x{} ...",
&detail.width, &detail.height
));
}
let img = self.image.as_ref().unwrap().resize(
detail.width,
detail.height,
image::imageops::FilterType::Triangle,
);
let output_file_name =
&self.params.output_dir.join(&detail.output_file_name);
if let Some(pb) = &self.progressbar {
pb.set_message("Encoding...");
}
let img = &self.encode_image(&img)?;
if let Some(pb) = &self.progressbar {
pb.set_message("Saving...");
}
let mut buf = File::create(output_file_name).unwrap();
if let Err(msg) = buf.write_all(&img) {
error!("{}", msg);
};
if let Some(pb) = &self.progressbar {
pb.set_message("...done!");
pb.inc(1);
}
}
Ok(())
}
fn get_output_file_name(&self) -> Result<PathBuf, String> {
let file_name = self.params.input.file_stem();
if let None = &file_name {
return Err("File name could not be extracted!".to_string());
}
let file_name = file_name.unwrap();
Ok(PathBuf::from(format!(
"{}.webp",
file_name.to_str().unwrap()
)))
}
}