Fix new clippy lints

This commit is contained in:
Josh Holmer 2017-09-03 23:36:09 -04:00
parent ec65bf7676
commit 0db03c6faf
16 changed files with 351 additions and 381 deletions

View file

@ -7,6 +7,7 @@
trials for optimizing the alpha channel, using the previously mentioned fast heuristic. trials for optimizing the alpha channel, using the previously mentioned fast heuristic.
This option will make optimization of images with transparency somewhat slower, This option will make optimization of images with transparency somewhat slower,
but may improve compression. but may improve compression.
- Code cleanup
### Version 0.16.3 ### Version 0.16.3
- Fix command-line help text ([#70](https://github.com/shssoichiro/oxipng/issues/70)) - Fix command-line help text ([#70](https://github.com/shssoichiro/oxipng/issues/70))

29
Cargo.lock generated
View file

@ -5,7 +5,7 @@ dependencies = [
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.25.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.25.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.144 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -98,21 +98,22 @@ dependencies = [
[[package]] [[package]]
name = "clippy" name = "clippy"
version = "0.0.144" version = "0.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy_lints 0.0.144 (registry+https://github.com/rust-lang/crates.io-index)", "clippy_lints 0.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "clippy_lints" name = "clippy_lints"
version = "0.0.144" version = "0.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"itertools 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -176,6 +177,11 @@ name = "gcc"
version = "0.3.51" version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "getopts"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "image" name = "image"
version = "0.14.0" version = "0.14.0"
@ -298,6 +304,15 @@ dependencies = [
"num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "pulldown-cmark"
version = "0.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "quine-mc_cluskey" name = "quine-mc_cluskey"
version = "0.2.4" version = "0.2.4"
@ -553,8 +568,8 @@ dependencies = [
"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
"checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b" "checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b"
"checksum clap 2.25.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7f1aabf260a8f3fefa8871f16b531038c98dd9eab1cfa2c575e78c459abfa3a0" "checksum clap 2.25.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7f1aabf260a8f3fefa8871f16b531038c98dd9eab1cfa2c575e78c459abfa3a0"
"checksum clippy 0.0.144 (registry+https://github.com/rust-lang/crates.io-index)" = "e0349a693e7dd889e2a008f3b8deaacf33b8b649df01982d29b4a56c13531365" "checksum clippy 0.0.156 (registry+https://github.com/rust-lang/crates.io-index)" = "2811d5bced647bd40952beab39ff901607d7091df81efcc6277efaf87d914291"
"checksum clippy_lints 0.0.144 (registry+https://github.com/rust-lang/crates.io-index)" = "0331317f8aa85cb8651ee449814ef7a8ef682009bef8d63b28ac10eedb969f92" "checksum clippy_lints 0.0.156 (registry+https://github.com/rust-lang/crates.io-index)" = "4d0b9204632f8fd5071c10ebfe95709e03438b29447850ac39ea71c575625b26"
"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" "checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd"
"checksum crc 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc1914fae6f18ae347320f0ba5e4fc270e17c037ea621fe41ec7e8adf67d11b0" "checksum crc 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc1914fae6f18ae347320f0ba5e4fc270e17c037ea621fe41ec7e8adf67d11b0"
"checksum deflate 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "e34344f1728e41039d49e87f428bbcb62b6bb82edfc2588253a8aa32a139a3a9" "checksum deflate 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "e34344f1728e41039d49e87f428bbcb62b6bb82edfc2588253a8aa32a139a3a9"
@ -563,6 +578,7 @@ dependencies = [
"checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
"checksum futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4b63a4792d4f8f686defe3b39b92127fea6344de5d38202b2ee5a11bbbf29d6a" "checksum futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4b63a4792d4f8f686defe3b39b92127fea6344de5d38202b2ee5a11bbbf29d6a"
"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a" "checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a"
"checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
"checksum image 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d61d2b3f000fb41d268312b92d4dd5ee7823163ceee71a67c676271585dfe598" "checksum image 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d61d2b3f000fb41d268312b92d4dd5ee7823163ceee71a67c676271585dfe598"
"checksum inflate 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d1238524675af3938a7c74980899535854b88ba07907bb1c944abe5b8fc437e5" "checksum inflate 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d1238524675af3938a7c74980899535854b88ba07907bb1c944abe5b8fc437e5"
"checksum itertools 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e58359414720377f59889192f1ec0e726049ce5735bc21fdb0c4c8ae638305bb" "checksum itertools 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e58359414720377f59889192f1ec0e726049ce5735bc21fdb0c4c8ae638305bb"
@ -579,6 +595,7 @@ dependencies = [
"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" "checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0"
"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584" "checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584"
"checksum png 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6535266009941ceac9a17e6d681cd2adc75611cd4833db853282e8d4c470239c" "checksum png 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6535266009941ceac9a17e6d681cd2adc75611cd4833db853282e8d4c470239c"
"checksum pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "378e941dbd392c101f2cb88097fa4d7167bc421d4b88de3ff7dbee503bc3233b"
"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" "checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"

View file

@ -1,7 +1,7 @@
#![feature(test)] #![feature(test)]
extern crate test;
extern crate oxipng; extern crate oxipng;
extern crate test;
use oxipng::png; use oxipng::png;
use oxipng::deflate; use oxipng::deflate;

View file

@ -1,7 +1,7 @@
#![feature(test)] #![feature(test)]
extern crate test;
extern crate oxipng; extern crate oxipng;
extern crate test;
use oxipng::png; use oxipng::png;
use test::Bencher; use test::Bencher;

View file

@ -1,7 +1,7 @@
#![feature(test)] #![feature(test)]
extern crate test;
extern crate oxipng; extern crate oxipng;
extern crate test;
use oxipng::png; use oxipng::png;
use test::Bencher; use test::Bencher;

View file

@ -1,7 +1,7 @@
#![feature(test)] #![feature(test)]
extern crate test;
extern crate oxipng; extern crate oxipng;
extern crate test;
use oxipng::png; use oxipng::png;
use oxipng::colors::AlphaOptim; use oxipng::colors::AlphaOptim;

View file

@ -1,7 +1,7 @@
#![feature(test)] #![feature(test)]
extern crate test;
extern crate oxipng; extern crate oxipng;
extern crate test;
use oxipng::png; use oxipng::png;
use oxipng::deflate; use oxipng::deflate;

View file

@ -1,6 +1,5 @@
use error::PngError; use error::PngError;
use miniz_sys; use miniz_sys;
use libc::c_int;
use std::cmp::max; use std::cmp::max;
use zopfli; use zopfli;
@ -30,8 +29,12 @@ pub fn deflate(data: &[u8], zc: u8, zm: u8, zs: u8, zw: u8) -> Result<Vec<u8>, P
// Compressed input should be smaller than decompressed, so allocate less than data.len() // 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 // 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 output = Vec::with_capacity(max(1024, data.len() / 20));
let mut stream = let mut stream = miniz_stream::Stream::new_compress(
miniz_stream::Stream::new_compress(zc as c_int, zw as c_int, zm as c_int, zs as c_int); i32::from(zc),
i32::from(zw),
i32::from(zm),
i32::from(zs),
);
loop { loop {
match stream.compress_vec(input.as_mut(), output.as_mut()) { match stream.compress_vec(input.as_mut(), output.as_mut()) {
miniz_sys::MZ_OK => output.reserve(max(1024, data.len() / 20)), miniz_sys::MZ_OK => output.reserve(max(1024, data.len() / 20)),

View file

@ -23,6 +23,8 @@ impl fmt::Display for PngError {
impl PngError { impl PngError {
#[inline] #[inline]
pub fn new(description: &str) -> PngError { pub fn new(description: &str) -> PngError {
PngError { description: description.to_owned() } PngError {
description: description.to_owned(),
}
} }
} }

View file

@ -6,17 +6,22 @@ pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> Vec
} }
1 => { 1 => {
filtered.extend_from_slice(&data[0..bpp]); filtered.extend_from_slice(&data[0..bpp]);
filtered.extend(data.iter().skip(bpp).zip(data.iter()).map(|(cur, last)| { filtered.extend(
cur.wrapping_sub(*last) data.iter()
})); .skip(bpp)
.zip(data.iter())
.map(|(cur, last)| cur.wrapping_sub(*last)),
);
} }
2 => { 2 => {
if last_line.is_empty() { if last_line.is_empty() {
filtered.extend_from_slice(data); filtered.extend_from_slice(data);
} else { } else {
filtered.extend(data.iter().zip(last_line.iter()).map(|(cur, last)| { filtered.extend(
cur.wrapping_sub(*last) data.iter()
})); .zip(last_line.iter())
.map(|(cur, last)| cur.wrapping_sub(*last)),
);
}; };
} }
3 => { 3 => {
@ -28,31 +33,29 @@ pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> Vec
}); });
} else { } else {
filtered.push(match i.checked_sub(bpp) { filtered.push(match i.checked_sub(bpp) {
Some(x) => { Some(x) => byte.wrapping_sub(
byte.wrapping_sub(((data[x] as u16 + last_line[i] as u16) >> 1) as u8) ((u16::from(data[x]) + u16::from(last_line[i])) >> 1) as u8,
} ),
None => byte.wrapping_sub(last_line[i] >> 1), None => byte.wrapping_sub(last_line[i] >> 1),
}); });
}; };
} }
} }
4 => { 4 => for (i, byte) in data.iter().enumerate() {
for (i, byte) in data.iter().enumerate() { if last_line.is_empty() {
if last_line.is_empty() { filtered.push(match i.checked_sub(bpp) {
filtered.push(match i.checked_sub(bpp) { Some(x) => byte.wrapping_sub(data[x]),
Some(x) => byte.wrapping_sub(data[x]), None => *byte,
None => *byte, });
}); } else {
} else { filtered.push(match i.checked_sub(bpp) {
filtered.push(match i.checked_sub(bpp) { Some(x) => {
Some(x) => { byte.wrapping_sub(paeth_predictor(data[x], last_line[i], last_line[x]))
byte.wrapping_sub(paeth_predictor(data[x], last_line[i], last_line[x])) }
} None => byte.wrapping_sub(last_line[i]),
None => byte.wrapping_sub(last_line[i]), });
}); };
}; },
}
}
_ => unreachable!(), _ => unreachable!(),
} }
filtered filtered
@ -64,8 +67,58 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V
0 => { 0 => {
unfiltered.extend_from_slice(data); unfiltered.extend_from_slice(data);
} }
1 => { 1 => for (i, byte) in data.iter().enumerate() {
for (i, byte) in data.iter().enumerate() { match i.checked_sub(bpp) {
Some(x) => {
let b = unfiltered[x];
unfiltered.push(byte.wrapping_add(b));
}
None => {
unfiltered.push(*byte);
}
};
},
2 => {
if last_line.is_empty() {
unfiltered.extend_from_slice(data);
} else {
unfiltered.extend(
data.iter()
.zip(last_line.iter())
.map(|(cur, last)| cur.wrapping_add(*last)),
);
};
}
3 => for (i, byte) in data.iter().enumerate() {
if last_line.is_empty() {
match i.checked_sub(bpp) {
Some(x) => {
let b = unfiltered[x];
unfiltered.push(byte.wrapping_add(b >> 1));
}
None => {
unfiltered.push(*byte);
}
};
} else {
match i.checked_sub(bpp) {
Some(x) => {
let b = unfiltered[x];
unfiltered
.push(
byte.wrapping_add(
((u16::from(b) + u16::from(last_line[i])) >> 1) as u8,
),
);
}
None => {
unfiltered.push(byte.wrapping_add(last_line[i] >> 1));
}
};
};
},
4 => for (i, byte) in data.iter().enumerate() {
if last_line.is_empty() {
match i.checked_sub(bpp) { match i.checked_sub(bpp) {
Some(x) => { Some(x) => {
let b = unfiltered[x]; let b = unfiltered[x];
@ -75,71 +128,20 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V
unfiltered.push(*byte); unfiltered.push(*byte);
} }
}; };
}
}
2 => {
if last_line.is_empty() {
unfiltered.extend_from_slice(data);
} else { } else {
unfiltered.extend(data.iter().zip(last_line.iter()).map(|(cur, last)| { match i.checked_sub(bpp) {
cur.wrapping_add(*last) Some(x) => {
})); let b = unfiltered[x];
unfiltered.push(
byte.wrapping_add(paeth_predictor(b, last_line[i], last_line[x])),
);
}
None => {
unfiltered.push(byte.wrapping_add(last_line[i]));
}
};
}; };
} },
3 => {
for (i, byte) in data.iter().enumerate() {
if last_line.is_empty() {
match i.checked_sub(bpp) {
Some(x) => {
let b = unfiltered[x];
unfiltered.push(byte.wrapping_add(b >> 1));
}
None => {
unfiltered.push(*byte);
}
};
} else {
match i.checked_sub(bpp) {
Some(x) => {
let b = unfiltered[x];
unfiltered.push(byte.wrapping_add(
((b as u16 + last_line[i] as u16) >> 1) as u8,
));
}
None => {
unfiltered.push(byte.wrapping_add(last_line[i] >> 1));
}
};
};
}
}
4 => {
for (i, byte) in data.iter().enumerate() {
if last_line.is_empty() {
match i.checked_sub(bpp) {
Some(x) => {
let b = unfiltered[x];
unfiltered.push(byte.wrapping_add(b));
}
None => {
unfiltered.push(*byte);
}
};
} else {
match i.checked_sub(bpp) {
Some(x) => {
let b = unfiltered[x];
unfiltered.push(byte.wrapping_add(
paeth_predictor(b, last_line[i], last_line[x]),
));
}
None => {
unfiltered.push(byte.wrapping_add(last_line[i]));
}
};
};
}
}
_ => unreachable!(), _ => unreachable!(),
} }
unfiltered unfiltered
@ -147,10 +149,10 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V
#[inline] #[inline]
fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 { fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {
let p = a as i32 + b as i32 - c as i32; let p = i32::from(a) + i32::from(b) - i32::from(c);
let pa = (p - a as i32).abs(); let pa = (p - i32::from(a)).abs();
let pb = (p - b as i32).abs(); let pb = (p - i32::from(b)).abs();
let pc = (p - c as i32).abs(); let pc = (p - i32::from(c)).abs();
if pa <= pb && pa <= pc { if pa <= pb && pa <= pc {
a a
} else if pb <= pc { } else if pb <= pc {

View file

@ -41,33 +41,27 @@ pub fn interlace_image(png: &mut PngData) {
let bit_vec = BitVec::from_bytes(&line.data); let bit_vec = BitVec::from_bytes(&line.data);
for (i, bit) in bit_vec.iter().enumerate() { for (i, bit) in bit_vec.iter().enumerate() {
// Avoid moving padded 0's into new image // Avoid moving padded 0's into new image
if i >= (png.ihdr_data.width * bits_per_pixel as u32) as usize { if i >= (png.ihdr_data.width * u32::from(bits_per_pixel)) as usize {
break; break;
} }
// Copy pixels into interlaced passes // Copy pixels into interlaced passes
let pix_modulo = (i / bits_per_pixel as usize) % 8; let pix_modulo = (i / bits_per_pixel as usize) % 8;
match index % 8 { match index % 8 {
0 => { 0 => match pix_modulo {
match pix_modulo { 0 => passes[0].push(bit),
0 => passes[0].push(bit), 4 => passes[1].push(bit),
4 => passes[1].push(bit), 2 | 6 => passes[3].push(bit),
2 | 6 => passes[3].push(bit), _ => passes[5].push(bit),
_ => passes[5].push(bit), },
} 4 => match pix_modulo {
} 0 | 4 => passes[2].push(bit),
4 => { 2 | 6 => passes[3].push(bit),
match pix_modulo { _ => passes[5].push(bit),
0 | 4 => passes[2].push(bit), },
2 | 6 => passes[3].push(bit), 2 | 6 => match pix_modulo % 2 {
_ => passes[5].push(bit), 0 => passes[4].push(bit),
} _ => passes[5].push(bit),
} },
2 | 6 => {
match pix_modulo % 2 {
0 => passes[4].push(bit),
_ => passes[5].push(bit),
}
}
_ => { _ => {
passes[6].push(bit); passes[6].push(bit);
} }
@ -99,8 +93,8 @@ pub fn deinterlace_image(png: &mut PngData) {
let mut current_y: usize = pass_constants.y_shift as usize; let mut current_y: usize = pass_constants.y_shift as usize;
for line in png.scan_lines() { for line in png.scan_lines() {
let bit_vec = BitVec::from_bytes(&line.data); let bit_vec = BitVec::from_bytes(&line.data);
let bits_in_line = ((png.ihdr_data.width - pass_constants.x_shift as u32) as f32 / let bits_in_line = ((png.ihdr_data.width - u32::from(pass_constants.x_shift)) as f32 /
pass_constants.x_step as f32) f32::from(pass_constants.x_step))
.ceil() as usize * bits_per_pixel as usize; .ceil() as usize * bits_per_pixel as usize;
for (i, bit) in bit_vec.iter().enumerate() { for (i, bit) in bit_vec.iter().enumerate() {
// Avoid moving padded 0's into new image // Avoid moving padded 0's into new image
@ -150,62 +144,48 @@ struct InterlacedConstants {
fn interlaced_constants(pass: u8) -> InterlacedConstants { fn interlaced_constants(pass: u8) -> InterlacedConstants {
match pass { match pass {
1 => { 1 => InterlacedConstants {
InterlacedConstants { x_shift: 0,
x_shift: 0, y_shift: 0,
y_shift: 0, x_step: 8,
x_step: 8, y_step: 8,
y_step: 8, },
} 2 => InterlacedConstants {
} x_shift: 4,
2 => { y_shift: 0,
InterlacedConstants { x_step: 8,
x_shift: 4, y_step: 8,
y_shift: 0, },
x_step: 8, 3 => InterlacedConstants {
y_step: 8, x_shift: 0,
} y_shift: 4,
} x_step: 4,
3 => { y_step: 8,
InterlacedConstants { },
x_shift: 0, 4 => InterlacedConstants {
y_shift: 4, x_shift: 2,
x_step: 4, y_shift: 0,
y_step: 8, x_step: 4,
} y_step: 4,
} },
4 => { 5 => InterlacedConstants {
InterlacedConstants { x_shift: 0,
x_shift: 2, y_shift: 2,
y_shift: 0, x_step: 2,
x_step: 4, y_step: 4,
y_step: 4, },
} 6 => InterlacedConstants {
} x_shift: 1,
5 => { y_shift: 0,
InterlacedConstants { x_step: 2,
x_shift: 0, y_step: 2,
y_shift: 2, },
x_step: 2, 7 => InterlacedConstants {
y_step: 4, x_shift: 0,
} y_shift: 1,
} x_step: 1,
6 => { y_step: 2,
InterlacedConstants { },
x_shift: 1,
y_shift: 0,
x_step: 2,
y_step: 2,
}
}
7 => {
InterlacedConstants {
x_shift: 0,
y_shift: 1,
x_step: 1,
y_step: 2,
}
}
_ => unreachable!(), _ => unreachable!(),
} }
} }

View file

@ -1,5 +1,5 @@
#![cfg_attr(feature="dev", feature(plugin))] #![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature="dev", plugin(clippy))] #![cfg_attr(feature = "dev", plugin(clippy))]
extern crate bit_vec; extern crate bit_vec;
extern crate byteorder; extern crate byteorder;
@ -14,13 +14,13 @@ extern crate zopfli;
use deflate::Deflaters; use deflate::Deflaters;
pub use error::PngError; pub use error::PngError;
use image::{GenericImage, Pixel, ImageFormat}; use image::{GenericImage, ImageFormat, Pixel};
use headers::Headers; use headers::Headers;
use png::PngData; use png::PngData;
use rayon::prelude::*; use rayon::prelude::*;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fs::{File, copy}; use std::fs::{copy, File};
use std::io::{BufWriter, Write, stdout}; use std::io::{stdout, BufWriter, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub mod colors; pub mod colors;
@ -360,38 +360,28 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), PngError> {
let readonly = metadata.permissions().readonly(); let readonly = metadata.permissions().readonly();
out_meta.permissions().set_readonly(readonly); out_meta.permissions().set_readonly(readonly);
} }
Err(_) => { Err(_) => if opts.verbosity.is_some() {
if opts.verbosity.is_some() { eprintln!("Failed to set permissions on output file");
eprintln!( },
"Failed to set permissions on output file"
);
}
}
} }
} }
} }
Err(_) => { Err(_) => if opts.verbosity.is_some() {
if opts.verbosity.is_some() { eprintln!("Failed to read permissions on input file");
eprintln!("Failed to read permissions on input file"); },
}
}
}
}
Err(_) => {
if opts.verbosity.is_some() {
eprintln!("Failed to read permissions on input file");
} }
} }
Err(_) => if opts.verbosity.is_some() {
eprintln!("Failed to read permissions on input file");
},
}; };
} }
let mut buffer = BufWriter::new(out_file); let mut buffer = BufWriter::new(out_file);
match buffer.write_all(&optimized_output) { match buffer.write_all(&optimized_output) {
Ok(_) => { Ok(_) => if opts.verbosity.is_some() {
if opts.verbosity.is_some() { eprintln!("Output: {}", opts.out_file.display());
eprintln!("Output: {}", opts.out_file.display()); },
}
}
Err(_) => { Err(_) => {
return Err(PngError::new(&format!( return Err(PngError::new(&format!(
"Unable to write to file {}", "Unable to write to file {}",
@ -543,42 +533,41 @@ fn optimize_png(
let original_len = original_png.idat_data.len(); let original_len = original_png.idat_data.len();
let added_interlacing = opts.interlace == Some(1) && original_png.ihdr_data.interlaced == 0; let added_interlacing = opts.interlace == Some(1) && original_png.ihdr_data.interlaced == 0;
let best: Option<TrialWithData> = let best: Option<TrialWithData> = results
results .into_par_iter()
.into_par_iter() .with_max_len(1)
.with_max_len(1) .filter_map(|trial| {
.filter_map(|trial| { let filtered = &filters[&trial.filter];
let filtered = &filters[&trial.filter]; let new_idat = if opts.deflate == Deflaters::Zlib {
let new_idat = if opts.deflate == Deflaters::Zlib { deflate::deflate(
deflate::deflate( filtered,
filtered, trial.compression,
trial.compression, trial.memory,
trial.memory, trial.strategy,
trial.strategy, opts.window,
opts.window, )
) } else {
} else { deflate::zopfli_deflate(filtered)
deflate::zopfli_deflate(filtered) }.unwrap();
}.unwrap();
if opts.verbosity == Some(1) { if opts.verbosity == Some(1) {
eprintln!( eprintln!(
" zc = {} zm = {} zs = {} f = {} {} bytes", " zc = {} zm = {} zs = {} f = {} {} bytes",
trial.compression, trial.compression,
trial.memory, trial.memory,
trial.strategy, trial.strategy,
trial.filter, trial.filter,
new_idat.len() new_idat.len()
); );
} }
if new_idat.len() < original_len || added_interlacing || opts.force { if new_idat.len() < original_len || added_interlacing || opts.force {
Some((trial, new_idat)) Some((trial, new_idat))
} else { } else {
None None
} }
}) })
.reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j }); .reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j });
if let Some(better) = best { if let Some(better) = best {
png.idat_data = better.1; png.idat_data = better.1;
@ -732,11 +721,9 @@ fn perform_strip(png: &mut png::PngData, opts: &Options) {
match opts.strip { match opts.strip {
// Strip headers // Strip headers
Headers::None => (), Headers::None => (),
Headers::Some(ref hdrs) => { Headers::Some(ref hdrs) => for hdr in hdrs {
for hdr in hdrs { png.aux_headers.remove(hdr);
png.aux_headers.remove(hdr); },
}
}
Headers::Safe => { Headers::Safe => {
const PRESERVED_HEADERS: [&'static str; 9] = [ const PRESERVED_HEADERS: [&'static str; 9] = [
"cHRM", "cHRM",

View file

@ -1,14 +1,14 @@
#![cfg_attr(feature="clippy", feature(plugin))] #![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))] #![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(enum_glob_use))] #![cfg_attr(feature = "clippy", warn(enum_glob_use))]
#![cfg_attr(feature="clippy", warn(if_not_else))] #![cfg_attr(feature = "clippy", warn(if_not_else))]
#![cfg_attr(feature="clippy", warn(string_add))] #![cfg_attr(feature = "clippy", warn(string_add))]
#![cfg_attr(feature="clippy", warn(string_add_assign))] #![cfg_attr(feature = "clippy", warn(string_add_assign))]
#![warn(trivial_casts, trivial_numeric_casts, unused_import_braces)] #![warn(trivial_casts, trivial_numeric_casts, unused_import_braces)]
#![deny(missing_debug_implementations, missing_copy_implementations)] #![deny(missing_debug_implementations, missing_copy_implementations)]
extern crate oxipng;
extern crate clap; extern crate clap;
extern crate oxipng;
extern crate regex; extern crate regex;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
@ -414,8 +414,7 @@ fn parse_opts_into_struct(matches: &ArgMatches) -> Result<Options, String> {
if hdrs.contains(&"safe".to_owned()) || hdrs.contains(&"all".to_owned()) { if hdrs.contains(&"safe".to_owned()) || hdrs.contains(&"all".to_owned()) {
if hdrs.len() > 1 { if hdrs.len() > 1 {
return Err( return Err(
"'safe' or 'all' presets for --strip should be used by themselves" "'safe' or 'all' presets for --strip should be used by themselves".to_owned(),
.to_owned(),
); );
} }
if hdrs[0] == "safe" { if hdrs[0] == "safe" {
@ -483,11 +482,9 @@ fn parse_numeric_range_opts(
items.insert(first); items.insert(first);
items.insert(second); items.insert(second);
} }
"-" => { "-" => for i in first..second + 1 {
for i in first..second + 1 { items.insert(i);
items.insert(i); },
}
}
_ => unreachable!(), _ => unreachable!(),
}; };

View file

@ -1,12 +1,12 @@
use bit_vec::BitVec; use bit_vec::BitVec;
use byteorder::{BigEndian, WriteBytesExt}; use byteorder::{BigEndian, WriteBytesExt};
use colors::{BitDepth, ColorType, AlphaOptim}; use colors::{AlphaOptim, BitDepth, ColorType};
use crc::crc32; use crc::crc32;
use deflate; use deflate;
use error::PngError; use error::PngError;
use filters::*; use filters::*;
use headers::*; use headers::*;
use interlace::{interlace_image, deinterlace_image}; use interlace::{deinterlace_image, interlace_image};
use itertools::Itertools; use itertools::Itertools;
use reduction::*; use reduction::*;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -56,8 +56,8 @@ impl<'a> Iterator for ScanLines<'a> {
pass.1 = 0; pass.1 = 0;
} }
} }
let bits_per_pixel = self.png.ihdr_data.bit_depth.as_u8() as u32 * let bits_per_pixel = u32::from(self.png.ihdr_data.bit_depth.as_u8()) *
self.png.channels_per_pixel() as u32; u32::from(self.png.channels_per_pixel());
let y_steps; let y_steps;
let pixels_factor; let pixels_factor;
match self.pass { match self.pass {
@ -95,21 +95,15 @@ impl<'a> Iterator for ScanLines<'a> {
1 | 3 | 5 => { 1 | 3 | 5 => {
pixels_per_line += 1; pixels_per_line += 1;
} }
2 => { 2 => if gap >= 5 {
if gap >= 5 { pixels_per_line += 1;
pixels_per_line += 1; },
} 4 => if gap >= 3 {
} pixels_per_line += 1;
4 => { },
if gap >= 3 { 6 => if gap >= 2 {
pixels_per_line += 1; pixels_per_line += 1;
} },
}
6 => {
if gap >= 2 {
pixels_per_line += 1;
}
}
_ => (), _ => (),
}; };
} }
@ -324,8 +318,7 @@ impl PngData {
// Ancillary headers // Ancillary headers
for (key, header) in self.aux_headers.iter().filter(|&(key, _)| { for (key, header) in self.aux_headers.iter().filter(|&(key, _)| {
!(*key == "bKGD" || *key == "hIST" || *key == "tRNS") !(*key == "bKGD" || *key == "hIST" || *key == "tRNS")
}) }) {
{
write_png_block(key.as_bytes(), header, &mut output); write_png_block(key.as_bytes(), header, &mut output);
} }
// Palette // Palette
@ -340,10 +333,9 @@ impl PngData {
write_png_block(b"tRNS", transparency_pixel, &mut output); write_png_block(b"tRNS", transparency_pixel, &mut output);
} }
// Special ancillary headers that need to come after PLTE but before IDAT // Special ancillary headers that need to come after PLTE but before IDAT
for (key, header) in self.aux_headers.iter().filter(|&(key, _)| { for (key, header) in self.aux_headers.iter().filter(
*key == "bKGD" || *key == "hIST" || *key == "tRNS" |&(key, _)| *key == "bKGD" || *key == "hIST" || *key == "tRNS",
}) ) {
{
write_png_block(key.as_bytes(), header, &mut output); write_png_block(key.as_bytes(), header, &mut output);
} }
// IDAT data // IDAT data
@ -368,8 +360,8 @@ impl PngData {
/// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream /// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream
pub fn unfilter_image(&self) -> Vec<u8> { pub fn unfilter_image(&self) -> Vec<u8> {
let mut unfiltered = Vec::with_capacity(self.raw_data.len()); let mut unfiltered = Vec::with_capacity(self.raw_data.len());
let bpp = (((self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel()) as f32) / let bpp = ((f32::from(self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel())) /
8f32) 8f32)
.ceil() as usize; .ceil() as usize;
let mut last_line: Vec<u8> = Vec::new(); let mut last_line: Vec<u8> = Vec::new();
for line in self.scan_lines() { for line in self.scan_lines() {
@ -390,8 +382,8 @@ impl PngData {
/// 5: All (heuristically pick the best filter for each line) /// 5: All (heuristically pick the best filter for each line)
pub fn filter_image(&self, filter: u8) -> Vec<u8> { pub fn filter_image(&self, filter: u8) -> Vec<u8> {
let mut filtered = Vec::with_capacity(self.raw_data.len()); let mut filtered = Vec::with_capacity(self.raw_data.len());
let bpp = (((self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel()) as f32) / let bpp = ((f32::from(self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel())) /
8f32) 8f32)
.ceil() as usize; .ceil() as usize;
let mut last_line: Vec<u8> = Vec::new(); let mut last_line: Vec<u8> = Vec::new();
let mut last_pass: Option<u8> = None; let mut last_pass: Option<u8> = None;
@ -400,9 +392,8 @@ impl PngData {
0 | 1 | 2 | 3 | 4 => { 0 | 1 | 2 | 3 | 4 => {
if last_pass == line.pass || filter <= 1 { if last_pass == line.pass || filter <= 1 {
filtered.push(filter); filtered.push(filter);
filtered.extend_from_slice( filtered
&filter_line(filter, bpp, &line.data, &last_line), .extend_from_slice(&filter_line(filter, bpp, &line.data, &last_line));
);
} else { } else {
// Avoid vertical filtering on first line of each interlacing pass // Avoid vertical filtering on first line of each interlacing pass
filtered.push(0); filtered.push(0);
@ -423,7 +414,7 @@ impl PngData {
.min_by_key(|x| { .min_by_key(|x| {
x.1.iter().fold(0u64, |acc, &x| { x.1.iter().fold(0u64, |acc, &x| {
let signed = x as i8; let signed = x as i8;
acc + (signed as i16).abs() as u64 acc + i16::from(signed).abs() as u64
}) })
}) })
.unwrap(); .unwrap();
@ -452,8 +443,8 @@ impl PngData {
// Reduce from 16 to 8 bits per channel per pixel // Reduce from 16 to 8 bits per channel per pixel
let mut reduced = Vec::with_capacity( let mut reduced = Vec::with_capacity(
(self.ihdr_data.width * self.ihdr_data.height * self.channels_per_pixel() as u32 + (self.ihdr_data.width * self.ihdr_data.height * u32::from(self.channels_per_pixel()) +
self.ihdr_data.height) as usize, self.ihdr_data.height) as usize,
); );
let mut high_byte = 0; let mut high_byte = 0;
@ -543,11 +534,9 @@ impl PngData {
let mut seen = HashSet::with_capacity(indexed_palette.len()); let mut seen = HashSet::with_capacity(indexed_palette.len());
for line in self.scan_lines() { for line in self.scan_lines() {
match self.ihdr_data.bit_depth { match self.ihdr_data.bit_depth {
BitDepth::Eight => { BitDepth::Eight => for byte in &line.data {
for byte in &line.data { seen.insert(*byte);
seen.insert(*byte); },
}
}
BitDepth::Four => { BitDepth::Four => {
let bitvec = BitVec::from_bytes(&line.data); let bitvec = BitVec::from_bytes(&line.data);
let mut current = 0u8; let mut current = 0u8;
@ -628,63 +617,57 @@ impl PngData {
for line in self.scan_lines() { for line in self.scan_lines() {
new_data.push(line.filter); new_data.push(line.filter);
match self.ihdr_data.bit_depth { match self.ihdr_data.bit_depth {
BitDepth::Eight => { BitDepth::Eight => for byte in &line.data {
for byte in &line.data { if let Some(new_idx) = index_map.get(byte) {
if let Some(new_idx) = index_map.get(byte) { new_data.push(*new_idx);
new_data.push(*new_idx); } else {
} else { new_data.push(*byte);
new_data.push(*byte);
}
} }
} },
BitDepth::Four => { BitDepth::Four => for byte in &line.data {
for byte in &line.data { let upper = *byte & 0b1111_0000;
let upper = *byte & 0b11110000; let lower = *byte & 0b0000_1111;
let lower = *byte & 0b00001111; let mut new_byte = 0u8;
let mut new_byte = 0u8; new_byte |= if let Some(new_idx) = index_map.get(&upper) {
new_byte |= if let Some(new_idx) = index_map.get(&upper) { *new_idx << 4
*new_idx << 4 } else {
} else { upper
upper };
}; new_byte |= if let Some(new_idx) = index_map.get(&lower) {
new_byte |= if let Some(new_idx) = index_map.get(&lower) { *new_idx
*new_idx } else {
} else { lower
lower };
}; new_data.push(new_byte);
new_data.push(new_byte); },
} BitDepth::Two => for byte in &line.data {
} let one = *byte & 0b1100_0000;
BitDepth::Two => { let two = *byte & 0b0011_0000;
for byte in &line.data { let three = *byte & 0b0000_1100;
let one = *byte & 0b11000000; let four = *byte & 0b0000_0011;
let two = *byte & 0b00110000; let mut new_byte = 0u8;
let three = *byte & 0b00001100; new_byte |= if let Some(new_idx) = index_map.get(&one) {
let four = *byte & 0b00000011; *new_idx << 6
let mut new_byte = 0u8; } else {
new_byte |= if let Some(new_idx) = index_map.get(&one) { one << 6
*new_idx << 6 };
} else { new_byte |= if let Some(new_idx) = index_map.get(&two) {
one << 6 *new_idx << 4
}; } else {
new_byte |= if let Some(new_idx) = index_map.get(&two) { two << 4
*new_idx << 4 };
} else { new_byte |= if let Some(new_idx) = index_map.get(&three) {
two << 4 *new_idx << 2
}; } else {
new_byte |= if let Some(new_idx) = index_map.get(&three) { three << 2
*new_idx << 2 };
} else { new_byte |= if let Some(new_idx) = index_map.get(&four) {
three << 2 *new_idx
}; } else {
new_byte |= if let Some(new_idx) = index_map.get(&four) { four
*new_idx };
} else { new_data.push(new_byte);
four },
};
new_data.push(new_byte);
}
}
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -774,8 +757,7 @@ impl PngData {
pub fn reduce_alpha_channel(&mut self, optim: AlphaOptim) -> bool { pub fn reduce_alpha_channel(&mut self, optim: AlphaOptim) -> bool {
let (bpc, bpp) = match self.ihdr_data.color_type { let (bpc, bpp) = match self.ihdr_data.color_type {
ColorType::RGBA | ColorType::RGBA | ColorType::GrayscaleAlpha => {
ColorType::GrayscaleAlpha => {
let cpp = self.channels_per_pixel(); let cpp = self.channels_per_pixel();
let bpc = self.ihdr_data.bit_depth.as_u8() / 8; let bpc = self.ihdr_data.bit_depth.as_u8() / 8;
(bpc as usize, (bpc * cpp) as usize) (bpc as usize, (bpc * cpp) as usize)

View file

@ -135,12 +135,11 @@ pub fn reduce_rgba_to_palette(png: &mut PngData) -> bool {
} }
let mut color_palette = Vec::with_capacity( let mut color_palette = Vec::with_capacity(
palette.len() * 3 + palette.len() * 3 + if png.aux_headers.contains_key(&"bKGD".to_string()) {
if png.aux_headers.contains_key(&"bKGD".to_string()) { 6
6 } else {
} else { 0
0 },
},
); );
let mut trans_palette = Vec::with_capacity(palette.len()); let mut trans_palette = Vec::with_capacity(palette.len());
for color in &palette { for color in &palette {
@ -167,9 +166,9 @@ pub fn reduce_rgba_to_palette(png: &mut PngData) -> bool {
.step(2) .step(2)
.cloned() .cloned()
.collect::<Vec<u8>>(); .collect::<Vec<u8>>();
if let Some(entry) = color_palette.chunks(3).position( if let Some(entry) = color_palette
|x| x == header_pixels.as_slice(), .chunks(3)
) .position(|x| x == header_pixels.as_slice())
{ {
*bkgd_header = vec![entry as u8]; *bkgd_header = vec![entry as u8];
} else if color_palette.len() / 3 == 256 { } else if color_palette.len() / 3 == 256 {
@ -246,9 +245,9 @@ pub fn reduce_rgb_to_palette(png: &mut PngData) -> bool {
.step(2) .step(2)
.cloned() .cloned()
.collect::<Vec<u8>>(); .collect::<Vec<u8>>();
if let Some(entry) = color_palette.chunks(3).position( if let Some(entry) = color_palette
|x| x == header_pixels.as_slice(), .chunks(3)
) .position(|x| x == header_pixels.as_slice())
{ {
*bkgd_header = vec![entry as u8]; *bkgd_header = vec![entry as u8];
} else if color_palette.len() == 255 { } else if color_palette.len() == 255 {

View file

@ -1,6 +1,6 @@
extern crate oxipng; extern crate oxipng;
use oxipng::colors::{BitDepth, ColorType, AlphaOptim}; use oxipng::colors::{AlphaOptim, BitDepth, ColorType};
use oxipng::png; use oxipng::png;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;