Allow --zc with libdeflater

This commit is contained in:
Andrew 2022-10-21 13:38:19 +13:00 committed by Josh Holmer
parent a02d4ff0dd
commit 6c1d071f70
6 changed files with 60 additions and 30 deletions

View file

@ -13,7 +13,7 @@ fn libdeflater_16_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap(); let png = PngData::new(&input, false).unwrap();
b.iter(|| { b.iter(|| {
libdeflater_deflate(png.raw.data.as_ref(), &AtomicMin::new(None)).ok(); libdeflater_deflate(png.raw.data.as_ref(), 12, &AtomicMin::new(None)).ok();
}); });
} }
@ -23,7 +23,7 @@ fn libdeflater_8_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap(); let png = PngData::new(&input, false).unwrap();
b.iter(|| { b.iter(|| {
libdeflater_deflate(png.raw.data.as_ref(), &AtomicMin::new(None)).ok(); libdeflater_deflate(png.raw.data.as_ref(), 12, &AtomicMin::new(None)).ok();
}); });
} }
@ -35,7 +35,7 @@ fn libdeflater_4_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap(); let png = PngData::new(&input, false).unwrap();
b.iter(|| { b.iter(|| {
libdeflater_deflate(png.raw.data.as_ref(), &AtomicMin::new(None)).ok(); libdeflater_deflate(png.raw.data.as_ref(), 12, &AtomicMin::new(None)).ok();
}); });
} }
@ -47,7 +47,7 @@ fn libdeflater_2_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap(); let png = PngData::new(&input, false).unwrap();
b.iter(|| { b.iter(|| {
libdeflater_deflate(png.raw.data.as_ref(), &AtomicMin::new(None)).ok(); libdeflater_deflate(png.raw.data.as_ref(), 12, &AtomicMin::new(None)).ok();
}); });
} }
@ -59,6 +59,6 @@ fn libdeflater_1_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap(); let png = PngData::new(&input, false).unwrap();
b.iter(|| { b.iter(|| {
libdeflater_deflate(png.raw.data.as_ref(), &AtomicMin::new(None)).ok(); libdeflater_deflate(png.raw.data.as_ref(), 12, &AtomicMin::new(None)).ok();
}); });
} }

View file

@ -2,8 +2,8 @@ use crate::atomicmin::AtomicMin;
use crate::{PngError, PngResult}; use crate::{PngError, PngResult};
use libdeflater::{CompressionError, CompressionLvl, Compressor}; use libdeflater::{CompressionError, CompressionLvl, Compressor};
pub fn deflate(data: &[u8], max_size: &AtomicMin) -> PngResult<Vec<u8>> { pub fn deflate(data: &[u8], level: u8, max_size: &AtomicMin) -> PngResult<Vec<u8>> {
let mut compressor = Compressor::new(CompressionLvl::best()); let mut compressor = Compressor::new(CompressionLvl::new(level.into()).unwrap());
let capacity = max_size.get().unwrap_or(data.len() / 2); let capacity = max_size.get().unwrap_or(data.len() / 2);
let mut dest = vec![0; capacity]; let mut dest = vec![0; capacity];
let len = compressor let len = compressor

View file

@ -104,5 +104,8 @@ pub enum Deflaters {
}, },
#[cfg(feature = "libdeflater")] #[cfg(feature = "libdeflater")]
/// Use libdeflater. /// Use libdeflater.
Libdeflater, Libdeflater {
/// Which compression levels to try on the file (1-12)
compression: IndexSet<u8>,
},
} }

View file

