Dynamically set eval filters
This commit is contained in:
parent
a330998e4e
commit
301b413dc5
2 changed files with 18 additions and 15 deletions
|
|
@ -11,6 +11,7 @@ use crate::rayon;
|
||||||
use crate::Deadline;
|
use crate::Deadline;
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
|
use indexmap::IndexSet;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
#[cfg(not(feature = "parallel"))]
|
#[cfg(not(feature = "parallel"))]
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
@ -18,10 +19,6 @@ use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::atomic::Ordering::SeqCst;
|
use std::sync::atomic::Ordering::SeqCst;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Must use normal (lazy) compression, as faster ones (greedy) are not representative
|
|
||||||
const STD_COMPRESSION: u8 = 5;
|
|
||||||
const STD_FILTERS: [RowFilter; 2] = [RowFilter::None, RowFilter::MinSum];
|
|
||||||
|
|
||||||
pub struct Candidate {
|
pub struct Candidate {
|
||||||
pub image: PngData,
|
pub image: PngData,
|
||||||
pub filter: RowFilter,
|
pub filter: RowFilter,
|
||||||
|
|
@ -45,6 +42,8 @@ impl Candidate {
|
||||||
/// Collect image versions and pick one that compresses best
|
/// Collect image versions and pick one that compresses best
|
||||||
pub(crate) struct Evaluator {
|
pub(crate) struct Evaluator {
|
||||||
deadline: Arc<Deadline>,
|
deadline: Arc<Deadline>,
|
||||||
|
filters: IndexSet<RowFilter>,
|
||||||
|
compression: u8,
|
||||||
nth: AtomicUsize,
|
nth: AtomicUsize,
|
||||||
best_candidate_size: Arc<AtomicMin>,
|
best_candidate_size: Arc<AtomicMin>,
|
||||||
/// images are sent to the caller thread for evaluation
|
/// images are sent to the caller thread for evaluation
|
||||||
|
|
@ -56,11 +55,13 @@ pub(crate) struct Evaluator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Evaluator {
|
impl Evaluator {
|
||||||
pub fn new(deadline: Arc<Deadline>) -> Self {
|
pub fn new(deadline: Arc<Deadline>, filters: IndexSet<RowFilter>, compression: u8) -> Self {
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
let eval_channel = unbounded();
|
let eval_channel = unbounded();
|
||||||
Self {
|
Self {
|
||||||
deadline,
|
deadline,
|
||||||
|
filters,
|
||||||
|
compression,
|
||||||
best_candidate_size: Arc::new(AtomicMin::new(None)),
|
best_candidate_size: Arc::new(AtomicMin::new(None)),
|
||||||
nth: AtomicUsize::new(0),
|
nth: AtomicUsize::new(0),
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
|
|
@ -98,13 +99,15 @@ impl Evaluator {
|
||||||
let nth = self.nth.fetch_add(1, SeqCst);
|
let nth = self.nth.fetch_add(1, SeqCst);
|
||||||
// These clones are only cheap refcounts
|
// These clones are only cheap refcounts
|
||||||
let deadline = self.deadline.clone();
|
let deadline = self.deadline.clone();
|
||||||
|
let filters = self.filters.clone();
|
||||||
|
let compression = self.compression;
|
||||||
let best_candidate_size = self.best_candidate_size.clone();
|
let best_candidate_size = self.best_candidate_size.clone();
|
||||||
// sends it off asynchronously for compression,
|
// sends it off asynchronously for compression,
|
||||||
// but results will be collected via the message queue
|
// but results will be collected via the message queue
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
let eval_send = self.eval_channel.0.clone();
|
let eval_send = self.eval_channel.0.clone();
|
||||||
rayon::spawn(move || {
|
rayon::spawn(move || {
|
||||||
let filters_iter = STD_FILTERS.par_iter().with_max_len(1);
|
let filters_iter = filters.par_iter().with_max_len(1);
|
||||||
|
|
||||||
// Updating of best result inside the parallel loop would require locks,
|
// Updating of best result inside the parallel loop would require locks,
|
||||||
// which are dangerous to do in side Rayon's loop.
|
// which are dangerous to do in side Rayon's loop.
|
||||||
|
|
@ -116,7 +119,7 @@ impl Evaluator {
|
||||||
}
|
}
|
||||||
if let Ok(idat_data) = deflate::deflate(
|
if let Ok(idat_data) = deflate::deflate(
|
||||||
&image.filter_image(filter),
|
&image.filter_image(filter),
|
||||||
STD_COMPRESSION,
|
compression,
|
||||||
&best_candidate_size,
|
&best_candidate_size,
|
||||||
) {
|
) {
|
||||||
best_candidate_size.set_min(idat_data.len());
|
best_candidate_size.set_min(idat_data.len());
|
||||||
|
|
|
||||||
16
src/lib.rs
16
src/lib.rs
|
|
@ -484,22 +484,22 @@ fn optimize_png(
|
||||||
|
|
||||||
let mut filter = opts.filter.clone();
|
let mut filter = opts.filter.clone();
|
||||||
|
|
||||||
if opts.use_heuristics {
|
if filter.is_empty() {
|
||||||
// Heuristically determine which set of options to use
|
// Heuristically determine which set of options to use
|
||||||
let use_filter = if png.raw.ihdr.bit_depth.as_u8() >= 8
|
if png.raw.ihdr.bit_depth.as_u8() >= 8
|
||||||
&& png.raw.ihdr.color_type != colors::ColorType::Indexed
|
&& png.raw.ihdr.color_type != colors::ColorType::Indexed
|
||||||
{
|
{
|
||||||
RowFilter::MinSum
|
filter.insert(RowFilter::MinSum);
|
||||||
} else {
|
} else {
|
||||||
RowFilter::None
|
filter.insert(RowFilter::None);
|
||||||
};
|
};
|
||||||
if filter.is_empty() {
|
|
||||||
filter.insert(use_filter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must use normal (lazy) compression, as faster ones (greedy) are not representative
|
||||||
|
let eval_compression = 5;
|
||||||
|
let eval_filters = indexset! {RowFilter::None, RowFilter::MinSum};
|
||||||
// This will collect all versions of images and pick one that compresses best
|
// This will collect all versions of images and pick one that compresses best
|
||||||
let eval = Evaluator::new(deadline.clone());
|
let eval = Evaluator::new(deadline.clone(), eval_filters, eval_compression);
|
||||||
perform_reductions(png.raw.clone(), opts, &deadline, &eval);
|
perform_reductions(png.raw.clone(), opts, &deadline, &eval);
|
||||||
let reduction_occurred = if let Some(result) = eval.get_best_candidate() {
|
let reduction_occurred = if let Some(result) = eval.get_best_candidate() {
|
||||||
*png = result.image;
|
*png = result.image;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue