Switch main compressor to libdeflate

This commit is contained in:
Andrew 2022-10-22 22:56:58 +13:00
parent f688d20fe6
commit 3e8fcc1335
3 changed files with 46 additions and 94 deletions

View file

@ -190,7 +190,7 @@ pub struct Options {
pub strip: Headers, pub strip: Headers,
/// Which DEFLATE algorithm to use /// Which DEFLATE algorithm to use
/// ///
/// Default: `Zlib` /// Default: `Libdeflater`
pub deflate: Deflaters, pub deflate: Deflaters,
/// Whether to use heuristics to pick the best filter and compression /// Whether to use heuristics to pick the best filter and compression
/// ///
@ -212,10 +212,7 @@ impl Options {
1 => opts.apply_preset_1(), 1 => opts.apply_preset_1(),
2 => opts.apply_preset_2(), 2 => opts.apply_preset_2(),
3 => opts.apply_preset_3(), 3 => opts.apply_preset_3(),
4 => { 4 => opts.apply_preset_4(),
warn!("Level 4 is deprecated and is identical to level 3");
opts.apply_preset_4()
}
5 => opts.apply_preset_5(), 5 => opts.apply_preset_5(),
6 => opts.apply_preset_6(), 6 => opts.apply_preset_6(),
_ => { _ => {
@ -233,17 +230,20 @@ impl Options {
// on an `Options` struct generated by the `default` method. // on an `Options` struct generated by the `default` method.
fn apply_preset_0(mut self) -> Self { fn apply_preset_0(mut self) -> Self {
self.idat_recoding = false; self.idat_recoding = false;
if let Deflaters::Zlib { compression, .. } = &mut self.deflate { self.filter.clear();
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
compression.clear(); compression.clear();
compression.insert(3); compression.insert(5);
} }
self.apply_preset_1() self.use_heuristics = true;
self
} }
fn apply_preset_1(mut self) -> Self { fn apply_preset_1(mut self) -> Self {
self.filter.clear(); self.filter.clear();
if let Deflaters::Zlib { strategies, .. } = &mut self.deflate { if let Deflaters::Libdeflater { compression } = &mut self.deflate {
strategies.clear(); compression.clear();
compression.insert(10);
} }
self.use_heuristics = true; self.use_heuristics = true;
self self
@ -254,34 +254,38 @@ impl Options {
} }
fn apply_preset_3(mut self) -> Self { fn apply_preset_3(mut self) -> Self {
for i in 1..5 { for i in 1..=4 {
self.filter.insert(i); self.filter.insert(i);
} }
self self
} }
fn apply_preset_4(self) -> Self { fn apply_preset_4(mut self) -> Self {
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
compression.clear();
compression.insert(12);
}
self.apply_preset_3() self.apply_preset_3()
} }
fn apply_preset_5(mut self) -> Self { fn apply_preset_5(mut self) -> Self {
if let Deflaters::Zlib { compression, .. } = &mut self.deflate { if let Deflaters::Libdeflater { compression } = &mut self.deflate {
compression.clear(); compression.clear();
for i in 3..10 { for i in 9..=12 {
compression.insert(i); compression.insert(i);
} }
} }
self.apply_preset_4() self.apply_preset_3()
} }
fn apply_preset_6(mut self) -> Self { fn apply_preset_6(mut self) -> Self {
if let Deflaters::Zlib { compression, .. } = &mut self.deflate { if let Deflaters::Libdeflater { compression } = &mut self.deflate {
compression.clear(); compression.clear();
for i in 1..10 { for i in 1..=12 {
compression.insert(i); compression.insert(i);
} }
} }
self.apply_preset_4() self.apply_preset_3()
} }
} }
@ -292,11 +296,7 @@ impl Default for Options {
filter.insert(0); filter.insert(0);
filter.insert(5); filter.insert(5);
let mut compression = IndexSet::new(); let mut compression = IndexSet::new();
compression.insert(9); compression.insert(11);
let mut strategies = IndexSet::new();
for i in 0..4 {
strategies.insert(i);
}
// We always need NoOp to be present // We always need NoOp to be present
let mut alphas = IndexSet::new(); let mut alphas = IndexSet::new();
alphas.insert(AlphaOptim::NoOp); alphas.insert(AlphaOptim::NoOp);
@ -317,11 +317,7 @@ impl Default for Options {
grayscale_reduction: true, grayscale_reduction: true,
idat_recoding: true, idat_recoding: true,
strip: Headers::None, strip: Headers::None,
deflate: Deflaters::Zlib { deflate: Deflaters::Libdeflater { compression },
compression,
strategies,
window: 15,
},
use_heuristics: false, use_heuristics: false,
timeout: None, timeout: None,
} }

View file

@ -143,7 +143,7 @@ fn main() {
} }
})) }))
.arg(Arg::new("compression") .arg(Arg::new("compression")
.help("compression levels (zlib: 1-9, libdeflater: 1-12) - Default: 9 or 12") .help("zlib compression levels (1-12) - Default: 12")
.long("zc") .long("zc")
.takes_value(true) .takes_value(true)
.value_name("levels") .value_name("levels")
@ -153,7 +153,7 @@ fn main() {
Err(_) => Err("Invalid option for compression".to_owned()), Err(_) => Err("Invalid option for compression".to_owned()),
} }
}) })
.conflicts_with("zopfli")) .conflicts_with_all(&["zopfli", "libdeflater"]))
.arg(Arg::new("strategies") .arg(Arg::new("strategies")
.help("zlib compression strategies (0-3) - Default: 0-3") .help("zlib compression strategies (0-3) - Default: 0-3")
.long("zs") .long("zs")
@ -164,6 +164,7 @@ fn main() {
Err(_) => Err("Invalid option for strategies".to_owned()), Err(_) => Err("Invalid option for strategies".to_owned()),
} }
}) })
.hide(true)
.conflicts_with_all(&["zopfli", "libdeflater"])) .conflicts_with_all(&["zopfli", "libdeflater"]))
.arg(Arg::new("window") .arg(Arg::new("window")
.help("zlib window size - Default: 32k") .help("zlib window size - Default: 32k")
@ -178,6 +179,7 @@ fn main() {
.possible_value("8k") .possible_value("8k")
.possible_value("16k") .possible_value("16k")
.possible_value("32k") .possible_value("32k")
.hide(true)
.conflicts_with_all(&["zopfli", "libdeflater"])) .conflicts_with_all(&["zopfli", "libdeflater"]))
.arg(Arg::new("no-bit-reduction") .arg(Arg::new("no-bit-reduction")
.help("No bit depth reduction") .help("No bit depth reduction")
@ -204,7 +206,7 @@ fn main() {
.help("Write the output even if it is larger than the input") .help("Write the output even if it is larger than the input")
.long("force")) .long("force"))
.arg(Arg::new("zopfli") .arg(Arg::new("zopfli")
.help("Use the slower but better compressing Zopfli algorithm, overrides zlib-specific options") .help("Use the slower but better compressing Zopfli algorithm")
.short('Z') .short('Z')
.long("zopfli") .long("zopfli")
.conflicts_with("libdeflater")) .conflicts_with("libdeflater"))
@ -212,6 +214,7 @@ fn main() {
.help("Use an alternative Libdeflater algorithm, overrides zlib-specific options") .help("Use an alternative Libdeflater algorithm, overrides zlib-specific options")
.short('D') .short('D')
.long("libdeflater") .long("libdeflater")
.hide(true)
.conflicts_with("zopfli")) .conflicts_with("zopfli"))
.arg(Arg::new("timeout") .arg(Arg::new("timeout")
.help("Maximum amount of time, in seconds, to spend on optimizations") .help("Maximum amount of time, in seconds, to spend on optimizations")
@ -237,16 +240,16 @@ fn main() {
} }
})) }))
.after_help("Optimization levels: .after_help("Optimization levels:
-o 0 => --zc 3 --nz (0 or 1 trials) -o 0 => --zc 6 --nz (0 or 1 trials)
-o 1 => --zc 9 (1 trial, determined heuristically) -o 1 => --zc 10 (1 trial, determined heuristically)
-o 2 => --zc 9 --zs 0-3 -f 0,5 (8 trials with zlib or 2 trials with other compressors) -o 2 => --zc 11 -f 0,5 (2 trials)
-o 3 => --zc 9 --zs 0-3 -f 0-5 (24 trials with zlib or 6 trials with other compressors) -o 3 => --zc 11 -f 0-5 (6 trials)
-o 4 => (deprecated; same as `-o 3`) -o 4 => --zc 12 -f 0-5 (6 trials; same as `-o 3` for zopfli)
-o 5 => --zc 3-9 --zs 0-3 -f 0-5 (168 trials with zlib; same as `-o 3` for other compressors) -o 5 => --zc 9-12 -f 0-5 (24 trials; same as `-o 3` for zopfli)
-o 6 => --zc 1-9 --zs 0-3 -f 0-5 (216 trials with zlib; same as `-o 3` for other compressors) -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 max => (stable alias for the max compression)
Manually specifying a compression option (zc, zs, etc.) will override the optimization preset, Manually specifying a compression option (zc, f, etc.) will override the optimization preset,
regardless of the order you write the arguments.") regardless of the order you write the arguments.")
.get_matches_from(wild::args()); .get_matches_from(wild::args());
@ -501,46 +504,19 @@ fn parse_opts_into_struct(
}; };
} else if matches.is_present("libdeflater") { } else if matches.is_present("libdeflater") {
opts.deflate = Deflaters::Libdeflater { opts.deflate = Deflaters::Libdeflater {
compression: match matches.value_of("compression") { compression: indexset! { 12 },
Some(x) => parse_numeric_range_opts(x, 1, 12).unwrap(),
_ => indexset! { 12 },
},
}; };
} else if let Deflaters::Zlib { } else if let Deflaters::Libdeflater { compression } = &mut opts.deflate {
compression,
strategies,
window,
} = &mut opts.deflate
{
if let Some(x) = matches.value_of("compression") { if let Some(x) = matches.value_of("compression") {
*compression = parse_numeric_range_opts(x, 1, 9) *compression = parse_numeric_range_opts(x, 1, 12).unwrap();
.map_err(|_| "Compression levels 10-12 are only valid for libdeflater".to_owned())?
}
if let Some(x) = matches.value_of("strategies") {
*strategies = parse_numeric_range_opts(x, 0, 3).unwrap();
}
match matches.value_of("window") {
Some("256") => *window = 8,
Some("512") => *window = 9,
Some("1k") => *window = 10,
Some("2k") => *window = 11,
Some("4k") => *window = 12,
Some("8k") => *window = 13,
Some("16k") => *window = 14,
// 32k is default
_ => (),
} }
} }
if explicit_level > Some(3) { if explicit_level > Some(3) {
match opts.deflate { match opts.deflate {
Deflaters::Zlib { .. } => {} Deflaters::Libdeflater { .. } => {}
_ => { _ => {
warn!( warn!("Level 4 and above are equivalent to level 3 for zopfli");
"Level 4 and above are equivalent to level 3 for compressors other than zlib"
);
} }
} }
} }

