Make Rayon an optional dependency (#112)
On WebAssembly, Rayon does not work (thread pool fails to initialize since threads are stubbed out).
This commit is contained in:
parent
2d303f5dc2
commit
ae65e4be8d
3 changed files with 44 additions and 15 deletions
|
|
@ -32,10 +32,13 @@ byteorder = "^1.0.0"
|
||||||
crc = "^1.2.0"
|
crc = "^1.2.0"
|
||||||
itertools = "^0.7.7"
|
itertools = "^0.7.7"
|
||||||
num_cpus = "^1.0.0"
|
num_cpus = "^1.0.0"
|
||||||
rayon = "^1.0.0"
|
|
||||||
zopfli = "^0.3.4"
|
zopfli = "^0.3.4"
|
||||||
miniz_oxide = "0.1.2"
|
miniz_oxide = "0.1.2"
|
||||||
|
|
||||||
|
[dependencies.rayon]
|
||||||
|
optional = true
|
||||||
|
version = "^1.0.0"
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
optional = true
|
optional = true
|
||||||
version = "^2.10.0"
|
version = "^2.10.0"
|
||||||
|
|
@ -62,7 +65,8 @@ binary = [
|
||||||
"clap",
|
"clap",
|
||||||
"wild",
|
"wild",
|
||||||
]
|
]
|
||||||
default = ["binary"]
|
default = ["binary", "parallel"]
|
||||||
|
parallel = ["rayon"]
|
||||||
cfzlib = ["cloudflare-zlib-sys"]
|
cfzlib = ["cloudflare-zlib-sys"]
|
||||||
dev = [
|
dev = [
|
||||||
"nightly-binary",
|
"nightly-binary",
|
||||||
|
|
|
||||||
34
src/lib.rs
34
src/lib.rs
|
|
@ -8,6 +8,7 @@ extern crate image;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
extern crate miniz_oxide;
|
extern crate miniz_oxide;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
extern crate rayon;
|
extern crate rayon;
|
||||||
extern crate zopfli;
|
extern crate zopfli;
|
||||||
#[cfg(feature = "cfzlib")]
|
#[cfg(feature = "cfzlib")]
|
||||||
|
|
@ -15,6 +16,7 @@ extern crate cloudflare_zlib_sys;
|
||||||
|
|
||||||
use image::{DynamicImage, GenericImage, ImageFormat, Pixel};
|
use image::{DynamicImage, GenericImage, ImageFormat, Pixel};
|
||||||
use png::PngData;
|
use png::PngData;
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs::{copy, File};
|
use std::fs::{copy, File};
|
||||||
|
|
@ -277,7 +279,9 @@ impl Default for Options {
|
||||||
/// Perform optimization on the input file using the options provided
|
/// Perform optimization on the input file using the options provided
|
||||||
pub fn optimize(input_path: &Path, opts: &Options) -> Result<(), PngError> {
|
pub fn optimize(input_path: &Path, opts: &Options) -> Result<(), PngError> {
|
||||||
// Initialize the thread pool with correct number of threads
|
// Initialize the thread pool with correct number of threads
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
let thread_count = opts.threads;
|
let thread_count = opts.threads;
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
let _ = rayon::ThreadPoolBuilder::new()
|
let _ = rayon::ThreadPoolBuilder::new()
|
||||||
.num_threads(thread_count)
|
.num_threads(thread_count)
|
||||||
.build_global();
|
.build_global();
|
||||||
|
|
@ -356,7 +360,9 @@ pub fn optimize(input_path: &Path, opts: &Options) -> Result<(), PngError> {
|
||||||
/// loaded in-memory
|
/// loaded in-memory
|
||||||
pub fn optimize_from_memory(data: &[u8], opts: &Options) -> Result<Vec<u8>, PngError> {
|
pub fn optimize_from_memory(data: &[u8], opts: &Options) -> Result<Vec<u8>, PngError> {
|
||||||
// Initialize the thread pool with correct number of threads
|
// Initialize the thread pool with correct number of threads
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
let thread_count = opts.threads;
|
let thread_count = opts.threads;
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
let _ = rayon::ThreadPoolBuilder::new()
|
let _ = rayon::ThreadPoolBuilder::new()
|
||||||
.num_threads(thread_count)
|
.num_threads(thread_count)
|
||||||
.build_global();
|
.build_global();
|
||||||
|
|
@ -483,9 +489,11 @@ fn optimize_png(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let filters: HashMap<u8, Vec<u8>> = filter
|
#[cfg(feature = "parallel")]
|
||||||
.par_iter()
|
let filter_iter = filter.par_iter().with_max_len(1);
|
||||||
.with_max_len(1)
|
#[cfg(not(feature = "parallel"))]
|
||||||
|
let filter_iter = filter.iter();
|
||||||
|
let filters: HashMap<u8, Vec<u8>> = filter_iter
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
let png = png.clone();
|
let png = png.clone();
|
||||||
(*f, png.filter_image(*f))
|
(*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 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_size = AtomicMin::new(if opts.force {None} else {Some(original_len)});
|
||||||
let best: Option<TrialWithData> = results
|
#[cfg(feature = "parallel")]
|
||||||
.into_par_iter()
|
let results_iter = results.into_par_iter().with_max_len(1);
|
||||||
.with_max_len(1)
|
#[cfg(not(feature = "parallel"))]
|
||||||
|
let results_iter = results.into_iter();
|
||||||
|
let best = results_iter
|
||||||
.filter_map(|trial| {
|
.filter_map(|trial| {
|
||||||
let filtered = &filters[&trial.filter];
|
let filtered = &filters[&trial.filter];
|
||||||
let new_idat = if opts.deflate == Deflaters::Zlib {
|
let new_idat = if opts.deflate == Deflaters::Zlib {
|
||||||
|
|
@ -540,8 +550,18 @@ fn optimize_png(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
let best: Option<TrialWithData> = best
|
||||||
.reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j });
|
.reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j });
|
||||||
|
#[cfg(not(feature = "parallel"))]
|
||||||
|
let best: Option<TrialWithData> = 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 {
|
if let Some(better) = best {
|
||||||
png.idat_data = better.1;
|
png.idat_data = better.1;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use std::fs::File;
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use atomicmin::AtomicMin;
|
use atomicmin::AtomicMin;
|
||||||
|
|
||||||
|
|
@ -605,15 +606,19 @@ impl PngData {
|
||||||
assert!(!alphas.is_empty());
|
assert!(!alphas.is_empty());
|
||||||
let alphas = alphas.iter().collect::<Vec<_>>();
|
let alphas = alphas.iter().collect::<Vec<_>>();
|
||||||
let best_size = AtomicMin::new(None);
|
let best_size = AtomicMin::new(None);
|
||||||
let best = alphas
|
#[cfg(feature = "parallel")]
|
||||||
.par_iter()
|
let alphas_iter = alphas.par_iter().with_max_len(1);
|
||||||
.with_max_len(1)
|
#[cfg(not(feature = "parallel"))]
|
||||||
|
let alphas_iter = alphas.iter();
|
||||||
|
let best = alphas_iter
|
||||||
.filter_map(|&alpha| {
|
.filter_map(|&alpha| {
|
||||||
let mut image = self.clone();
|
let mut image = self.clone();
|
||||||
image.reduce_alpha_channel(*alpha);
|
image.reduce_alpha_channel(*alpha);
|
||||||
STD_FILTERS
|
#[cfg(feature = "parallel")]
|
||||||
.par_iter()
|
let filters_iter = STD_FILTERS.par_iter().with_max_len(1);
|
||||||
.with_max_len(1)
|
#[cfg(not(feature = "parallel"))]
|
||||||
|
let filters_iter = STD_FILTERS.iter();
|
||||||
|
filters_iter
|
||||||
.filter_map(|f| {
|
.filter_map(|f| {
|
||||||
deflate::deflate(
|
deflate::deflate(
|
||||||
&image.filter_image(*f),
|
&image.filter_image(*f),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue