diff --git a/src/evaluate.rs b/src/evaluate.rs index 5260bc2d..84c36cc0 100644 --- a/src/evaluate.rs +++ b/src/evaluate.rs @@ -9,7 +9,6 @@ use png::STD_COMPRESSION; use png::STD_FILTERS; use png::STD_STRATEGY; use png::STD_WINDOW; -#[cfg(feature = "parallel")] use rayon::prelude::*; use std::sync::mpsc::*; use std::sync::Arc; @@ -63,10 +62,7 @@ impl Evaluator { let best_result: Mutex> = Mutex::new(None); // ends when sender is dropped for (nth, (image, bias, is_reduction)) in from_channel.iter().enumerate() { - #[cfg(feature = "parallel")] let filters_iter = STD_FILTERS.par_iter().with_max_len(1); - #[cfg(not(feature = "parallel"))] - let filters_iter = STD_FILTERS.iter(); filters_iter.for_each(|&f| { if let Ok(idat_data) = deflate::deflate( diff --git a/src/lib.rs b/src/lib.rs index e2cdbc67..c523f1a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ extern crate miniz_oxide; extern crate num_cpus; #[cfg(feature = "parallel")] extern crate rayon; +#[cfg(not(feature = "parallel"))] +mod rayon; extern crate rgb; extern crate zopfli; @@ -21,7 +23,6 @@ use image::{DynamicImage, GenericImageView, ImageFormat, Pixel}; use png::PngImage; use png::PngData; use colors::BitDepth; -#[cfg(feature = "parallel")] use rayon::prelude::*; use std::collections::{HashMap, HashSet}; use std::fmt; @@ -569,10 +570,7 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR } } - #[cfg(feature = "parallel")] let filter_iter = filter.par_iter().with_max_len(1); - #[cfg(not(feature = "parallel"))] - let filter_iter = filter.iter(); let filters: HashMap> = filter_iter .map(|f| { let png = png.clone(); @@ -583,10 +581,7 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR let added_interlacing = opts.interlace == Some(1) && original_png.raw.ihdr.interlaced == 0; let best_size = AtomicMin::new(if opts.force { None } else { Some(original_len) }); - #[cfg(feature = "parallel")] let results_iter = results.into_par_iter().with_max_len(1); - #[cfg(not(feature = "parallel"))] - let results_iter = results.into_iter(); let best = results_iter.filter_map(|trial| { if deadline.passed() { return None; @@ -635,20 +630,8 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR None } }); - #[cfg(feature = "parallel")] - let best: Option = - best.reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j }); - #[cfg(not(feature = "parallel"))] - let best: Option = best.fold(None, |i, j| { - if let Some(i) = i { - if i.1.len() <= j.1.len() { - Some(i) - } else { - Some(j) - } - } else { - Some(j) - } + let best: Option = best.reduce_with(|i, j| { + if i.1.len() <= j.1.len() { i } else { j } }); if let Some(better) = best { @@ -704,15 +687,10 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR } } - let (old_png, new_png) = { - let old_png = || image::load_from_memory_with_format(original_data, ImageFormat::PNG); - let new_png = || image::load_from_memory_with_format(&output, ImageFormat::PNG); - #[cfg(feature = "parallel")] - let res = rayon::join(old_png, new_png); - #[cfg(not(feature = "parallel"))] - let res = (old_png(), new_png()); - res - }; + let (old_png, new_png) = rayon::join( + || image::load_from_memory_with_format(original_data, ImageFormat::PNG), + || image::load_from_memory_with_format(&output, ImageFormat::PNG) + ); if let Ok(new_png) = new_png { if let Ok(old_png) = old_png { diff --git a/src/rayon.rs b/src/rayon.rs new file mode 100644 index 00000000..a8e662f8 --- /dev/null +++ b/src/rayon.rs @@ -0,0 +1,53 @@ +pub mod prelude { + pub use super::*; +} + +pub trait ParallelIterator: Iterator + Sized { + fn with_max_len(self, _l: usize) -> Self {self} + fn reduce_with(mut self, op: OP) -> Option where OP: Fn(Self::Item, Self::Item) -> Self::Item + Sync { + if let Some(a) = self.next() { + Some(self.fold(a, op)) + } else { + None + } + } +} + +pub trait IntoParallelIterator { + type Iter: Iterator; + type Item: Send; + fn into_par_iter(self) -> Self::Iter; +} + +pub trait IntoParallelRefIterator<'data> { + type Iter: Iterator; + type Item: Send + 'data; + fn par_iter(&'data self) -> Self::Iter; +} + +impl IntoParallelIterator for I where I::Item: Send { + type Iter = I::IntoIter; + type Item = I::Item; + + fn into_par_iter(self) -> Self::Iter { + self.into_iter() + } +} + +impl<'data, I: 'data + ?Sized> IntoParallelRefIterator<'data> for I + where &'data I: IntoParallelIterator +{ + type Iter = <&'data I as IntoParallelIterator>::Iter; + type Item = <&'data I as IntoParallelIterator>::Item; + + fn par_iter(&'data self) -> Self::Iter { + self.into_par_iter() + } +} + +impl ParallelIterator for I { +} + +pub fn join(a: impl FnOnce() -> A, b: impl FnOnce() -> B) -> (A, B) { + (a(), b()) +} diff --git a/src/reduction/alpha.rs b/src/reduction/alpha.rs index e2bba245..055d67e0 100644 --- a/src/reduction/alpha.rs +++ b/src/reduction/alpha.rs @@ -7,16 +7,12 @@ use colors::AlphaOptim; use headers::IhdrData; use png::PngImage; use colors::ColorType; -#[cfg(feature = "parallel")] use rayon::prelude::*; pub fn try_alpha_reductions(png: Arc, alphas: &HashSet, eval: &Evaluator) { assert!(!alphas.is_empty()); let alphas = alphas.iter().collect::>(); - #[cfg(feature = "parallel")] let alphas_iter = alphas.par_iter().with_max_len(1); - #[cfg(not(feature = "parallel"))] - let alphas_iter = alphas.iter(); alphas_iter .filter_map(|&alpha| filtered_alpha_channel(&png, *alpha)) .for_each(|image| eval.try_image(Arc::new(image), 0.99));