Skip to content

Commit

Permalink
Add initial support for changing Zopfli iterations (#446)
Browse files Browse the repository at this point in the history
* Update and optimize dependencies

These changes update the dependencies to their latest versions, fixing
some known issues that prevented doing so in the first place.

In addition, the direct dependency on byteorder was dropped in favor
of stdlib functions that have been stabilized for some time in Rust, and
the transitive dependency on chrono, pulled by stderrlog, was also
dropped, which had been affected by security issues and improperly
maintained in the past:

- cardoe/stderrlog-rs#31
- https://www.reddit.com/r/rust/comments/ts84n4/chrono_or_time_03/

* Run rustfmt

* Bump MSRV to 1.56.1

Updating to this patch version should not be cumbersome for end-users,
and it is required by a transitive dependency.

* Bump MSRV to 1.57.0

os_str_bytes requires it.

* Add initial support for changing Zopfli iterations

PR #445 did some dependency
updates, which included using the latest zopfli version. The latest
version of this crate exposes new options in its API that allow users to
choose the desired number of Zopfli compression iterations, which
may greatly affect execution time. In fact, other optimizers such as
zopflipng dynamically select this number depending on the input file
size (see: #414).

As a first step towards making OxiPNG deal with Zopfli better, let's add
the necessary options for libraries to be able to choose the number of
iterations. This number is still fixed to 15 as before when using the
CLI.

* Fix Clippy lint

Co-authored-by: Josh Holmer <[email protected]>
  • Loading branch information
AlexTMjugador and shssoichiro authored Sep 5, 2022
1 parent 94ba8b7 commit 84bbec0
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 11 deletions.
14 changes: 9 additions & 5 deletions benches/zopfli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ extern crate oxipng;
extern crate test;

use oxipng::internal_tests::*;
use std::num::NonZeroU8;
use std::path::PathBuf;
use test::Bencher;

// SAFETY: trivially safe. Stopgap solution until const unwrap is stabilized.
const DEFAULT_ZOPFLI_ITERATIONS: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(15) };

#[bench]
fn zopfli_16_bits_strategy_0(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, false).unwrap();

b.iter(|| {
zopfli_deflate(png.raw.data.as_ref()).ok();
zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok();
});
}

Expand All @@ -23,7 +27,7 @@ fn zopfli_8_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap();

b.iter(|| {
zopfli_deflate(png.raw.data.as_ref()).ok();
zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok();
});
}

Expand All @@ -35,7 +39,7 @@ fn zopfli_4_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap();

b.iter(|| {
zopfli_deflate(png.raw.data.as_ref()).ok();
zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok();
});
}

Expand All @@ -47,7 +51,7 @@ fn zopfli_2_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap();

b.iter(|| {
zopfli_deflate(png.raw.data.as_ref()).ok();
zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok();
});
}

Expand All @@ -59,6 +63,6 @@ fn zopfli_1_bits_strategy_0(b: &mut Bencher) {
let png = PngData::new(&input, false).unwrap();

b.iter(|| {
zopfli_deflate(png.raw.data.as_ref()).ok();
zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok();
});
}
17 changes: 14 additions & 3 deletions src/deflate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use crate::Deadline;
use crate::PngResult;
use indexmap::IndexSet;

#[cfg(feature = "zopfli")]
use std::num::NonZeroU8;

#[doc(hidden)]
pub mod miniz_stream;

Expand Down Expand Up @@ -49,11 +52,14 @@ pub fn deflate(
}

#[cfg(feature = "zopfli")]
pub fn zopfli_deflate(data: &[u8]) -> PngResult<Vec<u8>> {
pub fn zopfli_deflate(data: &[u8], iterations: NonZeroU8) -> PngResult<Vec<u8>> {
use std::cmp::max;

let mut output = Vec::with_capacity(max(1024, data.len() / 20));
let options = zopfli::Options::default();
let options = zopfli::Options {
iteration_count: iterations,
..Default::default()
};
match zopfli::compress(&options, &zopfli::Format::Zlib, data, &mut output) {
Ok(_) => (),
Err(_) => return Err(PngError::new("Failed to compress in zopfli")),
Expand Down Expand Up @@ -85,7 +91,12 @@ pub enum Deflaters {
},
#[cfg(feature = "zopfli")]
/// Use the better but slower Zopfli implementation
Zopfli,
Zopfli {
/// The number of compression iterations to do. 15 iterations are fine
/// for small files, but bigger files will need to be compressed with
/// less iterations, or else they will be too slow.
iterations: NonZeroU8,
},
#[cfg(feature = "libdeflater")]
/// Use libdeflater.
Libdeflater,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ fn optimize_png(
&deadline,
),
#[cfg(feature = "zopfli")]
Deflaters::Zopfli => deflate::zopfli_deflate(filtered),
Deflaters::Zopfli { iterations } => deflate::zopfli_deflate(filtered, iterations),
#[cfg(feature = "libdeflater")]
Deflaters::Libdeflater => deflate::libdeflater_deflate(filtered, &best_size),
};
Expand Down
5 changes: 4 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use oxipng::Headers;
use oxipng::Options;
use oxipng::{InFile, OutFile};
use std::fs::DirBuilder;
use std::num::NonZeroU8;
use std::path::PathBuf;
use std::process::exit;
use std::time::Duration;
Expand Down Expand Up @@ -495,7 +496,9 @@ fn parse_opts_into_struct(
}

if matches.is_present("zopfli") {
opts.deflate = Deflaters::Zopfli;
opts.deflate = Deflaters::Zopfli {
iterations: NonZeroU8::new(15).unwrap(),
};
} else if matches.is_present("libdeflater") {
opts.deflate = Deflaters::Libdeflater;
} else if let Deflaters::Zlib {
Expand Down
5 changes: 4 additions & 1 deletion tests/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use oxipng::{InFile, OutFile};
#[cfg(feature = "filetime")]
use std::cell::RefCell;
use std::fs::remove_file;
use std::num::NonZeroU8;
#[cfg(feature = "filetime")]
use std::ops::Deref;
use std::path::Path;
Expand Down Expand Up @@ -582,7 +583,9 @@ fn fix_errors() {
fn zopfli_mode() {
let input = PathBuf::from("tests/files/zopfli_mode.png");
let (output, mut opts) = get_opts(&input);
opts.deflate = Deflaters::Zopfli;
opts.deflate = Deflaters::Zopfli {
iterations: NonZeroU8::new(15).unwrap(),
};

test_it_converts(
input,
Expand Down

0 comments on commit 84bbec0

Please sign in to comment.