Ensure evaluator returns no result when all candidates are too large (#754)

There was a small crack in the fast evaluation logic where a phase 2
candidate's IDAT could be compressed below the best size from phase 1,
but the estimated output size (which includes chunks like PLTE) was
above. This candidate could end up getting returned from the evaluator
if no other filter actually did compress better, and would then be used
for the final compression even though the phase 1 candidate was actually
better.

The issue could potentially affect small, indexed images in `--fast`
mode (o2 and below).

Fixes #753.
This commit is contained in:
andrews05 2025-12-18 12:22:36 +13:00 committed by GitHub
parent f99ce277da
commit fa2c69071c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -154,6 +154,19 @@ impl Evaluator {
let idat_data = deflater.deflate(&filtered, best_candidate_size.get());
if let Ok(idat_data) = idat_data {
let estimated_output_size = image.estimated_output_size(&idat_data);
trace!(
"Eval: {}-bit {:23} {:8} {} bytes",
image.ihdr.bit_depth, description, filter, estimated_output_size
);
// Skip if it exceeds best known size. (This is important to ensure
// the evaluator returns no result when all candidates are too large.)
if let Some(max) = best_candidate_size.get() {
if estimated_output_size > max {
return;
}
}
// We only need to retain the IDAT data in the final round
let new = Candidate {
image: image.clone(),
@ -164,10 +177,6 @@ impl Evaluator {
nth,
};
best_candidate_size.set_min(estimated_output_size);
trace!(
"Eval: {}-bit {:23} {:8} {} bytes",
image.ihdr.bit_depth, description, filter, estimated_output_size
);
#[cfg(feature = "parallel")]
{