Add brute filter
This commit is contained in:
parent
48453db292
commit
19030b8c6a
2 changed files with 34 additions and 1 deletions
|
|
@ -16,6 +16,7 @@ pub enum RowFilter {
|
||||||
Entropy,
|
Entropy,
|
||||||
Bigrams,
|
Bigrams,
|
||||||
BigEnt,
|
BigEnt,
|
||||||
|
Brute,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u8> for RowFilter {
|
impl TryFrom<u8> for RowFilter {
|
||||||
|
|
@ -44,13 +45,14 @@ impl Display for RowFilter {
|
||||||
Self::Entropy => "Entropy",
|
Self::Entropy => "Entropy",
|
||||||
Self::Bigrams => "Bigrams",
|
Self::Bigrams => "Bigrams",
|
||||||
Self::BigEnt => "BigEnt",
|
Self::BigEnt => "BigEnt",
|
||||||
|
Self::Brute => "Brute",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RowFilter {
|
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 STANDARD: [Self; 5] = [Self::None, Self::Sub, Self::Up, Self::Average, Self::Paeth];
|
||||||
pub const SINGLE_LINE: [Self; 2] = [Self::None, Self::Sub];
|
pub const SINGLE_LINE: [Self; 2] = [Self::None, Self::Sub];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use crate::headers::*;
|
||||||
use crate::interlace::{deinterlace_image, interlace_image};
|
use crate::interlace::{deinterlace_image, interlace_image};
|
||||||
use bitvec::bitarr;
|
use bitvec::bitarr;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use libdeflater::{CompressionLvl, Compressor};
|
||||||
use rgb::ComponentSlice;
|
use rgb::ComponentSlice;
|
||||||
use rgb::RGBA8;
|
use rgb::RGBA8;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
@ -19,6 +20,11 @@ pub(crate) mod scan_lines;
|
||||||
|
|
||||||
use self::scan_lines::{ScanLines, ScanLinesMut};
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PngImage {
|
pub struct PngImage {
|
||||||
/// The headers stored in the IHDR chunk
|
/// 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!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
filtered.extend_from_slice(&best_line);
|
filtered.extend_from_slice(&best_line);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue