Use deflater on iCCP chunk as well

This commit is contained in:
Chris Hennick 2023-06-21 09:20:00 -07:00
parent d1a78f97bf
commit a330187a51
No known key found for this signature in database
GPG key ID: 25653935CC8B6C74
3 changed files with 22 additions and 24 deletions

View file

@ -107,23 +107,26 @@ impl Deflater for BufferedZopfliDeflater {
maximum_block_splits: self.max_block_splits, maximum_block_splits: self.max_block_splits,
..Default::default() // for forward compatibility ..Default::default() // for forward compatibility
}; };
let mut out = Vec::with_capacity(self.output_buffer_size);
let mut buffer = BufWriter::with_capacity( let mut buffer = BufWriter::with_capacity(
self.input_buffer_size, self.input_buffer_size,
DeflateEncoder::new( DeflateEncoder::new(
options, options,
Default::default(), Default::default(),
Cursor::new(Vec::with_capacity(self.output_buffer_size)), &mut out,
), ),
); );
let result = (|| -> io::Result<Vec<u8>> { let result = (|| -> io::Result<()> {
buffer.write_all(data)?; buffer.write_all(data)?;
Ok(buffer.into_inner()?.finish()?.into_inner()) buffer.into_inner()?.finish()?;
Ok(())
})(); })();
let result = result.map_err(|e| PngError::new(&e.to_string()))?; result.map_err(|e| PngError::new(&e.to_string()))?;
if max_size.get().is_some_and(|max| max < result.len()) { println!("Compressed {} -> {} bytes", data.len(), out.len());
Err(PngError::DeflatedDataTooLong(result.len())) if max_size.get().is_some_and(|max| max < out.len()) {
Err(PngError::DeflatedDataTooLong(out.len()))
} else { } else {
Ok(result) Ok(out)
} }
} }
} }

View file

@ -403,7 +403,7 @@ impl RawImage {
.filter(|c| opts.strip.keep(&c.name)) .filter(|c| opts.strip.keep(&c.name))
.cloned() .cloned()
.collect(); .collect();
postprocess_chunks(&mut png, opts, &self.png.ihdr); postprocess_chunks(&mut png, opts, &self.png.ihdr, deflater);
Ok(png.output()) Ok(png.output())
} }
@ -579,7 +579,7 @@ fn optimize_png(
png.idat_data = new_png.idat_data; png.idat_data = new_png.idat_data;
} }
postprocess_chunks(png, opts, &raw.ihdr); postprocess_chunks(png, opts, &raw.ihdr, &opts.deflate);
let output = png.output(); let output = png.output();
@ -675,17 +675,9 @@ fn optimize_raw<T: Deflater>(
// We should have a result here - fail if not (e.g. deadline passed) // We should have a result here - fail if not (e.g. deadline passed)
let result = eval_result?; let result = eval_result?;
match opts.deflate { debug!("Trying: {}", result.filter);
Deflaters::Libdeflater { compression } if compression <= eval_compression => { let best_size = AtomicMin::new(max_size);
// No further compression required perform_trial(&result.filtered, opts, result.filter, &best_size, deflater)
Some((result.filter, result.idat_data))
}
_ => {
debug!("Trying: {}", result.filter);
let best_size = AtomicMin::new(max_size);
perform_trial(&result.filtered, opts, result.filter, &best_size, deflater)
}
}
} else { } else {
// Perform full compression trials of selected filters and determine the best // Perform full compression trials of selected filters and determine the best
@ -855,7 +847,8 @@ fn report_format(prefix: &str, png: &PngImage) {
} }
/// Perform cleanup of certain chunks from the `PngData` object, after optimization has been completed /// Perform cleanup of certain chunks from the `PngData` object, after optimization has been completed
fn postprocess_chunks(png: &mut PngData, opts: &Options, orig_ihdr: &IhdrData) { fn postprocess_chunks<T>(png: &mut PngData, opts: &Options, orig_ihdr: &IhdrData, deflater: &T)
where T: Deflater {
if let Some(iccp_idx) = png.aux_chunks.iter().position(|c| &c.name == b"iCCP") { if let Some(iccp_idx) = png.aux_chunks.iter().position(|c| &c.name == b"iCCP") {
// See if we can replace an iCCP chunk with an sRGB chunk // See if we can replace an iCCP chunk with an sRGB chunk
let may_replace_iccp = opts.strip != StripChunks::None && opts.strip.keep(b"sRGB"); let may_replace_iccp = opts.strip != StripChunks::None && opts.strip.keep(b"sRGB");
@ -877,7 +870,7 @@ fn postprocess_chunks(png: &mut PngData, opts: &Options, orig_ihdr: &IhdrData) {
name: *b"sRGB", name: *b"sRGB",
data: vec![intent], data: vec![intent],
}; };
} else if let Ok(iccp) = construct_iccp(&icc, &opts.deflate) { } else if let Ok(iccp) = construct_iccp(&icc, deflater) {
let cur_len = png.aux_chunks[iccp_idx].data.len(); let cur_len = png.aux_chunks[iccp_idx].data.len();
let new_len = iccp.data.len(); let new_len = iccp.data.len();
if new_len < cur_len { if new_len < cur_len {

View file

@ -14,6 +14,7 @@ fn get_opts() -> Options {
fn test_it_converts(input: &str) { fn test_it_converts(input: &str) {
let input = PathBuf::from(input); let input = PathBuf::from(input);
let opts = get_opts(); let opts = get_opts();
let deflater = BufferedZopfliDeflater::default();
let original_data = PngData::read_file(&PathBuf::from(input)).unwrap(); let original_data = PngData::read_file(&PathBuf::from(input)).unwrap();
let image = PngData::from_slice(&original_data, &opts).unwrap(); let image = PngData::from_slice(&original_data, &opts).unwrap();
@ -35,7 +36,7 @@ fn test_it_converts(input: &str) {
raw.add_png_chunk(chunk.name, chunk.data); raw.add_png_chunk(chunk.name, chunk.data);
} }
let output = raw.create_optimized_png(&opts).unwrap(); let output = raw.create_optimized_png(&opts, &deflater).unwrap();
let new = PngData::from_slice(&output, &opts).unwrap(); let new = PngData::from_slice(&output, &opts).unwrap();
assert!(new.aux_chunks.len() == num_chunks); assert!(new.aux_chunks.len() == num_chunks);
@ -52,6 +53,7 @@ fn from_file() {
#[test] #[test]
fn custom_indexed() { fn custom_indexed() {
let opts = get_opts(); let opts = get_opts();
let deflater = BufferedZopfliDeflater::default();
let raw = RawImage::new( let raw = RawImage::new(
4, 4,
@ -69,7 +71,7 @@ fn custom_indexed() {
) )
.unwrap(); .unwrap();
raw.create_optimized_png(&opts).unwrap(); raw.create_optimized_png(&opts, &deflater).unwrap();
} }
#[test] #[test]