Bug fix: need to implement Zlib format
This commit is contained in:
parent
a330187a51
commit
e8a8dede55
4 changed files with 83 additions and 16 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -367,6 +367,7 @@ dependencies = [
|
||||||
"rgb",
|
"rgb",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
|
"simd-adler32",
|
||||||
"stderrlog",
|
"stderrlog",
|
||||||
"wild",
|
"wild",
|
||||||
"zopfli",
|
"zopfli",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ required-features = ["zopfli"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zopfli = { version = "0.7.4", optional = true, default-features = false, features = ["std", "zlib"] }
|
zopfli = { version = "0.7.4", optional = true, default-features = false, features = ["std", "zlib"] }
|
||||||
|
simd-adler32 = { version = "0.3.5", optional = true, default-features = false }
|
||||||
rgb = "0.8.36"
|
rgb = "0.8.36"
|
||||||
indexmap = "1.9.3"
|
indexmap = "1.9.3"
|
||||||
libdeflater = "0.11.0"
|
libdeflater = "0.11.0"
|
||||||
|
|
@ -66,6 +67,7 @@ version = "0.24.6"
|
||||||
rustc_version = "0.4.0"
|
rustc_version = "0.4.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
zopfli = ["zopfli/std", "zopfli/zlib", "simd-adler32"]
|
||||||
binary = ["clap", "wild", "stderrlog"]
|
binary = ["clap", "wild", "stderrlog"]
|
||||||
default = ["binary", "filetime", "parallel", "zopfli"]
|
default = ["binary", "filetime", "parallel", "zopfli"]
|
||||||
parallel = ["rayon", "indexmap/rayon", "crossbeam-channel"]
|
parallel = ["rayon", "indexmap/rayon", "crossbeam-channel"]
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,19 @@ use crate::{PngError, PngResult};
|
||||||
pub use deflater::crc32;
|
pub use deflater::crc32;
|
||||||
pub use deflater::deflate;
|
pub use deflater::deflate;
|
||||||
pub use deflater::inflate;
|
pub use deflater::inflate;
|
||||||
use std::io::{BufWriter, Cursor, Write};
|
use std::io::{BufWriter, copy, Cursor, Write};
|
||||||
use std::{fmt, fmt::Display, io};
|
use std::{fmt, fmt::Display, io};
|
||||||
|
|
||||||
#[cfg(feature = "zopfli")]
|
#[cfg(feature = "zopfli")]
|
||||||
use std::num::NonZeroU8;
|
use std::num::NonZeroU8;
|
||||||
#[cfg(feature = "zopfli")]
|
#[cfg(feature = "zopfli")]
|
||||||
use zopfli::{DeflateEncoder, Options};
|
use zopfli::{DeflateEncoder, Options};
|
||||||
|
|
||||||
#[cfg(feature = "zopfli")]
|
#[cfg(feature = "zopfli")]
|
||||||
mod zopfli_oxipng;
|
mod zopfli_oxipng;
|
||||||
#[cfg(feature = "zopfli")]
|
#[cfg(feature = "zopfli")]
|
||||||
pub use zopfli_oxipng::deflate as zopfli_deflate;
|
pub use zopfli_oxipng::deflate as zopfli_deflate;
|
||||||
|
#[cfg(feature = "zopfli")]
|
||||||
|
use simd_adler32::Adler32;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
/// DEFLATE algorithms supported by oxipng
|
/// DEFLATE algorithms supported by oxipng
|
||||||
|
|
@ -100,6 +101,8 @@ impl Default for BufferedZopfliDeflater {
|
||||||
|
|
||||||
#[cfg(feature = "zopfli")]
|
#[cfg(feature = "zopfli")]
|
||||||
impl Deflater for BufferedZopfliDeflater {
|
impl Deflater for BufferedZopfliDeflater {
|
||||||
|
|
||||||
|
/// Fork of the zlib_compress function in Zopfli.
|
||||||
fn deflate(&self, data: &[u8], max_size: &AtomicMin) -> PngResult<Vec<u8>> {
|
fn deflate(&self, data: &[u8], max_size: &AtomicMin) -> PngResult<Vec<u8>> {
|
||||||
#[allow(clippy::needless_update)]
|
#[allow(clippy::needless_update)]
|
||||||
let options = Options {
|
let options = Options {
|
||||||
|
|
@ -107,22 +110,32 @@ 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 out = Cursor::new(Vec::with_capacity(self.output_buffer_size));
|
||||||
let mut buffer = BufWriter::with_capacity(
|
let cmf = 120; /* CM 8, CINFO 7. See zlib spec.*/
|
||||||
self.input_buffer_size,
|
let flevel = 3;
|
||||||
DeflateEncoder::new(
|
let fdict = 0;
|
||||||
options,
|
let mut cmfflg: u16 = 256 * cmf + fdict * 32 + flevel * 64;
|
||||||
Default::default(),
|
let fcheck = 31 - cmfflg % 31;
|
||||||
&mut out,
|
cmfflg += fcheck;
|
||||||
),
|
|
||||||
);
|
let out = (|| -> io::Result<Vec<u8>> {
|
||||||
let result = (|| -> io::Result<()> {
|
let mut rolling_adler = Adler32::new();
|
||||||
buffer.write_all(data)?;
|
let mut in_data = zopfli_oxipng::HashingAndCountingRead::new(data, &mut rolling_adler, None);
|
||||||
|
out.write_all(&cmfflg.to_be_bytes())?;
|
||||||
|
let mut buffer = BufWriter::with_capacity(
|
||||||
|
self.input_buffer_size,
|
||||||
|
DeflateEncoder::new(
|
||||||
|
options,
|
||||||
|
Default::default(),
|
||||||
|
&mut out,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
copy(&mut in_data, &mut buffer)?;
|
||||||
buffer.into_inner()?.finish()?;
|
buffer.into_inner()?.finish()?;
|
||||||
Ok(())
|
out.write_all(&rolling_adler.finish().to_be_bytes())?;
|
||||||
|
Ok(out.into_inner())
|
||||||
})();
|
})();
|
||||||
result.map_err(|e| PngError::new(&e.to_string()))?;
|
let out = out.map_err(|e| PngError::new(&e.to_string()))?;
|
||||||
println!("Compressed {} -> {} bytes", data.len(), out.len());
|
|
||||||
if max_size.get().is_some_and(|max| max < out.len()) {
|
if max_size.get().is_some_and(|max| max < out.len()) {
|
||||||
Err(PngError::DeflatedDataTooLong(out.len()))
|
Err(PngError::DeflatedDataTooLong(out.len()))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
use std::io::{Error, ErrorKind, Read};
|
||||||
use crate::{PngError, PngResult};
|
use crate::{PngError, PngResult};
|
||||||
use std::num::NonZeroU8;
|
use std::num::NonZeroU8;
|
||||||
|
use simd_adler32::Adler32;
|
||||||
|
|
||||||
pub fn deflate(data: &[u8], iterations: NonZeroU8) -> PngResult<Vec<u8>> {
|
pub fn deflate(data: &[u8], iterations: NonZeroU8) -> PngResult<Vec<u8>> {
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
@ -16,3 +18,52 @@ pub fn deflate(data: &[u8], iterations: NonZeroU8) -> PngResult<Vec<u8>> {
|
||||||
output.shrink_to_fit();
|
output.shrink_to_fit();
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Forked from zopfli crate
|
||||||
|
pub trait Hasher {
|
||||||
|
fn update(&mut self, data: &[u8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hasher for &mut Adler32 {
|
||||||
|
fn update(&mut self, data: &[u8]) {
|
||||||
|
Adler32::write(self, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A reader that wraps another reader, a hasher and an optional counter,
|
||||||
|
/// updating the hasher state and incrementing a counter of bytes read so
|
||||||
|
/// far for each block of data read.
|
||||||
|
pub struct HashingAndCountingRead<'counter, R: Read, H: Hasher> {
|
||||||
|
inner: R,
|
||||||
|
hasher: H,
|
||||||
|
bytes_read: Option<&'counter mut u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'counter, R: Read, H: Hasher> HashingAndCountingRead<'counter, R, H> {
|
||||||
|
pub fn new(inner: R, hasher: H, bytes_read: Option<&'counter mut u32>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
hasher,
|
||||||
|
bytes_read,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read, H: Hasher> Read for HashingAndCountingRead<'_, R, H> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
match self.inner.read(buf) {
|
||||||
|
Ok(bytes_read) => {
|
||||||
|
self.hasher.update(&buf[..bytes_read]);
|
||||||
|
|
||||||
|
if let Some(total_bytes_read) = &mut self.bytes_read {
|
||||||
|
**total_bytes_read = total_bytes_read
|
||||||
|
.checked_add(bytes_read.try_into().map_err(|_| ErrorKind::Other)?)
|
||||||
|
.ok_or(ErrorKind::Other)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(bytes_read)
|
||||||
|
}
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue