diff --git a/src/reduction/bit_depth.rs b/src/reduction/bit_depth.rs index df99973c..ee3f0665 100644 --- a/src/reduction/bit_depth.rs +++ b/src/reduction/bit_depth.rs @@ -2,23 +2,62 @@ use bit_vec::BitVec; use colors::{BitDepth, ColorType}; use png::PngData; +const ONE_BIT_PERMUTATIONS: [u8; 2] = [0b0000_0000, 0b1111_1111]; +const TWO_BIT_PERMUTATIONS: [u8; 5] = [ + 0b0000_0000, + 0b0000_1111, + 0b0011_1100, + 0b1111_0000, + 0b1111_1111, +]; +const FOUR_BIT_PERMUTATIONS: [u8; 11] = [ + 0b0000_0000, + 0b0000_0011, + 0b0000_1100, + 0b0011_0000, + 0b1100_0000, + 0b0000_1111, + 0b0011_1100, + 0b1111_0000, + 0b0011_1111, + 0b1111_1100, + 0b1111_1111, +]; + 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; for line in png.scan_lines() { let bit_vec = BitVec::from_bytes(&line.data); - for (i, bit) in bit_vec.iter().enumerate() { - let bit_index = if png.ihdr_data.color_type == ColorType::Indexed { - bit_depth - (i % bit_depth) - } else { - i % bit_depth - }; - if bit && bit_index > allowed_bits { - allowed_bits = bit_index.next_power_of_two(); - if allowed_bits == bit_depth { - // Not reducable - return false; + if png.ihdr_data.color_type == ColorType::Indexed { + for (i, bit) in bit_vec.iter().enumerate() { + let bit_index = bit_depth - (i % bit_depth); + if bit && bit_index > allowed_bits { + allowed_bits = bit_index.next_power_of_two(); + if allowed_bits == bit_depth { + // Not reducable + return false; + } + } + } + } else { + for byte in bit_vec.to_bytes() { + while allowed_bits < bit_depth { + let permutations: &[u8] = if allowed_bits == 1 { + &ONE_BIT_PERMUTATIONS + } else if allowed_bits == 2 { + &TWO_BIT_PERMUTATIONS + } else if allowed_bits == 4 { + &FOUR_BIT_PERMUTATIONS + } else { + return false; + }; + if permutations.iter().any(|perm| *perm == byte) { + break; + } else { + allowed_bits <<= 1; + } } } } diff --git a/tests/files/issue-140.png b/tests/files/issue-140.png new file mode 100644 index 00000000..12e9d2a4 Binary files /dev/null and b/tests/files/issue-140.png differ diff --git a/tests/regression.rs b/tests/regression.rs index 5a1855a8..ea3ecf24 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -536,3 +536,15 @@ fn issue_133_left() { BitDepth::Eight, ); } + +#[test] +fn issue_140() { + test_it_converts( + "tests/files/issue-140.png", + None, + ColorType::Grayscale, + BitDepth::Two, + ColorType::Grayscale, + BitDepth::Two, + ); +}