From 2810316f4e6c1afb9993b06a587fa12248385bb5 Mon Sep 17 00:00:00 2001 From: Josh Holmer Date: Mon, 12 Dec 2016 08:28:05 -0500 Subject: [PATCH] Reduce certain headers when reducing color type Fixes #52 --- .travis.yml | 2 +- CHANGELOG.md | 3 + Cargo.lock | 46 ++++---- README.md | 2 +- ci/script.sh | 2 +- src/png.rs | 50 ++------ src/reduction.rs | 165 +++++++++++++++++++++----- tests/files/issue-52-01.png | Bin 0 -> 1628 bytes tests/files/issue-52-02.png | Bin 0 -> 1505 bytes tests/files/issue-52-03.png | Bin 0 -> 1808 bytes tests/files/issue-52-04.png | Bin 0 -> 196 bytes tests/files/issue-52-05.png | Bin 0 -> 198 bytes tests/files/issue-52-06.png | Bin 0 -> 260 bytes tests/filters.rs | 17 --- tests/flags.rs | 41 ------- tests/regression.rs | 223 ++++++++++++++++++++++++++++++++++++ 16 files changed, 397 insertions(+), 154 deletions(-) create mode 100644 tests/files/issue-52-01.png create mode 100644 tests/files/issue-52-02.png create mode 100644 tests/files/issue-52-03.png create mode 100644 tests/files/issue-52-04.png create mode 100644 tests/files/issue-52-05.png create mode 100644 tests/files/issue-52-06.png create mode 100644 tests/regression.rs diff --git a/.travis.yml b/.travis.yml index 24ac3b1c..b9ab3199 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ matrix: env: TARGET=x86_64-apple-darwin cache: cargo - os: linux - rust: 1.9.0 + rust: 1.11.0 env: TARGET=x86_64-unknown-linux-gnu cache: cargo - os: linux diff --git a/CHANGELOG.md b/CHANGELOG.md index 3687309b..ce097794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +**Version 0.12.1 [unreleased]** + - Fix bug in certain PNG headers when reducing color type ([#52](https://github.com/shssoichiro/oxipng/issues/52)) + **Version 0.12.0** - Performance optimizations - Fix processing filenames that contain commas (@aliceatlas [#50](https://github.com/shssoichiro/oxipng/pull/50)) diff --git a/Cargo.lock b/Cargo.lock index 1c4f6175..429c2fec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "crc 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -58,7 +58,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -82,7 +82,7 @@ dependencies = [ "matches 0.1.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.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -117,13 +117,13 @@ name = "flate2" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gcc" -version = "0.3.38" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -171,7 +171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -179,8 +179,8 @@ name = "libz-sys" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -194,7 +194,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -202,8 +202,8 @@ name = "miniz-sys" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -229,7 +229,7 @@ dependencies = [ "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -257,7 +257,7 @@ dependencies = [ "num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -270,7 +270,7 @@ name = "num_cpus" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -299,7 +299,7 @@ name = "rand" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -308,7 +308,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -333,7 +333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-serialize" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -359,7 +359,7 @@ name = "term_size" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -368,7 +368,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -384,7 +384,7 @@ name = "toml" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -446,14 +446,14 @@ dependencies = [ "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum enum_primitive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f79eff5be92a4d7d5bddf7daa7d650717ea71628634efe6ca7bcda85b2183c23" "checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb" -"checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5" +"checksum gcc 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)" = "771e4a97ff6f237cf0f7d5f5102f6e28bb9743814b6198d684da5c58b76c11e0" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum image 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "76df2dce95fef56fd35dbc41c36e37b19aede703c6be7739e8b65d5788ffc728" "checksum inflate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e0062d2dc2f17d2f13750d95316ae8a2ff909af0fda957084f5defd87c43bb" "checksum itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c4a9b56eb56058f43dc66e58f40a214b2ccbc9f3df51861b63d51dec7b65bc3f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" -"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8" +"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70" "checksum libz-sys 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "283c2d162f78c5090522e13fc809820c33181570ae40de1bea84f3864c8759f9" "checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" @@ -473,7 +473,7 @@ dependencies = [ "checksum rayon 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0783f5880c56f5a308e219ac9309dbe781e064741dd5def4c617c440890305" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" -"checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818" +"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" "checksum semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d5b7638a1f03815d94e88cb3b3c08e87f0db4d683ef499d1836aaf70a45623f" "checksum simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5847c2d766ca7ce7227672850955802fabd779ba616aeabead4c2c3877023" "checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c" diff --git a/README.md b/README.md index fee056ae..3bbf968a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ cp target/release/oxipng /usr/local/bin Please note that zlib is a required build dependency. Oxipng should work with any 1.x version of zlib, but you are advised to use the latest version (currently 1.2.8) for security and bug fixes. -The current minimum supported Rust version is **1.9.0**. Oxipng may compile on earlier versions of Rust, +The current minimum supported Rust version is **1.11.0**. Oxipng may compile on earlier versions of Rust, but there is no guarantee. Oxipng follows Semantic Versioning. diff --git a/ci/script.sh b/ci/script.sh index 03ee8454..8e0c8a60 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -41,7 +41,7 @@ run_test_suite() { cargo test --target $TARGET --release # sanity check the file type - file target/$TARGET/debug/oxipng + file target/$TARGET/release/oxipng } main() { diff --git a/src/png.rs b/src/png.rs index 001dcead..851802e9 100644 --- a/src/png.rs +++ b/src/png.rs @@ -418,14 +418,7 @@ impl PngData { if self.ihdr_data.bit_depth != BitDepth::Sixteen { if self.ihdr_data.color_type == ColorType::Indexed || self.ihdr_data.color_type == ColorType::Grayscale { - return match reduce_bit_depth_8_or_less(self) { - Some((data, depth)) => { - self.raw_data = data; - self.ihdr_data.bit_depth = BitDepth::from_u8(depth); - true - } - None => false, - }; + return reduce_bit_depth_8_or_less(self); } return false; } @@ -660,57 +653,35 @@ impl PngData { // Go down one step at a time // Maybe not the most efficient, but it's safe if self.ihdr_data.color_type == ColorType::RGBA { - if let Some(data) = reduce_rgba_to_grayscale_alpha(self) { - self.raw_data = data; - self.ihdr_data.color_type = ColorType::GrayscaleAlpha; + if reduce_rgba_to_grayscale_alpha(self) { changed = true; - } else if let Some(data) = reduce_rgba_to_rgb(self) { - self.raw_data = data; - self.ihdr_data.color_type = ColorType::RGB; + } else if reduce_rgba_to_rgb(self) { changed = true; - } else if let Some((data, palette, trans)) = reduce_rgba_to_palette(self) { - self.raw_data = data; - self.palette = Some(palette); - if trans.iter().any(|x| *x != 255) { - self.transparency_palette = Some(trans); - } else { - self.transparency_palette = None; - } - self.ihdr_data.color_type = ColorType::Indexed; + } else if reduce_rgba_to_palette(self) { changed = true; should_reduce_bit_depth = true; } } if self.ihdr_data.color_type == ColorType::GrayscaleAlpha { - if let Some(data) = reduce_grayscale_alpha_to_grayscale(self) { - self.raw_data = data; - self.ihdr_data.color_type = ColorType::Grayscale; + if reduce_grayscale_alpha_to_grayscale(self) { changed = true; should_reduce_bit_depth = true; } } if self.ihdr_data.color_type == ColorType::RGB { - if let Some(data) = reduce_rgb_to_grayscale(self) { - self.raw_data = data; - self.ihdr_data.color_type = ColorType::Grayscale; + if reduce_rgb_to_grayscale(self) { changed = true; should_reduce_bit_depth = true; - } else if let Some((data, palette)) = reduce_rgb_to_palette(self) { - self.raw_data = data; - self.palette = Some(palette); - self.ihdr_data.color_type = ColorType::Indexed; + } else if reduce_rgb_to_palette(self) { changed = true; should_reduce_bit_depth = true; } } if self.ihdr_data.color_type == ColorType::Grayscale { - if let Some((data, palette)) = reduce_grayscale_to_palette(self) { - self.raw_data = data; - self.palette = Some(palette); - self.ihdr_data.color_type = ColorType::Indexed; + if reduce_grayscale_to_palette(self) { changed = true; should_reduce_bit_depth = true; } @@ -719,10 +690,7 @@ impl PngData { if should_reduce_bit_depth { // Some conversions will allow us to perform bit depth reduction that // wasn't possible before - if let Some((data, depth)) = reduce_bit_depth_8_or_less(self) { - self.raw_data = data; - self.ihdr_data.bit_depth = BitDepth::from_u8(depth); - } + reduce_bit_depth_8_or_less(self); } changed diff --git a/src/reduction.rs b/src/reduction.rs index 00a7a362..9df4bbc7 100644 --- a/src/reduction.rs +++ b/src/reduction.rs @@ -1,9 +1,9 @@ use bit_vec::BitVec; -use colors::BitDepth; +use colors::{BitDepth, ColorType}; use itertools::Itertools; use png::PngData; -pub fn reduce_bit_depth_8_or_less(png: &PngData) -> Option<(Vec, u8)> { +pub fn reduce_bit_depth_8_or_less(png: &mut PngData) -> bool { let mut reduced = BitVec::with_capacity(png.raw_data.len() * 8); let bit_depth: usize = png.ihdr_data.bit_depth.as_u8() as usize; let mut allowed_bits = 1; @@ -15,7 +15,7 @@ pub fn reduce_bit_depth_8_or_less(png: &PngData) -> Option<(Vec, u8)> { allowed_bits = bit_index.next_power_of_two(); if allowed_bits == bit_depth { // Not reducable - return None; + return false; } } } @@ -36,14 +36,22 @@ pub fn reduce_bit_depth_8_or_less(png: &PngData) -> Option<(Vec, u8)> { } } - Some((reduced.to_bytes(), allowed_bits as u8)) + png.raw_data = reduced.to_bytes(); + png.ihdr_data.bit_depth = BitDepth::from_u8(allowed_bits as u8); + true } -pub fn reduce_rgba_to_rgb(png: &PngData) -> Option> { - reduce_alpha_channel(png, 4) +pub fn reduce_rgba_to_rgb(png: &mut PngData) -> bool { + if let Some(reduced) = reduce_alpha_channel(png, 4) { + png.raw_data = reduced; + png.ihdr_data.color_type = ColorType::RGB; + true + } else { + false + } } -pub fn reduce_rgba_to_grayscale_alpha(png: &PngData) -> Option> { +pub fn reduce_rgba_to_grayscale_alpha(png: &mut PngData) -> bool { let mut reduced = Vec::with_capacity(png.raw_data.len()); let byte_depth: u8 = png.ihdr_data.bit_depth.as_u8() >> 3; let bpp: usize = 4 * byte_depth as usize; @@ -66,11 +74,11 @@ pub fn reduce_rgba_to_grayscale_alpha(png: &PngData) -> Option> { if i % bpp == bpp - 1 { if low_bytes.iter().unique().count() > 1 { - return None; + return false; } if byte_depth == 2 { if high_bytes.iter().unique().count() > 1 { - return None; + return false; } reduced.push(high_bytes[0]); high_bytes.clear(); @@ -83,12 +91,24 @@ pub fn reduce_rgba_to_grayscale_alpha(png: &PngData) -> Option> { } } - Some(reduced) + if let Some(sbit_header) = png.aux_headers.get_mut(&"sBIT".to_string()) { + assert_eq!(sbit_header.len(), 4); + sbit_header.remove(1); + sbit_header.remove(1); + } + if let Some(bkgd_header) = png.aux_headers.get_mut(&"bKGD".to_string()) { + assert_eq!(bkgd_header.len(), 6); + bkgd_header.truncate(2); + } + + png.raw_data = reduced; + png.ihdr_data.color_type = ColorType::GrayscaleAlpha; + true } -pub fn reduce_rgba_to_palette(png: &PngData) -> Option<(Vec, Vec, Vec)> { +pub fn reduce_rgba_to_palette(png: &mut PngData) -> bool { if png.ihdr_data.bit_depth != BitDepth::Eight { - return None; + return false; } let mut reduced = Vec::with_capacity(png.raw_data.len()); let mut palette = Vec::with_capacity(256); @@ -104,7 +124,7 @@ pub fn reduce_rgba_to_palette(png: &PngData) -> Option<(Vec, Vec, Vec Option<(Vec, Vec, Vec>(); + if let Some(entry) = color_palette.chunks(3).position(|x| x == header_pixels.as_slice()) { + *bkgd_header = vec![entry as u8]; + } else if color_palette.len() == 255 { + return false; + } else { + let entry = color_palette.len() / 3; + color_palette.extend_from_slice(&header_pixels); + *bkgd_header = vec![entry as u8]; + } + } + if let Some(sbit_header) = png.aux_headers.get_mut(&"sBIT".to_string()) { + assert_eq!(sbit_header.len(), 4); + sbit_header.pop(); + } + + png.raw_data = reduced; + png.palette = Some(color_palette); + if trans_palette.iter().any(|x| *x != 255) { + png.transparency_palette = Some(trans_palette); + } else { + png.transparency_palette = None; + } + png.ihdr_data.color_type = ColorType::Indexed; + true } -pub fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec, Vec)> { +pub fn reduce_rgb_to_palette(png: &mut PngData) -> bool { if png.ihdr_data.bit_depth != BitDepth::Eight { - return None; + return false; } let mut reduced = Vec::with_capacity(png.raw_data.len()); let mut palette = Vec::with_capacity(256); @@ -147,7 +193,7 @@ pub fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec, Vec)> { } else { let len = palette.len(); if len == 256 { - return None; + return false; } palette.push(cur_pixel); reduced.push(len as u8); @@ -162,12 +208,29 @@ pub fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec, Vec)> { color_palette.extend_from_slice(color); } - Some((reduced, color_palette)) + if let Some(bkgd_header) = png.aux_headers.get_mut(&"bKGD".to_string()) { + assert_eq!(bkgd_header.len(), 6); + let header_pixels = bkgd_header.iter().skip(1).step(2).cloned().collect::>(); + if let Some(entry) = color_palette.chunks(3).position(|x| x == header_pixels.as_slice()) { + *bkgd_header = vec![entry as u8]; + } else if color_palette.len() == 255 { + return false; + } else { + let entry = color_palette.len() / 3; + color_palette.extend_from_slice(&header_pixels); + *bkgd_header = vec![entry as u8]; + } + } + + png.raw_data = reduced; + png.palette = Some(color_palette); + png.ihdr_data.color_type = ColorType::Indexed; + true } -pub fn reduce_grayscale_to_palette(png: &PngData) -> Option<(Vec, Vec)> { +pub fn reduce_grayscale_to_palette(png: &mut PngData) -> bool { if png.ihdr_data.bit_depth == BitDepth::Sixteen { - return None; + return false; } let mut reduced = BitVec::with_capacity(png.raw_data.len() * 8); // Only perform reduction if we can get to 4-bits or less @@ -192,7 +255,7 @@ pub fn reduce_grayscale_to_palette(png: &PngData) -> Option<(Vec, Vec)> } else { let len = palette.len(); if len == 16 { - return None; + return false; } palette.push(pix_slice); let idx = BitVec::from_bytes(&[(len as u8) << bpp_inverse]); @@ -214,10 +277,34 @@ pub fn reduce_grayscale_to_palette(png: &PngData) -> Option<(Vec, Vec)> color_palette.extend_from_slice(color); } - Some((reduced.to_bytes(), color_palette)) + if let Some(bkgd_header) = png.aux_headers.get_mut(&"bKGD".to_string()) { + assert_eq!(bkgd_header.len(), 2); + let header_pixels = [bkgd_header[1], bkgd_header[1], bkgd_header[1]]; + if let Some(entry) = color_palette.chunks(3).position(|x| *x == header_pixels) { + *bkgd_header = vec![entry as u8]; + } else if color_palette.len() == 255 { + return false; + } else { + let entry = color_palette.len() / 3; + color_palette.extend_from_slice(&header_pixels); + *bkgd_header = vec![entry as u8]; + } + } + + if let Some(sbit_header) = png.aux_headers.get_mut(&"sBIT".to_string()) { + assert_eq!(sbit_header.len(), 1); + let byte = sbit_header[0]; + sbit_header.push(byte); + sbit_header.push(byte); + } + + png.raw_data = reduced.to_bytes(); + png.palette = Some(color_palette); + png.ihdr_data.color_type = ColorType::Indexed; + true } -pub fn reduce_rgb_to_grayscale(png: &PngData) -> Option> { +pub fn reduce_rgb_to_grayscale(png: &mut PngData) -> bool { let mut reduced = Vec::with_capacity(png.raw_data.len()); let byte_depth: u8 = png.ihdr_data.bit_depth.as_u8() >> 3; let bpp: usize = 3 * byte_depth as usize; @@ -229,7 +316,7 @@ pub fn reduce_rgb_to_grayscale(png: &PngData) -> Option> { if i % bpp == bpp - 1 { if bpp == 3 { if cur_pixel.iter().unique().count() > 1 { - return None; + return false; } reduced.push(cur_pixel[0]); } else { @@ -243,7 +330,7 @@ pub fn reduce_rgb_to_grayscale(png: &PngData) -> Option> { .unique() .collect::>(); if pixel_bytes.len() > 1 { - return None; + return false; } reduced.push(pixel_bytes[0].0); reduced.push(pixel_bytes[0].1); @@ -252,15 +339,31 @@ pub fn reduce_rgb_to_grayscale(png: &PngData) -> Option> { } } } + if let Some(sbit_header) = png.aux_headers.get_mut(&"sBIT".to_string()) { + assert_eq!(sbit_header.len(), 3); + sbit_header.truncate(1); + } + if let Some(bkgd_header) = png.aux_headers.get_mut(&"bKGD".to_string()) { + assert_eq!(bkgd_header.len(), 6); + bkgd_header.truncate(2); + } - Some(reduced) + png.raw_data = reduced; + png.ihdr_data.color_type = ColorType::Grayscale; + true } -pub fn reduce_grayscale_alpha_to_grayscale(png: &PngData) -> Option> { - reduce_alpha_channel(png, 2) +pub fn reduce_grayscale_alpha_to_grayscale(png: &mut PngData) -> bool { + if let Some(reduced) = reduce_alpha_channel(png, 2) { + png.raw_data = reduced; + png.ihdr_data.color_type = ColorType::Grayscale; + true + } else { + false + } } -fn reduce_alpha_channel(png: &PngData, bpp_factor: usize) -> Option> { +fn reduce_alpha_channel(png: &mut PngData, bpp_factor: usize) -> Option> { let mut reduced = Vec::with_capacity(png.raw_data.len()); let byte_depth: u8 = png.ihdr_data.bit_depth.as_u8() >> 3; let bpp: usize = bpp_factor * byte_depth as usize; @@ -277,6 +380,10 @@ fn reduce_alpha_channel(png: &PngData, bpp_factor: usize) -> Option> { } } } + if let Some(sbit_header) = png.aux_headers.get_mut(&"sBIT".to_string()) { + assert_eq!(sbit_header.len(), bpp_factor); + sbit_header.pop(); + } Some(reduced) } diff --git a/tests/files/issue-52-01.png b/tests/files/issue-52-01.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e7f96f359c16552f9bbf9c2483aacb62cf99b1 GIT binary patch literal 1628 zcmV-i2BZ0jP)83nPUm!CU|U03B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00qEFL_t(Y$E}uKh+S6| z$A4?@bI$#kOYTe>h;79p$|R;vCLcPJG&U+Vk-k{k2Kv;ZSWu9n(idNR5PT98BR1F~ z(kCCPR4qYkOHH-S$JF8^)7T*$jo1gZEy+wKGxy`1z4!8ApL_0HrlA!)aJlTe_g?F) z|N8&`>&mT1kJ(V<{QJ~1p8wOI`@xqI#Vz4V()|I+^@$*o6^iM-dL*&qH= zH7MUaItzdg%JJvUk~Er}{n{7a^ZpGF96WJSMTFoSp~%tTdHAz4JpS8LB+VAK-iQHg z%9*d;E$=(Q9Y6VQ6#1sAs>S~A{!GPM z(k&Bl!&pq30g$h*AVvs9ju=bQY=Ia6Op+i;O2~Tz=i=H}UViw9+z_JLwhw61XrkUT zzwjEncTNJJu841LEq{9TJa_J$s-P-Xm&sSIY>Jm`T+IC77Ybl&HD3GPqg4fiCloos zIc&YbJDolu@5KZfnEC!wh_(9C7eDjv5FI>mQbUn*?a~D%Zr_g>%lvn~LdbJMkptj* z9n|}g#0QQ)jjh$nXokcXPCay3HYa%O8NIsj3L=7SjM3gPO~`vy1I4DCedHbh_I~>@ zf^*bv+D_7JvAT2-4W4bgX8^Dwyzt=RVGj2VB7z3b_^y4p zwbjU^BxUvQud{9cVb6gNi()iA7Fgvo>;{j3#cj>L+iGW61>CryHOt-S)`%#xl%O2xrn>5`0je#Ql-6P z8nKquHx^>TT0N@V`-;YDqTr--Au|xwKv?6u_QghJHPC?1`?$^;&6__2!0OUPY^{OG z>JUPNr^rDJCe09QEAf5mr_ZZ7^u)6&Mo`uG@t*a6E}=g50S#4U*Do)ku0X8C)*DEY zmJExQ(cUo~wNm6PFU%pbi&Szeq6AZ@i{qHbpdf0Jg_U3CTR z$tkS07~kvCo}8*mo%r}CDEd5%OMSjE&}J8)MwG=OAJtMT2v^_|T`t zyz=M+f*2;IW}^jY*|ZQUj7^#`F*O@08AA1&vC%n$FIktQm|7ixbC2936^mZ|)`KEO z*z%EGv20g>iB(jM9(YTVM%Ixz*|<3L#Iq_Q6$Ps|wiYphNs<`YN2+S@_sG9r zJ^r-bFacs%UAlQR;6lhm6->6exGexKl6MTCJ9T>sl&D+Rynz=uEZ*4Qf}2!b!I{~u31 zo){6~&0jy6NRq}uQNFpl0u7$+cO2p719y$k)HcRQnynbLF-DSQSQGC#AOFi<{`rqB a8uY)nH1cS{{bM%(0000PatU37J(Rr zgaj0^Br7CTsj}<;fE9=xLIPAasFWoO1QKaFC~d4}Q_Dzh9Z{!gX+oPcgJVCxdsui# zZ9ix9L-PiiqE7+xdKc(%p-JbUGa| z#+)Z4^1kn{IF2(IhV8kzxeJq%ldtYtfOI+?>Fw=354_Pqzv}RPe>p7j`FwtSa&qzr zpk_A)(&=<08jb!0yxCFDswkJsp^?9R`*w{y06P{Sl}h#W_4S<-k+(YPSrz4SdC_&< zFv#o7%*^jsuU309AEjvDjJQoe&(v^Ssc=XS3Oh)6>&01L%Q$*wK;tGYLMCUX=(2m1rpIhz?tr_&M7^L}$2=UAs51W;9RT{jwnUA=z&`cgKVeF1=>0rb!? zvbqlaQE)1mFAta7M?kIGtLlrLcChLsBGPfYzV9-2WS(67tUWzEfXSC4Uz9pyM6_W#7lyrc*mXvE=c`#Y-$LRLLJ2 zMu!IK^UXQm9DHtNkvh3t?t9?Fj(bQY>|JNmhO;XaiXP(=6Ra#g06<4a+P1sO0<8-o zR>4|aRdsINy7irieAICdsgMsN?AO*yj89Cky7aIz;5umE)*V#@uoh zB9S9NK4^orHtPHS!P;-nYD-u>ZrnA3=T{aurus1eBVY_z-N;XCF$$(CkmoCHOyh_k z0%fa|t!3)kRSL_GKm>be7_(B?k~{!qADLT#q6dJR$z(p=-p>CI77L|1c^w^rfj(#> zfQU4RDsv0q+xqX$$L4%lU%>G_J&JY%Mp$mP{u9d3>;|Yd~|4 zXQhCdU#zr%#zX%vkx1mdQ>RX~92UA-fX8cqs$%Xx3|jVMEEfBCY;3Hh2SS&pKIt;j={MnDa+)*AMz`c)#4_-s4mp-F%Py#}{UOc+ziw`cb|*q~bn z;B0add3vDz>|n&uV}$31+W$ZH1@!)Ah{)%OL?UtK%$Y6M*3j_9epl#sH-EVqjBJ)Q z?VUW>zx9jIP;}QBsUmVJ9*=*y(~Vu70#*2``bi>@_;xp&yIp{ih@4C$66beopqm9) z1Ky3t~$z<~KUTysgZ6aAg-}bNk00000NkvXX Hu0mjfsuI}P literal 0 HcmV?d00001 diff --git a/tests/files/issue-52-03.png b/tests/files/issue-52-03.png new file mode 100644 index 0000000000000000000000000000000000000000..d7648d874738c713aa262fe5efc825e65276854b GIT binary patch literal 1808 zcmV+r2k-caP)Q4gtIgM8bzcTey@9;)KM7e}DrAp!v~;B#o?4K~*)ZohpeF(!}}Y*jev-=W(!; zcw2dG(>yynJNEG5!^`^(o!;FWsCx!xip63- z5Ye@IZTZQz?JZr`yA_!B!f6kIsXXhcYXueHe5(qMloGnGD~UPLXf(A4hucGtJ)Hmn z1TUmxuOMcoxw*M-i0J(~o%NNJ5<)aS3t?tNqfwfho9h7baEfD2sg4Dx0Ot-fe{f7c z50xr7Qc6UlQ9OP6^q{e|rSb#Jd@Mlmc>E@S50CR~HMvqkQvk4Y^A=NCc-l@5da&+{!`JS}{ok>*wgR94=X6`;2a0!>WPocdHB=U! zPEHM+YzZ6$1K?Bo!GQAc0{|eRd=0j=2Dq~N3VQVD(a+4>3&5>3hM7Hkqr%*I2MYZ} z1VMygpz&~Wxg5sFy(NPF`Uj0a7UFpwY_&M z8OYimI6#FJWSVA}h;G;I@d;`>wS&=-VOW-R&?t&mcF8K=`^f-F1|?R3Y}q#4DqBIO zX@1gda8@@{7#_ZxhocGb8UM@1(j*W7ASGz=8L*oz?b#rrKFaPY89`~U=How8tD&>S;g4Tr<=y@o(M9{)%M94>k@>{ArhqlJ|urpL!1rJIM+ z(*@c}7tQCtoe2cCY+!LUe`tH6B9Q8Sf!(jq@ySus3i&9`&*R~ z&ytYR1@s{J#cqJPw)rv^L~_dy&Wx1ujiI5T)zXdtfKx|tu9_n!mNE(fdGTz1d--n{ zuNa`!O~0OLUDx~i`}^1ZIz6XY+F_(@>1$sTgzi>>#U0n@1?1JD0p~*Halpzk*0cV0 z0pNT{!%D`*uIq1DBf_Qj61zlsI0YTLjd!!4RJyF0nePc9-tO=3PgQ!<18CcpNbLBR zTtNYJx9Uh*5>ADGq)it%=aabZx{Rd?b^)L#q+!dJNZI~&1HuPTu}TgBPkNZPKuc?7 zrGa^;iu>Sh0?peZ~*+2PN!d~1_x+rK-Eu1%4GfE{AgL$yOBub zu!Ct|O$kV^cJWwVHUI#K=sU}@u0XD9vq;V0hNe$cKw=}i24n~xN}^)%?oHR_A&#{HvC3NW`1vA zVBq_DrfFV4K>l)lc@0Q=44P_;9y;FfF=dxy6$6uU#7&W--)$JMDG}eajy|` zH6m?ODTH?1_owp8CxMMS??mKBafA`Q1)s(%3rrv>UkPk0~z0000RR%P=vF< zBeIx*fm;}a85w5HkpK#^mw5WRvOnNv7dO>bFE;B03dv@MM3gw^=jNv7l>oU649-QV zi6yBi3gww484B*6z5ywEsq8>;4Nn)x5R22vI=PWw<}-5csQhX4fO+?}T(^hDh5;OI hyb`7fR~il`F)*}Qv+g?Zx6Tk`n5V0s%Q~loCIFG;H2VMm literal 0 HcmV?d00001 diff --git a/tests/files/issue-52-05.png b/tests/files/issue-52-05.png new file mode 100644 index 0000000000000000000000000000000000000000..929a0ec83409df44b702bfbaf9aa3f0c1e42455d GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)2UF&M~|RR%P=vF< zBeIx*fm;}a85w5HkpK#^mw5WRvOnNv7dI0;6}0CEP)Ig2B%;JQKQ}iuuLQ_tU~n!< zO)N=GQ7F$W$xv|j^bJVSOJxU&Yk9gjhFF|V*2#_hGQW}Oz!3*Ui3(RR%P=vF< zBeIx*fm;}a85w5HkpK#^mw5WRvOnNv7dMlNZAdKu3dv@MM3gw^=jNv7l>oU649-QV zi6yBi3gww484B*6z5ywEsq8@U3{Mxw5R22vDF+z6qQ(BYKP$g}#4suNxJOS~O`Czx zYRTz}JxelTYtV37z%9w-v2jah#MvEBW=LqT_#Cki to-gyX(PL`H5-+DkUO~&e(v(cCnHl)=+}C}aIL8F!7*AI}mvv4FO#lLtPe1?w literal 0 HcmV?d00001 diff --git a/tests/filters.rs b/tests/filters.rs index f0dd9834..4e04be7d 100644 --- a/tests/filters.rs +++ b/tests/filters.rs @@ -1183,20 +1183,3 @@ fn filter_5_for_palette_1() { ColorType::Indexed, BitDepth::One); } - -#[test] -fn issue_29() { - let input = PathBuf::from("tests/files/issue-29.png"); - let mut opts = get_opts(&input); - opts.filter = HashSet::new(); - opts.filter.insert(0); - let output = opts.out_file.clone(); - - test_it_converts(&input, - &output, - &opts, - ColorType::RGB, - BitDepth::Eight, - ColorType::RGB, - BitDepth::Eight); -} diff --git a/tests/flags.rs b/tests/flags.rs index 77bb3164..2ebac551 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -491,47 +491,6 @@ fn fix_errors() { remove_file(output).ok(); } -#[test] -fn issue_42() { - let input = PathBuf::from("tests/files/issue_42.png"); - let mut opts = get_opts(&input); - opts.interlace = Some(1); - let output = opts.out_file.clone(); - - let png = png::PngData::new(&input, opts.fix_errors).unwrap(); - - assert_eq!(png.ihdr_data.interlaced, 0); - assert_eq!(png.ihdr_data.color_type, ColorType::GrayscaleAlpha); - assert_eq!(png.ihdr_data.bit_depth, BitDepth::Eight); - - match oxipng::optimize(&input, &opts) { - Ok(_) => (), - Err(x) => panic!(x.description().to_owned()), - }; - assert!(output.exists()); - - let png = match png::PngData::new(&output, opts.fix_errors) { - Ok(x) => x, - Err(x) => { - remove_file(output).ok(); - panic!(x.description().to_owned()) - } - }; - - assert_eq!(png.ihdr_data.interlaced, 1); - assert_eq!(png.ihdr_data.color_type, ColorType::GrayscaleAlpha); - assert_eq!(png.ihdr_data.bit_depth, BitDepth::Eight); - - let old_png = image::open(&input).unwrap(); - let new_png = image::open(&output).unwrap(); - - // Conversion should be lossless - assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>(), - new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); - - remove_file(output).ok(); -} - #[test] fn zopfli_mode() { let input = PathBuf::from("tests/files/zopfli_mode.png"); diff --git a/tests/regression.rs b/tests/regression.rs new file mode 100644 index 00000000..9b03b296 --- /dev/null +++ b/tests/regression.rs @@ -0,0 +1,223 @@ +extern crate image; +extern crate oxipng; + +use image::GenericImage; +use image::Pixel; +use oxipng::colors::{BitDepth, ColorType}; +use oxipng::png; +use std::collections::HashSet; +use std::error::Error; +use std::fs::remove_file; +use std::path::Path; +use std::path::PathBuf; + +fn get_opts(input: &Path) -> oxipng::Options { + let mut options = oxipng::Options::default(); + options.out_file = input.with_extension("out.png").to_owned(); + options.verbosity = None; + options.force = true; + let mut filter = HashSet::new(); + filter.insert(0); + options.filter = filter; + + options +} + +fn test_it_converts(input: &Path, + output: &Path, + opts: &oxipng::Options, + color_type_in: ColorType, + bit_depth_in: BitDepth, + color_type_out: ColorType, + bit_depth_out: BitDepth) { + let png = png::PngData::new(input, opts.fix_errors).unwrap(); + + assert_eq!(png.ihdr_data.color_type, color_type_in); + assert_eq!(png.ihdr_data.bit_depth, bit_depth_in); + + match oxipng::optimize(input, opts) { + Ok(_) => (), + Err(x) => panic!(x.description().to_owned()), + }; + assert!(output.exists()); + + let png = match png::PngData::new(output, opts.fix_errors) { + Ok(x) => x, + Err(x) => { + remove_file(output).ok(); + panic!(x.description().to_owned()) + } + }; + + assert_eq!(png.ihdr_data.color_type, color_type_out); + assert_eq!(png.ihdr_data.bit_depth, bit_depth_out); + + let old_png = image::open(input).unwrap(); + let new_png = image::open(output).unwrap(); + + // Conversion should be lossless + assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>(), + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn issue_29() { + let input = PathBuf::from("tests/files/issue-29.png"); + let mut opts = get_opts(&input); + opts.filter = HashSet::new(); + opts.filter.insert(0); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + ColorType::RGB, + BitDepth::Eight, + ColorType::RGB, + BitDepth::Eight); +} + +#[test] +fn issue_42() { + let input = PathBuf::from("tests/files/issue_42.png"); + let mut opts = get_opts(&input); + opts.interlace = Some(1); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); + + assert_eq!(png.ihdr_data.interlaced, 0); + assert_eq!(png.ihdr_data.color_type, ColorType::GrayscaleAlpha); + assert_eq!(png.ihdr_data.bit_depth, BitDepth::Eight); + + match oxipng::optimize(&input, &opts) { + Ok(_) => (), + Err(x) => panic!(x.description().to_owned()), + }; + assert!(output.exists()); + + let png = match png::PngData::new(&output, opts.fix_errors) { + Ok(x) => x, + Err(x) => { + remove_file(output).ok(); + panic!(x.description().to_owned()) + } + }; + + assert_eq!(png.ihdr_data.interlaced, 1); + assert_eq!(png.ihdr_data.color_type, ColorType::GrayscaleAlpha); + assert_eq!(png.ihdr_data.bit_depth, BitDepth::Eight); + + let old_png = image::open(&input).unwrap(); + let new_png = image::open(&output).unwrap(); + + // Conversion should be lossless + assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>(), + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn issue_52_01() { + let input = PathBuf::from("tests/files/issue-52-01.png"); + let mut opts = get_opts(&input); + opts.filter = HashSet::new(); + opts.filter.insert(0); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + ColorType::RGBA, + BitDepth::Eight, + ColorType::Indexed, + BitDepth::Eight); +} + +#[test] +fn issue_52_02() { + let input = PathBuf::from("tests/files/issue-52-02.png"); + let mut opts = get_opts(&input); + opts.filter = HashSet::new(); + opts.filter.insert(0); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + ColorType::RGBA, + BitDepth::Eight, + ColorType::Indexed, + BitDepth::Eight); +} + +#[test] +fn issue_52_03() { + let input = PathBuf::from("tests/files/issue-52-03.png"); + let mut opts = get_opts(&input); + opts.filter = HashSet::new(); + opts.filter.insert(0); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + ColorType::RGBA, + BitDepth::Eight, + ColorType::Indexed, + BitDepth::Eight); +} + +#[test] +fn issue_52_04() { + let input = PathBuf::from("tests/files/issue-52-04.png"); + let mut opts = get_opts(&input); + opts.filter = HashSet::new(); + opts.filter.insert(0); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + ColorType::RGBA, + BitDepth::Eight, + ColorType::Indexed, + BitDepth::One); +} + +#[test] +fn issue_52_05() { + let input = PathBuf::from("tests/files/issue-52-05.png"); + let mut opts = get_opts(&input); + opts.filter = HashSet::new(); + opts.filter.insert(0); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + ColorType::RGBA, + BitDepth::Eight, + ColorType::Indexed, + BitDepth::One); +} + +#[test] +fn issue_52_06() { + let input = PathBuf::from("tests/files/issue-52-06.png"); + let mut opts = get_opts(&input); + opts.filter = HashSet::new(); + opts.filter.insert(0); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + ColorType::RGBA, + BitDepth::Eight, + ColorType::Indexed, + BitDepth::Two); +}