parent
d417a712d3
commit
681089f8c7
3 changed files with 12 additions and 31 deletions
|
|
@ -24,8 +24,6 @@ use std::thread;
|
||||||
|
|
||||||
struct Candidate {
|
struct Candidate {
|
||||||
image: PngData,
|
image: PngData,
|
||||||
// compressed size multiplier. Fudge factor to prefer more promising formats.
|
|
||||||
bias: f32,
|
|
||||||
// if false, that's baseline file to throw away
|
// if false, that's baseline file to throw away
|
||||||
is_reduction: bool,
|
is_reduction: bool,
|
||||||
filter: u8,
|
filter: u8,
|
||||||
|
|
@ -42,30 +40,16 @@ impl Comparator {
|
||||||
fn evaluate(&mut self, new: Candidate) {
|
fn evaluate(&mut self, new: Candidate) {
|
||||||
// a tie-breaker is required to make evaluation deterministic
|
// a tie-breaker is required to make evaluation deterministic
|
||||||
let is_best = if let Some(ref old) = self.best_result {
|
let is_best = if let Some(ref old) = self.best_result {
|
||||||
// ordering is important - later file gets to use bias over earlier, but not the other way
|
|
||||||
// (this way bias=0 replaces, but doesn't forbid later optimizations)
|
|
||||||
let new_len = (new.image.idat_data.len() as f64
|
|
||||||
* if new.nth > old.nth {
|
|
||||||
f64::from(new.bias)
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
}) as usize;
|
|
||||||
let old_len = (old.image.idat_data.len() as f64
|
|
||||||
* if new.nth < old.nth {
|
|
||||||
f64::from(old.bias)
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
}) as usize;
|
|
||||||
// choose smallest compressed, or if compresses the same, smallest uncompressed, or cheaper filter
|
// choose smallest compressed, or if compresses the same, smallest uncompressed, or cheaper filter
|
||||||
let new = (
|
let new = (
|
||||||
new_len,
|
new.image.idat_data.len(),
|
||||||
new.image.raw.data.len(),
|
new.image.raw.data.len(),
|
||||||
new.image.raw.ihdr.bit_depth,
|
new.image.raw.ihdr.bit_depth,
|
||||||
new.filter,
|
new.filter,
|
||||||
new.nth,
|
new.nth,
|
||||||
);
|
);
|
||||||
let old = (
|
let old = (
|
||||||
old_len,
|
old.image.idat_data.len(),
|
||||||
old.image.raw.data.len(),
|
old.image.raw.data.len(),
|
||||||
old.image.raw.ihdr.bit_depth,
|
old.image.raw.ihdr.bit_depth,
|
||||||
old.filter,
|
old.filter,
|
||||||
|
|
@ -141,17 +125,15 @@ impl Evaluator {
|
||||||
|
|
||||||
/// Set baseline image. It will be used only to measure minimum compression level required
|
/// Set baseline image. It will be used only to measure minimum compression level required
|
||||||
pub fn set_baseline(&self, image: Arc<PngImage>) {
|
pub fn set_baseline(&self, image: Arc<PngImage>) {
|
||||||
self.try_image_inner(image, 1.0, false)
|
self.try_image_inner(image, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the image is smaller than others
|
/// Check if the image is smaller than others
|
||||||
/// Bias is a value in 0..=1 range. Compressed size is multiplied by
|
pub fn try_image(&self, image: Arc<PngImage>) {
|
||||||
/// this fraction when comparing to the best, so 0.95 allows 5% larger size.
|
self.try_image_inner(image, true)
|
||||||
pub fn try_image(&self, image: Arc<PngImage>, bias: f32) {
|
|
||||||
self.try_image_inner(image, bias, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_image_inner(&self, image: Arc<PngImage>, bias: f32, is_reduction: bool) {
|
fn try_image_inner(&self, image: Arc<PngImage>, is_reduction: bool) {
|
||||||
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();
|
||||||
|
|
@ -186,7 +168,6 @@ impl Evaluator {
|
||||||
idat_data,
|
idat_data,
|
||||||
raw: Arc::clone(&image),
|
raw: Arc::clone(&image),
|
||||||
},
|
},
|
||||||
bias,
|
|
||||||
filter,
|
filter,
|
||||||
is_reduction,
|
is_reduction,
|
||||||
nth,
|
nth,
|
||||||
|
|
|
||||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -740,7 +740,7 @@ fn perform_reductions(
|
||||||
if let Some(interlacing) = opts.interlace {
|
if let Some(interlacing) = opts.interlace {
|
||||||
if let Some(reduced) = png.change_interlacing(interlacing) {
|
if let Some(reduced) = png.change_interlacing(interlacing) {
|
||||||
png = Arc::new(reduced);
|
png = Arc::new(reduced);
|
||||||
eval.try_image(png.clone(), 0.);
|
eval.try_image(png.clone());
|
||||||
}
|
}
|
||||||
if deadline.passed() {
|
if deadline.passed() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -750,7 +750,7 @@ fn perform_reductions(
|
||||||
if opts.palette_reduction {
|
if opts.palette_reduction {
|
||||||
if let Some(reduced) = reduced_palette(&png) {
|
if let Some(reduced) = reduced_palette(&png) {
|
||||||
png = Arc::new(reduced);
|
png = Arc::new(reduced);
|
||||||
eval.try_image(png.clone(), 0.95);
|
eval.try_image(png.clone());
|
||||||
if opts.verbosity == Some(1) {
|
if opts.verbosity == Some(1) {
|
||||||
report_reduction(&png);
|
report_reduction(&png);
|
||||||
}
|
}
|
||||||
|
|
@ -765,13 +765,13 @@ fn perform_reductions(
|
||||||
let previous = png.clone();
|
let previous = png.clone();
|
||||||
let bits = reduced.ihdr.bit_depth;
|
let bits = reduced.ihdr.bit_depth;
|
||||||
png = Arc::new(reduced);
|
png = Arc::new(reduced);
|
||||||
eval.try_image(png.clone(), 1.0);
|
eval.try_image(png.clone());
|
||||||
if (bits == BitDepth::One || bits == BitDepth::Two)
|
if (bits == BitDepth::One || bits == BitDepth::Two)
|
||||||
&& previous.ihdr.bit_depth != BitDepth::Four
|
&& previous.ihdr.bit_depth != BitDepth::Four
|
||||||
{
|
{
|
||||||
// Also try 16-color mode for all lower bits images, since that may compress better
|
// Also try 16-color mode for all lower bits images, since that may compress better
|
||||||
if let Some(reduced) = reduce_bit_depth(&previous, 4) {
|
if let Some(reduced) = reduce_bit_depth(&previous, 4) {
|
||||||
eval.try_image(Arc::new(reduced), 0.98);
|
eval.try_image(Arc::new(reduced));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if opts.verbosity == Some(1) {
|
if opts.verbosity == Some(1) {
|
||||||
|
|
@ -786,7 +786,7 @@ fn perform_reductions(
|
||||||
if opts.color_type_reduction {
|
if opts.color_type_reduction {
|
||||||
if let Some(reduced) = reduce_color_type(&png) {
|
if let Some(reduced) = reduce_color_type(&png) {
|
||||||
png = Arc::new(reduced);
|
png = Arc::new(reduced);
|
||||||
eval.try_image(png.clone(), 0.96);
|
eval.try_image(png.clone());
|
||||||
if opts.verbosity == Some(1) {
|
if opts.verbosity == Some(1) {
|
||||||
report_reduction(&png);
|
report_reduction(&png);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub(crate) fn try_alpha_reductions(
|
||||||
let alphas_iter = alphas.par_iter().with_max_len(1);
|
let alphas_iter = alphas.par_iter().with_max_len(1);
|
||||||
alphas_iter
|
alphas_iter
|
||||||
.filter_map(|&alpha| filtered_alpha_channel(&png, *alpha))
|
.filter_map(|&alpha| filtered_alpha_channel(&png, *alpha))
|
||||||
.for_each(|image| eval.try_image(Arc::new(image), 0.99));
|
.for_each(|image| eval.try_image(Arc::new(image)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filtered_alpha_channel(png: &PngImage, optim: AlphaOptim) -> Option<PngImage> {
|
pub fn filtered_alpha_channel(png: &PngImage, optim: AlphaOptim) -> Option<PngImage> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue