diff --git a/src/lib.rs b/src/lib.rs index 64f6a51a..3f054a51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ use std::collections::{HashMap, HashSet}; use std::fs::{File, copy}; use std::io::{BufWriter, Write, stderr, stdout}; use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex}; pub mod deflate { pub mod deflate; @@ -80,6 +81,8 @@ pub struct Options { /// Perform optimization on the input file using the options provided pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { + type TrialWithData = (u8, u8, u8, u8, Vec); + // Decode PNG from file if opts.verbosity.is_some() { writeln!(&mut stderr(), "Processing: {}", filepath.to_str().unwrap()).ok(); @@ -196,9 +199,9 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { let thread_count = num_cpus + (num_cpus >> 1); let pool = Pool::new(thread_count); // Go through selected permutations and determine the best - let mut best: Option<(u8, u8, u8, u8, Vec)> = None; + let best: Arc>> = Arc::new(Mutex::new(None)); let combinations = filter.len() * compression.len() * memory.len() * strategies.len(); - let mut results: Vec<(u8, u8, u8, u8, Vec)> = Vec::with_capacity(combinations); + let mut results: Vec<(u8, u8, u8, u8)> = Vec::with_capacity(combinations); let mut filters: HashMap> = HashMap::with_capacity(filter.len()); if opts.verbosity.is_some() { writeln!(&mut stderr(), "Trying: {} combinations", combinations).ok(); @@ -210,14 +213,18 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { for zc in &compression { for zm in &memory { for zs in &strategies { - results.push((*f, *zc, *zm, *zs, Vec::new())); + results.push((*f, *zc, *zm, *zs)); } } } } pool.scoped(|scope| { - for trial in &mut results { + let original_len = png.idat_data.len(); + let interlacing_changed = opts.interlace.is_some() && + opts.interlace != Some(png.ihdr_data.interlaced); + for trial in &results { let filtered = filters.get(&trial.0).unwrap(); + let best = best.clone(); scope.execute(move || { let new_idat = deflate::deflate::deflate(filtered, trial.1, @@ -237,22 +244,19 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { .ok(); } - trial.4 = new_idat; + let mut best = best.lock().unwrap(); + if (best.is_some() && + new_idat.len() < best.as_ref().map(|x| x.4.len()).unwrap()) || + (best.is_none() && + (new_idat.len() < original_len || interlacing_changed || opts.force)) { + *best = Some((trial.0, trial.1, trial.2, trial.3, new_idat)); + } }); } }); - for result in &results { - if (best.is_some() && result.4.len() < best.as_ref().map(|x| x.4.len()).unwrap()) || - (best.is_none() && - (result.4.len() < png.idat_data.len() || - (opts.interlace.is_some() && opts.interlace != Some(png.ihdr_data.interlaced)) || - opts.force)) { - best = Some(result.clone()); - } - } - - if let Some(better) = best { + let mut final_best = best.lock().unwrap(); + if let Some(better) = final_best.take() { png.idat_data = better.4.clone(); if opts.verbosity.is_some() { writeln!(&mut stderr(), "Found better combination:").ok();