Allow APNG with reductions disabled
This commit is contained in:
parent
129f1e6f76
commit
f436ef62a3
4 changed files with 43 additions and 13 deletions
|
|
@ -86,7 +86,9 @@ pub enum StripChunks {
|
|||
|
||||
impl StripChunks {
|
||||
/// List of chunks that will be kept when using the `Safe` option
|
||||
pub const KEEP_SAFE: [[u8; 4]; 4] = [*b"cICP", *b"iCCP", *b"sRGB", *b"pHYs"];
|
||||
pub const KEEP_SAFE: [[u8; 4]; 7] = [
|
||||
*b"cICP", *b"iCCP", *b"sRGB", *b"pHYs", *b"acTL", *b"fcTL", *b"fdAT",
|
||||
];
|
||||
|
||||
pub(crate) fn keep(&self, name: &[u8; 4]) -> bool {
|
||||
match &self {
|
||||
|
|
|
|||
18
src/lib.rs
18
src/lib.rs
|
|
@ -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};
|
||||
|
|
@ -564,17 +565,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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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"))
|
||||
{
|
||||
|
|
@ -202,8 +211,7 @@ 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")
|
||||
{
|
||||
|
|
@ -211,6 +219,12 @@ impl PngData {
|
|||
}
|
||||
// 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Reference in a new issue