Significant refactoring using itertools

This commit is contained in:
Josh Holmer 2016-07-14 08:09:58 -04:00
parent 0f790a1604
commit 332c27c7d0
6 changed files with 85 additions and 98 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ target
*.bk
.DS_Store
*.out.png
/.idea

View file

@ -1,3 +1,6 @@
**Version 0.8.3**
- Significant refactoring
**Version 0.8.2**
- Fix issue where images smaller than 4px width would crash on interlacing ([#42](https://github.com/shssoichiro/oxipng/issues/42))

6
Cargo.lock generated
View file

@ -7,6 +7,7 @@ dependencies = [
"clap 2.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"libz-sys 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -124,6 +125,11 @@ name = "inflate"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itertools"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"

View file

@ -26,6 +26,7 @@ bit-vec = "^0.4.2"
byteorder = "^0.5.0"
clap = "^2.2.5"
crc = "^1.2.0"
itertools = "^0.4.16"
libc = "^0.2.4"
libz-sys = "^1.0.0"
miniz-sys = "^0.1.7"

View file

@ -1,6 +1,7 @@
extern crate bit_vec;
extern crate byteorder;
extern crate crc;
extern crate itertools;
extern crate libc;
extern crate libz_sys;
extern crate miniz_sys;

View file

@ -1,6 +1,7 @@
use bit_vec::BitVec;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crc::crc32;
use itertools::Itertools;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::fs::File;
@ -612,80 +613,73 @@ impl PngData {
// Remove duplicates from the data
if !duplicates.is_empty() {
self.do_palette_reduction(&mut duplicates, &mut index_map, &mut indexed_palette);
self.do_palette_reduction(&duplicates, &mut index_map, &mut indexed_palette);
}
// A list of unused palette indices
let mut unused: Vec<u8> = Vec::new();
{
// Find palette entries that are never used
let mut seen = HashSet::with_capacity(indexed_palette.len());
for line in self.scan_lines() {
match self.ihdr_data.bit_depth {
BitDepth::Eight => {
for byte in &line.data {
seen.insert(*byte);
}
// Find palette entries that are never used
let mut seen = HashSet::with_capacity(indexed_palette.len());
for line in self.scan_lines() {
match self.ihdr_data.bit_depth {
BitDepth::Eight => {
for byte in &line.data {
seen.insert(*byte);
}
BitDepth::Four => {
let bitvec = BitVec::from_bytes(&line.data);
let mut current = 0u8;
for (i, bit) in bitvec.iter().enumerate() {
let mod_i = i % 4;
if bit {
current += 2u8.pow(3u32 - mod_i as u32);
}
if mod_i == 3 {
seen.insert(current);
current = 0;
}
}
}
BitDepth::Two => {
let bitvec = BitVec::from_bytes(&line.data);
let mut current = 0u8;
for (i, bit) in bitvec.iter().enumerate() {
let mod_i = i % 2;
if bit {
current += 2u8.pow(1u32 - mod_i as u32);
}
if mod_i == 1 {
seen.insert(current);
current = 0;
}
}
}
_ => unreachable!(),
}
if seen.len() == indexed_palette.len() {
// Exit early if no further possible optimizations
// Check at the end of each line
// Checking after every pixel would be overly expensive
return !duplicates.is_empty();
BitDepth::Four => {
let bitvec = BitVec::from_bytes(&line.data);
let mut current = 0u8;
for (i, bit) in bitvec.iter().enumerate() {
let mod_i = i % 4;
if bit {
current += 2u8.pow(3u32 - mod_i as u32);
}
if mod_i == 3 {
seen.insert(current);
current = 0;
}
}
}
BitDepth::Two => {
let bitvec = BitVec::from_bytes(&line.data);
let mut current = 0u8;
for (i, bit) in bitvec.iter().enumerate() {
let mod_i = i % 2;
if bit {
current += 2u8.pow(1u32 - mod_i as u32);
}
if mod_i == 1 {
seen.insert(current);
current = 0;
}
}
}
_ => unreachable!(),
}
for i in 0..indexed_palette.len() as u8 {
if !seen.contains(&i) {
unused.push(i);
}
if seen.len() == indexed_palette.len() {
// Exit early if no further possible optimizations
// Check at the end of each line
// Checking after every pixel would be overly expensive
return !duplicates.is_empty();
}
}
let unused: Vec<u8> =
(0..indexed_palette.len() as u8).filter(|i| !seen.contains(&i)).collect();
// Remove unused palette indices
self.do_palette_reduction(&mut unused, &mut index_map, &mut indexed_palette);
self.do_palette_reduction(&unused, &mut index_map, &mut indexed_palette);
true
}
fn do_palette_reduction(&mut self,
indices: &mut Vec<u8>,
indices: &[u8],
index_map: &mut HashMap<u8, u8>,
indexed_palette: &mut Vec<&[u8]>) {
let mut new_data = Vec::with_capacity(self.raw_data.len());
let mut alpha_palette = self.aux_headers.get("tRNS").cloned();
let original_len = indexed_palette.len();
indices.sort_by(|a, b| b.cmp(a));
for idx in indices {
for idx in indices.iter().sorted_by(|a, b| b.cmp(a)) {
for i in (*idx as usize + 1)..original_len {
let existing = index_map.entry(i as u8).or_insert(i as u8);
if *existing >= *idx {
@ -769,10 +763,7 @@ impl PngData {
}
index_map.clear();
self.raw_data = new_data;
let mut new_palette = Vec::with_capacity(indexed_palette.len() * 3);
for color in indexed_palette {
new_palette.extend_from_slice(color);
}
let new_palette = indexed_palette.iter().cloned().flatten().cloned().collect::<Vec<u8>>();
self.palette = Some(new_palette);
}
/// Attempt to reduce the color type of the image
@ -881,10 +872,7 @@ impl PngData {
}
fn interlace_image(png: &mut PngData) {
let mut passes: Vec<BitVec> = Vec::with_capacity(7);
for _ in 0..7 {
passes.push(BitVec::new());
}
let mut passes: Vec<BitVec> = vec![BitVec::new(); 7];
let bits_per_pixel = png.ihdr_data.bit_depth.as_u8() * png.channels_per_pixel();
for (index, line) in png.scan_lines().enumerate() {
match index % 8 {
@ -971,13 +959,11 @@ fn interlace_image(png: &mut PngData) {
fn deinterlace_image(png: &mut PngData) {
let bits_per_pixel = png.ihdr_data.bit_depth.as_u8() * png.channels_per_pixel();
let mut lines: Vec<BitVec> = Vec::with_capacity(png.ihdr_data.height as usize);
for _ in 0..png.ihdr_data.height {
// Initialize each output line with a starting filter byte of 0
// as well as some blank data
lines.push(BitVec::from_elem(8 + bits_per_pixel as usize * png.ihdr_data.width as usize,
false));
}
let bits_per_line = 8 + bits_per_pixel as usize * png.ihdr_data.width as usize;
// Initialize each output line with a starting filter byte of 0
// as well as some blank data
let mut lines: Vec<BitVec> =
vec![BitVec::from_elem(bits_per_line, false); png.ihdr_data.height as usize];
let mut current_pass = 1;
let mut pass_constants = interlaced_constants(current_pass);
let mut current_y: usize = pass_constants.y_shift as usize;
@ -1024,6 +1010,7 @@ fn deinterlace_image(png: &mut PngData) {
png.raw_data = output;
}
#[derive(Clone, Copy)]
struct InterlacedConstants {
x_shift: u8,
y_shift: u8,
@ -1321,15 +1308,11 @@ fn reduce_rgba_to_grayscale_alpha(png: &PngData) -> Option<Vec<u8>> {
}
if i % bpp == bpp - 1 {
low_bytes.sort();
low_bytes.dedup();
if low_bytes.len() > 1 {
if low_bytes.iter().unique().count() > 1 {
return None;
}
if byte_depth == 2 {
high_bytes.sort();
high_bytes.dedup();
if high_bytes.len() > 1 {
if high_bytes.iter().unique().count() > 1 {
return None;
}
reduced.push(high_bytes[0]);
@ -1359,8 +1342,7 @@ fn reduce_rgba_to_palette(png: &PngData) -> Option<(Vec<u8>, Vec<u8>, Vec<u8>)>
for (i, byte) in line.data.iter().enumerate() {
cur_pixel.push(*byte);
if i % bpp == bpp - 1 {
if palette.contains(&cur_pixel) {
let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0;
if let Some(idx) = palette.iter().position(|x| x == &cur_pixel) {
reduced.push(idx as u8);
} else {
let len = palette.len();
@ -1403,8 +1385,7 @@ fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec<u8>, Vec<u8>)> {
for (i, byte) in line.data.iter().enumerate() {
cur_pixel.push(*byte);
if i % bpp == bpp - 1 {
if palette.contains(&cur_pixel) {
let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0;
if let Some(idx) = palette.iter().position(|x| x == &cur_pixel) {
reduced.push(idx as u8);
} else {
let len = palette.len();
@ -1487,9 +1468,7 @@ fn reduce_palette_to_grayscale(png: &PngData) -> Option<Vec<u8>> {
for byte in &palette {
cur_pixel.push(*byte);
if cur_pixel.len() == 3 {
cur_pixel.sort();
cur_pixel.dedup();
if cur_pixel.len() > 1 {
if cur_pixel.iter().unique().count() > 1 {
return None;
}
cur_pixel.clear();
@ -1536,29 +1515,25 @@ fn reduce_rgb_to_grayscale(png: &PngData) -> Option<Vec<u8>> {
cur_pixel.push(*byte);
if i % bpp == bpp - 1 {
if bpp == 3 {
cur_pixel.sort();
cur_pixel.dedup();
if cur_pixel.len() > 1 {
if cur_pixel.iter().unique().count() > 1 {
return None;
}
reduced.push(cur_pixel[0]);
} else {
let mut pixel_zip = cur_pixel.iter()
.enumerate()
.filter(|&(i, _)| i % 2 == 0)
.map(|(_, x)| *x)
let pixel_bytes = cur_pixel.iter()
.step(2)
.cloned()
.zip(cur_pixel.iter()
.enumerate()
.filter(|&(i, _)| i % 2 == 1)
.map(|(_, x)| *x))
.skip(1)
.step(2)
.cloned())
.unique()
.collect::<Vec<(u8, u8)>>();
pixel_zip.sort();
pixel_zip.dedup();
if pixel_zip.len() > 1 {
if pixel_bytes.len() > 1 {
return None;
}
reduced.push(pixel_zip[0].0);
reduced.push(pixel_zip[0].1);
reduced.push(pixel_bytes[0].0);
reduced.push(pixel_bytes[0].1);
}
cur_pixel.clear();
}