parent
849d36ec6c
commit
c6452beb12
7 changed files with 67 additions and 34 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
- Fix program version that is displayed when running `oxipng -V`
|
- Fix program version that is displayed when running `oxipng -V`
|
||||||
- Ensure `--quiet` mode is actually quiet (@SethDusek [#20](https://github.com/shssoichiro/oxipng/pull/20))
|
- Ensure `--quiet` mode is actually quiet (@SethDusek [#20](https://github.com/shssoichiro/oxipng/pull/20))
|
||||||
- Imply `--quiet` when `--stdout` is enabled
|
- Imply `--quiet` when `--stdout` is enabled
|
||||||
|
- Use heuristics to determine best combination for `-o1` ([#21](https://github.com/shssoichiro/oxipng/issues/21))
|
||||||
|
|
||||||
**Version 0.1.1**
|
**Version 0.1.1**
|
||||||
- Fix `oxipng *` writing all input files to one output file ([#15](https://github.com/shssoichiro/oxipng/issues/15))
|
- Fix `oxipng *` writing all input files to one output file ([#15](https://github.com/shssoichiro/oxipng/issues/15))
|
||||||
|
|
|
||||||
83
src/lib.rs
83
src/lib.rs
|
|
@ -41,11 +41,14 @@ pub struct Options {
|
||||||
pub palette_reduction: bool,
|
pub palette_reduction: bool,
|
||||||
pub idat_recoding: bool,
|
pub idat_recoding: bool,
|
||||||
pub strip: bool,
|
pub strip: bool,
|
||||||
|
pub use_heuristics: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
// Decode PNG from file
|
// Decode PNG from file
|
||||||
if opts.verbosity.is_some() { println!("Processing: {}", filepath.to_str().unwrap()) };
|
if opts.verbosity.is_some() {
|
||||||
|
println!("Processing: {}", filepath.to_str().unwrap())
|
||||||
|
};
|
||||||
let in_file = Path::new(filepath);
|
let in_file = Path::new(filepath);
|
||||||
let mut png = match png::PngData::new(&in_file) {
|
let mut png = match png::PngData::new(&in_file) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
|
|
@ -73,6 +76,31 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
println!(" File size = {} bytes", file_original_size);
|
println!(" File size = {} bytes", file_original_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut filter = opts.filter.clone();
|
||||||
|
let compression = opts.compression.clone();
|
||||||
|
let memory = opts.memory.clone();
|
||||||
|
let mut strategies = opts.strategies.clone();
|
||||||
|
|
||||||
|
if opts.use_heuristics {
|
||||||
|
// Heuristically determine which set of options to use
|
||||||
|
if png.ihdr_data.bit_depth.as_u8() >= 8 &&
|
||||||
|
png.ihdr_data.color_type != png::ColorType::Indexed {
|
||||||
|
if filter.is_empty() {
|
||||||
|
filter.insert(5);
|
||||||
|
}
|
||||||
|
if strategies.is_empty() {
|
||||||
|
strategies.insert(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if filter.is_empty() {
|
||||||
|
filter.insert(0);
|
||||||
|
}
|
||||||
|
if strategies.is_empty() {
|
||||||
|
strategies.insert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut something_changed = false;
|
let mut something_changed = false;
|
||||||
|
|
||||||
if opts.bit_depth_reduction {
|
if opts.bit_depth_reduction {
|
||||||
|
|
@ -118,16 +146,17 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
if opts.idat_recoding || something_changed {
|
if opts.idat_recoding || something_changed {
|
||||||
// Go through selected permutations and determine the best
|
// Go through selected permutations and determine the best
|
||||||
let mut best: Option<(u8, u8, u8, u8, Vec<u8>)> = None;
|
let mut best: Option<(u8, u8, u8, u8, Vec<u8>)> = None;
|
||||||
let combinations = opts.filter.len() * opts.compression.len() * opts.memory.len() *
|
let combinations = filter.len() * compression.len() * memory.len() * strategies.len();
|
||||||
opts.strategies.len();
|
|
||||||
let mut results = Vec::with_capacity(combinations);
|
let mut results = Vec::with_capacity(combinations);
|
||||||
if opts.verbosity.is_some() { println!("Trying: {} combinations", combinations) };
|
if opts.verbosity.is_some() {
|
||||||
|
println!("Trying: {} combinations", combinations)
|
||||||
|
};
|
||||||
crossbeam::scope(|scope| {
|
crossbeam::scope(|scope| {
|
||||||
for f in &opts.filter {
|
for f in &filter {
|
||||||
let filtered = png.filter_image(*f);
|
let filtered = png.filter_image(*f);
|
||||||
for zc in &opts.compression {
|
for zc in &compression {
|
||||||
for zm in &opts.memory {
|
for zm in &memory {
|
||||||
for zs in &opts.strategies {
|
for zs in &strategies {
|
||||||
let moved_filtered = filtered.clone();
|
let moved_filtered = filtered.clone();
|
||||||
results.push(scope.spawn(move || {
|
results.push(scope.spawn(move || {
|
||||||
let new_idat = match deflate::deflate::deflate(&moved_filtered,
|
let new_idat = match deflate::deflate::deflate(&moved_filtered,
|
||||||
|
|
@ -173,13 +202,13 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
if let Some(better) = best {
|
if let Some(better) = best {
|
||||||
png.idat_data = better.4.clone();
|
png.idat_data = better.4.clone();
|
||||||
if opts.verbosity.is_some() {
|
if opts.verbosity.is_some() {
|
||||||
println!("Found better combination:");
|
println!("Found better combination:");
|
||||||
println!(" zc = {} zm = {} zs = {} f = {} {} bytes",
|
println!(" zc = {} zm = {} zs = {} f = {} {} bytes",
|
||||||
better.1,
|
better.1,
|
||||||
better.2,
|
better.2,
|
||||||
better.3,
|
better.3,
|
||||||
better.0,
|
better.0,
|
||||||
png.idat_data.len());
|
png.idat_data.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -240,25 +269,25 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
if opts.verbosity.is_some() {
|
if opts.verbosity.is_some() {
|
||||||
if idat_original_size >= png.idat_data.len() {
|
if idat_original_size >= png.idat_data.len() {
|
||||||
println!(" IDAT size = {} bytes ({} bytes decrease)",
|
println!(" IDAT size = {} bytes ({} bytes decrease)",
|
||||||
png.idat_data.len(),
|
png.idat_data.len(),
|
||||||
idat_original_size - png.idat_data.len());
|
idat_original_size - png.idat_data.len());
|
||||||
} else {
|
} else {
|
||||||
println!(" IDAT size = {} bytes ({} bytes increase)",
|
println!(" IDAT size = {} bytes ({} bytes increase)",
|
||||||
png.idat_data.len(),
|
png.idat_data.len(),
|
||||||
png.idat_data.len() - idat_original_size);
|
png.idat_data.len() - idat_original_size);
|
||||||
}
|
}
|
||||||
if file_original_size >= output_data.len() {
|
if file_original_size >= output_data.len() {
|
||||||
println!(" file size = {} bytes ({} bytes = {:.2}% decrease)",
|
println!(" file size = {} bytes ({} bytes = {:.2}% decrease)",
|
||||||
output_data.len(),
|
output_data.len(),
|
||||||
file_original_size - output_data.len(),
|
file_original_size - output_data.len(),
|
||||||
(file_original_size - output_data.len()) as f64 / file_original_size as f64 *
|
(file_original_size - output_data.len()) as f64 / file_original_size as f64 *
|
||||||
100f64);
|
100f64);
|
||||||
} else {
|
} else {
|
||||||
println!(" file size = {} bytes ({} bytes = {:.2}% increase)",
|
println!(" file size = {} bytes ({} bytes = {:.2}% increase)",
|
||||||
output_data.len(),
|
output_data.len(),
|
||||||
output_data.len() - file_original_size,
|
output_data.len() - file_original_size,
|
||||||
(output_data.len() - file_original_size) as f64 / file_original_size as f64 *
|
(output_data.len() - file_original_size) as f64 / file_original_size as f64 *
|
||||||
100f64);
|
100f64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
10
src/main.rs
10
src/main.rs
|
|
@ -46,6 +46,7 @@ fn main() {
|
||||||
palette_reduction: true,
|
palette_reduction: true,
|
||||||
idat_recoding: true,
|
idat_recoding: true,
|
||||||
strip: false,
|
strip: false,
|
||||||
|
use_heuristics: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let matches = App::new("oxipng")
|
let matches = App::new("oxipng")
|
||||||
|
|
@ -202,7 +203,7 @@ fn main() {
|
||||||
.long("strip"))
|
.long("strip"))
|
||||||
.after_help("Optimization levels:
|
.after_help("Optimization levels:
|
||||||
-o 0 => --zc 3 --nz (0 or 1 trials)
|
-o 0 => --zc 3 --nz (0 or 1 trials)
|
||||||
-o 1 => --zc 9 (1 trial)
|
-o 1 => --zc 9 (1 trial, determined heuristically)
|
||||||
-o 2 => --zc 9 --zs 0-3 --f 0,5 (8 trials)
|
-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 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 4 => --zc 9 --zm 8-9 --zs 0-3 --f 0-5 (48 trials)
|
||||||
|
|
@ -268,12 +269,11 @@ fn parse_opts_into_struct(matches: &ArgMatches, opts: &mut oxipng::Options) -> R
|
||||||
opts.compression = compression;
|
opts.compression = compression;
|
||||||
}
|
}
|
||||||
Some("1") => {
|
Some("1") => {
|
||||||
let mut filter = HashSet::new();
|
let filter = HashSet::new();
|
||||||
filter.insert(0);
|
|
||||||
opts.filter = filter;
|
opts.filter = filter;
|
||||||
let mut strategies = HashSet::new();
|
let strategies = HashSet::new();
|
||||||
strategies.insert(0);
|
|
||||||
opts.strategies = strategies;
|
opts.strategies = strategies;
|
||||||
|
opts.use_heuristics = true;
|
||||||
}
|
}
|
||||||
// 2 is the default
|
// 2 is the default
|
||||||
Some("3") => {
|
Some("3") => {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ impl fmt::Display for BitDepth {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitDepth {
|
impl BitDepth {
|
||||||
fn as_u8(&self) -> u8 {
|
pub fn as_u8(&self) -> u8 {
|
||||||
match *self {
|
match *self {
|
||||||
BitDepth::One => 1,
|
BitDepth::One => 1,
|
||||||
BitDepth::Two => 2,
|
BitDepth::Two => 2,
|
||||||
|
|
@ -77,7 +77,7 @@ impl BitDepth {
|
||||||
BitDepth::Sixteen => 16,
|
BitDepth::Sixteen => 16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn from_u8(depth: u8) -> BitDepth {
|
pub fn from_u8(depth: u8) -> BitDepth {
|
||||||
match depth {
|
match depth {
|
||||||
1 => BitDepth::One,
|
1 => BitDepth::One,
|
||||||
2 => BitDepth::Two,
|
2 => BitDepth::Two,
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
palette_reduction: true,
|
palette_reduction: true,
|
||||||
idat_recoding: true,
|
idat_recoding: true,
|
||||||
strip: false,
|
strip: false,
|
||||||
|
use_heuristics: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
palette_reduction: true,
|
palette_reduction: true,
|
||||||
idat_recoding: true,
|
idat_recoding: true,
|
||||||
strip: false,
|
strip: false,
|
||||||
|
use_heuristics: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
palette_reduction: true,
|
palette_reduction: true,
|
||||||
idat_recoding: true,
|
idat_recoding: true,
|
||||||
strip: false,
|
strip: false,
|
||||||
|
use_heuristics: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue