parent
e4cd9b85e7
commit
3a39b13acf
7 changed files with 54 additions and 139 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
- Minor compression improvement on interlaced images
|
- Minor compression improvement on interlaced images
|
||||||
- Performance optimizations
|
- Performance optimizations
|
||||||
- [SEMVER_MINOR] Move default Options into a Default impl
|
- [SEMVER_MINOR] Move default Options into a Default impl
|
||||||
|
- [SEMVER_MINOR] Add option for setting number of threads ([#39](https://github.com/shssoichiro/oxipng/issues/39))
|
||||||
|
|
||||||
**Version 0.6.0**
|
**Version 0.6.0**
|
||||||
- Fix issue where output directory would not be created if it did not exist
|
- Fix issue where output directory would not be created if it did not exist
|
||||||
|
|
|
||||||
11
src/lib.rs
11
src/lib.rs
|
|
@ -79,6 +79,8 @@ pub struct Options {
|
||||||
/// Whether to use heuristics to pick the best filter and compression
|
/// Whether to use heuristics to pick the best filter and compression
|
||||||
/// Intended for use with `-o 1` from the CLI interface
|
/// Intended for use with `-o 1` from the CLI interface
|
||||||
pub use_heuristics: bool,
|
pub use_heuristics: bool,
|
||||||
|
/// Number of threads to use, defaults to 1.5x CPU cores, rounded down
|
||||||
|
pub threads: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Options {
|
impl Default for Options {
|
||||||
|
|
@ -96,6 +98,10 @@ impl Default for Options {
|
||||||
strategies.insert(i);
|
strategies.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default to 1 thread on single-core, otherwise use threads = 1.5x CPU cores
|
||||||
|
let num_cpus = num_cpus::get();
|
||||||
|
let thread_count = num_cpus + (num_cpus >> 1);
|
||||||
|
|
||||||
Options {
|
Options {
|
||||||
backup: false,
|
backup: false,
|
||||||
out_file: PathBuf::new(),
|
out_file: PathBuf::new(),
|
||||||
|
|
@ -121,6 +127,7 @@ impl Default for Options {
|
||||||
idat_recoding: true,
|
idat_recoding: true,
|
||||||
strip: png::Headers::None,
|
strip: png::Headers::None,
|
||||||
use_heuristics: false,
|
use_heuristics: false,
|
||||||
|
threads: thread_count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,9 +207,7 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
let something_changed = perform_reductions(&mut png, &opts);
|
let something_changed = perform_reductions(&mut png, &opts);
|
||||||
|
|
||||||
if opts.idat_recoding || something_changed {
|
if opts.idat_recoding || something_changed {
|
||||||
// Use 1 thread on single-core, otherwise use threads = 1.5x CPU cores
|
let thread_count = opts.threads;
|
||||||
let num_cpus = num_cpus::get();
|
|
||||||
let thread_count = num_cpus + (num_cpus >> 1);
|
|
||||||
let pool = Pool::new(thread_count);
|
let pool = Pool::new(thread_count);
|
||||||
// Go through selected permutations and determine the best
|
// Go through selected permutations and determine the best
|
||||||
let best: Arc<Mutex<Option<TrialWithData>>> = Arc::new(Mutex::new(None));
|
let best: Arc<Mutex<Option<TrialWithData>>> = Arc::new(Mutex::new(None));
|
||||||
|
|
|
||||||
21
src/main.rs
21
src/main.rs
|
|
@ -172,6 +172,23 @@ fn main() {
|
||||||
.help("Strip safely-removable metadata objects")
|
.help("Strip safely-removable metadata objects")
|
||||||
.short("s")
|
.short("s")
|
||||||
.conflicts_with("strip"))
|
.conflicts_with("strip"))
|
||||||
|
.arg(Arg::with_name("threads")
|
||||||
|
.help("Set number of threads to use - default 1.5x CPU cores")
|
||||||
|
.long("threads")
|
||||||
|
.short("t")
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(|x| {
|
||||||
|
match x.parse::<usize>() {
|
||||||
|
Ok(val) => {
|
||||||
|
if val > 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("Thread count must be >= 1".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => Err("Thread count must be >= 1".to_owned()),
|
||||||
|
}
|
||||||
|
}))
|
||||||
.after_help("Optimization levels:
|
.after_help("Optimization levels:
|
||||||
-o 0 => --zc 3 --nz (0 or 1 trials)
|
-o 0 => --zc 3 --nz (0 or 1 trials)
|
||||||
-o 1 => --zc 9 (1 trial, determined heuristically)
|
-o 1 => --zc 9 (1 trial, determined heuristically)
|
||||||
|
|
@ -478,6 +495,10 @@ fn parse_opts_into_struct(matches: &ArgMatches, opts: &mut oxipng::Options) -> R
|
||||||
opts.strip = png::Headers::Safe;
|
opts.strip = png::Headers::Safe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(x) = matches.value_of("threads") {
|
||||||
|
opts.threads = x.parse::<usize>().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,43 +10,15 @@ use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn get_opts(input: &Path) -> oxipng::Options {
|
fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
|
let mut options = oxipng::Options::default();
|
||||||
|
options.out_file = input.with_extension("out.png").to_owned();
|
||||||
|
options.verbosity = None;
|
||||||
|
options.force = true;
|
||||||
let mut filter = HashSet::new();
|
let mut filter = HashSet::new();
|
||||||
filter.insert(0);
|
filter.insert(0);
|
||||||
let mut compression = HashSet::new();
|
options.filter = filter;
|
||||||
compression.insert(9);
|
|
||||||
let mut memory = HashSet::new();
|
|
||||||
memory.insert(9);
|
|
||||||
let mut strategies = HashSet::new();
|
|
||||||
for i in 0..4 {
|
|
||||||
strategies.insert(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
oxipng::Options {
|
options
|
||||||
backup: false,
|
|
||||||
out_file: input.with_extension("out.png").to_owned(),
|
|
||||||
out_dir: None,
|
|
||||||
stdout: false,
|
|
||||||
pretend: false,
|
|
||||||
recursive: false,
|
|
||||||
fix_errors: false,
|
|
||||||
force: true,
|
|
||||||
clobber: true,
|
|
||||||
create: true,
|
|
||||||
preserve_attrs: false,
|
|
||||||
verbosity: None,
|
|
||||||
filter: filter,
|
|
||||||
interlace: None,
|
|
||||||
compression: compression,
|
|
||||||
memory: memory,
|
|
||||||
strategies: strategies,
|
|
||||||
window: 15,
|
|
||||||
bit_depth_reduction: true,
|
|
||||||
color_type_reduction: true,
|
|
||||||
palette_reduction: true,
|
|
||||||
idat_recoding: true,
|
|
||||||
strip: png::Headers::None,
|
|
||||||
use_heuristics: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_it_converts(input: &Path,
|
fn test_it_converts(input: &Path,
|
||||||
|
|
|
||||||
|
|
@ -10,43 +10,15 @@ use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn get_opts(input: &Path) -> oxipng::Options {
|
fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
|
let mut options = oxipng::Options::default();
|
||||||
|
options.out_file = input.with_extension("out.png").to_owned();
|
||||||
|
options.verbosity = None;
|
||||||
|
options.force = true;
|
||||||
let mut filter = HashSet::new();
|
let mut filter = HashSet::new();
|
||||||
filter.insert(0);
|
filter.insert(0);
|
||||||
let mut compression = HashSet::new();
|
options.filter = filter;
|
||||||
compression.insert(9);
|
|
||||||
let mut memory = HashSet::new();
|
|
||||||
memory.insert(9);
|
|
||||||
let mut strategies = HashSet::new();
|
|
||||||
for i in 0..4 {
|
|
||||||
strategies.insert(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
oxipng::Options {
|
options
|
||||||
backup: false,
|
|
||||||
out_file: input.with_extension("out.png").to_owned(),
|
|
||||||
out_dir: None,
|
|
||||||
stdout: false,
|
|
||||||
pretend: false,
|
|
||||||
recursive: false,
|
|
||||||
fix_errors: false,
|
|
||||||
force: true,
|
|
||||||
clobber: true,
|
|
||||||
create: true,
|
|
||||||
preserve_attrs: false,
|
|
||||||
verbosity: None,
|
|
||||||
filter: filter,
|
|
||||||
interlace: None,
|
|
||||||
compression: compression,
|
|
||||||
memory: memory,
|
|
||||||
strategies: strategies,
|
|
||||||
window: 15,
|
|
||||||
bit_depth_reduction: true,
|
|
||||||
color_type_reduction: true,
|
|
||||||
palette_reduction: true,
|
|
||||||
idat_recoding: true,
|
|
||||||
strip: png::Headers::None,
|
|
||||||
use_heuristics: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_it_converts(input: &Path,
|
fn test_it_converts(input: &Path,
|
||||||
|
|
|
||||||
|
|
@ -10,43 +10,15 @@ use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn get_opts(input: &Path) -> oxipng::Options {
|
fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
|
let mut options = oxipng::Options::default();
|
||||||
|
options.out_file = input.with_extension("out.png").to_owned();
|
||||||
|
options.verbosity = None;
|
||||||
|
options.force = true;
|
||||||
let mut filter = HashSet::new();
|
let mut filter = HashSet::new();
|
||||||
filter.insert(0);
|
filter.insert(0);
|
||||||
let mut compression = HashSet::new();
|
options.filter = filter;
|
||||||
compression.insert(9);
|
|
||||||
let mut memory = HashSet::new();
|
|
||||||
memory.insert(9);
|
|
||||||
let mut strategies = HashSet::new();
|
|
||||||
for i in 0..4 {
|
|
||||||
strategies.insert(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
oxipng::Options {
|
options
|
||||||
backup: false,
|
|
||||||
out_file: input.with_extension("out.png").to_owned(),
|
|
||||||
out_dir: None,
|
|
||||||
stdout: false,
|
|
||||||
pretend: false,
|
|
||||||
recursive: false,
|
|
||||||
fix_errors: false,
|
|
||||||
force: true,
|
|
||||||
clobber: true,
|
|
||||||
create: true,
|
|
||||||
preserve_attrs: false,
|
|
||||||
verbosity: None,
|
|
||||||
filter: filter,
|
|
||||||
interlace: None,
|
|
||||||
compression: compression,
|
|
||||||
memory: memory,
|
|
||||||
strategies: strategies,
|
|
||||||
window: 15,
|
|
||||||
bit_depth_reduction: true,
|
|
||||||
color_type_reduction: true,
|
|
||||||
palette_reduction: true,
|
|
||||||
idat_recoding: true,
|
|
||||||
strip: png::Headers::None,
|
|
||||||
use_heuristics: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_it_converts(input: &Path,
|
fn test_it_converts(input: &Path,
|
||||||
|
|
|
||||||
|
|
@ -10,43 +10,15 @@ use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn get_opts(input: &Path) -> oxipng::Options {
|
fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
|
let mut options = oxipng::Options::default();
|
||||||
|
options.out_file = input.with_extension("out.png").to_owned();
|
||||||
|
options.verbosity = None;
|
||||||
|
options.force = true;
|
||||||
let mut filter = HashSet::new();
|
let mut filter = HashSet::new();
|
||||||
filter.insert(0);
|
filter.insert(0);
|
||||||
let mut compression = HashSet::new();
|
options.filter = filter;
|
||||||
compression.insert(9);
|
|
||||||
let mut memory = HashSet::new();
|
|
||||||
memory.insert(9);
|
|
||||||
let mut strategies = HashSet::new();
|
|
||||||
for i in 0..4 {
|
|
||||||
strategies.insert(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
oxipng::Options {
|
options
|
||||||
backup: false,
|
|
||||||
out_file: input.with_extension("out.png").to_owned(),
|
|
||||||
out_dir: None,
|
|
||||||
stdout: false,
|
|
||||||
pretend: false,
|
|
||||||
recursive: false,
|
|
||||||
fix_errors: false,
|
|
||||||
force: true,
|
|
||||||
clobber: true,
|
|
||||||
create: true,
|
|
||||||
preserve_attrs: false,
|
|
||||||
verbosity: None,
|
|
||||||
filter: filter,
|
|
||||||
interlace: None,
|
|
||||||
compression: compression,
|
|
||||||
memory: memory,
|
|
||||||
strategies: strategies,
|
|
||||||
window: 15,
|
|
||||||
bit_depth_reduction: true,
|
|
||||||
color_type_reduction: true,
|
|
||||||
palette_reduction: true,
|
|
||||||
idat_recoding: true,
|
|
||||||
strip: png::Headers::None,
|
|
||||||
use_heuristics: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_it_converts(input: &Path,
|
fn test_it_converts(input: &Path,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue