parent
aff99f2f7c
commit
2659776bd5
8 changed files with 307 additions and 258 deletions
|
|
@ -1,3 +1,6 @@
|
|||
**Version 0.8.2**
|
||||
- Fix issue where images smaller than 4px width would crash on interlacing ([#42](https://github.com/shssoichiro/oxipng/issues/42))
|
||||
|
||||
**Version 0.8.1**
|
||||
- Minor optimizations
|
||||
- Fix issue where interlaced images with certain widths would fail to optimize
|
||||
|
|
|
|||
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1,6 +1,6 @@
|
|||
[root]
|
||||
name = "oxipng"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "oxipng"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
authors = ["Joshua Holmer <jholmer.in@gmail.com>"]
|
||||
description = "A lossless PNG compression optimizer"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -388,11 +388,8 @@ fn optimize_png(mut png: &mut png::PngData, file_original_size: usize, opts: &Op
|
|||
let filtered = filters.get(&trial.0).unwrap();
|
||||
let best = best.clone();
|
||||
scope.execute(move || {
|
||||
let new_idat = deflate::deflate::deflate(filtered,
|
||||
trial.1,
|
||||
trial.2,
|
||||
trial.3,
|
||||
opts.window)
|
||||
let new_idat =
|
||||
deflate::deflate::deflate(filtered, trial.1, trial.2, trial.3, opts.window)
|
||||
.unwrap();
|
||||
|
||||
if opts.verbosity == Some(1) {
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@ use std::fs::DirBuilder;
|
|||
use std::io::{Write, stderr};
|
||||
use std::path::PathBuf;
|
||||
|
||||
const VERSION_STRING: &'static str = "0.8.1";
|
||||
const VERSION_STRING: &'static str = "0.8.2";
|
||||
|
||||
fn main() {
|
||||
let matches =
|
||||
App::new("oxipng")
|
||||
let matches = App::new("oxipng")
|
||||
.version(VERSION_STRING)
|
||||
.author("Joshua Holmer <jholmer.in@gmail.com>")
|
||||
.about("Losslessly improves compression of PNG files")
|
||||
|
|
|
|||
43
src/png.rs
43
src/png.rs
|
|
@ -416,9 +416,9 @@ impl PngData {
|
|||
ihdr_data.write_u8(self.ihdr_data.interlaced).ok();
|
||||
write_png_block(b"IHDR", &ihdr_data, &mut output);
|
||||
// Ancillary headers
|
||||
for (key, header) in self.aux_headers.iter().filter(|&(ref key, _)| {
|
||||
!(**key == "bKGD" || **key == "hIST" || **key == "tRNS")
|
||||
}) {
|
||||
for (key, header) in self.aux_headers
|
||||
.iter()
|
||||
.filter(|&(ref key, _)| !(**key == "bKGD" || **key == "hIST" || **key == "tRNS")) {
|
||||
write_png_block(key.as_bytes(), header, &mut output);
|
||||
}
|
||||
// Palette
|
||||
|
|
@ -433,9 +433,9 @@ impl PngData {
|
|||
write_png_block(b"tRNS", &transparency_pixel, &mut output);
|
||||
}
|
||||
// Special ancillary headers that need to come after PLTE but before IDAT
|
||||
for (key, header) in self.aux_headers.iter().filter(|&(ref key, _)| {
|
||||
**key == "bKGD" || **key == "hIST" || **key == "tRNS"
|
||||
}) {
|
||||
for (key, header) in self.aux_headers
|
||||
.iter()
|
||||
.filter(|&(ref key, _)| **key == "bKGD" || **key == "hIST" || **key == "tRNS") {
|
||||
write_png_block(key.as_bytes(), header, &mut output);
|
||||
}
|
||||
// IDAT data
|
||||
|
|
@ -548,10 +548,10 @@ impl PngData {
|
|||
}
|
||||
|
||||
// Reduce from 16 to 8 bits per channel per pixel
|
||||
let mut reduced =
|
||||
Vec::with_capacity((self.ihdr_data.width * self.ihdr_data.height *
|
||||
let mut reduced = Vec::with_capacity((self.ihdr_data.width * self.ihdr_data.height *
|
||||
self.channels_per_pixel() as u32 +
|
||||
self.ihdr_data.height) as usize);
|
||||
self.ihdr_data
|
||||
.height) as usize);
|
||||
let mut high_byte = 0;
|
||||
|
||||
for line in self.scan_lines() {
|
||||
|
|
@ -888,24 +888,34 @@ fn interlace_image(png: &mut PngData) {
|
|||
let bits_per_pixel = png.ihdr_data.bit_depth.as_u8() * png.channels_per_pixel();
|
||||
for (index, line) in png.scan_lines().enumerate() {
|
||||
match index % 8 {
|
||||
// Add filter bytes to appropriate lines
|
||||
// Add filter bytes to passes that will be in the output image
|
||||
0 => {
|
||||
passes[0].extend(BitVec::from_elem(8, false));
|
||||
passes[3].extend(BitVec::from_elem(8, false));
|
||||
passes[5].extend(BitVec::from_elem(8, false));
|
||||
if png.ihdr_data.width > 4 {
|
||||
if png.ihdr_data.width >= 5 {
|
||||
passes[1].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
if png.ihdr_data.width >= 3 {
|
||||
passes[3].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
if png.ihdr_data.width >= 2 {
|
||||
passes[5].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
passes[3].extend(BitVec::from_elem(8, false));
|
||||
passes[5].extend(BitVec::from_elem(8, false));
|
||||
passes[2].extend(BitVec::from_elem(8, false));
|
||||
if png.ihdr_data.width >= 3 {
|
||||
passes[3].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
if png.ihdr_data.width >= 2 {
|
||||
passes[5].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
}
|
||||
2 | 6 => {
|
||||
passes[4].extend(BitVec::from_elem(8, false));
|
||||
if png.ihdr_data.width >= 2 {
|
||||
passes[5].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
passes[6].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
|
|
@ -975,8 +985,7 @@ fn deinterlace_image(png: &mut PngData) {
|
|||
let bit_vec = BitVec::from_bytes(&line.data);
|
||||
let bits_in_line = ((png.ihdr_data.width - pass_constants.x_shift as u32) as f32 /
|
||||
pass_constants.x_step as f32)
|
||||
.ceil() as usize *
|
||||
bits_per_pixel as usize;
|
||||
.ceil() as usize * bits_per_pixel as usize;
|
||||
for (i, bit) in bit_vec.iter().enumerate() {
|
||||
// Avoid moving padded 0's into new image
|
||||
if i >= bits_in_line {
|
||||
|
|
|
|||
BIN
tests/files/issue_42.png
Normal file
BIN
tests/files/issue_42.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 B |
|
|
@ -486,3 +486,44 @@ fn fix_errors() {
|
|||
// Cannot check if pixels are equal because image crate cannot read corrupt (input) PNGs
|
||||
remove_file(output).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_42() {
|
||||
let input = PathBuf::from("tests/files/issue_42.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.interlace = Some(1);
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input, opts.fix_errors).unwrap();
|
||||
|
||||
assert_eq!(png.ihdr_data.interlaced, 0);
|
||||
assert_eq!(png.ihdr_data.color_type, png::ColorType::GrayscaleAlpha);
|
||||
assert_eq!(png.ihdr_data.bit_depth, png::BitDepth::Eight);
|
||||
|
||||
match oxipng::optimize(&input, &opts) {
|
||||
Ok(_) => (),
|
||||
Err(x) => panic!(x.to_owned()),
|
||||
};
|
||||
assert!(output.exists());
|
||||
|
||||
let png = match png::PngData::new(&output, opts.fix_errors) {
|
||||
Ok(x) => x,
|
||||
Err(x) => {
|
||||
remove_file(output).ok();
|
||||
panic!(x.to_owned())
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(png.ihdr_data.interlaced, 1);
|
||||
assert_eq!(png.ihdr_data.color_type, png::ColorType::GrayscaleAlpha);
|
||||
assert_eq!(png.ihdr_data.bit_depth, png::BitDepth::Eight);
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert_eq!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>(),
|
||||
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
|
||||
|
||||
remove_file(output).ok();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue