diff --git a/src/deflate/deflate.rs b/src/deflate/deflate.rs index e8044770..a146418b 100644 --- a/src/deflate/deflate.rs +++ b/src/deflate/deflate.rs @@ -1,5 +1,5 @@ use libz_sys; -use libc; +use libc::c_int; pub fn inflate(data: &[u8]) -> Result, String> { let mut input = data.to_owned(); @@ -19,10 +19,10 @@ pub fn inflate(data: &[u8]) -> Result, String> { pub fn deflate(data: &[u8], zc: u8, zm: u8, zs: u8, zw: u8) -> Result, String> { let mut input = data.to_owned(); - let mut stream = super::stream::Stream::new_compress(zc as libc::c_int, - zw as libc::c_int, - zm as libc::c_int, - zs as libc::c_int); + let mut stream = super::stream::Stream::new_compress(zc as c_int, + zw as c_int, + zm as c_int, + zs as c_int); let mut output = Vec::with_capacity(data.len() / 20); loop { match stream.compress_vec(input.as_mut(), output.as_mut()) { diff --git a/src/lib.rs b/src/lib.rs index 096cf459..f4f5e5aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,15 +5,10 @@ extern crate crossbeam; extern crate libc; extern crate libz_sys; -use std::collections::HashMap; -use std::collections::HashSet; -use std::fs; -use std::fs::File; -use std::io; -use std::io::BufWriter; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; +use std::collections::{HashMap, HashSet}; +use std::fs::{File, copy}; +use std::io::{BufWriter, Write, stdout}; +use std::path::{Path, PathBuf}; pub mod deflate { pub mod deflate; @@ -79,40 +74,43 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { let mut something_changed = false; - if opts.color_type_reduction { - if png.reduce_color_type() { - something_changed = true; - }; - } - if opts.bit_depth_reduction { if png.reduce_bit_depth() { something_changed = true; + if opts.verbosity == Some(1) { + report_reduction(&png); + } + }; + } + + if opts.color_type_reduction { + if png.reduce_color_type() { + something_changed = true; + if opts.verbosity == Some(1) { + report_reduction(&png); + } }; } if opts.palette_reduction { if png.reduce_palette() { something_changed = true; + if opts.verbosity == Some(1) { + report_reduction(&png); + } }; } - if something_changed { - if let Some(palette) = png.palette.clone() { - println!("Reducing image to {} bits/pixel, {} colors in palette", - png.ihdr_data.bit_depth, - palette.len() / 3); - } else { - println!("Reducing image to {}x{} bits/pixel, {}", - png.channels_per_pixel(), - png.ihdr_data.bit_depth, - png.ihdr_data.color_type); - } + if something_changed && opts.verbosity.is_some() { + report_reduction(&png); } if let Some(interlacing) = opts.interlace { if png.change_interlacing(interlacing) { something_changed = true; + if opts.verbosity == Some(1) { + report_reduction(&png); + } } } @@ -159,7 +157,8 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { for result in results { if let Ok(ok_result) = result.join() { - if (best.is_some() && ok_result.4.len() < best.clone().unwrap().4.len()) || + if (best.is_some() && + ok_result.4.len() < best.as_ref().map(|x| x.4.len()).unwrap()) || (best.is_none() && (ok_result.4.len() < png.idat_data.len() || (opts.interlace.is_some() && @@ -197,12 +196,12 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { println!("Running in pretend mode, no output"); } else { if opts.backup { - match fs::copy(in_file, - in_file.with_extension(format!("bak.{}", - in_file.extension() - .unwrap() - .to_str() - .unwrap()))) { + match copy(in_file, + in_file.with_extension(format!("bak.{}", + in_file.extension() + .unwrap() + .to_str() + .unwrap()))) { Ok(x) => x, Err(_) => { return Err(format!("Unable to write to backup file at {}", @@ -212,7 +211,7 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { } if opts.stdout { - let mut buffer = BufWriter::new(io::stdout()); + let mut buffer = BufWriter::new(stdout()); match buffer.write_all(&output_data) { Ok(_) => (), Err(_) => return Err("Unable to write to stdout".to_owned()), @@ -258,3 +257,16 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { Ok(()) } + +fn report_reduction(png: &png::PngData) { + if let Some(palette) = png.palette.clone() { + println!("Reducing image to {} bits/pixel, {} colors in palette", + png.ihdr_data.bit_depth, + palette.len() / 3); + } else { + println!("Reducing image to {}x{} bits/pixel, {}", + png.channels_per_pixel(), + png.ihdr_data.bit_depth, + png.ihdr_data.color_type); + } +} diff --git a/src/png.rs b/src/png.rs index 04ef2dd6..c11db699 100644 --- a/src/png.rs +++ b/src/png.rs @@ -90,28 +90,22 @@ impl BitDepth { } #[derive(Debug,Clone)] -struct ScanLines<'a> { - png: &'a PngData, +pub struct ScanLines<'a> { + pub png: &'a PngData, start: usize, end: usize, } -impl<'a> ScanLines<'a> { - fn len(&self) -> usize { - self.png.raw_data.len() - } -} - impl<'a> Iterator for ScanLines<'a> { type Item = ScanLine; fn next(&mut self) -> Option { - if self.end == self.len() { + if self.end == self.png.raw_data.len() { None } else { 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; - // This avoids casting to and from floats, which is expensive + // Round up without converting to float let bytes_per_line = (bits_per_line + bits_per_line % 8) >> 3; self.start = self.end; self.end = self.start + bytes_per_line + 1; @@ -124,9 +118,9 @@ impl<'a> Iterator for ScanLines<'a> { } #[derive(Debug,Clone)] -struct ScanLine { - filter: u8, - data: Vec, +pub struct ScanLine { + pub filter: u8, + pub data: Vec, } #[derive(Debug,Clone)] @@ -339,7 +333,7 @@ impl PngData { output } - fn scan_lines(&self) -> ScanLines { + pub fn scan_lines(&self) -> ScanLines { ScanLines { png: &self, start: 0, @@ -348,8 +342,8 @@ impl PngData { } pub fn unfilter_image(&self) -> Vec { let mut unfiltered = Vec::with_capacity(self.raw_data.len()); - // This avoids casting to and from floats, which is expensive let tmp = self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel(); + // Round up without converting to float let bpp = (tmp + tmp % 8) >> 3; let mut last_line: Vec = vec![]; for line in self.scan_lines() { @@ -375,10 +369,10 @@ impl PngData { 2 => { let mut data = Vec::with_capacity(line.data.len()); for (i, byte) in line.data.iter().enumerate() { - if !last_line.is_empty() { - data.push(byte.wrapping_add(last_line[i])); - } else { + if last_line.is_empty() { data.push(*byte); + } else { + data.push(byte.wrapping_add(last_line[i])); }; } last_line = data.clone(); @@ -387,7 +381,15 @@ impl PngData { 3 => { let mut data = Vec::with_capacity(line.data.len()); for (i, byte) in line.data.iter().enumerate() { - if !last_line.is_empty() { + if last_line.is_empty() { + match i.checked_sub(bpp as usize) { + Some(x) => { + let b = data[x]; + data.push(byte.wrapping_add(b >> 1)) + } + None => data.push(*byte), + }; + } else { match i.checked_sub(bpp as usize) { Some(x) => { let b = data[x]; @@ -397,14 +399,6 @@ impl PngData { } None => data.push(byte.wrapping_add(last_line[i] >> 1)), }; - } else { - match i.checked_sub(bpp as usize) { - Some(x) => { - let b = data[x]; - data.push(byte.wrapping_add(b >> 1)) - } - None => data.push(*byte), - }; }; } last_line = data.clone(); @@ -413,7 +407,15 @@ impl PngData { 4 => { let mut data = Vec::with_capacity(line.data.len()); for (i, byte) in line.data.iter().enumerate() { - if !last_line.is_empty() { + if last_line.is_empty() { + match i.checked_sub(bpp as usize) { + Some(x) => { + let b = data[x]; + data.push(byte.wrapping_add(b)) + } + None => data.push(*byte), + }; + } else { match i.checked_sub(bpp as usize) { Some(x) => { let b = data[x]; @@ -423,14 +425,6 @@ impl PngData { } None => data.push(byte.wrapping_add(last_line[i])), }; - } else { - match i.checked_sub(bpp as usize) { - Some(x) => { - let b = data[x]; - data.push(byte.wrapping_add(b)) - } - None => data.push(*byte), - }; }; } last_line = data.clone(); @@ -443,8 +437,8 @@ impl PngData { } pub fn filter_image(&self, filter: u8) -> Vec { let mut filtered = Vec::with_capacity(self.raw_data.len()); - // This avoids casting to and from floats, which is expensive let tmp = self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel(); + // Round up without converting to float let bpp = (tmp + tmp % 8) >> 3; let mut last_line: Vec = vec![]; // We could try a different filter method for each line @@ -468,33 +462,38 @@ impl PngData { } 2 => { for (i, byte) in line.data.iter().enumerate() { - if !last_line.is_empty() { - filtered.push(byte.wrapping_sub(last_line[i])); - } else { + if last_line.is_empty() { filtered.push(*byte); + } else { + filtered.push(byte.wrapping_sub(last_line[i])); }; } } 3 => { for (i, byte) in line.data.iter().enumerate() { - if !last_line.is_empty() { + if last_line.is_empty() { + filtered.push(match i.checked_sub(bpp as usize) { + Some(x) => byte.wrapping_sub(line.data[x] >> 1), + None => *byte, + }); + } else { filtered.push(match i.checked_sub(bpp as usize) { Some(x) => byte.wrapping_sub( ((line.data[x] as u16 + last_line[i] as u16) >> 1) as u8 ), None => byte.wrapping_sub(last_line[i] >> 1), }); - } else { - filtered.push(match i.checked_sub(bpp as usize) { - Some(x) => byte.wrapping_sub(line.data[x] >> 1), - None => *byte, - }); }; } } 4 => { for (i, byte) in line.data.iter().enumerate() { - if !last_line.is_empty() { + if last_line.is_empty() { + filtered.push(match i.checked_sub(bpp as usize) { + Some(x) => byte.wrapping_sub(line.data[x]), + None => *byte, + }); + } else { filtered.push(match i.checked_sub(bpp as usize) { Some(x) => { byte.wrapping_sub(paeth_predictor(line.data[x], @@ -503,11 +502,6 @@ impl PngData { } None => byte.wrapping_sub(last_line[i]), }); - } else { - filtered.push(match i.checked_sub(bpp as usize) { - Some(x) => byte.wrapping_sub(line.data[x]), - None => *byte, - }); }; } } @@ -525,7 +519,22 @@ impl PngData { let mut line_3 = Vec::with_capacity(line.data.len()); let mut line_4 = Vec::with_capacity(line.data.len()); for (i, byte) in line.data.iter().enumerate() { - if !last_line.is_empty() { + if last_line.is_empty() { + match i.checked_sub(bpp as usize) { + Some(x) => { + line_1.push(byte.wrapping_sub(line.data[x])); + line_2.push(*byte); + line_3.push(byte.wrapping_sub(line.data[x] >> 1)); + line_4.push(byte.wrapping_sub(line.data[x])); + } + None => { + line_1.push(*byte); + line_2.push(*byte); + line_3.push(*byte); + line_4.push(*byte); + } + } + } else { match i.checked_sub(bpp as usize) { Some(x) => { line_1.push(byte.wrapping_sub(line.data[x])); @@ -544,21 +553,6 @@ impl PngData { line_4.push(byte.wrapping_sub(last_line[i])); } } - } else { - match i.checked_sub(bpp as usize) { - Some(x) => { - line_1.push(byte.wrapping_sub(line.data[x])); - line_2.push(*byte); - line_3.push(byte.wrapping_sub(line.data[x] >> 1)); - line_4.push(byte.wrapping_sub(line.data[x])); - } - None => { - line_1.push(*byte); - line_2.push(*byte); - line_3.push(*byte); - line_4.push(*byte); - } - } }; } @@ -606,33 +600,42 @@ impl PngData { if self.ihdr_data.color_type == ColorType::Indexed || self.ihdr_data.color_type == ColorType::Grayscale { return match reduce_bit_depth_8_or_less(self) { - Some(_) => true, + Some((data, depth)) => { + self.raw_data = data; + self.ihdr_data.bit_depth = BitDepth::from_u8(depth); + true + } None => false, }; } return false; } - // It's difficult to estimate without knowing the number of ScanLines - // So we overallocate to prioritize speed over memory efficiency - let mut reduced = Vec::with_capacity(self.raw_data.len()); + // Reduce from 16 to 8 bits per channel per pixel + 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); + let mut high_byte = 0; for line in self.scan_lines() { reduced.push(line.filter); for (i, byte) in line.data.iter().enumerate() { if i % 2 == 0 { // High byte - if *byte != 0 { + high_byte = *byte; + } else { + // Low byte + if high_byte != *byte { // Can't reduce, exit early return false; } - } else { - // Low byte reduced.push(*byte); } } } + self.ihdr_data.bit_depth = BitDepth::Eight; self.raw_data = reduced; true } @@ -647,7 +650,6 @@ impl PngData { // Go down one step at a time // Maybe not the most efficient, but it's safe if self.ihdr_data.color_type == ColorType::RGBA { - // Do this first, it's more likely to exit early if let Some(data) = reduce_rgba_to_grayscale_alpha(self) { self.raw_data = data; self.ihdr_data.color_type = ColorType::GrayscaleAlpha; @@ -659,15 +661,33 @@ impl PngData { } else if let Some((data, palette, trans)) = reduce_rgba_to_palette(self) { self.raw_data = data; self.palette = Some(palette); - self.transparency_palette = Some(trans); + if trans.iter().any(|x| *x != 255) { + self.transparency_palette = Some(trans); + } else { + self.transparency_palette = None; + } self.ihdr_data.color_type = ColorType::Indexed; changed = true; should_reduce_bit_depth = true; } } + if self.ihdr_data.color_type == ColorType::GrayscaleAlpha { + if let Some(data) = reduce_grayscale_alpha_to_grayscale(self) { + self.raw_data = data; + self.ihdr_data.color_type = ColorType::Grayscale; + changed = true; + should_reduce_bit_depth = true; + } + } + if self.ihdr_data.color_type == ColorType::RGB { - if let Some((data, palette)) = reduce_rgb_to_palette(self) { + if let Some(data) = reduce_rgb_to_grayscale(self) { + self.raw_data = data; + self.ihdr_data.color_type = ColorType::Grayscale; + changed = true; + should_reduce_bit_depth = true; + } else if let Some((data, palette)) = reduce_rgb_to_palette(self) { self.raw_data = data; self.palette = Some(palette); self.ihdr_data.color_type = ColorType::Indexed; @@ -676,19 +696,20 @@ impl PngData { } } - if self.ihdr_data.color_type == ColorType::Indexed && self.transparency_palette.is_none() { + if self.ihdr_data.color_type == ColorType::Indexed && self.transparency_palette.is_none() && + self.palette.as_ref().map(|x| x.len()).unwrap() > 128 { if let Some(data) = reduce_palette_to_grayscale(self) { self.raw_data = data; self.palette = None; self.ihdr_data.color_type = ColorType::Grayscale; changed = true; + should_reduce_bit_depth = false; } - } - - if self.ihdr_data.color_type == ColorType::GrayscaleAlpha { - if let Some(data) = reduce_grayscale_alpha_to_grayscale(self) { + } else if self.ihdr_data.color_type == ColorType::Grayscale { + if let Some((data, palette)) = reduce_grayscale_to_palette(self) { self.raw_data = data; - self.ihdr_data.color_type = ColorType::Grayscale; + self.palette = Some(palette); + self.ihdr_data.color_type = ColorType::Indexed; changed = true; should_reduce_bit_depth = true; } @@ -743,6 +764,10 @@ fn reduce_bit_depth_8_or_less(png: &PngData) -> Option<(Vec, u8)> { reduced.push(bit); } } + // Pad end of line to get 8 bits per byte + while reduced.len() % 8 != 0 { + reduced.push(false); + } } Some((reduced.to_bytes(), allowed_bits as u8)) @@ -820,23 +845,23 @@ fn reduce_rgba_to_palette(png: &PngData) -> Option<(Vec, Vec, Vec)> } let mut reduced = Vec::with_capacity(png.raw_data.len()); let mut palette = Vec::with_capacity(256); - let bpp: usize = 4; + let bpp: usize = (4 * png.ihdr_data.bit_depth.as_u8() as usize) >> 3; for line in png.scan_lines() { reduced.push(line.filter); let mut cur_pixel = Vec::with_capacity(bpp); for (i, byte) in line.data.iter().enumerate() { cur_pixel.push(*byte); if i % bpp == bpp - 1 { - if !palette.contains(&cur_pixel) { + if palette.contains(&cur_pixel) { + let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0; + reduced.push(idx as u8); + } else { let len = palette.len(); if len == 256 { return None; } palette.push(cur_pixel.clone()); reduced.push(len as u8); - } else { - let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0; - reduced.push(idx as u8); } cur_pixel.clear(); } @@ -864,23 +889,23 @@ fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec, Vec)> { } let mut reduced = Vec::with_capacity(png.raw_data.len()); let mut palette = Vec::with_capacity(256); - let bpp: usize = 3; + let bpp: usize = (3 * png.ihdr_data.bit_depth.as_u8() as usize) >> 3; for line in png.scan_lines() { reduced.push(line.filter); let mut cur_pixel = Vec::with_capacity(bpp); for (i, byte) in line.data.iter().enumerate() { cur_pixel.push(*byte); if i % bpp == bpp - 1 { - if !palette.contains(&cur_pixel) { + if palette.contains(&cur_pixel) { + let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0; + reduced.push(idx as u8); + } else { let len = palette.len(); if len == 256 { return None; } palette.push(cur_pixel.clone()); reduced.push(len as u8); - } else { - let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0; - reduced.push(idx as u8); } cur_pixel.clear(); } @@ -895,6 +920,57 @@ fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec, Vec)> { Some((reduced, color_palette)) } +fn reduce_grayscale_to_palette(png: &PngData) -> Option<(Vec, Vec)> { + if png.ihdr_data.bit_depth == BitDepth::Sixteen { + return None; + } + let mut reduced = BitVec::with_capacity(png.raw_data.len() * 8); + // Only perform reduction if we can get to 4-bits or less + let mut palette = Vec::with_capacity(16); + let bpp: usize = png.ihdr_data.bit_depth.as_u8() as usize; + for line in png.scan_lines() { + reduced.extend(BitVec::from_bytes(&[line.filter])); + let bit_vec = BitVec::from_bytes(&line.data); + let mut cur_pixel = BitVec::with_capacity(bpp); + for (i, bit) in bit_vec.iter().enumerate() { + cur_pixel.push(bit); + if i % bpp == bpp - 1 { + let pix_value = cur_pixel.to_bytes()[0] >> (8 - bpp); + let pix_slice = vec![pix_value, pix_value, pix_value]; + if palette.contains(&pix_slice) { + let index = palette.iter().enumerate().find(|&x| x.1 == &pix_slice).unwrap().0; + let idx = BitVec::from_bytes(&[(index as u8) << (8 - bpp)]); + for b in idx.iter().take(bpp) { + reduced.push(b); + } + } else { + let len = palette.len(); + if len == 16 { + return None; + } + palette.push(pix_slice.clone()); + let idx = BitVec::from_bytes(&[(len as u8) << (8 - bpp)]); + for b in idx.iter().take(bpp) { + reduced.push(b); + } + } + cur_pixel = BitVec::with_capacity(bpp); + } + } + // Pad end of line to get 8 bits per byte + while reduced.len() % 8 != 0 { + reduced.push(false); + } + } + + let mut color_palette = Vec::with_capacity(palette.len() * 3); + for color in &palette { + color_palette.extend_from_slice(&color); + } + + Some((reduced.to_bytes(), color_palette)) +} + fn reduce_palette_to_grayscale(png: &PngData) -> Option> { let mut reduced = BitVec::with_capacity(png.raw_data.len() * 8); let mut cur_pixel = Vec::with_capacity(3); @@ -923,17 +999,66 @@ fn reduce_palette_to_grayscale(png: &PngData) -> Option> { // At the end of each pixel, push its grayscale value onto the reduced image cur_pixel.push(bit); if cur_pixel.len() == bit_depth { - let palette_idx: usize = cur_pixel.to_bytes()[0] as usize * 3; + // `to_bytes` gives us e.g. 10000000 for a 1-bit pixel, when we would want 00000001 + let padded_pixel = cur_pixel.to_bytes()[0] >> (8 - bit_depth); + let palette_idx: usize = padded_pixel as usize * 3; reduced.extend(BitVec::from_bytes(&[palette[palette_idx]])); // BitVec's clear function doesn't set len to 0 cur_pixel = BitVec::with_capacity(bit_depth); } } + // Pad end of line to get 8 bits per byte + while reduced.len() % 8 != 0 { + reduced.push(false); + } } Some(reduced.to_bytes()) } +fn reduce_rgb_to_grayscale(png: &PngData) -> Option> { + let mut reduced = Vec::with_capacity(png.raw_data.len()); + let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3; + let bpp: usize = 3 * byte_depth as usize; + let mut cur_pixel = Vec::with_capacity(bpp); + for line in png.scan_lines() { + reduced.push(line.filter); + for (i, byte) in line.data.iter().enumerate() { + cur_pixel.push(*byte); + if i % bpp == bpp - 1 { + if bpp == 3 { + cur_pixel.sort(); + cur_pixel.dedup(); + if cur_pixel.len() > 1 { + return None; + } + reduced.push(cur_pixel[0]); + } else { + let mut pixel_zip = cur_pixel.iter() + .enumerate() + .filter(|&(i, _)| i % 2 == 0) + .map(|(_, x)| *x) + .zip(cur_pixel.iter() + .enumerate() + .filter(|&(i, _)| i % 2 == 1) + .map(|(_, x)| *x)) + .collect::>(); + pixel_zip.sort(); + pixel_zip.dedup(); + if pixel_zip.len() > 1 { + return None; + } + reduced.push(pixel_zip[0].0); + reduced.push(pixel_zip[0].1); + } + cur_pixel.clear(); + } + } + } + + Some(reduced) +} + fn reduce_grayscale_alpha_to_grayscale(png: &PngData) -> Option> { let mut reduced = Vec::with_capacity(png.raw_data.len()); let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3; diff --git a/tests/files/grayscale_16_should_be_grayscale_16.png b/tests/files/grayscale_16_should_be_grayscale_16.png new file mode 100644 index 00000000..5eb0e8fc Binary files /dev/null and b/tests/files/grayscale_16_should_be_grayscale_16.png differ diff --git a/tests/files/grayscale_16_should_be_grayscale_8.png b/tests/files/grayscale_16_should_be_grayscale_8.png new file mode 100644 index 00000000..2492ff86 Binary files /dev/null and b/tests/files/grayscale_16_should_be_grayscale_8.png differ diff --git a/tests/files/grayscale_16_should_be_palette_1.png b/tests/files/grayscale_16_should_be_palette_1.png new file mode 100644 index 00000000..59ae4d0d Binary files /dev/null and b/tests/files/grayscale_16_should_be_palette_1.png differ diff --git a/tests/files/grayscale_16_should_be_palette_2.png b/tests/files/grayscale_16_should_be_palette_2.png new file mode 100644 index 00000000..31680af5 Binary files /dev/null and b/tests/files/grayscale_16_should_be_palette_2.png differ diff --git a/tests/files/grayscale_16_should_be_palette_4.png b/tests/files/grayscale_16_should_be_palette_4.png new file mode 100644 index 00000000..ce3bb08b Binary files /dev/null and b/tests/files/grayscale_16_should_be_palette_4.png differ diff --git a/tests/files/grayscale_8_should_be_grayscale_8.png b/tests/files/grayscale_8_should_be_grayscale_8.png new file mode 100644 index 00000000..21ee6424 Binary files /dev/null and b/tests/files/grayscale_8_should_be_grayscale_8.png differ diff --git a/tests/files/grayscale_8_should_be_palette_1.png b/tests/files/grayscale_8_should_be_palette_1.png new file mode 100644 index 00000000..61bee368 Binary files /dev/null and b/tests/files/grayscale_8_should_be_palette_1.png differ diff --git a/tests/files/grayscale_8_should_be_palette_2.png b/tests/files/grayscale_8_should_be_palette_2.png new file mode 100644 index 00000000..5933b710 Binary files /dev/null and b/tests/files/grayscale_8_should_be_palette_2.png differ diff --git a/tests/files/grayscale_8_should_be_palette_4.png b/tests/files/grayscale_8_should_be_palette_4.png new file mode 100644 index 00000000..66138edb Binary files /dev/null and b/tests/files/grayscale_8_should_be_palette_4.png differ diff --git a/tests/files/grayscale_alpha_16_should_be_grayscale_16.png b/tests/files/grayscale_alpha_16_should_be_grayscale_16.png new file mode 100644 index 00000000..65716043 Binary files /dev/null and b/tests/files/grayscale_alpha_16_should_be_grayscale_16.png differ diff --git a/tests/files/grayscale_alpha_16_should_be_grayscale_8.png b/tests/files/grayscale_alpha_16_should_be_grayscale_8.png new file mode 100644 index 00000000..1a227cec Binary files /dev/null and b/tests/files/grayscale_alpha_16_should_be_grayscale_8.png differ diff --git a/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png new file mode 100644 index 00000000..4291b283 Binary files /dev/null and b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png differ diff --git a/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png new file mode 100644 index 00000000..e4b33085 Binary files /dev/null and b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png differ diff --git a/tests/files/grayscale_alpha_16_should_be_palette_1.png b/tests/files/grayscale_alpha_16_should_be_palette_1.png new file mode 100644 index 00000000..129110a3 Binary files /dev/null and b/tests/files/grayscale_alpha_16_should_be_palette_1.png differ diff --git a/tests/files/grayscale_alpha_16_should_be_palette_2.png b/tests/files/grayscale_alpha_16_should_be_palette_2.png new file mode 100644 index 00000000..d0538235 Binary files /dev/null and b/tests/files/grayscale_alpha_16_should_be_palette_2.png differ diff --git a/tests/files/grayscale_alpha_16_should_be_palette_4.png b/tests/files/grayscale_alpha_16_should_be_palette_4.png new file mode 100644 index 00000000..ab2aafdf Binary files /dev/null and b/tests/files/grayscale_alpha_16_should_be_palette_4.png differ diff --git a/tests/files/grayscale_alpha_should_be_grayscale.png b/tests/files/grayscale_alpha_8_should_be_grayscale_8.png similarity index 100% rename from tests/files/grayscale_alpha_should_be_grayscale.png rename to tests/files/grayscale_alpha_8_should_be_grayscale_8.png diff --git a/tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png b/tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png new file mode 100644 index 00000000..79a1a540 Binary files /dev/null and b/tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png differ diff --git a/tests/files/grayscale_alpha_8_should_be_palette_1.png b/tests/files/grayscale_alpha_8_should_be_palette_1.png new file mode 100644 index 00000000..454f41c2 Binary files /dev/null and b/tests/files/grayscale_alpha_8_should_be_palette_1.png differ diff --git a/tests/files/grayscale_alpha_8_should_be_palette_2.png b/tests/files/grayscale_alpha_8_should_be_palette_2.png new file mode 100644 index 00000000..96fa6633 Binary files /dev/null and b/tests/files/grayscale_alpha_8_should_be_palette_2.png differ diff --git a/tests/files/grayscale_alpha_8_should_be_palette_4.png b/tests/files/grayscale_alpha_8_should_be_palette_4.png new file mode 100644 index 00000000..22b6bf9e Binary files /dev/null and b/tests/files/grayscale_alpha_8_should_be_palette_4.png differ diff --git a/tests/files/palette_1_should_be_palette_1.png b/tests/files/palette_1_should_be_palette_1.png new file mode 100644 index 00000000..fbcc40fb Binary files /dev/null and b/tests/files/palette_1_should_be_palette_1.png differ diff --git a/tests/files/palette_2_should_be_palette_1.png b/tests/files/palette_2_should_be_palette_1.png new file mode 100644 index 00000000..dc400713 Binary files /dev/null and b/tests/files/palette_2_should_be_palette_1.png differ diff --git a/tests/files/palette_2_should_be_palette_2.png b/tests/files/palette_2_should_be_palette_2.png new file mode 100644 index 00000000..4e3ef4f3 Binary files /dev/null and b/tests/files/palette_2_should_be_palette_2.png differ diff --git a/tests/files/palette_4_should_be_palette_1.png b/tests/files/palette_4_should_be_palette_1.png new file mode 100644 index 00000000..9f88d93c Binary files /dev/null and b/tests/files/palette_4_should_be_palette_1.png differ diff --git a/tests/files/palette_4_should_be_palette_2.png b/tests/files/palette_4_should_be_palette_2.png new file mode 100644 index 00000000..da5f29af Binary files /dev/null and b/tests/files/palette_4_should_be_palette_2.png differ diff --git a/tests/files/palette_4_should_be_palette_4.png b/tests/files/palette_4_should_be_palette_4.png new file mode 100644 index 00000000..56052b33 Binary files /dev/null and b/tests/files/palette_4_should_be_palette_4.png differ diff --git a/tests/files/palette_8_should_be_grayscale_8.png b/tests/files/palette_8_should_be_grayscale_8.png new file mode 100644 index 00000000..6d456758 Binary files /dev/null and b/tests/files/palette_8_should_be_grayscale_8.png differ diff --git a/tests/files/palette_8_should_be_palette_1.png b/tests/files/palette_8_should_be_palette_1.png new file mode 100644 index 00000000..2977b017 Binary files /dev/null and b/tests/files/palette_8_should_be_palette_1.png differ diff --git a/tests/files/palette_8_should_be_palette_2.png b/tests/files/palette_8_should_be_palette_2.png new file mode 100644 index 00000000..3627ad2f Binary files /dev/null and b/tests/files/palette_8_should_be_palette_2.png differ diff --git a/tests/files/palette_8_should_be_palette_4.png b/tests/files/palette_8_should_be_palette_4.png new file mode 100644 index 00000000..72414145 Binary files /dev/null and b/tests/files/palette_8_should_be_palette_4.png differ diff --git a/tests/files/palette_should_be_palette.png b/tests/files/palette_8_should_be_palette_8.png similarity index 100% rename from tests/files/palette_should_be_palette.png rename to tests/files/palette_8_should_be_palette_8.png diff --git a/tests/files/palette_should_be_grayscale.png b/tests/files/palette_should_be_grayscale.png deleted file mode 100644 index dc820aea..00000000 Binary files a/tests/files/palette_should_be_grayscale.png and /dev/null differ diff --git a/tests/files/rgb_16_should_be_grayscale_16.png b/tests/files/rgb_16_should_be_grayscale_16.png new file mode 100644 index 00000000..b79a99ee Binary files /dev/null and b/tests/files/rgb_16_should_be_grayscale_16.png differ diff --git a/tests/files/rgb_16_should_be_grayscale_8.png b/tests/files/rgb_16_should_be_grayscale_8.png new file mode 100644 index 00000000..eb5375a3 Binary files /dev/null and b/tests/files/rgb_16_should_be_grayscale_8.png differ diff --git a/tests/files/rgb_16_should_be_palette_1.png b/tests/files/rgb_16_should_be_palette_1.png new file mode 100644 index 00000000..ee125faa Binary files /dev/null and b/tests/files/rgb_16_should_be_palette_1.png differ diff --git a/tests/files/rgb_16_should_be_palette_1_grayscale.png b/tests/files/rgb_16_should_be_palette_1_grayscale.png new file mode 100644 index 00000000..b072008c Binary files /dev/null and b/tests/files/rgb_16_should_be_palette_1_grayscale.png differ diff --git a/tests/files/rgb_16_should_be_palette_2.png b/tests/files/rgb_16_should_be_palette_2.png new file mode 100644 index 00000000..cae6ec7b Binary files /dev/null and b/tests/files/rgb_16_should_be_palette_2.png differ diff --git a/tests/files/rgb_16_should_be_palette_2_grayscale.png b/tests/files/rgb_16_should_be_palette_2_grayscale.png new file mode 100644 index 00000000..a15724be Binary files /dev/null and b/tests/files/rgb_16_should_be_palette_2_grayscale.png differ diff --git a/tests/files/rgb_16_should_be_palette_4.png b/tests/files/rgb_16_should_be_palette_4.png new file mode 100644 index 00000000..928de6ea Binary files /dev/null and b/tests/files/rgb_16_should_be_palette_4.png differ diff --git a/tests/files/rgb_16_should_be_palette_4_grayscale.png b/tests/files/rgb_16_should_be_palette_4_grayscale.png new file mode 100644 index 00000000..a45e1618 Binary files /dev/null and b/tests/files/rgb_16_should_be_palette_4_grayscale.png differ diff --git a/tests/files/rgb_16_should_be_palette_8.png b/tests/files/rgb_16_should_be_palette_8.png new file mode 100644 index 00000000..6492a2f9 Binary files /dev/null and b/tests/files/rgb_16_should_be_palette_8.png differ diff --git a/tests/files/rgb_16_should_be_rgb_16.png b/tests/files/rgb_16_should_be_rgb_16.png new file mode 100644 index 00000000..e1f41729 Binary files /dev/null and b/tests/files/rgb_16_should_be_rgb_16.png differ diff --git a/tests/files/rgb_16_should_be_rgb_8.png b/tests/files/rgb_16_should_be_rgb_8.png new file mode 100644 index 00000000..f1fa62bc Binary files /dev/null and b/tests/files/rgb_16_should_be_rgb_8.png differ diff --git a/tests/files/rgb_should_be_grayscale.png b/tests/files/rgb_8_should_be_grayscale_8.png similarity index 100% rename from tests/files/rgb_should_be_grayscale.png rename to tests/files/rgb_8_should_be_grayscale_8.png diff --git a/tests/files/rgb_8_should_be_palette_1.png b/tests/files/rgb_8_should_be_palette_1.png new file mode 100644 index 00000000..cfa64eb2 Binary files /dev/null and b/tests/files/rgb_8_should_be_palette_1.png differ diff --git a/tests/files/rgb_8_should_be_palette_1_grayscale.png b/tests/files/rgb_8_should_be_palette_1_grayscale.png new file mode 100644 index 00000000..b5579f2d Binary files /dev/null and b/tests/files/rgb_8_should_be_palette_1_grayscale.png differ diff --git a/tests/files/rgb_8_should_be_palette_2.png b/tests/files/rgb_8_should_be_palette_2.png new file mode 100644 index 00000000..c479811f Binary files /dev/null and b/tests/files/rgb_8_should_be_palette_2.png differ diff --git a/tests/files/rgb_8_should_be_palette_2_grayscale.png b/tests/files/rgb_8_should_be_palette_2_grayscale.png new file mode 100644 index 00000000..289951b4 Binary files /dev/null and b/tests/files/rgb_8_should_be_palette_2_grayscale.png differ diff --git a/tests/files/rgb_8_should_be_palette_4.png b/tests/files/rgb_8_should_be_palette_4.png new file mode 100644 index 00000000..818c30d3 Binary files /dev/null and b/tests/files/rgb_8_should_be_palette_4.png differ diff --git a/tests/files/rgb_8_should_be_palette_4_grayscale.png b/tests/files/rgb_8_should_be_palette_4_grayscale.png new file mode 100644 index 00000000..99092ee8 Binary files /dev/null and b/tests/files/rgb_8_should_be_palette_4_grayscale.png differ diff --git a/tests/files/rgb_should_be_palette.png b/tests/files/rgb_8_should_be_palette_8.png similarity index 100% rename from tests/files/rgb_should_be_palette.png rename to tests/files/rgb_8_should_be_palette_8.png diff --git a/tests/files/rgb_should_be_rgb.png b/tests/files/rgb_8_should_be_rgb_8.png similarity index 100% rename from tests/files/rgb_should_be_rgb.png rename to tests/files/rgb_8_should_be_rgb_8.png diff --git a/tests/files/rgba_16_should_be_grayscale_16.png b/tests/files/rgba_16_should_be_grayscale_16.png new file mode 100644 index 00000000..bf178b63 Binary files /dev/null and b/tests/files/rgba_16_should_be_grayscale_16.png differ diff --git a/tests/files/rgba_16_should_be_grayscale_8.png b/tests/files/rgba_16_should_be_grayscale_8.png new file mode 100644 index 00000000..d6460208 Binary files /dev/null and b/tests/files/rgba_16_should_be_grayscale_8.png differ diff --git a/tests/files/rgba_16_should_be_grayscale_alpha_16.png b/tests/files/rgba_16_should_be_grayscale_alpha_16.png new file mode 100644 index 00000000..aebed341 Binary files /dev/null and b/tests/files/rgba_16_should_be_grayscale_alpha_16.png differ diff --git a/tests/files/rgba_16_should_be_grayscale_alpha_8.png b/tests/files/rgba_16_should_be_grayscale_alpha_8.png new file mode 100644 index 00000000..36dc1679 Binary files /dev/null and b/tests/files/rgba_16_should_be_grayscale_alpha_8.png differ diff --git a/tests/files/rgba_16_should_be_palette_1.png b/tests/files/rgba_16_should_be_palette_1.png new file mode 100644 index 00000000..94f15f01 Binary files /dev/null and b/tests/files/rgba_16_should_be_palette_1.png differ diff --git a/tests/files/rgba_16_should_be_palette_1_grayscale.png b/tests/files/rgba_16_should_be_palette_1_grayscale.png new file mode 100644 index 00000000..f13f5c62 Binary files /dev/null and b/tests/files/rgba_16_should_be_palette_1_grayscale.png differ diff --git a/tests/files/rgba_16_should_be_palette_2.png b/tests/files/rgba_16_should_be_palette_2.png new file mode 100644 index 00000000..66a68782 Binary files /dev/null and b/tests/files/rgba_16_should_be_palette_2.png differ diff --git a/tests/files/rgba_16_should_be_palette_2_grayscale.png b/tests/files/rgba_16_should_be_palette_2_grayscale.png new file mode 100644 index 00000000..ec02c1b0 Binary files /dev/null and b/tests/files/rgba_16_should_be_palette_2_grayscale.png differ diff --git a/tests/files/rgba_16_should_be_palette_4.png b/tests/files/rgba_16_should_be_palette_4.png new file mode 100644 index 00000000..8c159806 Binary files /dev/null and b/tests/files/rgba_16_should_be_palette_4.png differ diff --git a/tests/files/rgba_16_should_be_palette_4_grayscale.png b/tests/files/rgba_16_should_be_palette_4_grayscale.png new file mode 100644 index 00000000..b84ece8a Binary files /dev/null and b/tests/files/rgba_16_should_be_palette_4_grayscale.png differ diff --git a/tests/files/rgba_16_should_be_palette_8.png b/tests/files/rgba_16_should_be_palette_8.png new file mode 100644 index 00000000..081b2a64 Binary files /dev/null and b/tests/files/rgba_16_should_be_palette_8.png differ diff --git a/tests/files/rgba_16_should_be_rgb_16.png b/tests/files/rgba_16_should_be_rgb_16.png new file mode 100644 index 00000000..2c2bf7f2 Binary files /dev/null and b/tests/files/rgba_16_should_be_rgb_16.png differ diff --git a/tests/files/rgba_16_should_be_rgb_8.png b/tests/files/rgba_16_should_be_rgb_8.png new file mode 100644 index 00000000..852f8ffa Binary files /dev/null and b/tests/files/rgba_16_should_be_rgb_8.png differ diff --git a/tests/files/rgba_16_should_be_rgba_16.png b/tests/files/rgba_16_should_be_rgba_16.png new file mode 100644 index 00000000..bfc0e07d Binary files /dev/null and b/tests/files/rgba_16_should_be_rgba_16.png differ diff --git a/tests/files/rgba_16_should_be_rgba_8.png b/tests/files/rgba_16_should_be_rgba_8.png new file mode 100644 index 00000000..ccc59f7d Binary files /dev/null and b/tests/files/rgba_16_should_be_rgba_8.png differ diff --git a/tests/files/rgba_should_be_grayscale.png b/tests/files/rgba_8_should_be_grayscale_8.png similarity index 100% rename from tests/files/rgba_should_be_grayscale.png rename to tests/files/rgba_8_should_be_grayscale_8.png diff --git a/tests/files/rgba_should_be_grayscale_alpha.png b/tests/files/rgba_8_should_be_grayscale_alpha_8.png similarity index 100% rename from tests/files/rgba_should_be_grayscale_alpha.png rename to tests/files/rgba_8_should_be_grayscale_alpha_8.png diff --git a/tests/files/rgba_8_should_be_palette_1.png b/tests/files/rgba_8_should_be_palette_1.png new file mode 100644 index 00000000..b74454b7 Binary files /dev/null and b/tests/files/rgba_8_should_be_palette_1.png differ diff --git a/tests/files/rgba_8_should_be_palette_1_grayscale.png b/tests/files/rgba_8_should_be_palette_1_grayscale.png new file mode 100644 index 00000000..79deb6df Binary files /dev/null and b/tests/files/rgba_8_should_be_palette_1_grayscale.png differ diff --git a/tests/files/rgba_8_should_be_palette_2.png b/tests/files/rgba_8_should_be_palette_2.png new file mode 100644 index 00000000..834bfe84 Binary files /dev/null and b/tests/files/rgba_8_should_be_palette_2.png differ diff --git a/tests/files/rgba_8_should_be_palette_2_grayscale.png b/tests/files/rgba_8_should_be_palette_2_grayscale.png new file mode 100644 index 00000000..bbf51bec Binary files /dev/null and b/tests/files/rgba_8_should_be_palette_2_grayscale.png differ diff --git a/tests/files/rgba_8_should_be_palette_4.png b/tests/files/rgba_8_should_be_palette_4.png new file mode 100644 index 00000000..2efe3501 Binary files /dev/null and b/tests/files/rgba_8_should_be_palette_4.png differ diff --git a/tests/files/rgba_8_should_be_palette_4_grayscale.png b/tests/files/rgba_8_should_be_palette_4_grayscale.png new file mode 100644 index 00000000..804b406c Binary files /dev/null and b/tests/files/rgba_8_should_be_palette_4_grayscale.png differ diff --git a/tests/files/rgba_should_be_palette.png b/tests/files/rgba_8_should_be_palette_8.png similarity index 100% rename from tests/files/rgba_should_be_palette.png rename to tests/files/rgba_8_should_be_palette_8.png diff --git a/tests/files/rgba_should_be_rgb.png b/tests/files/rgba_8_should_be_rgb_8.png similarity index 100% rename from tests/files/rgba_should_be_rgb.png rename to tests/files/rgba_8_should_be_rgb_8.png diff --git a/tests/files/rgba_should_be_rgba.png b/tests/files/rgba_8_should_be_rgba_8.png similarity index 100% rename from tests/files/rgba_should_be_rgba.png rename to tests/files/rgba_8_should_be_rgba_8.png diff --git a/tests/oxipng.rs b/tests/oxipng.rs index d102a674..6bef3c28 100644 --- a/tests/oxipng.rs +++ b/tests/oxipng.rs @@ -49,11 +49,54 @@ fn get_opts(input: &Path) -> oxipng::Options { } #[test] -fn rgba_should_be_rgba() { - let input = PathBuf::from("tests/files/rgba_should_be_rgba.png"); +fn rgba_16_should_be_rgba_16() { + let input = PathBuf::from("tests/files/rgba_16_should_be_rgba_16.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_rgba_8() { + let input = PathBuf::from("tests/files/rgba_16_should_be_rgba_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -82,11 +125,92 @@ fn rgba_should_be_rgba() { } #[test] -fn rgba_should_be_rgb() { - let input = PathBuf::from("tests/files/rgba_should_be_rgb.png"); +fn rgba_8_should_be_rgba_8() { + let input = PathBuf::from("tests/files/rgba_8_should_be_rgba_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::RGBA); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_rgb_16() { + let input = PathBuf::from("tests/files/rgba_16_should_be_rgb_16.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_rgb_8() { + let input = PathBuf::from("tests/files/rgba_16_should_be_rgb_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -115,11 +239,54 @@ fn rgba_should_be_rgb() { } #[test] -fn rgba_should_be_palette() { - let input = PathBuf::from("tests/files/rgba_should_be_palette.png"); +fn rgba_8_should_be_rgb_8() { + let input = PathBuf::from("tests/files/rgba_8_should_be_rgb_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::RGB); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_palette_8() { + let input = PathBuf::from("tests/files/rgba_16_should_be_palette_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -148,11 +315,320 @@ fn rgba_should_be_palette() { } #[test] -fn rgba_should_be_grayscale_alpha() { - let input = PathBuf::from("tests/files/rgba_should_be_grayscale_alpha.png"); +fn rgba_8_should_be_palette_8() { + let input = PathBuf::from("tests/files/rgba_8_should_be_palette_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::Indexed); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_palette_4() { + let input = PathBuf::from("tests/files/rgba_16_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_8_should_be_palette_4() { + let input = PathBuf::from("tests/files/rgba_8_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_palette_2() { + let input = PathBuf::from("tests/files/rgba_16_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_8_should_be_palette_2() { + let input = PathBuf::from("tests/files/rgba_8_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_palette_1() { + let input = PathBuf::from("tests/files/rgba_16_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_8_should_be_palette_1() { + let input = PathBuf::from("tests/files/rgba_8_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_grayscale_alpha_16() { + let input = PathBuf::from("tests/files/rgba_16_should_be_grayscale_alpha_16.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_grayscale_alpha_8() { + let input = PathBuf::from("tests/files/rgba_16_should_be_grayscale_alpha_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -181,11 +657,92 @@ fn rgba_should_be_grayscale_alpha() { } #[test] -fn rgba_should_be_grayscale() { - let input = PathBuf::from("tests/files/rgba_should_be_grayscale.png"); +fn rgba_8_should_be_grayscale_alpha_8() { + let input = PathBuf::from("tests/files/rgba_8_should_be_grayscale_alpha_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::GrayscaleAlpha); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_grayscale_16() { + let input = PathBuf::from("tests/files/rgba_16_should_be_grayscale_16.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/rgba_16_should_be_grayscale_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -214,11 +771,320 @@ fn rgba_should_be_grayscale() { } #[test] -fn rgb_should_be_rgb() { - let input = PathBuf::from("tests/files/rgb_should_be_rgb.png"); +fn rgba_8_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/rgba_8_should_be_grayscale_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::Grayscale); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_palette_4_grayscale() { + let input = PathBuf::from("tests/files/rgba_16_should_be_palette_4_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_8_should_be_palette_4_grayscale() { + let input = PathBuf::from("tests/files/rgba_8_should_be_palette_4_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_palette_2_grayscale() { + let input = PathBuf::from("tests/files/rgba_16_should_be_palette_2_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_8_should_be_palette_2_grayscale() { + let input = PathBuf::from("tests/files/rgba_8_should_be_palette_2_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_16_should_be_palette_1_grayscale() { + let input = PathBuf::from("tests/files/rgba_16_should_be_palette_1_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgba_8_should_be_palette_1_grayscale() { + let input = PathBuf::from("tests/files/rgba_8_should_be_palette_1_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_rgb_16() { + let input = PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_rgb_8() { + let input = PathBuf::from("tests/files/rgb_16_should_be_rgb_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -247,11 +1113,54 @@ fn rgb_should_be_rgb() { } #[test] -fn rgb_should_be_palette() { - let input = PathBuf::from("tests/files/rgb_should_be_palette.png"); +fn rgb_8_should_be_rgb_8() { + let input = PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + 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.color_type == png::ColorType::RGB); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_palette_8() { + let input = PathBuf::from("tests/files/rgb_16_should_be_palette_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -280,44 +1189,16 @@ fn rgb_should_be_palette() { } #[test] -fn rgb_should_be_grayscale() { - let input = PathBuf::from("tests/files/rgb_should_be_grayscale.png"); +fn rgb_8_should_be_palette_8() { + let input = PathBuf::from("tests/files/rgb_8_should_be_palette_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); - match oxipng::optimize(&input, &opts) { - Ok(_) => (), - Err(x) => panic!(x.to_owned()), - }; - assert!(output.exists()); + let png = png::PngData::new(&input).unwrap(); - let png = match png::PngData::new(&output) { - Ok(x) => x, - Err(x) => { - remove_file(output).ok(); - panic!(x.to_owned()) - } - }; - - assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.color_type == png::ColorType::RGB); assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == - new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); - - remove_file(output).ok(); -} - -#[test] -fn palette_should_be_palette() { - let input = PathBuf::from("tests/files/palette_should_be_palette.png"); - let opts = get_opts(&input); - let output = opts.out_file.clone(); - match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -346,11 +1227,282 @@ fn palette_should_be_palette() { } #[test] -fn palette_should_be_grayscale() { - let input = PathBuf::from("tests/files/palette_should_be_grayscale.png"); +fn rgb_16_should_be_palette_4() { + let input = PathBuf::from("tests/files/rgb_16_should_be_palette_4.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_8_should_be_palette_4() { + let input = PathBuf::from("tests/files/rgb_8_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_palette_2() { + let input = PathBuf::from("tests/files/rgb_16_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_8_should_be_palette_2() { + let input = PathBuf::from("tests/files/rgb_8_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_palette_1() { + let input = PathBuf::from("tests/files/rgb_16_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_8_should_be_palette_1() { + let input = PathBuf::from("tests/files/rgb_8_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_grayscale_16() { + let input = PathBuf::from("tests/files/rgb_16_should_be_grayscale_16.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/rgb_16_should_be_grayscale_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -379,11 +1531,16 @@ fn palette_should_be_grayscale() { } #[test] -fn grayscale_alpha_should_be_grayscale() { - let input = PathBuf::from("tests/files/grayscale_alpha_should_be_grayscale.png"); +fn rgb_8_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/rgb_8_should_be_grayscale_8.png"); let opts = get_opts(&input); let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -411,13 +1568,1423 @@ fn grayscale_alpha_should_be_grayscale() { remove_file(output).ok(); } +#[test] +fn rgb_16_should_be_palette_4_grayscale() { + let input = PathBuf::from("tests/files/rgb_16_should_be_palette_4_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_8_should_be_palette_4_grayscale() { + let input = PathBuf::from("tests/files/rgb_8_should_be_palette_4_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_palette_2_grayscale() { + let input = PathBuf::from("tests/files/rgb_16_should_be_palette_2_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_8_should_be_palette_2_grayscale() { + let input = PathBuf::from("tests/files/rgb_8_should_be_palette_2_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_16_should_be_palette_1_grayscale() { + let input = PathBuf::from("tests/files/rgb_16_should_be_palette_1_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn rgb_8_should_be_palette_1_grayscale() { + let input = PathBuf::from("tests/files/rgb_8_should_be_palette_1_grayscale.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGB); + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_8_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/palette_8_should_be_grayscale_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + 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.color_type == png::ColorType::Grayscale); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_8_should_be_palette_8() { + let input = PathBuf::from("tests/files/palette_8_should_be_palette_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + 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.color_type == png::ColorType::Indexed); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_8_should_be_palette_4() { + let input = PathBuf::from("tests/files/palette_8_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_4_should_be_palette_4() { + let input = PathBuf::from("tests/files/palette_4_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_8_should_be_palette_2() { + let input = PathBuf::from("tests/files/palette_8_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_4_should_be_palette_2() { + let input = PathBuf::from("tests/files/palette_4_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_2_should_be_palette_2() { + let input = PathBuf::from("tests/files/palette_2_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_8_should_be_palette_1() { + let input = PathBuf::from("tests/files/palette_8_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_4_should_be_palette_1() { + let input = PathBuf::from("tests/files/palette_4_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_2_should_be_palette_1() { + let input = PathBuf::from("tests/files/palette_2_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn palette_1_should_be_palette_1() { + let input = PathBuf::from("tests/files/palette_1_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::One); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_16_should_be_grayscale_alpha_16() { + let input = PathBuf::from("tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_16_should_be_grayscale_alpha_8() { + let input = PathBuf::from("tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::GrayscaleAlpha); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_8_should_be_grayscale_alpha_8() { + let input = PathBuf::from("tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + 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.color_type == png::ColorType::GrayscaleAlpha); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_16_should_be_grayscale_16() { + let input = PathBuf::from("tests/files/grayscale_alpha_16_should_be_grayscale_16.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_16_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/grayscale_alpha_16_should_be_grayscale_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Grayscale); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_8_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/grayscale_alpha_8_should_be_grayscale_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + 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.color_type == png::ColorType::Grayscale); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_16_should_be_palette_4() { + let input = PathBuf::from("tests/files/grayscale_alpha_16_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_8_should_be_palette_4() { + let input = PathBuf::from("tests/files/grayscale_alpha_8_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_16_should_be_palette_2() { + let input = PathBuf::from("tests/files/grayscale_alpha_16_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_8_should_be_palette_2() { + let input = PathBuf::from("tests/files/grayscale_alpha_8_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_16_should_be_palette_1() { + let input = PathBuf::from("tests/files/grayscale_alpha_16_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_alpha_8_should_be_palette_1() { + let input = PathBuf::from("tests/files/grayscale_alpha_8_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::GrayscaleAlpha); + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_16_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/grayscale_16_should_be_grayscale_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Grayscale); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_16_should_be_palette_4() { + let input = PathBuf::from("tests/files/grayscale_16_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_16_should_be_palette_2() { + let input = PathBuf::from("tests/files/grayscale_16_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_16_should_be_palette_1() { + let input = PathBuf::from("tests/files/grayscale_16_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Sixteen); + + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_8_should_be_grayscale_8() { + let input = PathBuf::from("tests/files/grayscale_8_should_be_grayscale_8.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + 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.color_type == png::ColorType::Grayscale); + assert!(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!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_8_should_be_palette_4() { + let input = PathBuf::from("tests/files/grayscale_8_should_be_palette_4.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Four); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_8_should_be_palette_2() { + let input = PathBuf::from("tests/files/grayscale_8_should_be_palette_2.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + 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.color_type == png::ColorType::Indexed); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Two); + + 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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + +#[test] +fn grayscale_8_should_be_palette_1() { + let input = PathBuf::from("tests/files/grayscale_8_should_be_palette_1.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + 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.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::>>() == + new_png.pixels().map(|x| x.2.channels().to_owned()).collect::>>()); + + remove_file(output).ok(); +} + #[test] fn strip_headers() { - let input = PathBuf::from("tests/files/rgb_should_be_rgb.png"); + let input = PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"); let mut opts = get_opts(&input); opts.strip = true; let output = opts.out_file.clone(); + let png = png::PngData::new(&input).unwrap(); + + assert!(png.aux_headers.contains_key("tEXt")); + match oxipng::optimize(&input, &opts) { Ok(_) => (), Err(x) => panic!(x.to_owned()), @@ -443,45 +3010,3 @@ fn strip_headers() { remove_file(output).ok(); } - -#[test] -#[ignore] -fn downgrade_16_to_8() { - unimplemented!(); -} - -#[test] -#[ignore] -fn downgrade_8_to_4() { - unimplemented!(); -} - -#[test] -#[ignore] -fn downgrade_8_to_2() { - unimplemented!(); -} - -#[test] -#[ignore] -fn downgrade_8_to_1() { - unimplemented!(); -} - -#[test] -#[ignore] -fn downgrade_4_to_2() { - unimplemented!(); -} - -#[test] -#[ignore] -fn downgrade_4_to_1() { - unimplemented!(); -} - -#[test] -#[ignore] -fn downgrade_2_to_1() { - unimplemented!(); -}