diff --git a/benches/zopfli.rs b/benches/zopfli.rs index 345a71ab..a189a35f 100644 --- a/benches/zopfli.rs +++ b/benches/zopfli.rs @@ -3,20 +3,18 @@ extern crate oxipng; extern crate test; -use std::{num::NonZeroU8, path::PathBuf}; +use std::path::PathBuf; use oxipng::{internal_tests::*, *}; use test::Bencher; -const DEFAULT_ZOPFLI_ITERATIONS: NonZeroU8 = NonZeroU8::new(15).unwrap(); - #[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, &Options::default()).unwrap(); b.iter(|| { - zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok(); + zopfli_deflate(png.raw.data.as_ref(), ZopfliOptions::default()).ok(); }); } @@ -26,7 +24,7 @@ fn zopfli_8_bits_strategy_0(b: &mut Bencher) { let png = PngData::new(&input, &Options::default()).unwrap(); b.iter(|| { - zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok(); + zopfli_deflate(png.raw.data.as_ref(), ZopfliOptions::default()).ok(); }); } @@ -38,7 +36,7 @@ fn zopfli_4_bits_strategy_0(b: &mut Bencher) { let png = PngData::new(&input, &Options::default()).unwrap(); b.iter(|| { - zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok(); + zopfli_deflate(png.raw.data.as_ref(), ZopfliOptions::default()).ok(); }); } @@ -50,7 +48,7 @@ fn zopfli_2_bits_strategy_0(b: &mut Bencher) { let png = PngData::new(&input, &Options::default()).unwrap(); b.iter(|| { - zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok(); + zopfli_deflate(png.raw.data.as_ref(), ZopfliOptions::default()).ok(); }); } @@ -62,6 +60,6 @@ fn zopfli_1_bits_strategy_0(b: &mut Bencher) { let png = PngData::new(&input, &Options::default()).unwrap(); b.iter(|| { - zopfli_deflate(png.raw.data.as_ref(), DEFAULT_ZOPFLI_ITERATIONS).ok(); + zopfli_deflate(png.raw.data.as_ref(), ZopfliOptions::default()).ok(); }); } diff --git a/src/cli.rs b/src/cli.rs index a3fd3aaa..aab9681a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{num::NonZeroU64, path::PathBuf}; use clap::{Arg, ArgAction, Command, builder::ArgPredicate, value_parser}; @@ -337,7 +337,7 @@ speed up compression for large files. This option requires '--zopfli' to be set. .long("zi") .value_name("iterations") .default_value("15") - .value_parser(1..=255) + .value_parser(value_parser!(NonZeroU64)) .requires("zopfli"), ) .arg( diff --git a/src/deflate/mod.rs b/src/deflate/mod.rs index 4de86b7f..1ae49bcb 100644 --- a/src/deflate/mod.rs +++ b/src/deflate/mod.rs @@ -1,14 +1,14 @@ mod deflater; -#[cfg(feature = "zopfli")] -use std::num::NonZeroU8; -use std::{fmt, fmt::Display}; - pub use deflater::{crc32, deflate, inflate}; use crate::{PngError, PngResult}; +use std::{fmt, fmt::Display}; + #[cfg(feature = "zopfli")] mod zopfli_oxipng; #[cfg(feature = "zopfli")] +pub use zopfli::Options as ZopfliOptions; +#[cfg(feature = "zopfli")] pub use zopfli_oxipng::deflate as zopfli_deflate; /// DEFLATE algorithms supported by oxipng (for use in [`Options`][crate::Options]) @@ -21,12 +21,7 @@ pub enum Deflater { }, #[cfg(feature = "zopfli")] /// Use the better but slower Zopfli implementation - 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, - }, + Zopfli(ZopfliOptions), } impl Deflater { @@ -34,7 +29,7 @@ impl Deflater { let compressed = match self { Self::Libdeflater { compression } => deflate(data, compression, max_size)?, #[cfg(feature = "zopfli")] - Self::Zopfli { iterations } => zopfli_deflate(data, iterations)?, + Self::Zopfli(options) => zopfli_deflate(data, options)?, }; if let Some(max) = max_size { if compressed.len() > max { @@ -51,7 +46,7 @@ impl Display for Deflater { match self { Self::Libdeflater { compression } => write!(f, "zc = {compression}"), #[cfg(feature = "zopfli")] - Self::Zopfli { iterations } => write!(f, "zopfli, zi = {iterations}"), + Self::Zopfli(options) => write!(f, "zopfli, zi = {}", options.iteration_count), } } } diff --git a/src/deflate/zopfli_oxipng.rs b/src/deflate/zopfli_oxipng.rs index b77e3164..c66a0c65 100644 --- a/src/deflate/zopfli_oxipng.rs +++ b/src/deflate/zopfli_oxipng.rs @@ -1,13 +1,7 @@ -use std::num::NonZeroU8; - use crate::{PngError, PngResult}; -pub fn deflate(data: &[u8], iterations: NonZeroU8) -> PngResult> { +pub fn deflate(data: &[u8], options: zopfli::Options) -> PngResult> { let mut output = Vec::with_capacity(data.len()); - let options = zopfli::Options { - iteration_count: iterations.into(), - ..Default::default() - }; // Since Rust v1.74, passing &[u8] directly into zopfli causes a regression in compressed size // for some files. Wrapping the slice in another Read implementer such as Box fixes it for now. match zopfli::compress(options, zopfli::Format::Zlib, Box::new(data), &mut output) { diff --git a/src/lib.rs b/src/lib.rs index af1a6653..0f43631d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,8 @@ use log::{debug, info, trace, warn}; use rayon::prelude::*; pub use rgb::{RGB16, RGBA8}; +#[cfg(feature = "zopfli")] +pub use crate::deflate::ZopfliOptions; pub use crate::{ colors::{BitDepth, ColorType}, deflate::Deflater, diff --git a/src/main.rs b/src/main.rs index 704996f8..9c55b74d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ mod rayon; #[cfg(feature = "zopfli")] -use std::num::NonZeroU8; +use std::num::NonZeroU64; use std::{ ffi::OsString, fs::DirBuilder, io::Write, path::PathBuf, process::ExitCode, time::Duration, }; @@ -26,6 +26,8 @@ use clap::ArgMatches; mod cli; use indexmap::IndexSet; use log::{Level, LevelFilter, error, warn}; +#[cfg(feature = "zopfli")] +use oxipng::ZopfliOptions; use oxipng::{Deflater, FilterStrategy, InFile, Options, OutFile, PngError, StripChunks}; use rayon::prelude::*; @@ -331,10 +333,11 @@ fn parse_opts_into_struct( #[cfg(feature = "zopfli")] if matches.get_flag("zopfli") { - let iterations = *matches.get_one::("iterations").unwrap(); - opts.deflater = Deflater::Zopfli { - iterations: NonZeroU8::new(iterations as u8).unwrap(), - }; + let iteration_count = *matches.get_one::("iterations").unwrap(); + opts.deflater = Deflater::Zopfli(ZopfliOptions { + iteration_count, + ..Default::default() + }); } if let (Deflater::Libdeflater { compression }, Some(x)) = (&mut opts.deflater, matches.get_one::("compression")) diff --git a/tests/flags.rs b/tests/flags.rs index 897b9fe9..de6afbde 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "zopfli")] -use std::num::NonZeroU8; use std::{ fs::remove_file, path::{Path, PathBuf}, @@ -641,9 +639,7 @@ fn scale_16() { fn zopfli_mode() { let input = PathBuf::from("tests/files/zopfli_mode.png"); let (output, mut opts) = get_opts(&input); - opts.deflater = Deflater::Zopfli { - iterations: NonZeroU8::new(15).unwrap(), - }; + opts.deflater = Deflater::Zopfli(ZopfliOptions::default()); test_it_converts( input,