diff --git a/src/evaluate.rs b/src/evaluate.rs index d131e6f9..58d724aa 100644 --- a/src/evaluate.rs +++ b/src/evaluate.rs @@ -11,6 +11,7 @@ use crate::rayon; use crate::Deadline; #[cfg(feature = "parallel")] use crossbeam_channel::{unbounded, Receiver, Sender}; +use indexmap::IndexSet; use rayon::prelude::*; #[cfg(not(feature = "parallel"))] use std::cell::RefCell; @@ -18,10 +19,6 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::Arc; -/// Must use normal (lazy) compression, as faster ones (greedy) are not representative -const STD_COMPRESSION: u8 = 5; -const STD_FILTERS: [RowFilter; 2] = [RowFilter::None, RowFilter::MinSum]; - pub struct Candidate { pub image: PngData, pub filter: RowFilter, @@ -45,6 +42,8 @@ impl Candidate { /// Collect image versions and pick one that compresses best pub(crate) struct Evaluator { deadline: Arc, + filters: IndexSet, + compression: u8, nth: AtomicUsize, best_candidate_size: Arc, /// images are sent to the caller thread for evaluation @@ -56,11 +55,13 @@ pub(crate) struct Evaluator { } impl Evaluator { - pub fn new(deadline: Arc) -> Self { + pub fn new(deadline: Arc, filters: IndexSet, compression: u8) -> Self { #[cfg(feature = "parallel")] let eval_channel = unbounded(); Self { deadline, + filters, + compression, best_candidate_size: Arc::new(AtomicMin::new(None)), nth: AtomicUsize::new(0), #[cfg(feature = "parallel")] @@ -98,13 +99,15 @@ impl Evaluator { let nth = self.nth.fetch_add(1, SeqCst); // These clones are only cheap refcounts let deadline = self.deadline.clone(); + let filters = self.filters.clone(); + let compression = self.compression; let best_candidate_size = self.best_candidate_size.clone(); // sends it off asynchronously for compression, // but results will be collected via the message queue #[cfg(feature = "parallel")] let eval_send = self.eval_channel.0.clone(); rayon::spawn(move || { - let filters_iter = STD_FILTERS.par_iter().with_max_len(1); + let filters_iter = filters.par_iter().with_max_len(1); // Updating of best result inside the parallel loop would require locks, // which are dangerous to do in side Rayon's loop. @@ -116,7 +119,7 @@ impl Evaluator { } if let Ok(idat_data) = deflate::deflate( &image.filter_image(filter), - STD_COMPRESSION, + compression, &best_candidate_size, ) { best_candidate_size.set_min(idat_data.len()); diff --git a/src/lib.rs b/src/lib.rs index d172f1c1..ae480bf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -484,22 +484,22 @@ fn optimize_png( let mut filter = opts.filter.clone(); - if opts.use_heuristics { + if filter.is_empty() { // Heuristically determine which set of options to use - let use_filter = if png.raw.ihdr.bit_depth.as_u8() >= 8 + if png.raw.ihdr.bit_depth.as_u8() >= 8 && png.raw.ihdr.color_type != colors::ColorType::Indexed { - RowFilter::MinSum + filter.insert(RowFilter::MinSum); } else { - RowFilter::None + filter.insert(RowFilter::None); }; - if filter.is_empty() { - filter.insert(use_filter); - } } + // Must use normal (lazy) compression, as faster ones (greedy) are not representative + let eval_compression = 5; + let eval_filters = indexset! {RowFilter::None, RowFilter::MinSum}; // This will collect all versions of images and pick one that compresses best - let eval = Evaluator::new(deadline.clone()); + let eval = Evaluator::new(deadline.clone(), eval_filters, eval_compression); perform_reductions(png.raw.clone(), opts, &deadline, &eval); let reduction_occurred = if let Some(result) = eval.get_best_candidate() { *png = result.image;