Fix two bugs in reducing palette and release version 0.14.2

This commit is contained in:
Josh Holmer 2017-02-22 15:45:46 -05:00
parent a5e34cce52
commit 44818a854b
10 changed files with 116 additions and 91 deletions

View file

@ -1,3 +1,7 @@
**Version 0.14.2**
- Fix a bug when reducing palette in images with bit depth less than 8
- Fix a bug when reducing palette in images with transparency
**Version 0.14.1** **Version 0.14.1**
- Remove zlib dependency and switch entirely to miniz, since zlib 1.2.11 was not working with oxipng. This costs some performance, but is better than having a broken application. - Remove zlib dependency and switch entirely to miniz, since zlib 1.2.11 was not working with oxipng. This costs some performance, but is better than having a broken application.

2
Cargo.lock generated
View file

@ -1,6 +1,6 @@
[root] [root]
name = "oxipng" name = "oxipng"
version = "0.14.1" version = "0.14.2"
dependencies = [ dependencies = [
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -8,7 +8,7 @@ homepage = "https://github.com/shssoichiro/oxipng"
license = "MIT" license = "MIT"
name = "oxipng" name = "oxipng"
repository = "https://github.com/shssoichiro/oxipng" repository = "https://github.com/shssoichiro/oxipng"
version = "0.14.1" version = "0.14.2"
[lib] [lib]
name = "oxipng" name = "oxipng"
@ -55,4 +55,4 @@ nightly-binary = ["binary", "nightly", "regex/simd-accel"]
dev = ["nightly-binary", "clippy"] dev = ["nightly-binary", "clippy"]
[profile.dev] [profile.dev]
opt-level = 2 opt-level = 2

View file

@ -465,9 +465,29 @@ impl PngData {
return false; return false;
} }
// A palette with RGB slices // A palette with RGB or RGBA slices
let palette = self.palette.clone().unwrap(); let palette = if let Some(ref trns) = self.transparency_palette {
let mut indexed_palette: Vec<&[u8]> = palette.chunks(3).collect(); self.palette
.clone()
.unwrap()
.chunks(3)
.zip(trns.iter())
.flat_map(|(pixel, trns)| {
let mut pixel = pixel.to_owned();
pixel.push(*trns);
pixel
})
.collect()
} else {
self.palette.clone().unwrap()
};
let mut indexed_palette: Vec<&[u8]> =
palette.chunks(if self.transparency_palette.is_some() {
4
} else {
3
})
.collect();
// A map of old indexes to new ones, for any moved // A map of old indexes to new ones, for any moved
let mut index_map: HashMap<u8, u8> = HashMap::new(); let mut index_map: HashMap<u8, u8> = HashMap::new();
@ -589,49 +609,49 @@ impl PngData {
} }
BitDepth::Four => { BitDepth::Four => {
for byte in &line.data { for byte in &line.data {
let upper = *byte >> 4; let upper = *byte & 0b11110000;
let lower = *byte & 0b00001111; let lower = *byte & 0b00001111;
let mut new_byte = 0u8; let mut new_byte = 0u8;
if let Some(new_idx) = index_map.get(&upper) { new_byte |= if let Some(new_idx) = index_map.get(&upper) {
new_byte &= *new_idx << 4; *new_idx << 4
} else { } else {
new_byte &= upper << 4; upper << 4
} };
if let Some(new_idx) = index_map.get(&lower) { new_byte |= if let Some(new_idx) = index_map.get(&lower) {
new_byte &= *new_idx; *new_idx
} else { } else {
new_byte &= lower; lower
} };
new_data.push(new_byte); new_data.push(new_byte);
} }
} }
BitDepth::Two => { BitDepth::Two => {
for byte in &line.data { for byte in &line.data {
let one = *byte >> 6; let one = *byte & 0b11000000;
let two = (*byte >> 4) & 0b00000011; let two = *byte & 0b00110000;
let three = (*byte >> 2) & 0b00000011; let three = *byte & 0b00001100;
let four = *byte & 0b00000011; let four = *byte & 0b00000011;
let mut new_byte = 0u8; let mut new_byte = 0u8;
if let Some(new_idx) = index_map.get(&one) { new_byte |= if let Some(new_idx) = index_map.get(&one) {
new_byte &= *new_idx << 6; *new_idx << 6
} else { } else {
new_byte &= one << 6; one << 6
} };
if let Some(new_idx) = index_map.get(&two) { new_byte |= if let Some(new_idx) = index_map.get(&two) {
new_byte &= *new_idx << 4; *new_idx << 4
} else { } else {
new_byte &= two << 4; two << 4
} };
if let Some(new_idx) = index_map.get(&three) { new_byte |= if let Some(new_idx) = index_map.get(&three) {
new_byte &= *new_idx << 2; *new_idx << 2
} else { } else {
new_byte &= three << 2; three << 2
} };
if let Some(new_idx) = index_map.get(&four) { new_byte |= if let Some(new_idx) = index_map.get(&four) {
new_byte &= *new_idx; *new_idx
} else { } else {
new_byte &= four; four
} };
new_data.push(new_byte); new_data.push(new_byte);
} }
} }

BIN
tests/files/issue-56.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

View file