@ -560,26 +560,43 @@ fn optimize_png(
let mut results: Vec<TrialOptions> = Vec::with_capacity(combinations); let mut results: Vec<TrialOptions> = Vec::with_capacity(combinations);
for f in &filter { for f in &filter {
if let Deflaters::Zlib { compression, .. } = &opts.deflate { match &opts.deflate {
for zc in compression { Deflaters::Zlib { compression, .. } => {
for zs in strategies.as_ref().unwrap() { for zc in compression {
for zs in strategies.as_ref().unwrap() {
results.push(TrialOptions {
filter: *f,
compression: *zc,
strategy: *zs,
});
}
if deadline.passed() {
break;
}
}
}
#[cfg(feature = "zopfli")]
Deflaters::Zopfli { .. } => {
// Zopfli has no additional options.
results.push(TrialOptions {
filter: *f,
compression: 0,
strategy: 0,
});
}
#[cfg(feature = "libdeflater")]
Deflaters::Libdeflater { compression } => {
for zc in compression {
results.push(TrialOptions { results.push(TrialOptions {
filter: *f, filter: *f,
compression: *zc, compression: *zc,
strategy: *zs, strategy: 0,
}); });
} if deadline.passed() {
if deadline.passed() { break;
break; }
} }
} }
} else {
// Zopfli and Libdeflater compression have no additional options.
results.push(TrialOptions {
filter: *f,
compression: 0,
strategy: 0,
});
} }
if deadline.passed() { if deadline.passed() {
@ -620,7 +637,9 @@ fn optimize_png(
#[cfg(feature = "zopfli")] #[cfg(feature = "zopfli")]
Deflaters::Zopfli { iterations } => deflate::zopfli_deflate(filtered, iterations), Deflaters::Zopfli { iterations } => deflate::zopfli_deflate(filtered, iterations),
#[cfg(feature = "libdeflater")] #[cfg(feature = "libdeflater")]
Deflaters::Libdeflater => deflate::libdeflater_deflate(filtered, &best_size), Deflaters::Libdeflater { .. } => {
deflate::libdeflater_deflate(filtered, trial.compression, &best_size)
}
}; };
let new_idat = match new_idat { let new_idat = match new_idat {

View file

@ -14,7 +14,7 @@
#![allow(clippy::cognitive_complexity)] #![allow(clippy::cognitive_complexity)]
use clap::{AppSettings, Arg, ArgMatches, Command}; use clap::{AppSettings, Arg, ArgMatches, Command};
use indexmap::IndexSet; use indexmap::{indexset, IndexSet};
use log::{error, warn}; use log::{error, warn};
use oxipng::AlphaOptim; use oxipng::AlphaOptim;
use oxipng::Deflaters; use oxipng::Deflaters;
@ -143,17 +143,17 @@ fn main() {
} }
})) }))
.arg(Arg::new("compression") .arg(Arg::new("compression")
.help("zlib compression levels (1-9) - Default: 9") .help("compression levels (zlib: 1-9, libdeflater: 1-12) - Default: 9 or 12")
.long("zc") .long("zc")
.takes_value(true) .takes_value(true)
.value_name("levels") .value_name("levels")
.validator(|x| { .validator(|x| {
match parse_numeric_range_opts(x, 1, 9) { match parse_numeric_range_opts(x, 1, 12) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(_) => Err("Invalid option for compression".to_owned()), Err(_) => Err("Invalid option for compression".to_owned()),
} }
}) })
.conflicts_with_all(&["zopfli", "libdeflater"])) .conflicts_with("zopfli"))
.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")
@ -500,7 +500,12 @@ fn parse_opts_into_struct(
iterations: NonZeroU8::new(15).unwrap(), iterations: NonZeroU8::new(15).unwrap(),
}; };
} 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") {
Some(x) => parse_numeric_range_opts(x, 1, 12).unwrap(),
_ => indexset! { 12 },
},
};
} else if let Deflaters::Zlib { } else if let Deflaters::Zlib {
compression, compression,
strategies, strategies,
@ -508,7 +513,8 @@ fn parse_opts_into_struct(
} = &mut opts.deflate } = &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).unwrap(); *compression = parse_numeric_range_opts(x, 1, 9)
.map_err(|_| "Compression levels 10-12 are only valid for libdeflater".to_owned())?
} }
if let Some(x) = matches.value_of("strategies") { if let Some(x) = matches.value_of("strategies") {

View file

@ -603,7 +603,9 @@ fn zopfli_mode() {
fn libdeflater_mode() { fn libdeflater_mode() {
let input = PathBuf::from("tests/files/zopfli_mode.png"); let input = PathBuf::from("tests/files/zopfli_mode.png");
let (output, mut opts) = get_opts(&input); let (output, mut opts) = get_opts(&input);
opts.deflate = Deflaters::Libdeflater; let mut compression = IndexSet::new();
compression.insert(0);
opts.deflate = Deflaters::Libdeflater { compression };
test_it_converts( test_it_converts(
input, input,