oxipng/src/reduction/bit_depth.rs
2018-10-11 08:29:48 -04:00

84 lines
2.6 KiB
Rust

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);
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;
}
}
}
}
}
for line in png.scan_lines() {
reduced.extend(BitVec::from_bytes(&[line.filter]));
let bit_vec = BitVec::from_bytes(&line.data);
for (i, bit) in bit_vec.iter().enumerate() {
let bit_index = bit_depth - (i % bit_depth);
if bit_index <= allowed_bits {
reduced.push(bit);
}
}
// Pad end of line to get 8 bits per byte
while reduced.len() % 8 != 0 {
reduced.push(false);
}
}
png.raw_data = reduced.to_bytes();
png.ihdr_data.bit_depth = BitDepth::from_u8(allowed_bits as u8);
true
}