Don't use atomicmin in deflater

This commit is contained in:
Andrew 2025-01-21 17:35:22 +13:00
parent 74c5c4173b
commit 78b9bd47d0
7 changed files with 35 additions and 64 deletions

View file

@ -13,10 +13,7 @@ fn deflate_16_bits(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png")); let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, &Options::default()).unwrap(); let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| { b.iter(|| deflate(png.raw.data.as_ref(), 12, None));
let min = AtomicMin::new(None);
deflate(png.raw.data.as_ref(), 12, &min)
});
} }
#[bench] #[bench]
@ -24,10 +21,7 @@ fn deflate_8_bits(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png")); let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap(); let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| { b.iter(|| deflate(png.raw.data.as_ref(), 12, None));
let min = AtomicMin::new(None);
deflate(png.raw.data.as_ref(), 12, &min)
});
} }
#[bench] #[bench]
@ -37,10 +31,7 @@ fn deflate_4_bits(b: &mut Bencher) {
)); ));
let png = PngData::new(&input, &Options::default()).unwrap(); let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| { b.iter(|| deflate(png.raw.data.as_ref(), 12, None));
let min = AtomicMin::new(None);
deflate(png.raw.data.as_ref(), 12, &min)
});
} }
#[bench] #[bench]
@ -50,10 +41,7 @@ fn deflate_2_bits(b: &mut Bencher) {
)); ));
let png = PngData::new(&input, &Options::default()).unwrap(); let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| { b.iter(|| deflate(png.raw.data.as_ref(), 12, None));
let min = AtomicMin::new(None);
deflate(png.raw.data.as_ref(), 12, &min)
});
} }
#[bench] #[bench]
@ -63,10 +51,7 @@ fn deflate_1_bits(b: &mut Bencher) {
)); ));
let png = PngData::new(&input, &Options::default()).unwrap(); let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| { b.iter(|| deflate(png.raw.data.as_ref(), 12, None));
let min = AtomicMin::new(None);
deflate(png.raw.data.as_ref(), 12, &min)
});
} }
#[bench] #[bench]

View file

