From ae65e4be8dab8a4a823092600c553f6d661bed40 Mon Sep 17 00:00:00 2001 From: myfreeweb Date: Fri, 6 Jul 2018 17:50:36 +0300 Subject: [PATCH] Make Rayon an optional dependency (#112) On WebAssembly, Rayon does not work (thread pool fails to initialize since threads are stubbed out). --- Cargo.toml | 8 ++++++-- src/lib.rs | 34 +++++++++++++++++++++++++++------- src/png/mod.rs | 17 +++++++++++------ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5eb3b1a4..5fd809df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,10 +32,13 @@ byteorder = "^1.0.0" crc = "^1.2.0" itertools = "^0.7.7" num_cpus = "^1.0.0" -rayon = "^1.0.0" zopfli = "^0.3.4" miniz_oxide = "0.1.2" +[dependencies.rayon] +optional = true +version = "^1.0.0" + [dependencies.clap] optional = true version = "^2.10.0" @@ -62,7 +65,8 @@ binary = [ "clap", "wild", ] -default = ["binary"] +default = ["binary", "parallel"] +parallel = ["rayon"] cfzlib = ["cloudflare-zlib-sys"] dev = [ "nightly-binary", diff --git a/src/lib.rs b/src/lib.rs index fdcd0288..228a4c83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate image; extern crate itertools; extern crate miniz_oxide; extern crate num_cpus; +#[cfg(feature = "parallel")] extern crate rayon; extern crate zopfli; #[cfg(feature = "cfzlib")] @@ -15,6 +16,7 @@ extern crate cloudflare_zlib_sys; use image::{DynamicImage, GenericImage, ImageFormat, Pixel}; use png::PngData; +#[cfg(feature = "parallel")] use rayon::prelude::*; use std::collections::{HashMap, HashSet}; use std::fs::{copy, File}; @@ -277,7 +279,9 @@ impl Default for Options { /// Perform optimization on the input file using the options provided pub fn optimize(input_path: &Path, opts: &Options) -> Result<(), PngError> { // Initialize the thread pool with correct number of threads + #[cfg(feature = "parallel")] let thread_count = opts.threads; + #[cfg(feature = "parallel")] let _ = rayon::ThreadPoolBuilder::new() .num_threads(thread_count) .build_global(); @@ -356,7 +360,9 @@ pub fn optimize(input_path: &Path, opts: &Options) -> Result<(), PngError> { /// loaded in-memory pub fn optimize_from_memory(data: &[u8], opts: &Options) -> Result, PngError> { // Initialize the thread pool with correct number of threads + #[cfg(feature = "parallel")] let thread_count = opts.threads; + #[cfg(feature = "parallel")] let _ = rayon::ThreadPoolBuilder::new() .num_threads(thread_count) .build_global(); @@ -483,9 +489,11 @@ fn optimize_png( } } - let filters: HashMap> = filter - .par_iter() - .with_max_len(1) + #[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(); (*f, png.filter_image(*f)) @@ -496,9 +504,11 @@ fn optimize_png( let added_interlacing = opts.interlace == Some(1) && original_png.ihdr_data.interlaced == 0; let best_size = AtomicMin::new(if opts.force {None} else {Some(original_len)}); - let best: Option = results - .into_par_iter() - .with_max_len(1) + #[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| { let filtered = &filters[&trial.filter]; let new_idat = if opts.deflate == Deflaters::Zlib { @@ -540,8 +550,18 @@ fn optimize_png( } else { 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) + } + }); if let Some(better) = best { png.idat_data = better.1; diff --git a/src/png/mod.rs b/src/png/mod.rs index 9304bb28..47176f87 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -15,6 +15,7 @@ use std::fs::File; use std::io::{Read, Seek, SeekFrom}; use std::iter::Iterator; use std::path::Path; +#[cfg(feature = "parallel")] use rayon::prelude::*; use atomicmin::AtomicMin; @@ -605,15 +606,19 @@ impl PngData { assert!(!alphas.is_empty()); let alphas = alphas.iter().collect::>(); let best_size = AtomicMin::new(None); - let best = alphas - .par_iter() - .with_max_len(1) + #[cfg(feature = "parallel")] + let alphas_iter = alphas.par_iter().with_max_len(1); + #[cfg(not(feature = "parallel"))] + let alphas_iter = alphas.iter(); + let best = alphas_iter .filter_map(|&alpha| { let mut image = self.clone(); image.reduce_alpha_channel(*alpha); - STD_FILTERS - .par_iter() - .with_max_len(1) + #[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 .filter_map(|f| { deflate::deflate( &image.filter_image(*f),