diff --git a/Cargo.lock b/Cargo.lock index d7941738..745c3562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "bytemuck" version = "1.12.1" @@ -194,6 +206,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "glob" version = "0.3.0" @@ -363,6 +381,7 @@ name = "oxipng" version = "6.0.1" dependencies = [ "bit-vec", + "bitvec", "clap", "crossbeam-channel", "filetime", @@ -391,6 +410,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rayon" version = "1.5.3" @@ -472,6 +497,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "termcolor" version = "1.1.3" @@ -585,6 +616,15 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + [[package]] name = "zopfli" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index c087d365..71031c44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ libdeflater = "0.11.0" log = "0.4.17" stderrlog = { version = "0.5.3", optional = true, default-features = false } crossbeam-channel = "0.5.6" +bitvec = "1.0.1" [dependencies.filetime] optional = true diff --git a/src/filters.rs b/src/filters.rs index 100f629a..c0adf872 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -14,6 +14,7 @@ pub enum RowFilter { // Heuristic strategies MinSum, Entropy, + Bigrams, } impl TryFrom for RowFilter { @@ -40,13 +41,14 @@ impl Display for RowFilter { Self::Paeth => "Paeth", Self::MinSum => "MinSum", Self::Entropy => "Entropy", + Self::Bigrams => "Bigrams", } ) } } impl RowFilter { - pub const LAST: u8 = Self::Entropy as u8; + pub const LAST: u8 = Self::Bigrams as u8; pub const STANDARD: [Self; 5] = [Self::None, Self::Sub, Self::Up, Self::Average, Self::Paeth]; pub const SINGLE_LINE: [Self; 2] = [Self::None, Self::Sub]; diff --git a/src/png/mod.rs b/src/png/mod.rs index 59ed44d6..6641161d 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -4,6 +4,7 @@ use crate::error::PngError; use crate::filters::*; use crate::headers::*; use crate::interlace::{deinterlace_image, interlace_image}; +use bitvec::bitarr; use indexmap::IndexMap; use rgb::ComponentSlice; use rgb::RGBA8; @@ -367,6 +368,24 @@ impl PngImage { } } } + RowFilter::Bigrams => { + // Count distinct bigrams, from pngwolf + // https://bjoern.hoehrmann.de/pngwolf/ + let mut best_size = usize::MAX; + for try_filter in try_filters { + try_filter.filter_line(bpp, line.data, last_line, &mut f_buf); + let mut set = bitarr![0; 0x10000]; + for i in 1..f_buf.len() { + let bigram = (f_buf[i - 1] as usize) << 8 | f_buf[i] as usize; + set.set(bigram, true); + } + let size = set.count_ones(); + if size < best_size { + best_size = size; + std::mem::swap(&mut best_line, &mut f_buf); + } + } + } _ => unreachable!(), } filtered.extend_from_slice(&best_line);