@ -22,11 +22,6 @@ impl AtomicMin {
} }
} }
/// Unset value is `usize_max`
pub const fn as_atomic_usize(&self) -> &AtomicUsize {
&self.val
}
/// Try a new value, returning true if it is the new minimum /// Try a new value, returning true if it is the new minimum
pub fn set_min(&self, new_val: usize) -> bool { pub fn set_min(&self, new_val: usize) -> bool {
new_val < self.val.fetch_min(new_val, SeqCst) new_val < self.val.fetch_min(new_val, SeqCst)

View file

@ -1,12 +1,10 @@
use libdeflater::*; use libdeflater::*;
use crate::{atomicmin::AtomicMin, PngError, PngResult}; use crate::{PngError, PngResult};
pub fn deflate(data: &[u8], level: u8, max_size: &AtomicMin) -> PngResult<Vec<u8>> { pub fn deflate(data: &[u8], level: u8, max_size: Option<usize>) -> PngResult<Vec<u8>> {
let mut compressor = Compressor::new(CompressionLvl::new(level.into()).unwrap()); let mut compressor = Compressor::new(CompressionLvl::new(level.into()).unwrap());
let capacity = max_size let capacity = max_size.unwrap_or_else(|| compressor.zlib_compress_bound(data.len()));
.get()
.unwrap_or_else(|| compressor.zlib_compress_bound(data.len()));
let mut dest = vec![0; capacity]; let mut dest = vec![0; capacity];
let len = compressor let len = compressor
.zlib_compress(data, &mut dest) .zlib_compress(data, &mut dest)

View file

@ -5,7 +5,7 @@ use std::{fmt, fmt::Display};
pub use deflater::{crc32, deflate, inflate}; pub use deflater::{crc32, deflate, inflate};
use crate::{AtomicMin, PngError, PngResult}; use crate::{PngError, PngResult};
#[cfg(feature = "zopfli")] #[cfg(feature = "zopfli")]
mod zopfli_oxipng; mod zopfli_oxipng;
#[cfg(feature = "zopfli")] #[cfg(feature = "zopfli")]
@ -30,13 +30,13 @@ pub enum Deflaters {
} }
impl Deflaters { impl Deflaters {
pub(crate) fn deflate(self, data: &[u8], max_size: &AtomicMin) -> PngResult<Vec<u8>> { pub(crate) fn deflate(self, data: &[u8], max_size: Option<usize>) -> PngResult<Vec<u8>> {
let compressed = match self { let compressed = match self {
Self::Libdeflater { compression } => deflate(data, compression, max_size)?, Self::Libdeflater { compression } => deflate(data, compression, max_size)?,
#[cfg(feature = "zopfli")] #[cfg(feature = "zopfli")]
Self::Zopfli { iterations } => zopfli_deflate(data, iterations)?, Self::Zopfli { iterations } => zopfli_deflate(data, iterations)?,
}; };
if let Some(max) = max_size.get() { if let Some(max) = max_size {
if compressed.len() > max { if compressed.len() > max {
return Err(PngError::DeflatedDataTooLong(max)); return Err(PngError::DeflatedDataTooLong(max));
} }

View file

@ -147,7 +147,7 @@ impl Evaluator {
return; return;
} }
let filtered = image.filter_image(filter, optimize_alpha); let filtered = image.filter_image(filter, optimize_alpha);
let idat_data = deflater.deflate(&filtered, &best_candidate_size); let idat_data = deflater.deflate(&filtered, best_candidate_size.get());
if let Ok(idat_data) = idat_data { if let Ok(idat_data) = idat_data {
let new = Candidate { let new = Candidate {
image: image.clone(), image: image.clone(),

View file

@ -8,7 +8,7 @@ use crate::{
display_chunks::DISPLAY_CHUNKS, display_chunks::DISPLAY_CHUNKS,
error::PngError, error::PngError,
interlace::Interlacing, interlace::Interlacing,
AtomicMin, Deflaters, PngResult, Deflaters, PngResult,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -275,9 +275,9 @@ pub fn extract_icc(iccp: &Chunk) -> Option<Vec<u8>> {
} }
} }
/// Construct an iCCP chunk by compressing the ICC profile /// Make an iCCP chunk by compressing the ICC profile
pub fn construct_iccp(icc: &[u8], deflater: Deflaters) -> PngResult<Chunk> { pub fn make_iccp(icc: &[u8], deflater: Deflaters, max_size: Option<usize>) -> PngResult<Chunk> {
let mut compressed = deflater.deflate(icc, &AtomicMin::new(None))?; let mut compressed = deflater.deflate(icc, max_size)?;
let mut data = Vec::with_capacity(compressed.len() + 5); let mut data = Vec::with_capacity(compressed.len() + 5);
data.extend(b"icc"); // Profile name - generally unused, can be anything data.extend(b"icc"); // Profile name - generally unused, can be anything
data.extend([0, 0]); // Null separator, zlib compression method data.extend([0, 0]); // Null separator, zlib compression method

View file

@ -42,13 +42,6 @@ use log::{debug, info, trace, warn};
use rayon::prelude::*; use rayon::prelude::*;
pub use rgb::{RGB16, RGBA8}; pub use rgb::{RGB16, RGBA8};
use crate::{
atomicmin::AtomicMin,
evaluate::{Candidate, Evaluator},
headers::*,
png::{PngData, PngImage},
reduction::*,
};
pub use crate::{ pub use crate::{
colors::{BitDepth, ColorType}, colors::{BitDepth, ColorType},
deflate::Deflaters, deflate::Deflaters,
@ -58,6 +51,12 @@ pub use crate::{
interlace::Interlacing, interlace::Interlacing,
options::{InFile, Options, OutFile}, options::{InFile, Options, OutFile},
}; };
use crate::{
evaluate::{Candidate, Evaluator},
headers::*,
png::{PngData, PngImage},
reduction::*,
};
mod apng; mod apng;
mod atomicmin; mod atomicmin;
@ -80,7 +79,7 @@ mod sanity_checks;
pub mod internal_tests { pub mod internal_tests {
#[cfg(feature = "sanity-checks")] #[cfg(feature = "sanity-checks")]
pub use crate::sanity_checks::*; pub use crate::sanity_checks::*;
pub use crate::{atomicmin::*, deflate::*, png::*, reduction::*}; pub use crate::{deflate::*, png::*, reduction::*};
} }
pub type PngResult<T> = Result<T, PngError>; pub type PngResult<T> = Result<T, PngError>;
@ -149,7 +148,7 @@ impl RawImage {
pub fn add_icc_profile(&mut self, data: &[u8]) { pub fn add_icc_profile(&mut self, data: &[u8]) {
// Compress with fastest compression level - will be recompressed during optimization // Compress with fastest compression level - will be recompressed during optimization
let deflater = Deflaters::Libdeflater { compression: 1 }; let deflater = Deflaters::Libdeflater { compression: 1 };
if let Ok(iccp) = construct_iccp(data, deflater) { if let Ok(iccp) = make_iccp(data, deflater, None) {
self.aux_chunks.push(iccp); self.aux_chunks.push(iccp);
} }
} }
@ -529,10 +528,7 @@ fn perform_trials(
// Recompress with the main deflater // Recompress with the main deflater
debug!("Trying filter {}, zc = {}", result.filter, opts.deflate); debug!("Trying filter {}, zc = {}", result.filter, opts.deflate);
match opts match opts.deflate.deflate(&result.filtered, max_size) {
.deflate
.deflate(&result.filtered, &AtomicMin::new(max_size))
{
Ok(idat_data) => { Ok(idat_data) => {
result.idat_data = idat_data; result.idat_data = idat_data;
trace!("{} bytes", result.estimated_output_size()); trace!("{} bytes", result.estimated_output_size());
@ -649,17 +645,14 @@ fn postprocess_chunks(png: &mut PngData, opts: &Options, orig_ihdr: &IhdrData) {
}; };
} else if opts.idat_recoding { } else if opts.idat_recoding {
// Try recompressing the profile // Try recompressing the profile
if let Ok(iccp) = construct_iccp(&icc, opts.deflate) { let cur_len = png.aux_chunks[iccp_idx].data.len();
let cur_len = png.aux_chunks[iccp_idx].data.len(); if let Ok(iccp) = make_iccp(&icc, opts.deflate, Some(cur_len - 1)) {
let new_len = iccp.data.len(); debug!(
if new_len < cur_len { "Recompressed iCCP chunk: {} ({} bytes decrease)",
debug!( iccp.data.len(),
"Recompressed iCCP chunk: {} ({} bytes decrease)", cur_len - iccp.data.len()
new_len, );
cur_len - new_len png.aux_chunks[iccp_idx] = iccp;
);
png.aux_chunks[iccp_idx] = iccp;
}
} }
} }
} }
@ -706,8 +699,8 @@ fn recompress_frames(png: &mut PngData, opts: &Options, deadline: Arc<Deadline>)
ihdr.height = frame.height; ihdr.height = frame.height;
let image = PngImage::new(ihdr, &frame.data)?; let image = PngImage::new(ihdr, &frame.data)?;
let filtered = image.filter_image(filter, opts.optimize_alpha); let filtered = image.filter_image(filter, opts.optimize_alpha);
let max_size = AtomicMin::new(Some(frame.data.len() - 1)); let max_size = Some(frame.data.len() - 1);
if let Ok(data) = opts.deflate.deflate(&filtered, &max_size) { if let Ok(data) = opts.deflate.deflate(&filtered, max_size) {
debug!( debug!(
"Recompressed fdAT #{:<2}: {} ({} bytes decrease)", "Recompressed fdAT #{:<2}: {} ({} bytes decrease)",
i, i,