Merge branch 'master' of github.com:shssoichiro/oxipng
|
|
@ -1,3 +1,8 @@
|
|||
**Version 0.3.0 (unreleased)**
|
||||
- Support interlaced images
|
||||
- Allow converting between progressive and interlaced images
|
||||
- Fix a bug that would cause oxipng to crash on very small images
|
||||
|
||||
**Version 0.2.2**
|
||||
- Limit number of threads to 1.5x number of cores
|
||||
- Significantly improve memory usage, especially with high optimization levels. ([#32](https://github.com/shssoichiro/oxipng/issues/32))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Oxipng
|
||||
|
||||
[](https://travis-ci.org/shssoichiro/oxipng)
|
||||
[](https://travis-ci.org/shssoichiro/oxipng)
|
||||
[](https://crates.io/crates/oxipng)
|
||||
[](https://github.com/shssoichiro/oxipng/blob/master/LICENSE)
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ More advanced options can be found by running `oxipng -h`.
|
|||
|
||||
## History
|
||||
|
||||
Oxipng began as a completely rewrite of the OptiPNG project,
|
||||
Oxipng began as a complete rewrite of the OptiPNG project,
|
||||
which is assumed to be dead as no commit has been made to it since March 2014.
|
||||
The name has been changed to avoid confusion and potential legal issues.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use libz_sys;
|
||||
use libc::c_int;
|
||||
use std::cmp::max;
|
||||
|
||||
/// Decompress a data stream using the DEFLATE algorithm
|
||||
pub fn inflate(data: &[u8]) -> Result<Vec<u8>, String> {
|
||||
|
|
@ -25,10 +26,12 @@ pub fn deflate(data: &[u8], zc: u8, zm: u8, zs: u8, zw: u8) -> Result<Vec<u8>, S
|
|||
zw as c_int,
|
||||
zm as c_int,
|
||||
zs as c_int);
|
||||
let mut output = Vec::with_capacity(data.len() / 20);
|
||||
// Compressed input should be smaller than decompressed, so allocate less than data.len()
|
||||
// However, it needs a minimum capacity in order to handle very small images
|
||||
let mut output = Vec::with_capacity(max(1024, data.len() / 20));
|
||||
loop {
|
||||
match stream.compress_vec(input.as_mut(), output.as_mut()) {
|
||||
libz_sys::Z_OK => output.reserve(data.len() / 20),
|
||||
libz_sys::Z_OK => output.reserve(max(1024, data.len() / 20)),
|
||||
libz_sys::Z_STREAM_END => break,
|
||||
c => return Err(format!("Error code on compress: {}", c)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,10 +186,8 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
|||
|
||||
if let Some(interlacing) = opts.interlace {
|
||||
if png.change_interlacing(interlacing) {
|
||||
png.ihdr_data.interlaced = interlacing;
|
||||
something_changed = true;
|
||||
if opts.verbosity == Some(1) {
|
||||
report_reduction(&png);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
310
src/png.rs
|
|
@ -123,6 +123,8 @@ pub struct ScanLines<'a> {
|
|||
pub png: &'a PngData,
|
||||
start: usize,
|
||||
end: usize,
|
||||
/// Current pass number, and 0-indexed row within the pass
|
||||
pass: Option<(u8, u32)>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ScanLines<'a> {
|
||||
|
|
@ -130,12 +132,97 @@ impl<'a> Iterator for ScanLines<'a> {
|
|||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.end == self.png.raw_data.len() {
|
||||
None
|
||||
} else if self.png.ihdr_data.interlaced == 1 {
|
||||
// Scanlines for interlaced PNG files
|
||||
if self.pass.is_none() {
|
||||
self.pass = Some((1, 0));
|
||||
}
|
||||
// Handle edge cases for images smaller than 5 pixels in either direction
|
||||
if self.png.ihdr_data.width < 5 && self.pass.unwrap().0 == 2 {
|
||||
if let Some(pass) = self.pass.as_mut() {
|
||||
pass.0 = 3;
|
||||
pass.1 = 4;
|
||||
}
|
||||
}
|
||||
// Intentionally keep these separate so that they can be applied one after another
|
||||
if self.png.ihdr_data.height < 5 && self.pass.unwrap().0 == 3 {
|
||||
if let Some(pass) = self.pass.as_mut() {
|
||||
pass.0 = 4;
|
||||
pass.1 = 0;
|
||||
}
|
||||
}
|
||||
let bits_per_pixel = self.png.ihdr_data.bit_depth.as_u8() as usize *
|
||||
self.png.channels_per_pixel() as usize;
|
||||
let mut bits_per_line = self.png.ihdr_data.width as usize * bits_per_pixel;
|
||||
let y_steps;
|
||||
match self.pass {
|
||||
Some((1, _)) | Some((2, _)) => {
|
||||
bits_per_line = (bits_per_line as f32 / 8f32).ceil() as usize;
|
||||
y_steps = 8;
|
||||
}
|
||||
Some((3, _)) => {
|
||||
bits_per_line = (bits_per_line as f32 / 4f32).ceil() as usize;
|
||||
y_steps = 8;
|
||||
}
|
||||
Some((4, _)) => {
|
||||
bits_per_line = (bits_per_line as f32 / 4f32).ceil() as usize;
|
||||
y_steps = 4;
|
||||
}
|
||||
Some((5, _)) => {
|
||||
bits_per_line = (bits_per_line as f32 / 2f32).ceil() as usize;
|
||||
y_steps = 4;
|
||||
}
|
||||
Some((6, _)) => {
|
||||
bits_per_line = (bits_per_line as f32 / 2f32).ceil() as usize;
|
||||
y_steps = 2;
|
||||
}
|
||||
Some((7, _)) => {
|
||||
y_steps = 2;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// Determine whether to trim the last (overflow) pixel for rows on this pass
|
||||
let gap = bits_per_line % bits_per_pixel;
|
||||
if gap != 0 {
|
||||
let x_start = bits_per_pixel *
|
||||
match self.pass.unwrap().0 {
|
||||
2 => 4,
|
||||
4 => 2,
|
||||
6 => 1,
|
||||
_ => 0,
|
||||
};
|
||||
if gap >= x_start {
|
||||
bits_per_line += bits_per_pixel - gap;
|
||||
} else {
|
||||
bits_per_line -= gap;
|
||||
}
|
||||
}
|
||||
let bytes_per_line = (bits_per_line as f32 / 8f32).ceil() as usize;
|
||||
self.start = self.end;
|
||||
self.end = self.start + bytes_per_line + 1;
|
||||
if let Some(pass) = self.pass.as_mut() {
|
||||
if pass.1 + y_steps >= self.png.ihdr_data.height {
|
||||
pass.0 += 1;
|
||||
pass.1 = match pass.0 {
|
||||
3 => 4,
|
||||
5 => 2,
|
||||
7 => 1,
|
||||
_ => 0,
|
||||
};
|
||||
} else {
|
||||
pass.1 += y_steps;
|
||||
}
|
||||
}
|
||||
Some(ScanLine {
|
||||
filter: self.png.raw_data[self.start],
|
||||
data: self.png.raw_data[(self.start + 1)..self.end].to_owned(),
|
||||
})
|
||||
} else {
|
||||
// Standard, non-interlaced PNG scanlines
|
||||
let bits_per_line = self.png.ihdr_data.width as usize *
|
||||
self.png.ihdr_data.bit_depth.as_u8() as usize *
|
||||
self.png.channels_per_pixel() as usize;
|
||||
// Round up without converting to float
|
||||
let bytes_per_line = (bits_per_line + bits_per_line % 8) >> 3;
|
||||
let bytes_per_line = (bits_per_line as f32 / 8f32).ceil() as usize;
|
||||
self.start = self.end;
|
||||
self.end = self.start + bytes_per_line + 1;
|
||||
Some(ScanLine {
|
||||
|
|
@ -338,6 +425,7 @@ impl PngData {
|
|||
png: &self,
|
||||
start: 0,
|
||||
end: 0,
|
||||
pass: None,
|
||||
}
|
||||
}
|
||||
/// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream
|
||||
|
|
@ -542,13 +630,225 @@ impl PngData {
|
|||
}
|
||||
/// Convert the image to the specified interlacing type
|
||||
/// Returns true if the interlacing was changed, false otherwise
|
||||
/// The `interlace` parameter specifies the *new* interlacing mode
|
||||
/// Assumes that the data has already been de-filtered
|
||||
pub fn change_interlacing(&mut self, interlace: u8) -> bool {
|
||||
// TODO: Implement
|
||||
if interlace != self.ihdr_data.interlaced {
|
||||
if interlace == self.ihdr_data.interlaced {
|
||||
return false;
|
||||
}
|
||||
|
||||
false
|
||||
if interlace == 1 {
|
||||
// Convert progressive to interlaced data
|
||||
interlace_image(self);
|
||||
} else {
|
||||
// Convert interlaced to progressive data
|
||||
deinterlace_image(self);
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn interlace_image(png: &mut PngData) {
|
||||
let mut passes: Vec<BitVec> = Vec::with_capacity(7);
|
||||
for _ in 0..7 {
|
||||
passes.push(BitVec::new());
|
||||
}
|
||||
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
|
||||
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 {
|
||||
passes[1].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));
|
||||
}
|
||||
2 | 6 => {
|
||||
passes[4].extend(BitVec::from_elem(8, false));
|
||||
passes[5].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
_ => {
|
||||
passes[6].extend(BitVec::from_elem(8, false));
|
||||
}
|
||||
}
|
||||
let bit_vec = BitVec::from_bytes(&line.data);
|
||||
for (i, bit) in bit_vec.iter().enumerate() {
|
||||
// Avoid moving padded 0's into new image
|
||||
if i >= (png.ihdr_data.width * bits_per_pixel as u32) as usize {
|
||||
break;
|
||||
}
|
||||
// Copy pixels into interlaced passes
|
||||
let pix_modulo = (((i / bits_per_pixel as usize) as f32).floor() as usize) % 8;
|
||||
match index % 8 {
|
||||
0 => {
|
||||
match pix_modulo {
|
||||
0 => passes[0].push(bit),
|
||||
4 => passes[1].push(bit),
|
||||
2 | 6 => passes[3].push(bit),
|
||||
_ => passes[5].push(bit),
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
match pix_modulo {
|
||||
0 | 4 => passes[2].push(bit),
|
||||
2 | 6 => passes[3].push(bit),
|
||||
_ => passes[5].push(bit),
|
||||
}
|
||||
}
|
||||
2 | 6 => {
|
||||
match pix_modulo % 2 {
|
||||
0 => passes[4].push(bit),
|
||||
_ => passes[5].push(bit),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
passes[6].push(bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pad end of line on each pass to get 8 bits per byte
|
||||
for pass in &mut passes {
|
||||
while pass.len() % 8 != 0 {
|
||||
pass.push(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut output = Vec::new();
|
||||
for pass in &passes {
|
||||
output.extend(pass.to_bytes());
|
||||
}
|
||||
png.raw_data = output;
|
||||
}
|
||||
|
||||
fn deinterlace_image(png: &mut PngData) {
|
||||
let bits_per_pixel = png.ihdr_data.bit_depth.as_u8() * png.channels_per_pixel();
|
||||
let mut lines: Vec<BitVec> = Vec::with_capacity(png.ihdr_data.height as usize);
|
||||
for _ in 0..png.ihdr_data.height {
|
||||
// Initialize each output line with a starting filter byte of 0
|
||||
// as well as some blank data
|
||||
lines.push(BitVec::from_elem(8 + bits_per_pixel as usize * png.ihdr_data.width as usize,
|
||||
false));
|
||||
}
|
||||
let mut current_pass = 1;
|
||||
let mut pass_constants = interlaced_constants(current_pass);
|
||||
let mut current_y: usize = pass_constants.y_shift as usize;
|
||||
for line in png.scan_lines() {
|
||||
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;
|
||||
for (i, bit) in bit_vec.iter().enumerate() {
|
||||
// Avoid moving padded 0's into new image
|
||||
if i >= bits_in_line {
|
||||
break;
|
||||
}
|
||||
let current_x: usize = pass_constants.x_shift as usize +
|
||||
(i / bits_per_pixel as usize) * pass_constants.x_step as usize;
|
||||
// Copy this bit into the output line, offset by 8 because of filter byte
|
||||
let index = 8 + (i % bits_per_pixel as usize) + current_x * bits_per_pixel as usize;
|
||||
lines[current_y].set(index, bit);
|
||||
}
|
||||
// Calculate the next line and move to next pass if necessary
|
||||
current_y += pass_constants.y_step as usize;
|
||||
if current_y >= png.ihdr_data.height as usize {
|
||||
if current_pass == 7 {
|
||||
break;
|
||||
}
|
||||
current_pass += 1;
|
||||
if current_pass == 2 && png.ihdr_data.width <= 4 {
|
||||
current_pass += 1;
|
||||
}
|
||||
if current_pass == 3 && png.ihdr_data.height <= 4 {
|
||||
current_pass += 1;
|
||||
}
|
||||
pass_constants = interlaced_constants(current_pass);
|
||||
current_y = pass_constants.y_shift as usize;
|
||||
}
|
||||
}
|
||||
let mut output = Vec::new();
|
||||
for line in &mut lines {
|
||||
while line.len() % 8 != 0 {
|
||||
line.push(false);
|
||||
}
|
||||
output.extend(line.to_bytes());
|
||||
}
|
||||
png.raw_data = output;
|
||||
}
|
||||
|
||||
struct InterlacedConstants {
|
||||
x_shift: u8,
|
||||
y_shift: u8,
|
||||
x_step: u8,
|
||||
y_step: u8,
|
||||
}
|
||||
|
||||
fn interlaced_constants(pass: u8) -> InterlacedConstants {
|
||||
match pass {
|
||||
1 => {
|
||||
InterlacedConstants {
|
||||
x_shift: 0,
|
||||
y_shift: 0,
|
||||
x_step: 8,
|
||||
y_step: 8,
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
InterlacedConstants {
|
||||
x_shift: 4,
|
||||
y_shift: 0,
|
||||
x_step: 8,
|
||||
y_step: 8,
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
InterlacedConstants {
|
||||
x_shift: 0,
|
||||
y_shift: 4,
|
||||
x_step: 4,
|
||||
y_step: 8,
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
InterlacedConstants {
|
||||
x_shift: 2,
|
||||
y_shift: 0,
|
||||
x_step: 4,
|
||||
y_step: 4,
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
InterlacedConstants {
|
||||
x_shift: 0,
|
||||
y_shift: 2,
|
||||
x_step: 2,
|
||||
y_step: 4,
|
||||
}
|
||||
}
|
||||
6 => {
|
||||
InterlacedConstants {
|
||||
x_shift: 1,
|
||||
y_shift: 0,
|
||||
x_step: 2,
|
||||
y_step: 2,
|
||||
}
|
||||
}
|
||||
7 => {
|
||||
InterlacedConstants {
|
||||
x_shift: 0,
|
||||
y_shift: 1,
|
||||
x_step: 1,
|
||||
y_step: 2,
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
tests/files/interlaced_grayscale_16_should_be_grayscale_16.png
Normal file
|
After Width: | Height: | Size: 235 KiB |
BIN
tests/files/interlaced_grayscale_16_should_be_grayscale_8.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
tests/files/interlaced_grayscale_16_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_grayscale_16_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
tests/files/interlaced_grayscale_16_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_grayscale_8_should_be_grayscale_8.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
tests/files/interlaced_grayscale_8_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
tests/files/interlaced_grayscale_8_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
tests/files/interlaced_grayscale_8_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 114 KiB |
BIN
tests/files/interlaced_grayscale_alpha_8_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_grayscale_alpha_8_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_grayscale_alpha_8_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
tests/files/interlaced_palette_1_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/files/interlaced_palette_2_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/files/interlaced_palette_2_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
tests/files/interlaced_palette_4_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
tests/files/interlaced_palette_4_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
tests/files/interlaced_palette_4_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
tests/files/interlaced_palette_8_should_be_grayscale_8.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
tests/files/interlaced_palette_8_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
tests/files/interlaced_palette_8_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
tests/files/interlaced_palette_8_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
tests/files/interlaced_palette_8_should_be_palette_8.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_grayscale_16.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_grayscale_8.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_palette_1_grayscale.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_palette_2_grayscale.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_palette_4_grayscale.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_palette_8.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_rgb_16.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
tests/files/interlaced_rgb_16_should_be_rgb_8.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_grayscale_8.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_palette_1_grayscale.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_palette_2_grayscale.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_palette_4_grayscale.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_palette_8.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
tests/files/interlaced_rgb_8_should_be_rgb_8.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_grayscale_16.png
Normal file
|
After Width: | Height: | Size: 365 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_grayscale_8.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_grayscale_alpha_16.png
Normal file
|
After Width: | Height: | Size: 483 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_grayscale_alpha_8.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_palette_1_grayscale.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_palette_2_grayscale.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_palette_4_grayscale.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_palette_8.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_rgb_16.png
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_rgb_8.png
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_rgba_16.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
tests/files/interlaced_rgba_16_should_be_rgba_8.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_grayscale_8.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_grayscale_alpha_8.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_palette_1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_palette_1_grayscale.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_palette_2.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_palette_2_grayscale.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_palette_4.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_palette_4_grayscale.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_palette_8.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_rgb_8.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
tests/files/interlaced_rgba_8_should_be_rgba_8.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
tests/files/interlaced_small_files.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
tests/files/interlacing_0_to_1.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
tests/files/interlacing_0_to_1_small_files.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
tests/files/interlacing_1_to_0.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
tests/files/interlacing_1_to_0_small_files.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
tests/files/small_files.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
156
tests/flags.rs
|
|
@ -267,3 +267,159 @@ fn strip_headers_none() {
|
|||
|
||||
remove_file(output).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interlacing_0_to_1() {
|
||||
let input = PathBuf::from("tests/files/interlacing_0_to_1.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.interlace = Some(1);
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 0);
|
||||
|
||||
match oxipng::optimize(&input, &opts) {
|
||||
Ok(_) => (),
|
||||
Err(x) => panic!(x.to_owned()),
|
||||
};
|
||||
assert!(output.exists());
|
||||
|
||||
let png = match png::PngData::new(&output) {
|
||||
Ok(x) => x,
|
||||
Err(x) => {
|
||||
remove_file(output).ok();
|
||||
panic!(x.to_owned())
|
||||
}
|
||||
};
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 1);
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert!(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();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interlacing_1_to_0() {
|
||||
let input = PathBuf::from("tests/files/interlacing_1_to_0.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.interlace = Some(0);
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 1);
|
||||
|
||||
match oxipng::optimize(&input, &opts) {
|
||||
Ok(_) => (),
|
||||
Err(x) => panic!(x.to_owned()),
|
||||
};
|
||||
assert!(output.exists());
|
||||
|
||||
let png = match png::PngData::new(&output) {
|
||||
Ok(x) => x,
|
||||
Err(x) => {
|
||||
remove_file(output).ok();
|
||||
panic!(x.to_owned())
|
||||
}
|
||||
};
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 0);
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert!(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();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interlacing_0_to_1_small_files() {
|
||||
let input = PathBuf::from("tests/files/interlacing_0_to_1_small_files.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.interlace = Some(1);
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 0);
|
||||
assert!(png.ihdr_data.color_type == png::ColorType::Indexed);
|
||||
assert!(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) {
|
||||
Ok(x) => x,
|
||||
Err(x) => {
|
||||
remove_file(output).ok();
|
||||
panic!(x.to_owned())
|
||||
}
|
||||
};
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 1);
|
||||
assert!(png.ihdr_data.color_type == png::ColorType::Indexed);
|
||||
assert!(png.ihdr_data.bit_depth == png::BitDepth::One);
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert!(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();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interlacing_1_to_0_small_files() {
|
||||
let input = PathBuf::from("tests/files/interlacing_1_to_0_small_files.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.interlace = Some(0);
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 1);
|
||||
assert!(png.ihdr_data.color_type == png::ColorType::Indexed);
|
||||
assert!(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) {
|
||||
Ok(x) => x,
|
||||
Err(x) => {
|
||||
remove_file(output).ok();
|
||||
panic!(x.to_owned())
|
||||
}
|
||||
};
|
||||
|
||||
assert!(png.ihdr_data.interlaced == 0);
|
||||
assert!(png.ihdr_data.color_type == png::ColorType::Indexed);
|
||||
assert!(png.ihdr_data.bit_depth == png::BitDepth::One);
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert!(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();
|
||||
}
|
||||
|
|
|
|||
1276
tests/interlaced.rs
Normal file
|
|
@ -60,6 +60,7 @@ fn test_it_converts(input: &Path,
|
|||
|
||||
assert!(png.ihdr_data.color_type == color_type_in);
|
||||
assert!(png.ihdr_data.bit_depth == bit_depth_in);
|
||||
assert!(png.ihdr_data.interlaced == 0);
|
||||
|
||||
match oxipng::optimize(input, opts) {
|
||||
Ok(_) => (),
|
||||
|
|
@ -1258,3 +1259,18 @@ fn grayscale_8_should_be_palette_1() {
|
|||
png::ColorType::Indexed,
|
||||
png::BitDepth::One);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn small_files() {
|
||||
let input = PathBuf::from("tests/files/small_files.png");
|
||||
let opts = get_opts(&input);
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
test_it_converts(&input,
|
||||
&output,
|
||||
&opts,
|
||||
png::ColorType::Indexed,
|
||||
png::BitDepth::Eight,
|
||||
png::ColorType::Indexed,
|
||||
png::BitDepth::One);
|
||||
}
|
||||
|
|
|
|||