View file

@ -170,10 +170,10 @@ fn verbose_mode() {
}); });
let mut logs: Vec<_> = receiver.into_iter().collect(); let mut logs: Vec<_> = receiver.into_iter().collect();
assert_eq!(logs.len(), 4); assert_eq!(logs.len(), 1);
logs.sort(); logs.sort();
for (i, log) in logs.into_iter().enumerate() { for (i, log) in logs.into_iter().enumerate() {
let expected_prefix = format!(" zc = 9 zs = {} f = 0 ", i); let expected_prefix = format!(" zc = 11 zs = 0 f = 0 ");
assert!( assert!(
log.starts_with(&expected_prefix), log.starts_with(&expected_prefix),
"logs[{}] = {:?} doesn't start with {:?}", "logs[{}] = {:?} doesn't start with {:?}",
@ -597,23 +597,3 @@ fn zopfli_mode() {
BitDepth::Eight, BitDepth::Eight,
); );
} }
#[test]
#[cfg(feature = "libdeflater")]
fn libdeflater_mode() {
let input = PathBuf::from("tests/files/zopfli_mode.png");
let (output, mut opts) = get_opts(&input);
let mut compression = IndexSet::new();
compression.insert(0);
opts.deflate = Deflaters::Libdeflater { compression };
test_it_converts(
input,
&output,
&opts,
ColorType::RGB,
BitDepth::Eight,
ColorType::RGB,
BitDepth::Eight,
);
}