Skip to content

Commit

Permalink
Allow APNG with reductions disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
andrews05 committed May 23, 2023
1 parent ea5f188 commit e4312d5
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 12 deletions.
18 changes: 16 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use crate::png::PngImage;
use crate::reduction::*;
use log::{debug, info, trace, warn};
use rayon::prelude::*;
use std::borrow::Cow;
use std::fmt;
use std::fs::{copy, File, Metadata};
use std::io::{stdin, stdout, BufWriter, Read, Write};
Expand Down Expand Up @@ -559,17 +560,30 @@ fn optimize_png(
debug!(" IDAT size = {} bytes", idat_original_size);
debug!(" File size = {} bytes", file_original_size);

// Check for APNG by presence of acTL chunk
let opts = if png.aux_chunks.iter().any(|c| &c.name == b"acTL") {
warn!("APNG detected, disabling all reductions");
let mut opts = opts.to_owned();
opts.interlace = None;
opts.bit_depth_reduction = false;
opts.color_type_reduction = false;
opts.palette_reduction = false;
opts.grayscale_reduction = false;
Cow::Owned(opts)
} else {
Cow::Borrowed(opts)
};
let max_size = if opts.force {
None
} else {
Some(png.estimated_output_size())
};
if let Some(new_png) = optimize_raw(raw.clone(), opts, deadline, max_size) {
if let Some(new_png) = optimize_raw(raw.clone(), &opts, deadline, max_size) {
png.raw = new_png.raw;
png.idat_data = new_png.idat_data;
}

postprocess_chunks(png, opts, &raw.ihdr);
postprocess_chunks(png, &opts, &raw.ihdr);

let output = png.output();

Expand Down
28 changes: 21 additions & 7 deletions src/png/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,16 @@ impl PngData {
let mut aux_chunks: Vec<Chunk> = Vec::new();
while let Some(chunk) = parse_next_chunk(byte_data, &mut byte_offset, opts.fix_errors)? {
match &chunk.name {
b"IDAT" => idat_data.extend_from_slice(chunk.data),
b"acTL" => return Err(PngError::APNGNotSupported),
b"IDAT" => {
if idat_data.is_empty() {
// Keep track of where the first IDAT sits relative to other chunks
aux_chunks.push(Chunk {
name: chunk.name,
data: Vec::new(),
})
}
idat_data.extend_from_slice(chunk.data);
}
b"IHDR" | b"PLTE" | b"tRNS" => {
key_chunks.insert(chunk.name, chunk.data.to_owned());
}
Expand Down Expand Up @@ -165,9 +173,10 @@ impl PngData {
ihdr_data.write_all(&[0]).ok(); // Filter method -- 5-way adaptive filtering
ihdr_data.write_all(&[self.raw.ihdr.interlaced as u8]).ok();
write_png_block(b"IHDR", &ihdr_data, &mut output);
// Ancillary chunks
for chunk in self
.aux_chunks
// Ancillary chunks - split into those that come before IDAT and those that come after
let mut aux_split = self.aux_chunks.split(|c| &c.name == b"IDAT");
let aux_pre = aux_split.next().unwrap();
for chunk in aux_pre
.iter()
.filter(|c| !(&c.name == b"bKGD" || &c.name == b"hIST" || &c.name == b"tRNS"))
{
Expand Down Expand Up @@ -202,15 +211,20 @@ impl PngData {
_ => {}
}
// Special ancillary chunks that need to come after PLTE but before IDAT
for chunk in self
.aux_chunks
for chunk in aux_pre
.iter()
.filter(|c| &c.name == b"bKGD" || &c.name == b"hIST" || &c.name == b"tRNS")
{
write_png_block(&chunk.name, &chunk.data, &mut output);
}
// IDAT data
write_png_block(b"IDAT", &self.idat_data, &mut output);
// Ancillary chunks that come after IDAT
for aux_post in aux_split {
for chunk in aux_post {
write_png_block(&chunk.name, &chunk.data, &mut output);
}
}
// Stream end
write_png_block(b"IEND", &[], &mut output);

Expand Down
6 changes: 3 additions & 3 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn optimize_from_memory_apng() {
in_file.read_to_end(&mut in_file_buf).unwrap();

let result = oxipng::optimize_from_memory(&in_file_buf, &Options::default());
assert!(result.is_err());
assert!(result.is_ok());
}

#[test]
Expand Down Expand Up @@ -58,9 +58,9 @@ fn optimize_apng() {
let result = oxipng::optimize(
&"tests/files/apng_file.png".into(),
&OutFile::Path(None),
&Options::default(),
&Options::from_preset(0),
);
assert!(result.is_err());
assert!(result.is_ok());
}

#[test]
Expand Down

0 comments on commit e4312d5

Please sign in to comment.