Remove filtered from PngData

This commit is contained in:
Andrew 2023-05-20 16:09:13 +12:00
parent 4a1cd2db3e
commit 526429a522
3 changed files with 45 additions and 46 deletions

View file

@ -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<PngImage>,
pub idat_data: Vec<u8>,
pub filtered: Vec<u8>,
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")]
{

View file

@ -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!(

View file

@ -39,8 +39,6 @@ pub struct PngData {
pub raw: Arc<PngImage>,
/// The filtered and compressed data of the IDAT chunk
pub idat_data: Vec<u8>,
/// The filtered, uncompressed data of the IDAT chunk
pub filtered: Vec<u8>,
/// All non-critical chunks from the PNG are stored here
pub aux_chunks: Vec<Chunk>,
}
@ -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<'_> {