Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Zlib-specific options under Zlib compressor #210

Merged
merged 1 commit into from
Apr 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/deflate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::atomicmin::AtomicMin;
use crate::error::PngError;
use crate::Deadline;
use crate::PngResult;
use indexmap::IndexSet;
use miniz_oxide;
use std::cmp::max;
use zopfli;
Expand Down Expand Up @@ -59,11 +60,27 @@ pub fn zopfli_deflate(data: &[u8]) -> PngResult<Vec<u8>> {
Ok(output)
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
/// DEFLATE algorithms supported by oxipng
pub enum Deflaters {
/// Use the Zlib/Miniz DEFLATE implementation
Zlib,
Zlib {
/// Which zlib compression levels to try on the file (1-9)
///
/// Default: `9`
compression: IndexSet<u8>,
/// Which zlib compression strategies to try on the file (0-3)
///
/// Default: `0-3`
strategies: IndexSet<u8>,
/// Window size to use when compressing the file, as `2^window` bytes.
///
/// Doesn't affect compression but may affect speed and memory usage.
/// 8-15 are valid values.
///
/// Default: `15`
window: u8,
},
/// Use the better but slower Zopfli implementation
Zopfli,
/// Use libdeflater.
Expand Down
86 changes: 41 additions & 45 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,21 +155,6 @@ pub struct Options {
///
/// Default: `None`
pub interlace: Option<u8>,
/// Which zlib compression levels to try on the file (1-9)
///
/// Default: `9`
pub compression: IndexSet<u8>,
/// Which zlib compression strategies to try on the file (0-3)
///
/// Default: `0-3`
pub strategies: IndexSet<u8>,
/// Window size to use when compressing the file, as `2^window` bytes.
///
/// Doesn't affect compression but may affect speed and memory usage.
/// 8-15 are valid values.
///
/// Default: `15`
pub window: u8,
/// Alpha filtering strategies to use
pub alphas: IndexSet<colors::AlphaOptim>,
/// Whether to attempt bit depth reduction
Expand Down Expand Up @@ -229,14 +214,18 @@ impl Options {
// on an `Options` struct generated by the `default` method.
fn apply_preset_0(mut self) -> Self {
self.idat_recoding = false;
self.compression.clear();
self.compression.insert(3);
if let Deflaters::Zlib { compression, .. } = &mut self.deflate {
compression.clear();
compression.insert(3);
}
self
}

fn apply_preset_1(mut self) -> Self {
self.filter.clear();
self.strategies.clear();
if let Deflaters::Zlib { strategies, .. } = &mut self.deflate {
strategies.clear();
}
self.use_heuristics = true;
self
}
Expand All @@ -257,15 +246,21 @@ impl Options {
}

fn apply_preset_5(mut self) -> Self {
for i in 3..9 {
self.compression.insert(i);
if let Deflaters::Zlib { compression, .. } = &mut self.deflate {
compression.clear();
for i in 3..9 {
compression.insert(i);
}
}
self.apply_preset_4()
}

fn apply_preset_6(mut self) -> Self {
for i in 1..3 {
self.compression.insert(i);
if let Deflaters::Zlib { compression, .. } = &mut self.deflate {
compression.clear();
for i in 1..3 {
compression.insert(i);
}
}
self.apply_preset_5()
}
Expand Down Expand Up @@ -297,16 +292,17 @@ impl Default for Options {
verbosity: Some(0),
filter,
interlace: None,
compression,
strategies,
window: 15,
alphas,
bit_depth_reduction: true,
color_type_reduction: true,
palette_reduction: true,
idat_recoding: true,
strip: Headers::None,
deflate: Deflaters::Zlib,
deflate: Deflaters::Zlib {
compression,
strategies,
window: 15,
},
use_heuristics: false,
timeout: None,
}
Expand Down Expand Up @@ -476,26 +472,26 @@ fn optimize_png(
}

let mut filter = opts.filter.clone();
let compression = &opts.compression;
let mut strategies = opts.strategies.clone();
let mut strategies = match &opts.deflate {
Deflaters::Zlib { strategies, .. } => Some(strategies.clone()),
_ => None,
};

if opts.use_heuristics {
// Heuristically determine which set of options to use
if png.raw.ihdr.bit_depth.as_u8() >= 8
let (use_filter, use_strategy) = if png.raw.ihdr.bit_depth.as_u8() >= 8
&& png.raw.ihdr.color_type != colors::ColorType::Indexed
{
if filter.is_empty() {
filter.insert(5);
}
if strategies.is_empty() {
strategies.insert(1);
}
(5, 1)
} else {
if filter.is_empty() {
filter.insert(0);
}
(0, 0)
};
if filter.is_empty() {
filter.insert(use_filter);
}
if let Some(strategies) = &mut strategies {
if strategies.is_empty() {
strategies.insert(0);
strategies.insert(use_strategy);
}
}
}
Expand All @@ -517,17 +513,17 @@ fn optimize_png(

if opts.idat_recoding || reduction_occurred {
// Go through selected permutations and determine the best
let combinations = if opts.deflate == Deflaters::Zlib && !deadline.passed() {
filter.len() * compression.len() * strategies.len()
let combinations = if let Deflaters::Zlib { compression, .. } = &opts.deflate {
filter.len() * compression.len() * strategies.as_ref().unwrap().len()
} else {
filter.len()
};
let mut results: Vec<TrialOptions> = Vec::with_capacity(combinations);

for f in &filter {
if opts.deflate == Deflaters::Zlib {
if let Deflaters::Zlib { compression, .. } = &opts.deflate {
for zc in compression {
for zs in &strategies {
for zs in strategies.as_ref().unwrap() {
results.push(TrialOptions {
filter: *f,
compression: *zc,
Expand Down Expand Up @@ -576,11 +572,11 @@ fn optimize_png(
}
let filtered = &filters[&trial.filter];
let new_idat = match opts.deflate {
Deflaters::Zlib => deflate::deflate(
Deflaters::Zlib { window, .. } => deflate::deflate(
filtered,
trial.compression,
trial.strategy,
opts.window,
window,
&best_size,
&deadline,
),
Expand Down
57 changes: 37 additions & 20 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,33 +325,13 @@ fn parse_opts_into_struct(
opts.filter = parse_numeric_range_opts(x, 0, 5).unwrap();
}

if let Some(x) = matches.value_of("compression") {
opts.compression = 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();
}

if let Some(x) = matches.value_of("timeout") {
let num = x
.parse()
.map_err(|_| "Timeout must be a number".to_owned())?;
opts.timeout = Some(Duration::from_secs(num));
}

match matches.value_of("window") {
Some("256") => opts.window = 8,
Some("512") => opts.window = 9,
Some("1k") => opts.window = 10,
Some("2k") => opts.window = 11,
Some("4k") => opts.window = 12,
Some("8k") => opts.window = 13,
Some("16k") => opts.window = 14,
// 32k is default
_ => (),
}

let out_dir = if let Some(x) = matches.value_of("output_dir") {
let path = PathBuf::from(x);
if !path.exists() {
Expand Down Expand Up @@ -482,9 +462,46 @@ fn parse_opts_into_struct(
}

if matches.is_present("libdeflater") {
if matches.is_present("zopfli") {
return Err("zopfli and libdeflater can't be used simultaneously".to_owned());
}
opts.deflate = Deflaters::Libdeflater;
}

if let Deflaters::Zlib {
compression,
strategies,
window,
} = &mut opts.deflate
{
if let Some(x) = matches.value_of("compression") {
*compression = parse_numeric_range_opts(x, 1, 9).unwrap();
}

if let Some(x) = matches.value_of("strategies") {
*strategies = parse_numeric_range_opts(x, 0, 3).unwrap();
}

match matches.value_of("window") {
Some("256") => *window = 8,
Some("512") => *window = 9,
Some("1k") => *window = 10,
Some("2k") => *window = 11,
Some("4k") => *window = 12,
Some("8k") => *window = 13,
Some("16k") => *window = 14,
// 32k is default
_ => (),
}
} else if matches.is_present("compression")
|| matches.is_present("strategies")
|| matches.is_present("window")
{
return Err(
"compression, strategies and window options are compatible only with zlib".to_owned(),
);
}

if let Some(x) = matches.value_of("threads") {
let threads = x.parse::<usize>().unwrap();

Expand Down