Rebalance presets
This commit is contained in:
parent
6f68438591
commit
82ce66399b
2 changed files with 48 additions and 42 deletions
60
src/lib.rs
60
src/lib.rs
|
|
@ -147,9 +147,9 @@ pub struct Options {
|
|||
///
|
||||
/// Default: `false`
|
||||
pub preserve_attrs: bool,
|
||||
/// Which filters to try on the file (0-5)
|
||||
/// Which RowFilters to try on the file
|
||||
///
|
||||
/// Default: `0,5`
|
||||
/// Default: `None,Sub,Entropy,Bigrams`
|
||||
pub filter: IndexSet<RowFilter>,
|
||||
/// Whether to change the interlacing type of the file.
|
||||
///
|
||||
|
|
@ -194,9 +194,7 @@ pub struct Options {
|
|||
pub deflate: Deflaters,
|
||||
/// Whether to use fast evaluation to pick the best filter
|
||||
///
|
||||
/// Intended for use with `-o 1` from the CLI interface
|
||||
///
|
||||
/// Default: `false`
|
||||
/// Default: `true`
|
||||
pub fast_evaluation: bool,
|
||||
|
||||
/// Maximum amount of time to spend on optimizations.
|
||||
|
|
@ -229,12 +227,10 @@ impl Options {
|
|||
// The following methods make assumptions that they are operating
|
||||
// on an `Options` struct generated by the `default` method.
|
||||
fn apply_preset_0(mut self) -> Self {
|
||||
self.idat_recoding = false;
|
||||
self.filter.clear();
|
||||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||
*compression = 5;
|
||||
}
|
||||
self.fast_evaluation = true;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -243,7 +239,6 @@ impl Options {
|
|||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||
*compression = 10;
|
||||
}
|
||||
self.fast_evaluation = true;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -252,10 +247,13 @@ impl Options {
|
|||
}
|
||||
|
||||
fn apply_preset_3(mut self) -> Self {
|
||||
self.filter.insert(RowFilter::Sub);
|
||||
self.filter.insert(RowFilter::Up);
|
||||
self.filter.insert(RowFilter::Average);
|
||||
self.filter.insert(RowFilter::Paeth);
|
||||
self.fast_evaluation = false;
|
||||
self.filter = indexset! {
|
||||
RowFilter::None,
|
||||
RowFilter::Bigrams,
|
||||
RowFilter::BigEnt,
|
||||
RowFilter::Brute
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -266,12 +264,22 @@ impl Options {
|
|||
self.apply_preset_3()
|
||||
}
|
||||
|
||||
fn apply_preset_5(self) -> Self {
|
||||
self.apply_preset_4()
|
||||
fn apply_preset_5(mut self) -> Self {
|
||||
self.fast_evaluation = false;
|
||||
self.filter.insert(RowFilter::Up);
|
||||
self.filter.insert(RowFilter::MinSum);
|
||||
self.filter.insert(RowFilter::BigEnt);
|
||||
self.filter.insert(RowFilter::Brute);
|
||||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||
*compression = 12;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn apply_preset_6(self) -> Self {
|
||||
self.apply_preset_4()
|
||||
fn apply_preset_6(mut self) -> Self {
|
||||
self.filter.insert(RowFilter::Average);
|
||||
self.filter.insert(RowFilter::Paeth);
|
||||
self.apply_preset_5()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +293,7 @@ impl Default for Options {
|
|||
fix_errors: false,
|
||||
force: false,
|
||||
preserve_attrs: false,
|
||||
filter: indexset! {RowFilter::None, RowFilter::MinSum},
|
||||
filter: indexset! {RowFilter::None, RowFilter::Sub, RowFilter::Entropy, RowFilter::Bigrams},
|
||||
interlace: None,
|
||||
alphas: IndexSet::new(),
|
||||
bit_depth_reduction: true,
|
||||
|
|
@ -295,7 +303,7 @@ impl Default for Options {
|
|||
idat_recoding: true,
|
||||
strip: Headers::None,
|
||||
deflate: Deflaters::Libdeflater { compression: 11 },
|
||||
fast_evaluation: false,
|
||||
fast_evaluation: true,
|
||||
timeout: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -484,8 +492,10 @@ fn optimize_png(
|
|||
let stripped_png = png.clone();
|
||||
|
||||
// Must use normal (lazy) compression, as faster ones (greedy) are not representative
|
||||
// Alpha reductions can benefit from higher compression but otherwise it's not beneficial
|
||||
let eval_compression = 5;
|
||||
let eval_filters = indexset! {RowFilter::None, RowFilter::MinSum};
|
||||
// None and Bigrams work well together, especially for alpha reductions
|
||||
let eval_filters = indexset! {RowFilter::None, RowFilter::Bigrams};
|
||||
// This will collect all versions of images and pick one that compresses best
|
||||
let eval = Evaluator::new(deadline.clone(), eval_filters.clone(), eval_compression);
|
||||
perform_reductions(png.raw.clone(), opts, &deadline, &eval);
|
||||
|
|
@ -501,6 +511,7 @@ fn optimize_png(
|
|||
let fast_eval = opts.fast_evaluation && (filters.len() > 1 || eval_filter.is_some());
|
||||
let best: Option<TrialWithData> = if fast_eval {
|
||||
// Perform a fast evaluation of selected filters followed by a single main compression trial
|
||||
|
||||
if eval_filter.is_some() {
|
||||
// Some filters have already been evaluated, we don't need to try them again
|
||||
filters = filters.difference(&eval_filters).cloned().collect();
|
||||
|
|
@ -541,13 +552,14 @@ fn optimize_png(
|
|||
}
|
||||
} else {
|
||||
// Perform full compression trials of selected filters and determine the best
|
||||
|
||||
if filters.is_empty() {
|
||||
// Heuristically determine which filter to use
|
||||
if png.raw.ihdr.bit_depth.as_u8() >= 8
|
||||
&& png.raw.ihdr.color_type != colors::ColorType::Indexed
|
||||
{
|
||||
filters.insert(RowFilter::MinSum);
|
||||
// Pick a filter automatically
|
||||
if png.raw.ihdr.bit_depth.as_u8() >= 8 {
|
||||
// Bigrams is the best all-rounder when there's at least one byte per pixel
|
||||
filters.insert(RowFilter::Bigrams);
|
||||
} else {
|
||||
// Otherwise delta filters generally don't work well, so just stick with None
|
||||
filters.insert(RowFilter::None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
src/main.rs
30
src/main.rs
|
|
@ -261,14 +261,14 @@ fn main() {
|
|||
)
|
||||
.after_help(
|
||||
"Optimization levels:
|
||||
-o 0 => --zc 6 --nz (0 or 1 trials)
|
||||
-o 1 => --zc 10 (1 trial, determined heuristically)
|
||||
-o 2 => --zc 11 -f 0,5 (2 trials)
|
||||
-o 3 => --zc 11 -f 0-5 (6 trials)
|
||||
-o 4 => --zc 12 -f 0-5 (6 trials; same as `-o 3` for zopfli)
|
||||
-o 5 => --zc 9-12 -f 0-5 (24 trials; same as `-o 3` for zopfli)
|
||||
-o 6 => --zc 1-12 -f 0-5 (72 trials; same as `-o 3` for zopfli)
|
||||
-o max => (stable alias for the max compression)
|
||||
-o 0 => --zc 5 --fast (1 trial, determined heuristically)
|
||||
-o 1 => --zc 10 --fast (1 trial, determined heuristically)
|
||||
-o 2 => --zc 11 -f 0,1,6,7 --fast (1 trial, determined by fast evaluation)
|
||||
-o 3 => --zc 11 -f 0,7,8,9 (4 trials)
|
||||
-o 4 => --zc 12 -f 0,7,8,9 (4 trials; same as `-o 3` for zopfli)
|
||||
-o 5 => --zc 12 -f 0,1,2,5,6,7,8,9 (8 trials)
|
||||
-o 6 => --zc 12 -f 0-9 (10 trials)
|
||||
-o max => (stable alias for the max compression)
|
||||
|
||||
Manually specifying a compression option (zc, f, etc.) will override the optimization preset,
|
||||
regardless of the order you write the arguments.
|
||||
|
|
@ -387,13 +387,10 @@ fn parse_opts_into_struct(
|
|||
.init()
|
||||
.unwrap();
|
||||
|
||||
let (explicit_level, mut opts) = match matches.value_of("optimization") {
|
||||
None => (None, Options::default()),
|
||||
Some("max") => (None, Options::max_compression()),
|
||||
Some(level) => {
|
||||
let level = level.parse::<u8>().unwrap();
|
||||
(Some(level), Options::from_preset(level))
|
||||
}
|
||||
let mut opts = match matches.value_of("optimization") {
|
||||
None => Options::default(),
|
||||
Some("max") => Options::max_compression(),
|
||||
Some(level) => Options::from_preset(level.parse::<u8>().unwrap()),
|
||||
};
|
||||
|
||||
if let Some(x) = matches.value_of("interlace") {
|
||||
|
|
@ -539,9 +536,6 @@ fn parse_opts_into_struct(
|
|||
}
|
||||
|
||||
if matches.is_present("zopfli") {
|
||||
if explicit_level > Some(3) {
|
||||
warn!("Level 4 and above are equivalent to level 3 for zopfli");
|
||||
}
|
||||
#[cfg(feature = "zopfli")]
|
||||
if let Some(iterations) = NonZeroU8::new(15) {
|
||||
opts.deflate = Deflaters::Zopfli { iterations };
|
||||
|
|
|
|||
Loading…
Reference in a new issue