Allow setting libdeflate compression level (#455)
* Allow --zc with libdeflater * Update libdeflater * Allocate sufficient space for libdefate
This commit is contained in:
parent
a02d4ff0dd
commit
f688d20fe6
8 changed files with 71 additions and 37 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -281,18 +281,18 @@ checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libdeflate-sys"
|
name = "libdeflate-sys"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "43afa5b192ff058426ba20a4f35c290ef402478d6045ac934ac15aa947a3898d"
|
checksum = "cb6784b6b84b67d71b4307963d456a9c7c29f9b47c658f533e598de369e34277"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libdeflater"
|
name = "libdeflater"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e656b7960ec49e864badc7ad1b810427a7ac8b78511a699ce5cdc3ead0b32e5b"
|
checksum = "d8e285aa6a046fd338b2592c16bee148b2b00789138ed6b7bb56bb13d585050d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libdeflate-sys",
|
"libdeflate-sys",
|
||||||
]
|
]
|
||||||
|
|
@ -387,7 +387,7 @@ checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxipng"
|
name = "oxipng"
|
||||||
version = "6.0.0"
|
version = "6.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-vec",
|
"bit-vec",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ zopfli = { version = "0.7.1", optional = true }
|
||||||
miniz_oxide = "0.6.2"
|
miniz_oxide = "0.6.2"
|
||||||
rgb = "0.8.33"
|
rgb = "0.8.33"
|
||||||
indexmap = "1.9.1"
|
indexmap = "1.9.1"
|
||||||
libdeflater = { version = "0.10.0", optional = true }
|
libdeflater = { version = "0.11.0", optional = true }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
stderrlog = { version = "0.5.3", optional = true, default-features = false }
|
stderrlog = { version = "0.5.3", optional = true, default-features = false }
|
||||||
crossbeam-channel = "0.5.6"
|
crossbeam-channel = "0.5.6"
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@ 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);
|
// If adhering to a max_size we need to include at least 9 extra bytes of slack space (as specified in docs).
|
||||||
|
let capacity = max_size
|
||||||
|
.get()
|
||||||
|
.unwrap_or_else(|| compressor.zlib_compress_bound(data.len()))
|
||||||
|
+ 9;
|
||||||
let mut dest = vec![0; capacity];
|
let mut dest = vec![0; capacity];
|
||||||
let len = compressor
|
let len = compressor
|
||||||
.zlib_compress(data, &mut dest)
|
.zlib_compress(data, &mut dest)
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
src/lib.rs
49
src/lib.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
18
src/main.rs
18
src/main.rs
|
|
@ -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") {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue