Fix new clippy lints
This commit is contained in:
parent
ec65bf7676
commit
0db03c6faf
16 changed files with 351 additions and 381 deletions
|
|
@ -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
29
Cargo.lock
generated
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)),
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
192
src/filters.rs
192
src/filters.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
140
src/interlace.rs
140
src/interlace.rs
|
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
119
src/lib.rs
119
src/lib.rs
|
|
@ -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",
|
||||||
|
|
|
||||||
25
src/main.rs
25
src/main.rs
|
|
@ -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!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
178
src/png.rs
178
src/png.rs
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue