Remove the concept of size bias (#199)

Closes #198
This commit is contained in:
Kornel 2020-04-01 00:28:22 +01:00 committed by GitHub
parent d417a712d3
commit 681089f8c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 12 additions and 31 deletions

View file

@ -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,

View file

@ -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);
} }

View file

@ -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> {