From 526429a522a9e972d46efcdba0f0c646f919713a Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 20 May 2023 16:09:13 +1200 Subject: [PATCH] Remove filtered from PngData --- src/evaluate.rs | 32 +++++++++++++++----------------- src/lib.rs | 16 +++++++++------- src/png/mod.rs | 43 +++++++++++++++++++++---------------------- 3 files changed, 45 insertions(+), 46 deletions(-) diff --git a/src/evaluate.rs b/src/evaluate.rs index 80f50d16..8c05fde7 100644 --- a/src/evaluate.rs +++ b/src/evaluate.rs @@ -4,7 +4,6 @@ use crate::atomicmin::AtomicMin; use crate::deflate; use crate::filters::RowFilter; -use crate::png::PngData; use crate::png::PngImage; #[cfg(not(feature = "parallel"))] use crate::rayon; @@ -22,7 +21,9 @@ use std::sync::atomic::Ordering::SeqCst; use std::sync::Arc; pub struct Candidate { - pub image: PngData, + pub image: Arc, + pub idat_data: Vec, + pub filtered: Vec, pub filter: RowFilter, pub is_reduction: bool, // first wins tie-breaker @@ -32,9 +33,9 @@ pub struct Candidate { impl Candidate { fn cmp_key(&self) -> impl Ord { ( - self.image.estimated_output_size(), - self.image.raw.data.len(), - self.image.raw.ihdr.bit_depth, + self.idat_data.len() + self.image.key_chunks_size(), + self.image.data.len(), + self.image.ihdr.bit_depth, self.filter, self.nth, ) @@ -135,18 +136,7 @@ impl Evaluator { let filtered = image.filter_image(filter, optimize_alpha); let idat_data = deflate::deflate(&filtered, compression, &best_candidate_size); if let Ok(idat_data) = idat_data { - let new = Candidate { - image: PngData { - idat_data, - filtered, - raw: Arc::clone(&image), - aux_chunks: Vec::new(), - }, - filter, - is_reduction, - nth, - }; - let size = new.image.estimated_output_size(); + let size = idat_data.len() + image.key_chunks_size(); best_candidate_size.set_min(size); trace!( "Eval: {}-bit {:20} {:8} {} bytes", @@ -155,6 +145,14 @@ impl Evaluator { filter, size ); + let new = Candidate { + image: image.clone(), + idat_data, + filtered, + filter, + is_reduction, + nth, + }; #[cfg(feature = "parallel")] { diff --git a/src/lib.rs b/src/lib.rs index d102a72e..8918e558 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -632,7 +632,7 @@ fn optimize_raw( let mut eval_result = eval.get_best_candidate(); if let Some(ref result) = eval_result { if result.is_reduction { - png = Arc::clone(&result.image.raw); + png = result.image.clone(); reduction_occurred = true; } } @@ -656,7 +656,7 @@ fn optimize_raw( trace!("Evaluating: {} filters", filters.len()); let eval = Evaluator::new(deadline, filters, eval_compression, opts.optimize_alpha); if let Some(ref result) = eval_result { - eval.set_best_size(result.image.idat_data.len()); + eval.set_best_size(result.idat_data.len()); } eval.try_image(png.clone()); if let Some(result) = eval.get_best_candidate() { @@ -669,12 +669,12 @@ fn optimize_raw( match opts.deflate { Deflaters::Libdeflater { compression } if compression <= eval_compression => { // No further compression required - Some((result.filter, result.image.idat_data)) + Some((result.filter, result.idat_data)) } _ => { debug!("Trying: {}", result.filter); let best_size = AtomicMin::new(max_size); - perform_trial(&result.image.filtered, opts, result.filter, &best_size) + perform_trial(&result.filtered, opts, result.filter, &best_size) } } } else { @@ -714,8 +714,6 @@ fn optimize_raw( if let Some((filter, idat_data)) = best { let image = PngData { raw: png, - // The filtered data has not been retained here, but we don't need to return it - filtered: Vec::new(), idat_data, aux_chunks: Vec::new(), }; @@ -733,7 +731,11 @@ fn optimize_raw( } else if let Some(result) = eval_result { // If idat_recoding is off and reductions were attempted but ended up choosing the baseline, // we should still check if the evaluator compressed the baseline smaller than the original. - let image = result.image; + let image = PngData { + raw: result.image, + idat_data: result.idat_data, + aux_chunks: Vec::new(), + }; if image.estimated_output_size() < max_size.unwrap_or(usize::MAX) { debug!("Found better combination:"); debug!( diff --git a/src/png/mod.rs b/src/png/mod.rs index cfa307a2..49f7a89d 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -39,8 +39,6 @@ pub struct PngData { pub raw: Arc, /// The filtered and compressed data of the IDAT chunk pub idat_data: Vec, - /// The filtered, uncompressed data of the IDAT chunk - pub filtered: Vec, /// All non-critical chunks from the PNG are stored here pub aux_chunks: Vec, } @@ -135,35 +133,18 @@ impl PngData { ihdr, data: raw_data, }; - let unfiltered = raw.unfilter_image()?; + raw.data = raw.unfilter_image()?; // Return the PngData Ok(Self { idat_data, - filtered: std::mem::replace(&mut raw.data, unfiltered), raw: Arc::new(raw), aux_chunks, }) } - /// Return an estimate of the output size + /// Return an estimate of the output size which can help with evaluation of very small data pub fn estimated_output_size(&self) -> usize { - // Add the size of the PLTE and tRNS chunks to the compressed idat size - // This can help with evaluation of very small data - let size = self.idat_data.len(); - size + match &self.raw.ihdr.color_type { - ColorType::Indexed { palette } => { - let plte = 12 + palette.len() * 3; - let trns = palette.iter().filter(|p| p.a != 255).count(); - if trns != 0 { - plte + 12 + trns - } else { - plte - } - } - ColorType::Grayscale { transparent_shade } if transparent_shade.is_some() => 12 + 2, - ColorType::RGB { transparent_color } if transparent_color.is_some() => 12 + 6, - _ => 0, - } + self.idat_data.len() + self.raw.key_chunks_size() } /// Format the `PngData` struct into a valid PNG bytestream @@ -274,6 +255,24 @@ impl PngImage { } } + /// Calculate the size of the PLTE and tRNS chunks + pub fn key_chunks_size(&self) -> usize { + match &self.ihdr.color_type { + ColorType::Indexed { palette } => { + let plte = 12 + palette.len() * 3; + let trns = palette.iter().filter(|p| p.a != 255).count(); + if trns != 0 { + plte + 12 + trns + } else { + plte + } + } + ColorType::Grayscale { transparent_shade } if transparent_shade.is_some() => 12 + 2, + ColorType::RGB { transparent_color } if transparent_color.is_some() => 12 + 6, + _ => 0, + } + } + /// Return an iterator over the scanlines of the image #[inline] pub fn scan_lines(&self, has_filter: bool) -> ScanLines<'_> {