From 19030b8c6a7cde7d8f1f185b6a3f7a426cc33448 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 5 Nov 2022 14:48:24 +1300 Subject: [PATCH] Add brute filter --- src/filters.rs | 4 +++- src/png/mod.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/filters.rs b/src/filters.rs index 5cf84eda..ec62e8fe 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -16,6 +16,7 @@ pub enum RowFilter { Entropy, Bigrams, BigEnt, + Brute, } impl TryFrom for RowFilter { @@ -44,13 +45,14 @@ impl Display for RowFilter { Self::Entropy => "Entropy", Self::Bigrams => "Bigrams", Self::BigEnt => "BigEnt", + Self::Brute => "Brute", } ) } } impl RowFilter { - pub const LAST: u8 = Self::BigEnt as u8; + pub const LAST: u8 = Self::Brute 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 2867d997..30668eea 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -6,6 +6,7 @@ use crate::headers::*; use crate::interlace::{deinterlace_image, interlace_image}; use bitvec::bitarr; use indexmap::IndexMap; +use libdeflater::{CompressionLvl, Compressor}; use rgb::ComponentSlice; use rgb::RGBA8; use rustc_hash::FxHashMap; @@ -19,6 +20,11 @@ pub(crate) mod scan_lines; use self::scan_lines::{ScanLines, ScanLinesMut}; +/// Compression level to use for the Brute filter strategy +const BRUTE_LEVEL: i32 = 1; // 1 is fastest, 2-4 are not useful, 5 is slower but more effective +/// Number of lines to compress with the Brute filter strategy +const BRUTE_LINES: usize = 4; // Values over 8 are generally not useful + #[derive(Debug, Clone)] pub struct PngImage { /// The headers stored in the IHDR chunk @@ -406,6 +412,31 @@ impl PngImage { } } } + RowFilter::Brute => { + // Brute force by compressing each filter attempt + // Similar to that of LodePNG but includes some previous lines for context + let mut best_size = usize::MAX; + let line_start = filtered.len(); + filtered.resize(filtered.len() + line.data.len() + 1, 0); + let mut compressor = + Compressor::new(CompressionLvl::new(BRUTE_LEVEL).unwrap()); + let limit = filtered.len().min((line.data.len() + 1) * BRUTE_LINES); + let capacity = compressor.zlib_compress_bound(limit); + let mut dest = vec![0; capacity]; + + for try_filter in try_filters { + try_filter.filter_line(bpp, line.data, last_line, &mut f_buf); + filtered[line_start..].copy_from_slice(&f_buf); + let size = compressor + .zlib_compress(&filtered[filtered.len() - limit..], &mut dest) + .unwrap_or(usize::MAX); + if size < best_size { + best_size = size; + std::mem::swap(&mut best_line, &mut f_buf); + } + } + filtered.resize(line_start, 0); + } _ => unreachable!(), } filtered.extend_from_slice(&best_line);