Replace miniz_sys with miniz_oxide, a Rust implementation of miniz
This also improves decompression performance by 15%, and obsoletes the `--zm` parameter. Closes #57
This commit is contained in:
parent
0c2e3777b1
commit
fe9cd01b82
9 changed files with 104 additions and 260 deletions
|
|
@ -1,7 +1,11 @@
|
|||
### Version 0.19.1 (unreleased)
|
||||
### Version 0.20.0 (unreleased)
|
||||
- Refactor of internal code.
|
||||
- Implement unix-specific permissions copying for `-p` option
|
||||
- Performance optimizations
|
||||
- Remove the C dependency on miniz, and replace it with a Rust version ([#57](https://github.com/shssoichiro/oxipng/issues/57))
|
||||
- This improves decompression speed by 15%. Compression speed is not affected.
|
||||
- [SEMVER_MAJOR] This also obsoletes the `-zm` command line option and the `memory` key on the `Options` struct.
|
||||
- Presets will be updated automatically. This means that presets 3 and higher will run significantly more quickly.
|
||||
|
||||
### Version 0.19.0
|
||||
- [SEMVER_MAJOR] Default to overwriting the input file if `out_file` is not set.
|
||||
|
|
|
|||
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -71,11 +71,6 @@ dependencies = [
|
|||
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.29.2"
|
||||
|
|
@ -274,11 +269,11 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz-sys"
|
||||
version = "0.1.10"
|
||||
name = "miniz_oxide"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
|
@ -334,7 +329,7 @@ dependencies = [
|
|||
"image 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -657,7 +652,6 @@ dependencies = [
|
|||
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
|
||||
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
||||
"checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b"
|
||||
"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"
|
||||
"checksum clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4151c5790817c7d21bbdc6c3530811f798172915f93258244948b93ba19604a6"
|
||||
"checksum clippy 0.0.184 (registry+https://github.com/rust-lang/crates.io-index)" = "1e70c0ea9c5ad3eaab76ec3c4351d8d415c257093173416b0e2ed75e9be5cb74"
|
||||
"checksum clippy_lints 0.0.184 (registry+https://github.com/rust-lang/crates.io-index)" = "b86054e0d81947d1db08e5931521dbea49619079c5fad91583d710c854a0e994"
|
||||
|
|
@ -683,7 +677,7 @@ dependencies = [
|
|||
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
|
||||
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
|
||||
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
|
||||
"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4"
|
||||
"checksum miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aaa2d3ad070f428fffbd7d3ca2ea20bb0d8cffe9024405c44e1840bc1418b398"
|
||||
"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
|
||||
"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
|
||||
"checksum num-rational 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "0b950f75e042fdd710460084d19c8efdcd72d65183ead8ecd04b90483f5a55d2"
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ crc = "^1.2.0"
|
|||
glob = "0.2.11"
|
||||
itertools = "^0.7.0"
|
||||
libc = "^0.2.4"
|
||||
miniz-sys = "^0.1.7"
|
||||
num_cpus = "^1.0.0"
|
||||
rayon = "^0.9.0"
|
||||
zopfli = "^0.3.4"
|
||||
miniz_oxide = "0.1.2"
|
||||
|
||||
[dependencies.clap]
|
||||
optional = true
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ fn deflate_16_bits_strategy_0(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 0, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ fn deflate_8_bits_strategy_0(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 0, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ fn deflate_4_bits_strategy_0(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 0, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ fn deflate_2_bits_strategy_0(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 0, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ fn deflate_1_bits_strategy_0(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 0, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ fn deflate_16_bits_strategy_1(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 1, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ fn deflate_8_bits_strategy_1(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 1, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ fn deflate_4_bits_strategy_1(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 1, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ fn deflate_2_bits_strategy_1(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 1, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ fn deflate_1_bits_strategy_1(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 1, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ fn deflate_16_bits_strategy_2(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 2, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ fn deflate_8_bits_strategy_2(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 2, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ fn deflate_4_bits_strategy_2(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 2, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ fn deflate_2_bits_strategy_2(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 2, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ fn deflate_1_bits_strategy_2(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 2, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ fn deflate_16_bits_strategy_3(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 3, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ fn deflate_8_bits_strategy_3(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 3, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +204,7 @@ fn deflate_4_bits_strategy_3(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 3, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ fn deflate_2_bits_strategy_3(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 3, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ fn deflate_1_bits_strategy_3(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 9, 3, 15).ok();
|
||||
deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +238,6 @@ fn inflate_generic(b: &mut Bencher) {
|
|||
let png = png::PngData::new(&input, false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
deflate::inflate(png.idat_data.as_ref()).ok();
|
||||
deflate::inflate(png.idat_data.as_ref());
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,131 +1,52 @@
|
|||
// Raw un-exported bindings to miniz for encoding/decoding
|
||||
// Copyright (c) 2014 Alex Crichton, MIT & Apache licenses
|
||||
// Originally from flate2 crate
|
||||
// Modified for use in oxipng
|
||||
use miniz_oxide::deflate::core::*;
|
||||
|
||||
use std::marker;
|
||||
use std::mem;
|
||||
use libc::{c_int, c_uint};
|
||||
use miniz_sys;
|
||||
pub fn compress_to_vec_oxipng(input: &[u8], level: u8, window_bits: i32, strategy: i32) -> Vec<u8> {
|
||||
// The comp flags function sets the zlib flag if the window_bits parameter is > 0.
|
||||
let flags = create_comp_flags_from_zip_params(level.into(), window_bits, strategy);
|
||||
let mut compressor = CompressorOxide::new(flags);
|
||||
let mut output = Vec::with_capacity(input.len() / 2);
|
||||
// # Unsafe
|
||||
// We trust compress to not read the uninitialized bytes.
|
||||
unsafe {
|
||||
let cap = output.capacity();
|
||||
output.set_len(cap);
|
||||
}
|
||||
let mut in_pos = 0;
|
||||
let mut out_pos = 0;
|
||||
loop {
|
||||
let (status, bytes_in, bytes_out) = compress(
|
||||
&mut compressor,
|
||||
&input[in_pos..],
|
||||
&mut output[out_pos..],
|
||||
TDEFLFlush::Finish,
|
||||
);
|
||||
|
||||
pub struct Stream<D: Direction> {
|
||||
raw: miniz_sys::mz_stream,
|
||||
_marker: marker::PhantomData<D>,
|
||||
}
|
||||
out_pos += bytes_out;
|
||||
in_pos += bytes_in;
|
||||
|
||||
pub enum Compress {}
|
||||
pub enum Decompress {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Direction {
|
||||
unsafe fn destroy(stream: *mut miniz_sys::mz_stream) -> c_int;
|
||||
}
|
||||
|
||||
impl Stream<Compress> {
|
||||
pub fn new_compress(
|
||||
lvl: c_int,
|
||||
window_bits: c_int,
|
||||
mem_size: c_int,
|
||||
strategy: c_int,
|
||||
) -> Stream<Compress> {
|
||||
unsafe {
|
||||
let mut state: miniz_sys::mz_stream = mem::zeroed();
|
||||
let ret = miniz_sys::mz_deflateInit2(
|
||||
&mut state,
|
||||
lvl,
|
||||
miniz_sys::MZ_DEFLATED,
|
||||
window_bits,
|
||||
mem_size,
|
||||
strategy,
|
||||
);
|
||||
debug_assert_eq!(ret, 0);
|
||||
Stream {
|
||||
raw: state,
|
||||
_marker: marker::PhantomData,
|
||||
match status {
|
||||
TDEFLStatus::Done => {
|
||||
output.truncate(out_pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
TDEFLStatus::Okay => {
|
||||
// We need more space, so extend the vector.
|
||||
if output.len().saturating_sub(out_pos) < 30 {
|
||||
let current_len = output.len();
|
||||
output.reserve(current_len);
|
||||
|
||||
pub fn new_decompress() -> Stream<Decompress> {
|
||||
unsafe {
|
||||
let mut state: miniz_sys::mz_stream = mem::zeroed();
|
||||
let ret = miniz_sys::mz_inflateInit2(&mut state, 15);
|
||||
debug_assert_eq!(ret, 0);
|
||||
Stream {
|
||||
raw: state,
|
||||
_marker: marker::PhantomData,
|
||||
// # Unsafe
|
||||
// We trust compress to not read the uninitialized bytes.
|
||||
unsafe {
|
||||
let cap = output.capacity();
|
||||
output.set_len(cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not supposed to happen unless there is a bug.
|
||||
_ => panic!("Bug! Unexpectedly failed to compress!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Direction> Stream<T> {
|
||||
#[inline]
|
||||
pub fn total_in(&self) -> usize {
|
||||
self.raw.total_in as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn total_out(&self) -> usize {
|
||||
self.raw.total_out as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream<Decompress> {
|
||||
pub fn decompress_vec(&mut self, input: &mut [u8], output: &mut Vec<u8>) -> c_int {
|
||||
self.raw.avail_in = (input.len() - self.total_in()) as c_uint;
|
||||
self.raw.avail_out = (output.capacity() - self.total_out()) as c_uint;
|
||||
|
||||
unsafe {
|
||||
self.raw.next_in = input.as_mut_ptr().offset(self.total_in() as isize);
|
||||
self.raw.next_out = output.as_mut_ptr().offset(self.total_out() as isize);
|
||||
let rc = miniz_sys::mz_inflate(&mut self.raw, miniz_sys::MZ_NO_FLUSH);
|
||||
output.set_len(self.total_out() as usize);
|
||||
rc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream<Compress> {
|
||||
pub fn compress_vec(&mut self, input: &mut [u8], output: &mut Vec<u8>) -> c_int {
|
||||
self.raw.avail_in = (input.len() - self.total_in() as usize) as c_uint;
|
||||
self.raw.avail_out = (output.capacity() - self.total_out() as usize) as c_uint;
|
||||
|
||||
unsafe {
|
||||
self.raw.next_in = input.as_mut_ptr().offset(self.total_in() as isize);
|
||||
self.raw.next_out = output.as_mut_ptr().offset(self.total_out() as isize);
|
||||
let rc = miniz_sys::mz_deflate(
|
||||
&mut self.raw,
|
||||
if self.raw.avail_in > 0 {
|
||||
miniz_sys::MZ_NO_FLUSH
|
||||
} else {
|
||||
miniz_sys::MZ_FINISH
|
||||
},
|
||||
);
|
||||
output.set_len(self.total_out() as usize);
|
||||
rc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Direction for Compress {
|
||||
#[inline]
|
||||
unsafe fn destroy(stream: *mut miniz_sys::mz_stream) -> c_int {
|
||||
miniz_sys::mz_deflateEnd(stream)
|
||||
}
|
||||
}
|
||||
impl Direction for Decompress {
|
||||
#[inline]
|
||||
unsafe fn destroy(stream: *mut miniz_sys::mz_stream) -> c_int {
|
||||
miniz_sys::mz_inflateEnd(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Direction> Drop for Stream<D> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = <D as Direction>::destroy(&mut self.raw);
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use error::PngError;
|
||||
use miniz_sys;
|
||||
use miniz_oxide;
|
||||
use std::cmp::max;
|
||||
use zopfli;
|
||||
|
||||
|
|
@ -8,44 +8,13 @@ pub mod miniz_stream;
|
|||
|
||||
/// Decompress a data stream using the DEFLATE algorithm
|
||||
pub fn inflate(data: &[u8]) -> Result<Vec<u8>, PngError> {
|
||||
let mut input = data.to_owned();
|
||||
let mut stream = miniz_stream::Stream::new_decompress();
|
||||
let mut output = Vec::with_capacity(data.len());
|
||||
loop {
|
||||
match stream.decompress_vec(input.as_mut(), output.as_mut()) {
|
||||
miniz_sys::MZ_OK => output.reserve(data.len()),
|
||||
miniz_sys::MZ_STREAM_END => break,
|
||||
c => return Err(PngError::new(&format!("Error code on decompress: {}", c))),
|
||||
}
|
||||
}
|
||||
output.shrink_to_fit();
|
||||
|
||||
Ok(output)
|
||||
miniz_oxide::inflate::decompress_to_vec_zlib(data)
|
||||
.map_err(|e| PngError::new(&format!("Error on decompress: {:?}", e)))
|
||||
}
|
||||
|
||||
/// Compress a data stream using the DEFLATE algorithm
|
||||
pub fn deflate(data: &[u8], zc: u8, zm: u8, zs: u8, zw: u8) -> Result<Vec<u8>, PngError> {
|
||||
let mut input = data.to_owned();
|
||||
// Compressed input should be smaller than decompressed, so allocate less than data.len()
|
||||
// However, it needs a minimum capacity in order to handle very small images
|
||||
let mut output = Vec::with_capacity(max(1024, data.len() / 20));
|
||||
let mut stream = miniz_stream::Stream::new_compress(
|
||||
i32::from(zc),
|
||||
i32::from(zw),
|
||||
i32::from(zm),
|
||||
i32::from(zs),
|
||||
);
|
||||
loop {
|
||||
match stream.compress_vec(input.as_mut(), output.as_mut()) {
|
||||
miniz_sys::MZ_OK => output.reserve(max(1024, data.len() / 20)),
|
||||
miniz_sys::MZ_STREAM_END => break,
|
||||
c => return Err(PngError::new(&format!("Error code on compress: {}", c))),
|
||||
}
|
||||
}
|
||||
|
||||
output.shrink_to_fit();
|
||||
|
||||
Ok(output)
|
||||
pub fn deflate(data: &[u8], zc: u8, zs: u8, zw: u8) -> Vec<u8> {
|
||||
miniz_stream::compress_to_vec_oxipng(data, zc, zw.into(), zs.into())
|
||||
}
|
||||
|
||||
pub fn zopfli_deflate(data: &[u8]) -> Result<Vec<u8>, PngError> {
|
||||
|
|
|
|||
53
src/lib.rs
53
src/lib.rs
|
|
@ -7,7 +7,7 @@ extern crate crc;
|
|||
extern crate image;
|
||||
extern crate itertools;
|
||||
extern crate libc;
|
||||
extern crate miniz_sys;
|
||||
extern crate miniz_oxide;
|
||||
extern crate num_cpus;
|
||||
extern crate rayon;
|
||||
extern crate zopfli;
|
||||
|
|
@ -103,10 +103,6 @@ pub struct Options {
|
|||
///
|
||||
/// Default: `9`
|
||||
pub compression: HashSet<u8>,
|
||||
/// Which zlib memory levels to try on the file (1-9)
|
||||
///
|
||||
/// Default: `9`
|
||||
pub memory: HashSet<u8>,
|
||||
/// Which zlib compression strategies to try on the file (0-3)
|
||||
///
|
||||
/// Default: `0-3`
|
||||
|
|
@ -193,9 +189,8 @@ impl Options {
|
|||
self
|
||||
}
|
||||
|
||||
fn apply_preset_3(mut self) -> Self {
|
||||
self.memory.insert(8);
|
||||
self
|
||||
fn apply_preset_3(self) -> Self {
|
||||
self.apply_preset_2()
|
||||
}
|
||||
|
||||
fn apply_preset_4(mut self) -> Self {
|
||||
|
|
@ -216,7 +211,6 @@ impl Options {
|
|||
for i in 1..3 {
|
||||
self.compression.insert(i);
|
||||
}
|
||||
self.memory.insert(7);
|
||||
self.apply_preset_5()
|
||||
}
|
||||
}
|
||||
|
|
@ -229,8 +223,6 @@ impl Default for Options {
|
|||
filter.insert(5);
|
||||
let mut compression = HashSet::new();
|
||||
compression.insert(9);
|
||||
let mut memory = HashSet::new();
|
||||
memory.insert(9);
|
||||
let mut strategies = HashSet::new();
|
||||
for i in 0..4 {
|
||||
strategies.insert(i);
|
||||
|
|
@ -259,7 +251,6 @@ impl Default for Options {
|
|||
filter,
|
||||
interlace: None,
|
||||
compression,
|
||||
memory,
|
||||
strategies,
|
||||
window: 15,
|
||||
alphas,
|
||||
|
|
@ -381,7 +372,6 @@ pub fn optimize_from_memory(data: &[u8], opts: &Options) -> Result<Vec<u8>, PngE
|
|||
struct TrialOptions {
|
||||
pub filter: u8,
|
||||
pub compression: u8,
|
||||
pub memory: u8,
|
||||
pub strategy: u8,
|
||||
}
|
||||
|
||||
|
|
@ -423,7 +413,6 @@ fn optimize_png(
|
|||
|
||||
let mut filter = opts.filter.iter().cloned().collect::<Vec<u8>>();
|
||||
let compression = &opts.compression;
|
||||
let memory = &opts.memory;
|
||||
let mut strategies = opts.strategies.clone();
|
||||
|
||||
if opts.use_heuristics {
|
||||
|
|
@ -452,7 +441,7 @@ fn optimize_png(
|
|||
if opts.idat_recoding || reduction_occurred {
|
||||
// Go through selected permutations and determine the best
|
||||
let combinations = if opts.deflate == Deflaters::Zlib {
|
||||
filter.len() * compression.len() * memory.len() * strategies.len()
|
||||
filter.len() * compression.len() * strategies.len()
|
||||
} else {
|
||||
filter.len()
|
||||
};
|
||||
|
|
@ -464,15 +453,12 @@ fn optimize_png(
|
|||
for f in &filter {
|
||||
if opts.deflate == Deflaters::Zlib {
|
||||
for zc in compression {
|
||||
for zm in memory {
|
||||
for zs in &strategies {
|
||||
results.push(TrialOptions {
|
||||
filter: *f,
|
||||
compression: *zc,
|
||||
memory: *zm,
|
||||
strategy: *zs,
|
||||
});
|
||||
}
|
||||
for zs in &strategies {
|
||||
results.push(TrialOptions {
|
||||
filter: *f,
|
||||
compression: *zc,
|
||||
strategy: *zs,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -480,7 +466,6 @@ fn optimize_png(
|
|||
results.push(TrialOptions {
|
||||
filter: *f,
|
||||
compression: 0,
|
||||
memory: 0,
|
||||
strategy: 0,
|
||||
});
|
||||
}
|
||||
|
|
@ -504,22 +489,15 @@ fn optimize_png(
|
|||
.filter_map(|trial| {
|
||||
let filtered = &filters[&trial.filter];
|
||||
let new_idat = if opts.deflate == Deflaters::Zlib {
|
||||
deflate::deflate(
|
||||
filtered,
|
||||
trial.compression,
|
||||
trial.memory,
|
||||
trial.strategy,
|
||||
opts.window,
|
||||
)
|
||||
deflate::deflate(filtered, trial.compression, trial.strategy, opts.window)
|
||||
} else {
|
||||
deflate::zopfli_deflate(filtered)
|
||||
}.unwrap();
|
||||
deflate::zopfli_deflate(filtered).unwrap()
|
||||
};
|
||||
|
||||
if opts.verbosity == Some(1) {
|
||||
eprintln!(
|
||||
" zc = {} zm = {} zs = {} f = {} {} bytes",
|
||||
" zc = {} zs = {} f = {} {} bytes",
|
||||
trial.compression,
|
||||
trial.memory,
|
||||
trial.strategy,
|
||||
trial.filter,
|
||||
new_idat.len()
|
||||
|
|
@ -540,9 +518,8 @@ fn optimize_png(
|
|||
let opts = better.0;
|
||||
eprintln!("Found better combination:");
|
||||
eprintln!(
|
||||
" zc = {} zm = {} zs = {} f = {} {} bytes",
|
||||
" zc = {} zs = {} f = {} {} bytes",
|
||||
opts.compression,
|
||||
opts.memory,
|
||||
opts.strategy,
|
||||
opts.filter,
|
||||
png.idat_data.len()
|
||||
|
|
|
|||
34
src/main.rs
34
src/main.rs
|
|
@ -128,16 +128,6 @@ fn main() {
|
|||
Err(_) => Err("Invalid option for compression".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("memory")
|
||||
.help("zlib memory levels (1-9) - Default: 9")
|
||||
.long("zm")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 1, 9) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for memory".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("strategies")
|
||||
.help("zlib compression strategies (0-3) - Default: 0-3")
|
||||
.long("zs")
|
||||
|
|
@ -210,19 +200,15 @@ fn main() {
|
|||
}
|
||||
}))
|
||||
.after_help("Optimization levels:
|
||||
-o 0 => --zc 3 --nz (0 or 1 trials)
|
||||
-o 1 => --zc 9 (1 trial, determined heuristically)
|
||||
-o 2 => --zc 9 --zs 0-3 -f 0,5 (8 trials)
|
||||
-o 3 => --zc 9 --zm 8-9 --zs 0-3 -f 0,5 (16 trials)
|
||||
-o 4 => --zc 9 --zm 8-9 --zs 0-3 -f 0-5 (48 trials)
|
||||
-o 5 => --zc 3-9 --zm 8-9 --zs 0-3 -f 0-5 (192 trials)
|
||||
-o 6 => --zc 1-9 --zm 7-9 --zs 0-3 -f 0-5 (360 trials)
|
||||
-o 6 --zm 1-9 => --zc 1-9 --zm 1-9 --zs 0-3 -f 0-5 (1080 trials)
|
||||
-o 0 => --zc 3 --nz (0 or 1 trials)
|
||||
-o 1 => --zc 9 (1 trial, determined heuristically)
|
||||
-o 2 => --zc 9 --zs 0-3 -f 0,5 (8 trials)
|
||||
-o 3 => --zc 9 --zs 0-3 -f 0,5 (8 trials, currently identical to preset 3)
|
||||
-o 4 => --zc 9 --zs 0-3 -f 0-5 (24 trials)
|
||||
-o 5 => --zc 3-9 --zs 0-3 -f 0-5 (96 trials)
|
||||
-o 6 => --zc 1-9 --zs 0-3 -f 0-5 (180 trials)
|
||||
|
||||
Exhaustive combinations such as \"-o 6 --zm 1-9\" are not generally recommended.
|
||||
These are very slow and generally provide no compression gain.
|
||||
|
||||
Manually specifying a compression option (zc, zm, etc.) will override the optimization preset,
|
||||
Manually specifying a compression option (zc, zs, etc.) will override the optimization preset,
|
||||
regardless of the order you write the arguments.")
|
||||
.get_matches();
|
||||
|
||||
|
|
@ -303,10 +289,6 @@ fn parse_opts_into_struct(matches: &ArgMatches) -> Result<Options, String> {
|
|||
opts.compression = parse_numeric_range_opts(x, 1, 9).unwrap();
|
||||
}
|
||||
|
||||
if let Some(x) = matches.value_of("memory") {
|
||||
opts.memory = parse_numeric_range_opts(x, 1, 9).unwrap();
|
||||
}
|
||||
|
||||
if let Some(x) = matches.value_of("strategies") {
|
||||
opts.strategies = parse_numeric_range_opts(x, 0, 3).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ use std::iter::Iterator;
|
|||
use std::path::Path;
|
||||
|
||||
const STD_COMPRESSION: u8 = 8;
|
||||
const STD_MEMORY: u8 = 9;
|
||||
const STD_STRATEGY: u8 = 2; // Huffman only
|
||||
const STD_WINDOW: u8 = 15;
|
||||
const STD_FILTERS: [u8; 2] = [0, 5];
|
||||
|
|
@ -616,11 +615,9 @@ impl PngData {
|
|||
deflate::deflate(
|
||||
&image.filter_image(*f),
|
||||
STD_COMPRESSION,
|
||||
STD_MEMORY,
|
||||
STD_STRATEGY,
|
||||
STD_WINDOW,
|
||||
).unwrap()
|
||||
.len()
|
||||
).len()
|
||||
})
|
||||
.min()
|
||||
.unwrap();
|
||||
|
|
|
|||
Loading…
Reference in a new issue