diff --git a/.gitignore b/.gitignore index 6dd2dfeb..889c1d50 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ target Cargo.lock *.bk .DS_Store +*.out.png diff --git a/Cargo.toml b/Cargo.toml index c51b0a70..ccab37f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ bit-vec = "^0.4.2" byteorder = "^0.4.0" clap = "^1.5.4" crc = "^1.0.0" +crossbeam = "^0.2.1" libc = "^0.2.4" libz-sys = "^1.0.0" regex = "^0.1.8" diff --git a/src/lib.rs b/src/lib.rs index 86a9e685..bc47ceb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ extern crate bit_vec; extern crate byteorder; extern crate crc; +extern crate crossbeam; extern crate libc; extern crate libz_sys; @@ -13,6 +14,7 @@ use std::io::BufWriter; use std::io::Write; use std::path::Path; use std::path::PathBuf; +use std::sync::Arc; pub mod deflate { pub mod deflate; @@ -118,42 +120,51 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { if opts.idat_recoding || something_changed { // Go through selected permutations and determine the best let mut best: Option<(u8, u8, u8, u8)> = None; + let scoped_idat = Arc::new(png.idat_data.clone()); let combinations = opts.filter.len() * opts.compression.len() * opts.memory.len() * opts.strategies.len(); println!("Trying: {} combinations", combinations); - // TODO: Multithreading - for f in &opts.filter { - let filtered = png.filter_image(*f); - for zc in &opts.compression { - for zm in &opts.memory { - for zs in &opts.strategies { - let new_idat = match deflate::deflate::deflate(&filtered, - *zc, - *zm, - *zs, - opts.window) { - Ok(x) => x, - Err(x) => return Err(x), - }; - if new_idat.len() < png.idat_data.len() || - (best.is_none() && something_changed) { - best = Some((*f, *zc, *zm, *zs)); - png.idat_data = new_idat.clone(); - } - if opts.verbosity == Some(1) { - println!(" zc = {} zm = {} zs = {} f = {} {} bytes", - *zc, - *zm, - *zs, - *f, - new_idat.len()); + crossbeam::scope(|scope| { + for f in &opts.filter { + let filtered = png.filter_image(*f); + for zc in &opts.compression { + for zm in &opts.memory { + for zs in &opts.strategies { + let moved_png = png.clone(); + let mut moved_idat = scoped_idat.clone(); + let moved_filtered = filtered.clone(); + scope.spawn(move || { + let new_idat = match deflate::deflate::deflate(&moved_filtered, + *zc, + *zm, + *zs, + opts.window) { + Ok(x) => x, + Err(x) => return Err(x), + }; + if new_idat.len() < moved_png.idat_data.len() || + (best.is_none() && something_changed) { + best = Some((*f, *zc, *zm, *zs)); + *Arc::make_mut(&mut moved_idat) = new_idat.clone(); + } + if opts.verbosity == Some(1) { + println!(" zc = {} zm = {} zs = {} f = {} {} bytes", + *zc, + *zm, + *zs, + *f, + new_idat.len()); + } + Ok(()) + }); } } } } - } + }); if let Some(better) = best { + png.idat_data = Arc::try_unwrap(scoped_idat).unwrap().clone(); println!("Found better combination:"); println!(" zc = {} zm = {} zs = {} f = {} {} bytes", better.1, diff --git a/src/png.rs b/src/png.rs index f319c501..1cc60914 100644 --- a/src/png.rs +++ b/src/png.rs @@ -123,7 +123,7 @@ struct ScanLine { data: Vec, } -#[derive(Debug)] +#[derive(Debug,Clone)] pub struct PngData { pub idat_data: Vec, pub ihdr_data: IhdrData, @@ -134,7 +134,7 @@ pub struct PngData { pub aux_headers: HashMap>, } -#[derive(Debug)] +#[derive(Debug,Clone)] pub struct IhdrData { pub width: u32, pub height: u32, diff --git a/tests/oxipng.rs b/tests/oxipng.rs index 30362bb7..766aca53 100644 --- a/tests/oxipng.rs +++ b/tests/oxipng.rs @@ -371,7 +371,7 @@ fn palette_should_be_grayscale() { #[test] fn strip_headers() { - let input = PathBuf::from("tests/files/rgb_should_be_palette.png"); + let input = PathBuf::from("tests/files/rgb_should_be_rgb.png"); let mut opts = get_opts(&input); opts.strip = true; let output = opts.out_file.clone();