parent
5f834a06f8
commit
b889c73a3b
11 changed files with 348 additions and 169 deletions
|
|
@ -1,8 +1,10 @@
|
|||
**Version 0.1.2** (unreleased)
|
||||
**Version 0.2.0** (unreleased)
|
||||
- Fix program version that is displayed when running `oxipng -V`
|
||||
- Ensure `--quiet` mode is actually quiet (@SethDusek [#20](https://github.com/shssoichiro/oxipng/pull/20))
|
||||
- Write status/debug information to stderr instead of stdout
|
||||
- Use heuristics to determine best combination for `-o1` ([#21](https://github.com/shssoichiro/oxipng/issues/21))
|
||||
- [SEMVER_MAJOR] Allow 'safe', 'all', or comma-separated list as options for `--strip`
|
||||
- [SEMVER_MINOR] Add `-s` alias for `--strip`
|
||||
|
||||
**Version 0.1.1**
|
||||
- Fix `oxipng *` writing all input files to one output file ([#15](https://github.com/shssoichiro/oxipng/issues/15))
|
||||
|
|
|
|||
25
src/lib.rs
25
src/lib.rs
|
|
@ -40,7 +40,7 @@ pub struct Options {
|
|||
pub color_type_reduction: bool,
|
||||
pub palette_reduction: bool,
|
||||
pub idat_recoding: bool,
|
||||
pub strip: bool,
|
||||
pub strip: png::Headers,
|
||||
pub use_heuristics: bool,
|
||||
}
|
||||
|
||||
|
|
@ -227,9 +227,28 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
|||
}
|
||||
}
|
||||
|
||||
if opts.strip {
|
||||
match opts.strip.clone() {
|
||||
// Strip headers
|
||||
png.aux_headers = HashMap::new();
|
||||
png::Headers::None => (),
|
||||
png::Headers::Some(hdrs) => {
|
||||
for hdr in &hdrs {
|
||||
png.aux_headers.remove(hdr);
|
||||
}
|
||||
}
|
||||
png::Headers::Safe => {
|
||||
const PRESERVED_HEADERS: [&'static str; 9] = ["cHRM", "gAMA", "iCCP", "sBIT", "sRGB",
|
||||
"bKGD", "hIST", "pHYs", "sPLT"];
|
||||
let mut preserved = HashMap::new();
|
||||
for (hdr, contents) in png.aux_headers.iter() {
|
||||
if PRESERVED_HEADERS.contains(&hdr.as_ref()) {
|
||||
preserved.insert(hdr.clone(), contents.clone());
|
||||
}
|
||||
}
|
||||
png.aux_headers = preserved;
|
||||
}
|
||||
png::Headers::All => {
|
||||
png.aux_headers = HashMap::new();
|
||||
}
|
||||
}
|
||||
|
||||
let output_data = png.output();
|
||||
|
|
|
|||
341
src/main.rs
341
src/main.rs
|
|
@ -3,6 +3,7 @@ extern crate clap;
|
|||
extern crate regex;
|
||||
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use oxipng::png;
|
||||
use regex::Regex;
|
||||
use std::collections::HashSet;
|
||||
use std::io::{Write, stderr};
|
||||
|
|
@ -46,163 +47,166 @@ fn main() {
|
|||
color_type_reduction: true,
|
||||
palette_reduction: true,
|
||||
idat_recoding: true,
|
||||
strip: false,
|
||||
strip: png::Headers::None,
|
||||
use_heuristics: false,
|
||||
};
|
||||
|
||||
let matches = App::new("oxipng")
|
||||
.version(VERSION_STRING)
|
||||
.author("Joshua Holmer <jholmer.in@gmail.com>")
|
||||
.about("Losslessly improves compression of PNG files")
|
||||
.arg(Arg::with_name("files")
|
||||
.help("File(s) to compress")
|
||||
.index(1)
|
||||
.multiple(true)
|
||||
.required(true))
|
||||
.arg(Arg::with_name("optimization")
|
||||
.help("Optimization level - Default: 2")
|
||||
.short("o")
|
||||
.long("opt")
|
||||
.takes_value(true)
|
||||
.possible_value("0")
|
||||
.possible_value("1")
|
||||
.possible_value("2")
|
||||
.possible_value("3")
|
||||
.possible_value("4")
|
||||
.possible_value("5")
|
||||
.possible_value("6"))
|
||||
.arg(Arg::with_name("backup")
|
||||
.help("Back up modified files")
|
||||
.short("b")
|
||||
.long("backup"))
|
||||
.arg(Arg::with_name("force")
|
||||
.help("Write output even if larger than the original")
|
||||
.short("F")
|
||||
.long("force"))
|
||||
.arg(Arg::with_name("recursive")
|
||||
.help("Recurse into subdirectories")
|
||||
.short("r")
|
||||
.long("recursive"))
|
||||
.arg(Arg::with_name("output_dir")
|
||||
.help("Write output file(s) to <directory>")
|
||||
.long("dir")
|
||||
.takes_value(true)
|
||||
.conflicts_with("output_file")
|
||||
.conflicts_with("stdout"))
|
||||
.arg(Arg::with_name("output_file")
|
||||
.help("Write output file to <file>")
|
||||
.long("out")
|
||||
.takes_value(true)
|
||||
.conflicts_with("output_dir")
|
||||
.conflicts_with("stdout"))
|
||||
.arg(Arg::with_name("stdout")
|
||||
.help("Write output to stdout")
|
||||
.long("stdout")
|
||||
.conflicts_with("output_dir")
|
||||
.conflicts_with("output_file"))
|
||||
.arg(Arg::with_name("fix")
|
||||
.help("Enable error recovery")
|
||||
.long("fix"))
|
||||
.arg(Arg::with_name("no-clobber")
|
||||
.help("Do not overwrite existing files")
|
||||
.long("no-clobber"))
|
||||
.arg(Arg::with_name("pretend")
|
||||
.help("Do not write any files, only calculate compression gains")
|
||||
.short("P")
|
||||
.long("pretend"))
|
||||
.arg(Arg::with_name("preserve")
|
||||
.help("Preserve file attributes if possible")
|
||||
.short("p")
|
||||
.long("preserve"))
|
||||
.arg(Arg::with_name("quiet")
|
||||
.help("Run in quiet mode")
|
||||
.short("q")
|
||||
.long("quiet")
|
||||
.conflicts_with("verbose"))
|
||||
.arg(Arg::with_name("verbose")
|
||||
.help("Run in verbose mode")
|
||||
.short("v")
|
||||
.long("verbose")
|
||||
.conflicts_with("quiet"))
|
||||
.arg(Arg::with_name("filters")
|
||||
.help("PNG delta filters (0-5) - Default: 0,5")
|
||||
.short("f")
|
||||
.long("filters")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 0, 5) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for filters".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("interlace")
|
||||
.help("PNG interlace type")
|
||||
.short("i")
|
||||
.long("interlace")
|
||||
.takes_value(true)
|
||||
.possible_value("0")
|
||||
.possible_value("1"))
|
||||
.arg(Arg::with_name("compression")
|
||||
.help("zlib compression levels (1-9) - Default: 9")
|
||||
.long("zc")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 1, 9) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for compression".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("memory")
|
||||
.help("zlib memory levels (1-9) - Default: 9")
|
||||
.long("zm")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 1, 9) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for memory".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("strategies")
|
||||
.help("zlib compression strategies (0-3) - Default: 0-3")
|
||||
.long("zs")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 0, 3) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for strategies".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("window")
|
||||
.help("zlib window size - Default: 32k")
|
||||
.long("zw")
|
||||
.takes_value(true)
|
||||
.possible_value("256")
|
||||
.possible_value("512")
|
||||
.possible_value("1k")
|
||||
.possible_value("2k")
|
||||
.possible_value("4k")
|
||||
.possible_value("8k")
|
||||
.possible_value("16k")
|
||||
.possible_value("32k"))
|
||||
.arg(Arg::with_name("no-bit-reduction")
|
||||
.help("No bit depth reduction")
|
||||
.long("nb"))
|
||||
.arg(Arg::with_name("no-color-reduction")
|
||||
.help("No color type reduction")
|
||||
.long("nc"))
|
||||
.arg(Arg::with_name("no-palette-reduction")
|
||||
.help("No palette reduction")
|
||||
.long("np"))
|
||||
.arg(Arg::with_name("no-reductions")
|
||||
.help("No reductions")
|
||||
.long("nx"))
|
||||
.arg(Arg::with_name("no-recoding")
|
||||
.help("No IDAT recoding unless necessary")
|
||||
.long("nz"))
|
||||
.arg(Arg::with_name("strip")
|
||||
.help("Strip all metadata objects")
|
||||
.long("strip"))
|
||||
.after_help("Optimization levels:
|
||||
let matches =
|
||||
App::new("oxipng")
|
||||
.version(VERSION_STRING)
|
||||
.author("Joshua Holmer <jholmer.in@gmail.com>")
|
||||
.about("Losslessly improves compression of PNG files")
|
||||
.arg(Arg::with_name("files")
|
||||
.help("File(s) to compress")
|
||||
.index(1)
|
||||
.multiple(true)
|
||||
.required(true))
|
||||
.arg(Arg::with_name("optimization")
|
||||
.help("Optimization level - Default: 2")
|
||||
.short("o")
|
||||
.long("opt")
|
||||
.takes_value(true)
|
||||
.possible_value("0")
|
||||
.possible_value("1")
|
||||
.possible_value("2")
|
||||
.possible_value("3")
|
||||
.possible_value("4")
|
||||
.possible_value("5")
|
||||
.possible_value("6"))
|
||||
.arg(Arg::with_name("backup")
|
||||
.help("Back up modified files")
|
||||
.short("b")
|
||||
.long("backup"))
|
||||
.arg(Arg::with_name("force")
|
||||
.help("Write output even if larger than the original")
|
||||
.short("F")
|
||||
.long("force"))
|
||||
.arg(Arg::with_name("recursive")
|
||||
.help("Recurse into subdirectories")
|
||||
.short("r")
|
||||
.long("recursive"))
|
||||
.arg(Arg::with_name("output_dir")
|
||||
.help("Write output file(s) to <directory>")
|
||||
.long("dir")
|
||||
.takes_value(true)
|
||||
.conflicts_with("output_file")
|
||||
.conflicts_with("stdout"))
|
||||
.arg(Arg::with_name("output_file")
|
||||
.help("Write output file to <file>")
|
||||
.long("out")
|
||||
.takes_value(true)
|
||||
.conflicts_with("output_dir")
|
||||
.conflicts_with("stdout"))
|
||||
.arg(Arg::with_name("stdout")
|
||||
.help("Write output to stdout")
|
||||
.long("stdout")
|
||||
.conflicts_with("output_dir")
|
||||
.conflicts_with("output_file"))
|
||||
.arg(Arg::with_name("fix")
|
||||
.help("Enable error recovery")
|
||||
.long("fix"))
|
||||
.arg(Arg::with_name("no-clobber")
|
||||
.help("Do not overwrite existing files")
|
||||
.long("no-clobber"))
|
||||
.arg(Arg::with_name("pretend")
|
||||
.help("Do not write any files, only calculate compression gains")
|
||||
.short("P")
|
||||
.long("pretend"))
|
||||
.arg(Arg::with_name("preserve")
|
||||
.help("Preserve file attributes if possible")
|
||||
.short("p")
|
||||
.long("preserve"))
|
||||
.arg(Arg::with_name("quiet")
|
||||
.help("Run in quiet mode")
|
||||
.short("q")
|
||||
.long("quiet")
|
||||
.conflicts_with("verbose"))
|
||||
.arg(Arg::with_name("verbose")
|
||||
.help("Run in verbose mode")
|
||||
.short("v")
|
||||
.long("verbose")
|
||||
.conflicts_with("quiet"))
|
||||
.arg(Arg::with_name("filters")
|
||||
.help("PNG delta filters (0-5) - Default: 0,5")
|
||||
.short("f")
|
||||
.long("filters")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 0, 5) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for filters".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("interlace")
|
||||
.help("PNG interlace type")
|
||||
.short("i")
|
||||
.long("interlace")
|
||||
.takes_value(true)
|
||||
.possible_value("0")
|
||||
.possible_value("1"))
|
||||
.arg(Arg::with_name("compression")
|
||||
.help("zlib compression levels (1-9) - Default: 9")
|
||||
.long("zc")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 1, 9) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for compression".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("memory")
|
||||
.help("zlib memory levels (1-9) - Default: 9")
|
||||
.long("zm")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 1, 9) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for memory".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("strategies")
|
||||
.help("zlib compression strategies (0-3) - Default: 0-3")
|
||||
.long("zs")
|
||||
.takes_value(true)
|
||||
.validator(|x| {
|
||||
match parse_numeric_range_opts(&x, 0, 3) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for strategies".to_owned()),
|
||||
}
|
||||
}))
|
||||
.arg(Arg::with_name("window")
|
||||
.help("zlib window size - Default: 32k")
|
||||
.long("zw")
|
||||
.takes_value(true)
|
||||
.possible_value("256")
|
||||
.possible_value("512")
|
||||
.possible_value("1k")
|
||||
.possible_value("2k")
|
||||
.possible_value("4k")
|
||||
.possible_value("8k")
|
||||
.possible_value("16k")
|
||||
.possible_value("32k"))
|
||||
.arg(Arg::with_name("no-bit-reduction")
|
||||
.help("No bit depth reduction")
|
||||
.long("nb"))
|
||||
.arg(Arg::with_name("no-color-reduction")
|
||||
.help("No color type reduction")
|
||||
.long("nc"))
|
||||
.arg(Arg::with_name("no-palette-reduction")
|
||||
.help("No palette reduction")
|
||||
.long("np"))
|
||||
.arg(Arg::with_name("no-reductions")
|
||||
.help("No reductions")
|
||||
.long("nx"))
|
||||
.arg(Arg::with_name("no-recoding")
|
||||
.help("No IDAT recoding unless necessary")
|
||||
.long("nz"))
|
||||
.arg(Arg::with_name("strip")
|
||||
.help("Strip metadata objects ['safe', 'all', or comma-separated list]")
|
||||
.long("strip")
|
||||
.short("s")
|
||||
.takes_value(true))
|
||||
.after_help("Optimization levels:
|
||||
-o 0 => --zc 3 --nz (0 or 1 trials)
|
||||
-o 1 => --zc 9 (1 trial, determined heuristically)
|
||||
-o 2 => --zc 9 --zs 0-3 --f 0,5 (8 trials)
|
||||
|
|
@ -217,7 +221,7 @@ fn main() {
|
|||
|
||||
Manually specifying a compression option (zc, zm, etc.) will override the optimization preset,
|
||||
regardless of the order you write the arguments.")
|
||||
.get_matches();
|
||||
.get_matches();
|
||||
|
||||
let mut opts = default_opts;
|
||||
|
||||
|
|
@ -477,8 +481,27 @@ fn parse_opts_into_struct(matches: &ArgMatches, opts: &mut oxipng::Options) -> R
|
|||
opts.idat_recoding = false;
|
||||
}
|
||||
|
||||
if matches.is_present("strip") {
|
||||
opts.strip = true;
|
||||
if let Some(hdrs) = matches.value_of("strip") {
|
||||
let hdrs = hdrs.split(',').map(|x| x.trim().to_owned()).collect::<Vec<String>>();
|
||||
if hdrs.contains(&"safe".to_owned()) || hdrs.contains(&"all".to_owned()) {
|
||||
if hdrs.len() > 1 {
|
||||
return Err("'safe' or 'all' presets for --strip should be used by themselves"
|
||||
.to_owned());
|
||||
}
|
||||
if hdrs[0] == "safe" {
|
||||
opts.strip = png::Headers::Safe;
|
||||
} else {
|
||||
opts.strip = png::Headers::All;
|
||||
}
|
||||
} else {
|
||||
const FORBIDDEN_CHUNKS: [&'static str; 5] = ["IHDR", "IDAT", "tRNS", "PLTE", "IEND"];
|
||||
for i in &hdrs {
|
||||
if FORBIDDEN_CHUNKS.contains(&i.as_ref()) {
|
||||
return Err(format!("{} chunk is not allowed to be stripped", i));
|
||||
}
|
||||
}
|
||||
opts.strip = png::Headers::Some(hdrs);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -488,8 +511,8 @@ fn parse_numeric_range_opts(input: &str,
|
|||
min_value: u8,
|
||||
max_value: u8)
|
||||
-> Result<HashSet<u8>, String> {
|
||||
let one_item = Regex::new(format!("^[{}-{}]$", min_value, max_value).as_ref()).unwrap();
|
||||
let multiple_items = Regex::new(format!("^([{}-{}])(,|-)([{}-{}])$",
|
||||
let one_item = Regex::new(format!(r"^[{}-{}]$", min_value, max_value).as_ref()).unwrap();
|
||||
let multiple_items = Regex::new(format!(r"^([{}-{}])(,|-)([{}-{}])$",
|
||||
min_value,
|
||||
max_value,
|
||||
min_value,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,14 @@ impl BitDepth {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
pub enum Headers {
|
||||
None,
|
||||
Some(Vec<String>),
|
||||
Safe,
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct ScanLines<'a> {
|
||||
pub png: &'a PngData,
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 115 KiB |
BIN
tests/files/strip_headers_list.png
Normal file
BIN
tests/files/strip_headers_list.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
BIN
tests/files/strip_headers_none.png
Normal file
BIN
tests/files/strip_headers_none.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
BIN
tests/files/strip_headers_safe.png
Normal file
BIN
tests/files/strip_headers_safe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
|
|
@ -44,7 +44,7 @@ fn get_opts(input: &Path) -> oxipng::Options {
|
|||
color_type_reduction: true,
|
||||
palette_reduction: true,
|
||||
idat_recoding: true,
|
||||
strip: false,
|
||||
strip: png::Headers::None,
|
||||
use_heuristics: false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
135
tests/flags.rs
135
tests/flags.rs
|
|
@ -44,7 +44,7 @@ fn get_opts(input: &Path) -> oxipng::Options {
|
|||
color_type_reduction: true,
|
||||
palette_reduction: true,
|
||||
idat_recoding: true,
|
||||
strip: false,
|
||||
strip: png::Headers::None,
|
||||
use_heuristics: false,
|
||||
}
|
||||
}
|
||||
|
|
@ -105,15 +105,17 @@ fn verbose_mode() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn strip_headers() {
|
||||
let input = PathBuf::from("tests/files/strip_headers.png");
|
||||
fn strip_headers_list() {
|
||||
let input = PathBuf::from("tests/files/strip_headers_list.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.strip = true;
|
||||
opts.strip = png::Headers::Some(vec!["iCCP".to_owned(), "tEXt".to_owned()]);
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.aux_headers.contains_key("tEXt"));
|
||||
assert!(png.aux_headers.contains_key("iTXt"));
|
||||
assert!(png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
match oxipng::optimize(&input, &opts) {
|
||||
Ok(_) => (),
|
||||
|
|
@ -130,6 +132,131 @@ fn strip_headers() {
|
|||
};
|
||||
|
||||
assert!(!png.aux_headers.contains_key("tEXt"));
|
||||
assert!(png.aux_headers.contains_key("iTXt"));
|
||||
assert!(!png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
|
||||
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
|
||||
|
||||
remove_file(output).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_headers_safe() {
|
||||
let input = PathBuf::from("tests/files/strip_headers_safe.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.strip = png::Headers::Safe;
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.aux_headers.contains_key("tEXt"));
|
||||
assert!(png.aux_headers.contains_key("iTXt"));
|
||||
assert!(png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
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.aux_headers.contains_key("tEXt"));
|
||||
assert!(!png.aux_headers.contains_key("iTXt"));
|
||||
assert!(png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
|
||||
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
|
||||
|
||||
remove_file(output).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_headers_all() {
|
||||
let input = PathBuf::from("tests/files/strip_headers_all.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.strip = png::Headers::All;
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.aux_headers.contains_key("tEXt"));
|
||||
assert!(png.aux_headers.contains_key("iTXt"));
|
||||
assert!(png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
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.aux_headers.contains_key("tEXt"));
|
||||
assert!(!png.aux_headers.contains_key("iTXt"));
|
||||
assert!(!png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
||||
// Conversion should be lossless
|
||||
assert!(old_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>() ==
|
||||
new_png.pixels().map(|x| x.2.channels().to_owned()).collect::<Vec<Vec<u8>>>());
|
||||
|
||||
remove_file(output).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_headers_none() {
|
||||
let input = PathBuf::from("tests/files/strip_headers_none.png");
|
||||
let mut opts = get_opts(&input);
|
||||
opts.strip = png::Headers::None;
|
||||
let output = opts.out_file.clone();
|
||||
|
||||
let png = png::PngData::new(&input).unwrap();
|
||||
|
||||
assert!(png.aux_headers.contains_key("tEXt"));
|
||||
assert!(png.aux_headers.contains_key("iTXt"));
|
||||
assert!(png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
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.aux_headers.contains_key("tEXt"));
|
||||
assert!(png.aux_headers.contains_key("iTXt"));
|
||||
assert!(png.aux_headers.contains_key("iCCP"));
|
||||
|
||||
let old_png = image::open(&input).unwrap();
|
||||
let new_png = image::open(&output).unwrap();
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ fn get_opts(input: &Path) -> oxipng::Options {
|
|||
color_type_reduction: true,
|
||||
palette_reduction: true,
|
||||
idat_recoding: true,
|
||||
strip: false,
|
||||
strip: png::Headers::None,
|
||||
use_heuristics: false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue