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**
|
**Version 0.8.1**
|
||||||
- Minor optimizations
|
- Minor optimizations
|
||||||
- Fix issue where interlaced images with certain widths would fail to optimize
|
- 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]
|
[root]
|
||||||
name = "oxipng"
|
name = "oxipng"
|
||||||
version = "0.8.0"
|
version = "0.8.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"byteorder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "oxipng"
|
name = "oxipng"
|
||||||
version = "0.8.1"
|
version = "0.8.2"
|
||||||
authors = ["Joshua Holmer <jholmer.in@gmail.com>"]
|
authors = ["Joshua Holmer <jholmer.in@gmail.com>"]
|
||||||
description = "A lossless PNG compression optimizer"
|
description = "A lossless PNG compression optimizer"
|
||||||
license = "MIT"
|
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 filtered = filters.get(&trial.0).unwrap();
|
||||||
let best = best.clone();
|
let best = best.clone();
|
||||||
scope.execute(move || {
|
scope.execute(move || {
|
||||||
let new_idat = deflate::deflate::deflate(filtered,
|
let new_idat =
|
||||||
trial.1,
|
deflate::deflate::deflate(filtered, trial.1, trial.2, trial.3, opts.window)
|
||||||
trial.2,
|
|
||||||
trial.3,
|
|
||||||
opts.window)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if opts.verbosity == Some(1) {
|
if opts.verbosity == Some(1) {
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,10 @@ use std::fs::DirBuilder;
|
||||||
use std::io::{Write, stderr};
|
use std::io::{Write, stderr};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
const VERSION_STRING: &'static str = "0.8.1";
|
const VERSION_STRING: &'static str = "0.8.2";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches =
|
let matches = App::new("oxipng")
|
||||||
App::new("oxipng")
|
|
||||||
.version(VERSION_STRING)
|
.version(VERSION_STRING)
|
||||||
.author("Joshua Holmer <jholmer.in@gmail.com>")
|
.author("Joshua Holmer <jholmer.in@gmail.com>")
|
||||||
.about("Losslessly improves compression of PNG files")
|
.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();
|
ihdr_data.write_u8(self.ihdr_data.interlaced).ok();
|
||||||
write_png_block(b"IHDR", &ihdr_data, &mut output);
|
write_png_block(b"IHDR", &ihdr_data, &mut output);
|
||||||
// Ancillary headers
|
// Ancillary headers
|
||||||
for (key, header) in self.aux_headers.iter().filter(|&(ref key, _)| {
|
for (key, header) in self.aux_headers
|
||||||
!(**key == "bKGD" || **key == "hIST" || **key == "tRNS")
|
.iter()
|
||||||
}) {
|
.filter(|&(ref key, _)| !(**key == "bKGD" || **key == "hIST" || **key == "tRNS")) {
|
||||||
write_png_block(key.as_bytes(), header, &mut output);
|
write_png_block(key.as_bytes(), header, &mut output);
|
||||||
}
|
}
|
||||||
// Palette
|
// Palette
|
||||||
|
|
@ -433,9 +433,9 @@ impl PngData {
|
||||||
write_png_block(b"tRNS", &transparency_pixel, &mut output);
|
write_png_block(b"tRNS", &transparency_pixel, &mut output);
|
||||||
}
|
}
|
||||||
// Special ancillary headers that need to come after PLTE but before IDAT
|
// Special ancillary headers that need to come after PLTE but before IDAT
|
||||||
for (key, header) in self.aux_headers.iter().filter(|&(ref key, _)| {
|
for (key, header) in self.aux_headers
|
||||||
**key == "bKGD" || **key == "hIST" || **key == "tRNS"
|
.iter()
|
||||||
}) {
|
.filter(|&(ref key, _)| **key == "bKGD" || **key == "hIST" || **key == "tRNS") {
|
||||||
write_png_block(key.as_bytes(), header, &mut output);
|
write_png_block(key.as_bytes(), header, &mut output);
|
||||||
}
|
}
|
||||||
// IDAT data
|
// IDAT data
|
||||||
|
|
@ -548,10 +548,10 @@ impl PngData {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduce from 16 to 8 bits per channel per pixel
|
// Reduce from 16 to 8 bits per channel per pixel
|
||||||
let mut reduced =
|
let mut reduced = Vec::with_capacity((self.ihdr_data.width * self.ihdr_data.height *
|
||||||
Vec::with_capacity((self.ihdr_data.width * self.ihdr_data.height *
|
|
||||||
self.channels_per_pixel() as u32 +
|
self.channels_per_pixel() as u32 +
|
||||||
self.ihdr_data.height) as usize);
|
self.ihdr_data
|
||||||
|
.height) as usize);
|
||||||
let mut high_byte = 0;
|
let mut high_byte = 0;
|
||||||
|
|
||||||
for line in self.scan_lines() {
|
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();
|
let bits_per_pixel = png.ihdr_data.bit_depth.as_u8() * png.channels_per_pixel();
|
||||||
for (index, line) in png.scan_lines().enumerate() {
|
for (index, line) in png.scan_lines().enumerate() {
|
||||||
match index % 8 {
|
match index % 8 {
|
||||||
// Add filter bytes to appropriate lines
|
// Add filter bytes to passes that will be in the output image
|
||||||
0 => {
|
0 => {
|
||||||
passes[0].extend(BitVec::from_elem(8, false));
|
passes[0].extend(BitVec::from_elem(8, false));
|
||||||
passes[3].extend(BitVec::from_elem(8, false));
|
if png.ihdr_data.width >= 5 {
|
||||||
passes[5].extend(BitVec::from_elem(8, false));
|
|
||||||
if png.ihdr_data.width > 4 {
|
|
||||||
passes[1].extend(BitVec::from_elem(8, false));
|
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 => {
|
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));
|
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 => {
|
2 | 6 => {
|
||||||
passes[4].extend(BitVec::from_elem(8, false));
|
passes[4].extend(BitVec::from_elem(8, false));
|
||||||
|
if png.ihdr_data.width >= 2 {
|
||||||
passes[5].extend(BitVec::from_elem(8, false));
|
passes[5].extend(BitVec::from_elem(8, false));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
passes[6].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 bit_vec = BitVec::from_bytes(&line.data);
|
||||||
let bits_in_line = ((png.ihdr_data.width - pass_constants.x_shift as u32) as f32 /
|
let bits_in_line = ((png.ihdr_data.width - pass_constants.x_shift as u32) as f32 /
|
||||||
pass_constants.x_step as f32)
|
pass_constants.x_step as f32)
|
||||||
.ceil() as usize *
|
.ceil() as usize * bits_per_pixel as usize;
|
||||||
bits_per_pixel as usize;
|
|
||||||
for (i, bit) in bit_vec.iter().enumerate() {
|
for (i, bit) in bit_vec.iter().enumerate() {
|
||||||
// Avoid moving padded 0's into new image
|
// Avoid moving padded 0's into new image
|
||||||
if i >= bits_in_line {
|
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
|
// Cannot check if pixels are equal because image crate cannot read corrupt (input) PNGs
|
||||||
remove_file(output).ok();
|
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