@ -56,8 +56,8 @@ fn test_it_converts(input: &Path,
let new_png = image::open(output).unwrap(); let new_png = image::open(output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }

View file

@ -58,8 +58,8 @@ fn test_it_converts(input: &Path,
let new_png = image::open(output).unwrap(); let new_png = image::open(output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -115,8 +115,8 @@ fn strip_headers_list() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -156,8 +156,8 @@ fn strip_headers_safe() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -197,8 +197,8 @@ fn strip_headers_all() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -238,8 +238,8 @@ fn strip_headers_none() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -275,8 +275,8 @@ fn interlacing_0_to_1() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -312,8 +312,8 @@ fn interlacing_1_to_0() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -353,8 +353,8 @@ fn interlacing_0_to_1_small_files() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -394,8 +394,8 @@ fn interlacing_1_to_0_small_files() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -434,8 +434,8 @@ fn interlaced_0_to_1_other_filter_mode() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }

View file

@ -57,8 +57,8 @@ fn test_it_converts(input: &Path,
let new_png = image::open(output).unwrap(); let new_png = image::open(output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }

View file

@ -57,8 +57,8 @@ fn test_it_converts(input: &Path,
let new_png = image::open(output).unwrap(); let new_png = image::open(output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -1268,8 +1268,8 @@ fn palette_should_be_reduced_with_dupes() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -1308,8 +1308,8 @@ fn palette_should_be_reduced_with_unused() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -1348,8 +1348,8 @@ fn palette_should_be_reduced_with_both() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }

View file

@ -56,8 +56,8 @@ fn test_it_converts(input: &Path,
let new_png = image::open(output).unwrap(); let new_png = image::open(output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -65,9 +65,7 @@ fn test_it_converts(input: &Path,
#[test] #[test]
fn issue_29() { fn issue_29() {
let input = PathBuf::from("tests/files/issue-29.png"); let input = PathBuf::from("tests/files/issue-29.png");
let mut opts = get_opts(&input); let opts = get_opts(&input);
opts.filter = HashSet::new();
opts.filter.insert(0);
let output = opts.out_file.clone(); let output = opts.out_file.clone();
test_it_converts(&input, test_it_converts(&input,
@ -114,8 +112,8 @@ fn issue_42() {
let new_png = image::open(&output).unwrap(); let new_png = image::open(&output).unwrap();
// Conversion should be lossless // Conversion should be lossless
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(), assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>()); new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
remove_file(output).ok(); remove_file(output).ok();
} }
@ -123,9 +121,7 @@ fn issue_42() {
#[test] #[test]
fn issue_52_01() { fn issue_52_01() {
let input = PathBuf::from("tests/files/issue-52-01.png"); let input = PathBuf::from("tests/files/issue-52-01.png");
let mut opts = get_opts(&input); let opts = get_opts(&input);
opts.filter = HashSet::new();
opts.filter.insert(0);
let output = opts.out_file.clone(); let output = opts.out_file.clone();
test_it_converts(&input, test_it_converts(&input,
@ -140,9 +136,7 @@ fn issue_52_01() {
#[test] #[test]
fn issue_52_02() { fn issue_52_02() {
let input = PathBuf::from("tests/files/issue-52-02.png"); let input = PathBuf::from("tests/files/issue-52-02.png");
let mut opts = get_opts(&input); let opts = get_opts(&input);
opts.filter = HashSet::new();
opts.filter.insert(0);
let output = opts.out_file.clone(); let output = opts.out_file.clone();
test_it_converts(&input, test_it_converts(&input,
@ -157,9 +151,7 @@ fn issue_52_02() {
#[test] #[test]
fn issue_52_03() { fn issue_52_03() {
let input = PathBuf::from("tests/files/issue-52-03.png"); let input = PathBuf::from("tests/files/issue-52-03.png");
let mut opts = get_opts(&input); let opts = get_opts(&input);
opts.filter = HashSet::new();
opts.filter.insert(0);
let output = opts.out_file.clone(); let output = opts.out_file.clone();
test_it_converts(&input, test_it_converts(&input,
@ -174,9 +166,7 @@ fn issue_52_03() {
#[test] #[test]
fn issue_52_04() { fn issue_52_04() {
let input = PathBuf::from("tests/files/issue-52-04.png"); let input = PathBuf::from("tests/files/issue-52-04.png");
let mut opts = get_opts(&input); let opts = get_opts(&input);
opts.filter = HashSet::new();
opts.filter.insert(0);
let output = opts.out_file.clone(); let output = opts.out_file.clone();
test_it_converts(&input, test_it_converts(&input,
@ -191,9 +181,7 @@ fn issue_52_04() {
#[test] #[test]
fn issue_52_05() { fn issue_52_05() {
let input = PathBuf::from("tests/files/issue-52-05.png"); let input = PathBuf::from("tests/files/issue-52-05.png");
let mut opts = get_opts(&input); let opts = get_opts(&input);
opts.filter = HashSet::new();
opts.filter.insert(0);
let output = opts.out_file.clone(); let output = opts.out_file.clone();
test_it_converts(&input, test_it_converts(&input,
@ -208,9 +196,7 @@ fn issue_52_05() {
#[test] #[test]
fn issue_52_06() { fn issue_52_06() {
let input = PathBuf::from("tests/files/issue-52-06.png"); let input = PathBuf::from("tests/files/issue-52-06.png");
let mut opts = get_opts(&input); let opts = get_opts(&input);
opts.filter = HashSet::new();
opts.filter.insert(0);
let output = opts.out_file.clone(); let output = opts.out_file.clone();
test_it_converts(&input, test_it_converts(&input,
@ -221,3 +207,18 @@ fn issue_52_06() {
ColorType::Indexed, ColorType::Indexed,
BitDepth::Two); BitDepth::Two);
} }
#[test]
fn issue_56() {
let input = PathBuf::from("tests/files/issue-56.png");
let opts = get_opts(&input);
let output = opts.out_file.clone();
test_it_converts(&input,
&output,
&opts,
ColorType::Indexed,
BitDepth::Four,
ColorType::Indexed,
BitDepth::Four);
}