Use single compression level

This commit is contained in:
Andrew 2022-11-14 17:25:59 +13:00 committed by Josh Holmer
parent 15a9b4a3e7
commit 9b5353f0c6
3 changed files with 29 additions and 84 deletions

View file

@ -1,5 +1,3 @@
use indexmap::IndexSet;
mod deflater; mod deflater;
pub use deflater::crc32; pub use deflater::crc32;
pub use deflater::deflate; pub use deflater::deflate;
@ -17,8 +15,8 @@ pub use zopfli_oxipng::deflate as zopfli_deflate;
pub enum Deflaters { pub enum Deflaters {
/// Use libdeflater. /// Use libdeflater.
Libdeflater { Libdeflater {
/// Which compression levels to try on the file (1-12) /// Which compression level to use on the file (1-12)
compression: IndexSet<u8>, compression: u8,
}, },
#[cfg(feature = "zopfli")] #[cfg(feature = "zopfli")]
/// Use the better but slower Zopfli implementation /// Use the better but slower Zopfli implementation
@ -29,3 +27,5 @@ pub enum Deflaters {
iterations: NonZeroU8, iterations: NonZeroU8,
}, },
} }
impl Copy for Deflaters {}

View file

@ -47,7 +47,7 @@ pub use crate::deflate::Deflaters;
pub use crate::error::PngError; pub use crate::error::PngError;
pub use crate::filters::RowFilter; pub use crate::filters::RowFilter;
pub use crate::headers::Headers; pub use crate::headers::Headers;
pub use indexmap::{IndexMap, IndexSet}; pub use indexmap::{indexset, IndexMap, IndexSet};
mod atomicmin; mod atomicmin;
mod colors; mod colors;
@ -232,8 +232,7 @@ impl Options {
self.idat_recoding = false; self.idat_recoding = false;
self.filter.clear(); self.filter.clear();
if let Deflaters::Libdeflater { compression } = &mut self.deflate { if let Deflaters::Libdeflater { compression } = &mut self.deflate {
compression.clear(); *compression = 5;
compression.insert(5);
} }
self.use_heuristics = true; self.use_heuristics = true;
self self
@ -242,8 +241,7 @@ impl Options {
fn apply_preset_1(mut self) -> Self { fn apply_preset_1(mut self) -> Self {
self.filter.clear(); self.filter.clear();
if let Deflaters::Libdeflater { compression } = &mut self.deflate { if let Deflaters::Libdeflater { compression } = &mut self.deflate {
compression.clear(); *compression = 10;
compression.insert(10);
} }
self.use_heuristics = true; self.use_heuristics = true;
self self
@ -263,45 +261,23 @@ impl Options {
fn apply_preset_4(mut self) -> Self { fn apply_preset_4(mut self) -> Self {
if let Deflaters::Libdeflater { compression } = &mut self.deflate { if let Deflaters::Libdeflater { compression } = &mut self.deflate {
compression.clear(); *compression = 12;
compression.insert(12);
} }
self.apply_preset_3() self.apply_preset_3()
} }
fn apply_preset_5(mut self) -> Self { fn apply_preset_5(self) -> Self {
if let Deflaters::Libdeflater { compression } = &mut self.deflate { self.apply_preset_4()
compression.clear();
for i in 9..=12 {
compression.insert(i);
}
}
self.apply_preset_3()
} }
fn apply_preset_6(mut self) -> Self { fn apply_preset_6(self) -> Self {
if let Deflaters::Libdeflater { compression } = &mut self.deflate { self.apply_preset_4()
compression.clear();
for i in 1..=12 {
compression.insert(i);
}
}
self.apply_preset_3()
} }
} }
impl Default for Options { impl Default for Options {
fn default() -> Options { fn default() -> Options {
// Default settings based on -o 2 from the CLI interface // Default settings based on -o 2 from the CLI interface
let mut filter = IndexSet::new();
filter.insert(RowFilter::None);
filter.insert(RowFilter::MinSum);
let mut compression = IndexSet::new();
compression.insert(11);
// We always need NoOp to be present
let mut alphas = IndexSet::new();
alphas.insert(AlphaOptim::NoOp);
Options { Options {
backup: false, backup: false,
check: false, check: false,
@ -309,16 +285,16 @@ impl Default for Options {
fix_errors: false, fix_errors: false,
force: false, force: false,
preserve_attrs: false, preserve_attrs: false,
filter, filter: indexset! {RowFilter::None, RowFilter::MinSum},
interlace: None, interlace: None,
alphas, alphas: IndexSet::new(),
bit_depth_reduction: true, bit_depth_reduction: true,
color_type_reduction: true, color_type_reduction: true,
palette_reduction: true, palette_reduction: true,
grayscale_reduction: true, grayscale_reduction: true,
idat_recoding: true, idat_recoding: true,
strip: Headers::None, strip: Headers::None,
deflate: Deflaters::Libdeflater { compression }, deflate: Deflaters::Libdeflater { compression: 11 },
use_heuristics: false, use_heuristics: false,
timeout: None, timeout: None,
} }
@ -539,48 +515,20 @@ fn optimize_png(
if opts.idat_recoding || reduction_occurred { if opts.idat_recoding || reduction_occurred {
// Go through selected permutations and determine the best // Go through selected permutations and determine the best
let combinations = if let Deflaters::Libdeflater { compression } = &opts.deflate { let mut results: Vec<TrialOptions> = Vec::with_capacity(filter.len());
filter.len() * compression.len()
} else {
filter.len()
};
let mut results: Vec<TrialOptions> = Vec::with_capacity(combinations);
for f in &filter { for f in &filter {
if let Deflaters::Libdeflater { compression } = &opts.deflate { results.push(TrialOptions {
for zc in compression { filter: *f,
results.push(TrialOptions { compression: match opts.deflate {
filter: *f, Deflaters::Libdeflater { compression } => compression,
compression: *zc, _ => 0,
}); },
if deadline.passed() { });
break;
}
}
} else {
// Zopfli has no additional options.
results.push(TrialOptions {
filter: *f,
compression: 0,
});
}
if deadline.passed() {
break;
}
} }
info!("Trying: {} combinations", results.len()); info!("Trying: {} combinations", results.len());
let filters: IndexMap<RowFilter, Vec<u8>> = filter
.par_iter()
.with_max_len(1)
.map(|f| {
let png = png.clone();
(*f, png.raw.filter_image(*f))
})
.collect();
let original_len = original_png.idat_data.len(); let original_len = original_png.idat_data.len();
let added_interlacing = opts.interlace == Some(1) && original_png.raw.ihdr.interlaced == 0; let added_interlacing = opts.interlace == Some(1) && original_png.raw.ihdr.interlaced == 0;
@ -590,7 +538,7 @@ fn optimize_png(
if deadline.passed() { if deadline.passed() {
return None; return None;
} }
let filtered = &filters[&trial.filter]; let filtered = &png.raw.filter_image(trial.filter);
let new_idat = match opts.deflate { let new_idat = match opts.deflate {
Deflaters::Libdeflater { .. } => { Deflaters::Libdeflater { .. } => {
deflate::deflate(filtered, trial.compression, &best_size) deflate::deflate(filtered, trial.compression, &best_size)

View file

@ -184,14 +184,11 @@ fn main() {
) )
.arg( .arg(
Arg::new("compression") Arg::new("compression")
.help("zlib compression levels (1-12) - Default: 12") .help("zlib compression level (1-12) - Default: 11")
.long("zc") .long("zc")
.takes_value(true) .takes_value(true)
.value_name("levels") .value_name("level")
.validator(|x| match parse_numeric_range_opts(x, 1, 12) { .value_parser(1..=12)
Ok(_) => Ok(()),
Err(_) => Err("Invalid option for compression".to_owned()),
})
.conflicts_with("zopfli"), .conflicts_with("zopfli"),
) )
.arg( .arg(
@ -541,8 +538,8 @@ fn parse_opts_into_struct(
opts.deflate = Deflaters::Zopfli { iterations }; opts.deflate = Deflaters::Zopfli { iterations };
} }
} else if let Deflaters::Libdeflater { compression } = &mut opts.deflate { } else if let Deflaters::Libdeflater { compression } = &mut opts.deflate {
if let Some(x) = matches.value_of("compression") { if let Some(x) = matches.get_one::<i64>("compression") {
*compression = parse_numeric_range_opts(x, 1, 12).unwrap(); *compression = *x as u8;
} }
} }