Resize gifs once, outside the loop

This commit is contained in:
atanunq 2020-09-13 22:35:22 +03:00
parent e61a742a5f
commit f726f46ad0
No known key found for this signature in database
GPG Key ID: 628280A0217A7B0F
2 changed files with 46 additions and 57 deletions

2
.gitignore vendored
View File

@ -12,3 +12,5 @@ wapm.lock
# These are backup files generated by rustfmt
**/*.rs.bk
.vscode

View File

@ -10,6 +10,8 @@ use std::{thread, time::Duration};
const THIRTY_MILLIS: Duration = Duration::from_millis(30);
type TxRx<'a> = (&'a mpsc::Sender<bool>, &'a mpsc::Receiver<bool>);
pub fn run(mut conf: Config) {
//create two channels so that ctrlc-handler and the main thread can pass messages in order to
// communicate when printing must be stopped without distorting the current frame
@ -48,9 +50,11 @@ pub fn run(mut conf: Config) {
.read_to_end(&mut buf)
.expect("Could not read until EOF.");
if try_print_gif(&conf, BufReader::new(&*buf), &tx_print, &rx_print).is_err() {
if try_print_gif(&conf, BufReader::new(&*buf), (&tx_print, &rx_print)).is_err() {
if let Ok(img) = image::load_from_memory(&buf) {
resize_and_print(&conf, true, &img);
let new_img = resize(&conf, true, &img);
printer::print(&new_img, conf.transparent, conf.truecolor);
} else {
let err = String::from("Data from stdin could not be decoded as an image.");
//we want to exit the program => be verbose and have no tolerance
@ -58,11 +62,11 @@ pub fn run(mut conf: Config) {
};
}
} else {
view_passed_files(&mut conf, &tx_print, &rx_print);
view_passed_files(&mut conf, (&tx_print, &rx_print));
}
}
fn view_passed_files(conf: &mut Config, tx: &mpsc::Sender<bool>, rx: &mpsc::Receiver<bool>) {
fn view_passed_files(conf: &mut Config, (tx, rx): TxRx) {
//loop throught all files passed
for filename in &conf.files {
match fs::metadata(filename) {
@ -70,11 +74,11 @@ fn view_passed_files(conf: &mut Config, tx: &mpsc::Sender<bool>, rx: &mpsc::Rece
//if its a directory, stop gif looping because there will probably be more files
if m.is_dir() {
conf.loop_gif = false;
view_directory(conf, filename, tx, rx);
view_directory(conf, filename, (tx, rx));
}
//if a file has been passed individually and fails, do so intolerantly
else {
view_file(conf, filename, false, tx, rx);
view_file(conf, filename, false, (tx, rx));
}
}
Err(e) => eprintln!("{}", e),
@ -82,12 +86,7 @@ fn view_passed_files(conf: &mut Config, tx: &mpsc::Sender<bool>, rx: &mpsc::Rece
}
}
fn view_directory(
conf: &Config,
dirname: &str,
tx: &mpsc::Sender<bool>,
rx: &mpsc::Receiver<bool>,
) {
fn view_directory(conf: &Config, dirname: &str, (tx, rx): TxRx) {
match fs::read_dir(dirname) {
Ok(iter) => {
for dir_entry_result in iter {
@ -105,12 +104,12 @@ fn view_directory(
if metadata.is_dir() {
//if -r is passed, continue down
if conf.recursive {
view_directory(conf, path_name, tx, rx);
view_directory(conf, path_name, (tx, rx));
}
}
//if it is a regular file, viu it with tolerance = true
else {
view_file(conf, path_name, true, tx, rx);
view_file(conf, path_name, true, (tx, rx));
}
} else {
eprintln!("Could not get path name, skipping...");
@ -130,13 +129,7 @@ fn view_directory(
//the tolerance argument specifies either if the program will
// - exit on error (when one of the passed files could not be viewed)
// - fail silently and continue (for a file in a directory)
fn view_file(
conf: &Config,
filename: &str,
tolerant: bool,
tx: &mpsc::Sender<bool>,
rx: &mpsc::Receiver<bool>,
) {
fn view_file(conf: &Config, filename: &str, tolerant: bool, (tx, rx): TxRx) {
if conf.name {
println!("{}:", filename);
}
@ -149,31 +142,33 @@ fn view_file(
};
//errors should be reported if -v is passed or if we do not tolerate them
let should_report_err = conf.verbose || !tolerant;
let should_report = conf.verbose || !tolerant;
if try_print_gif(conf, BufReader::new(file_in), tx, rx).is_err() {
if try_print_gif(conf, BufReader::new(file_in), (tx, rx)).is_err() {
//the provided image is not a gif so try to view it
match image::io::Reader::open(filename) {
Ok(i) => match i.with_guessed_format() {
Ok(img) => match img.decode() {
Ok(decoded) => {
resize_and_print(conf, true, &decoded);
let new_img = resize(conf, true, &decoded);
printer::print(&new_img, conf.transparent, conf.truecolor);
}
//Could not guess format
Err(e) => error_and_quit(filename, e.to_string(), should_report_err, tolerant),
Err(e) => error_and_quit(filename, e.to_string(), should_report, tolerant),
},
Err(e) => error_and_quit(
filename,
format!("An IO error occured while docoding: {}", e),
should_report_err,
format!("An IO error occured while decoding: {}", e),
should_report,
tolerant,
),
},
Err(e) => error_and_quit(
filename,
format!("Could not open file: {}", e),
should_report_err,
should_report,
tolerant,
),
};
@ -183,21 +178,22 @@ fn view_file(
fn try_print_gif<R: Read>(
conf: &Config,
input_stream: R,
tx: &mpsc::Sender<bool>,
rx: &mpsc::Receiver<bool>,
(tx, rx): TxRx,
) -> Result<(), image::ImageError> {
let decoder = GifDecoder::new(input_stream)?;
//if it is a legit gif read the frames and start printing them
let frames = decoder.into_frames().collect_frames()?;
//read all frames of the gif and resize them all at once before starting to print them
let resized_frames: Vec<DynamicImage> = decoder
.into_frames()
.collect_frames()?
.into_iter()
.map(|f| resize(conf, false, &DynamicImage::ImageRgba8(f.into_buffer())))
.collect();
'infinite: loop {
let mut iter = frames.iter().peekable();
let mut iter = resized_frames.iter().peekable();
while let Some(frame) = iter.next() {
let (_, height) = resize_and_print(
conf,
false,
&DynamicImage::ImageRgba8(frame.buffer().to_owned()),
);
let height = frame.height();
printer::print(&frame, conf.transparent, conf.truecolor);
if conf.static_gif {
break 'infinite;
@ -207,7 +203,6 @@ fn try_print_gif<R: Read>(
{
thread::sleep(THIRTY_MILLIS);
//TODO: listen for user input to stop(e.g 'q'), not only Ctrl-C
//if ctrlc is received then respond so the handler can clear the
//terminal from leftover colors
if rx.try_recv().is_ok() {
@ -244,6 +239,8 @@ fn error_and_quit(filename: &str, e: String, verbose: bool, tolerant: bool) {
}
fn resize(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> DynamicImage {
let should_report = conf.verbose && is_not_gif;
let mut new_img;
let (width, height) = img.dimensions();
let (mut print_width, mut print_height) = (width, height);
@ -257,7 +254,7 @@ fn resize(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> DynamicImage {
}
match (conf.width, conf.height) {
(None, None) => {
if conf.verbose && is_not_gif {
if should_report {
println!(
"Neither width, nor height is specified, therefore terminal size will be matched"
);
@ -271,7 +268,7 @@ fn resize(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> DynamicImage {
Err(e) => {
//If getting terminal size fails, fall back to some default size
size = (100, 40);
if conf.verbose && is_not_gif {
if should_report {
eprintln!("{}", e);
}
}
@ -288,7 +285,7 @@ fn resize(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> DynamicImage {
if height > h {
print_height = 2 * h;
}
if conf.verbose && is_not_gif {
if should_report {
println!(
"Usable space is {}x{}, resizing and preserving aspect ratio",
print_width, print_height
@ -297,7 +294,7 @@ fn resize(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> DynamicImage {
new_img = img.thumbnail(print_width, print_height);
}
(Some(_), None) | (None, Some(_)) => {
if conf.verbose && is_not_gif {
if should_report {
println!(
"Either width or height is specified, resizing to {}x{} and preserving aspect ratio",
print_width, print_height
@ -306,7 +303,7 @@ fn resize(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> DynamicImage {
new_img = img.thumbnail(print_width, print_height);
}
(Some(_w), Some(_h)) => {
if conf.verbose && is_not_gif {
if should_report {
println!(
"Both width and height are specified, resizing to {}x{} without preserving aspect ratio",
print_width,
@ -320,24 +317,14 @@ fn resize(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> DynamicImage {
if conf.mirror {
new_img = new_img.fliph();
};
new_img
}
fn resize_and_print(conf: &Config, is_not_gif: bool, img: &DynamicImage) -> (u32, u32) {
let new_img = resize(conf, is_not_gif, img);
printer::print(&new_img, conf.transparent, conf.truecolor);
let (print_width, print_height) = new_img.dimensions();
let (width, height) = img.dimensions();
if conf.verbose && is_not_gif {
if should_report {
println!(
"From {}x{} the image is now {}x{}",
width, height, print_width, print_height
);
}
(print_width, print_height)
new_img
}
#[cfg(test)]
@ -408,6 +395,6 @@ mod test {
fn test_view_without_extension() {
let conf = Config::test_config();
let (tx, rx) = mpsc::channel();
view_file(&conf, "img/bfa", false, &tx, &rx);
view_file(&conf, "img/bfa", false, (&tx, &rx));
}
}