Don't use atomicmin in deflater
This commit is contained in:
parent
74c5c4173b
commit
78b9bd47d0
7 changed files with 35 additions and 64 deletions
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
45
src/lib.rs
45
src/lib.rs
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue