Remove filtered from PngData
This commit is contained in:
parent
4a1cd2db3e
commit
526429a522
3 changed files with 45 additions and 46 deletions
|
|
@ -4,7 +4,6 @@
|
||||||
use crate::atomicmin::AtomicMin;
|
use crate::atomicmin::AtomicMin;
|
||||||
use crate::deflate;
|
use crate::deflate;
|
||||||
use crate::filters::RowFilter;
|
use crate::filters::RowFilter;
|
||||||
use crate::png::PngData;
|
|
||||||
use crate::png::PngImage;
|
use crate::png::PngImage;
|
||||||
#[cfg(not(feature = "parallel"))]
|
#[cfg(not(feature = "parallel"))]
|
||||||
use crate::rayon;
|
use crate::rayon;
|
||||||
|
|
@ -22,7 +21,9 @@ use std::sync::atomic::Ordering::SeqCst;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct Candidate {
|
pub struct Candidate {
|
||||||
pub image: PngData,
|
pub image: Arc<PngImage>,
|
||||||
|
pub idat_data: Vec<u8>,
|
||||||
|
pub filtered: Vec<u8>,
|
||||||
pub filter: RowFilter,
|
pub filter: RowFilter,
|
||||||
pub is_reduction: bool,
|
pub is_reduction: bool,
|
||||||
// first wins tie-breaker
|
// first wins tie-breaker
|
||||||
|
|
@ -32,9 +33,9 @@ pub struct Candidate {
|
||||||
impl Candidate {
|
impl Candidate {
|
||||||
fn cmp_key(&self) -> impl Ord {
|
fn cmp_key(&self) -> impl Ord {
|
||||||
(
|
(
|
||||||
self.image.estimated_output_size(),
|
self.idat_data.len() + self.image.key_chunks_size(),
|
||||||
self.image.raw.data.len(),
|
self.image.data.len(),
|
||||||
self.image.raw.ihdr.bit_depth,
|
self.image.ihdr.bit_depth,
|
||||||
self.filter,
|
self.filter,
|
||||||
self.nth,
|
self.nth,
|
||||||
)
|
)
|
||||||
|
|
@ -135,18 +136,7 @@ impl Evaluator {
|
||||||
let filtered = image.filter_image(filter, optimize_alpha);
|
let filtered = image.filter_image(filter, optimize_alpha);
|
||||||
let idat_data = deflate::deflate(&filtered, compression, &best_candidate_size);
|
let idat_data = deflate::deflate(&filtered, compression, &best_candidate_size);
|
||||||
if let Ok(idat_data) = idat_data {
|
if let Ok(idat_data) = idat_data {
|
||||||
let new = Candidate {
|
let size = idat_data.len() + image.key_chunks_size();
|
||||||
image: PngData {
|
|
||||||
idat_data,
|
|
||||||
filtered,
|
|
||||||
raw: Arc::clone(&image),
|
|
||||||
aux_chunks: Vec::new(),
|
|
||||||
},
|
|
||||||
filter,
|
|
||||||
is_reduction,
|
|
||||||
nth,
|
|
||||||
};
|
|
||||||
let size = new.image.estimated_output_size();
|
|
||||||
best_candidate_size.set_min(size);
|
best_candidate_size.set_min(size);
|
||||||
trace!(
|
trace!(
|
||||||
"Eval: {}-bit {:20} {:8} {} bytes",
|
"Eval: {}-bit {:20} {:8} {} bytes",
|
||||||
|
|
@ -155,6 +145,14 @@ impl Evaluator {
|
||||||
filter,
|
filter,
|
||||||
size
|
size
|
||||||
);
|
);
|
||||||
|
let new = Candidate {
|
||||||
|
image: image.clone(),
|
||||||
|
idat_data,
|
||||||
|
filtered,
|
||||||
|
filter,
|
||||||
|
is_reduction,
|
||||||
|
nth,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
{
|
{
|
||||||
|
|
|
||||||
16
src/lib.rs
16
src/lib.rs
|
|
@ -632,7 +632,7 @@ fn optimize_raw(
|
||||||
let mut eval_result = eval.get_best_candidate();
|
let mut eval_result = eval.get_best_candidate();
|
||||||
if let Some(ref result) = eval_result {
|
if let Some(ref result) = eval_result {
|
||||||
if result.is_reduction {
|
if result.is_reduction {
|
||||||
png = Arc::clone(&result.image.raw);
|
png = result.image.clone();
|
||||||
reduction_occurred = true;
|
reduction_occurred = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -656,7 +656,7 @@ fn optimize_raw(
|
||||||
trace!("Evaluating: {} filters", filters.len());
|
trace!("Evaluating: {} filters", filters.len());
|
||||||
let eval = Evaluator::new(deadline, filters, eval_compression, opts.optimize_alpha);
|
let eval = Evaluator::new(deadline, filters, eval_compression, opts.optimize_alpha);
|
||||||
if let Some(ref result) = eval_result {
|
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());
|
eval.try_image(png.clone());
|
||||||
if let Some(result) = eval.get_best_candidate() {
|
if let Some(result) = eval.get_best_candidate() {
|
||||||
|
|
@ -669,12 +669,12 @@ fn optimize_raw(
|
||||||
match opts.deflate {
|
match opts.deflate {
|
||||||
Deflaters::Libdeflater { compression } if compression <= eval_compression => {
|
Deflaters::Libdeflater { compression } if compression <= eval_compression => {
|
||||||
// No further compression required
|
// No further compression required
|
||||||
Some((result.filter, result.image.idat_data))
|
Some((result.filter, result.idat_data))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Trying: {}", result.filter);
|
debug!("Trying: {}", result.filter);
|
||||||
let best_size = AtomicMin::new(max_size);
|
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 {
|
} else {
|
||||||
|
|
@ -714,8 +714,6 @@ fn optimize_raw(
|
||||||
if let Some((filter, idat_data)) = best {
|
if let Some((filter, idat_data)) = best {
|
||||||
let image = PngData {
|
let image = PngData {
|
||||||
raw: png,
|
raw: png,
|
||||||
// The filtered data has not been retained here, but we don't need to return it
|
|
||||||
filtered: Vec::new(),
|
|
||||||
idat_data,
|
idat_data,
|
||||||
aux_chunks: Vec::new(),
|
aux_chunks: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
@ -733,7 +731,11 @@ fn optimize_raw(
|
||||||
} else if let Some(result) = eval_result {
|
} else if let Some(result) = eval_result {
|
||||||
// If idat_recoding is off and reductions were attempted but ended up choosing the baseline,
|
// 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.
|
// 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) {
|
if image.estimated_output_size() < max_size.unwrap_or(usize::MAX) {
|
||||||
debug!("Found better combination:");
|
debug!("Found better combination:");
|
||||||
debug!(
|
debug!(
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,6 @@ pub struct PngData {
|
||||||
pub raw: Arc<PngImage>,
|
pub raw: Arc<PngImage>,
|
||||||
/// The filtered and compressed data of the IDAT chunk
|
/// The filtered and compressed data of the IDAT chunk
|
||||||
pub idat_data: Vec<u8>,
|
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
|
/// All non-critical chunks from the PNG are stored here
|
||||||
pub aux_chunks: Vec<Chunk>,
|
pub aux_chunks: Vec<Chunk>,
|
||||||
}
|
}
|
||||||
|
|
@ -135,35 +133,18 @@ impl PngData {
|
||||||
ihdr,
|
ihdr,
|
||||||
data: raw_data,
|
data: raw_data,
|
||||||
};
|
};
|
||||||
let unfiltered = raw.unfilter_image()?;
|
raw.data = raw.unfilter_image()?;
|
||||||
// Return the PngData
|
// Return the PngData
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
idat_data,
|
idat_data,
|
||||||
filtered: std::mem::replace(&mut raw.data, unfiltered),
|
|
||||||
raw: Arc::new(raw),
|
raw: Arc::new(raw),
|
||||||
aux_chunks,
|
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 {
|
pub fn estimated_output_size(&self) -> usize {
|
||||||
// Add the size of the PLTE and tRNS chunks to the compressed idat size
|
self.idat_data.len() + self.raw.key_chunks_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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format the `PngData` struct into a valid PNG bytestream
|
/// 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
|
/// Return an iterator over the scanlines of the image
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn scan_lines(&self, has_filter: bool) -> ScanLines<'_> {
|
pub fn scan_lines(&self, has_filter: bool) -> ScanLines<'_> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue