diff --git a/CHANGELOG.md b/CHANGELOG.md index d52bffb8..08ed4ba0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## Version 10.0.0 + +- [Breaking] CLI: Change short zopfli flag from `-Z` to `-z`. +- [Breaking] CLI: Change `--pretend`/`-P` to `--dry-run`/`-D`. +- [Breaking] CLI: Change `--interlace` options from `1`/`0` to `on`/`off`. +- [Breaking] API: Change `Options.interlace: Option` to `Options.interlace: Option`. +- [Breaking] API: Change `Options.filter: IndexSet` to `Options.filters: IndexSet`. +- [Breaking] API: Change `Options.deflate: Deflaters` to `Options.deflater: Deflater`. +- [Breaking] API: Change `Deflaters::Zopfli { .. }` to `Deflater::Zopfli(ZopfliOptions { .. })`. +- [Breaking] API: Change `optimize()` to return `(usize, usize)`, with original and optimized sizes. +- [Feature] Allow zopfli iterations higher than 255. +- [Feature] Add `--ziwi` option for zopfli iterations without improvement. +- [Feature] Add `--max-raw-size` option to skip images that are too large. +- [Feature] Add `--brute-level` and `--brute-lines` options for advanced control of Brute filter. +- [Improvement] Reduce memory usage for `fast` mode. +- [Improvement] Slightly improve compression with Brute filter at levels 5 and 6. +- [Misc] Change `--preserve` option to no longer preserve last access time. +- [Misc] Make help output colored. + ## Version 9.1.5 - [Feature] Add `--sequential` option to process files sequentially rather than in parallel. diff --git a/Cargo.lock b/Cargo.lock index afcdcf38..63e55230 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,7 +406,7 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "oxipng" -version = "9.1.5" +version = "10.0.0" dependencies = [ "bitvec", "clap", diff --git a/Cargo.toml b/Cargo.toml index 36587a5a..acdfcbfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ homepage = "https://github.com/oxipng/oxipng" license = "MIT" name = "oxipng" repository = "https://github.com/oxipng/oxipng" -version = "9.1.5" +version = "10.0.0" rust-version = "1.85.1" [badges] @@ -113,18 +113,36 @@ assets = [ ["CHANGELOG.md", "usr/share/doc/oxipng/", "644"], ] +[lints.rust] +missing_copy_implementations = "deny" +missing_debug_implementations = "deny" +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_import_braces = "warn" + [lints.clippy] -unnecessary_semicolon = "warn" -use_self = "warn" -ignored_unit_patterns = "warn" -unseparated_literal_suffix = "warn" -missing_const_for_fn = "warn" -redundant_closure_for_method_calls = "warn" -option_if_let_else = "warn" -redundant_clone = "warn" -semicolon_if_nothing_returned = "warn" cloned_instead_of_copied = "warn" +expl_impl_clone_on_copy = "warn" +float_cmp_const = "warn" if_not_else = "warn" -useless_let_if_seq = "warn" +ignored_unit_patterns = "warn" +linkedlist = "warn" manual_let_else = "warn" +map_flatten = "warn" map_unwrap_or = "warn" +match_same_arms = "warn" +mem_forget = "warn" +missing_const_for_fn = "warn" +mut_mut = "warn" +mutex_integer = "warn" +needless_continue = "warn" +option_if_let_else = "warn" +path_buf_push_overwrite = "warn" +range_plus_one = "warn" +redundant_clone = "warn" +redundant_closure_for_method_calls = "warn" +semicolon_if_nothing_returned = "warn" +unnecessary_semicolon = "warn" +unseparated_literal_suffix = "warn" +use_self = "warn" +useless_let_if_seq = "warn" diff --git a/MANUAL.txt b/MANUAL.txt index dd969663..7eb0fc1a 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,4 +1,4 @@ -oxipng 9.1.5 +oxipng 10.0.0 Losslessly improve compression of PNG files Usage: oxipng [OPTIONS] ... @@ -13,14 +13,14 @@ Options: compression. Lower levels are faster, higher levels provide better compression, though with increasingly diminishing returns. - 0 => --zc 5 --fast (1 trial, determined heuristically) - 1 => --zc 10 --fast (1 trial, determined heuristically) - 2 => --zc 11 -f 0,1,6,7 --fast (4 fast trials, 1 main trial) - 3 => --zc 11 -f 0,7,8,9 (4 trials) - 4 => --zc 12 -f 0,7,8,9 (4 trials) - 5 => --zc 12 -f 0,1,2,5,6,7,8,9 (8 trials) - 6 => --zc 12 -f 0-9 (10 trials) - max => (stable alias for the max level) + 0 => --zc 5 --fast (filter chosen heuristically) + 1 => --zc 10 --fast (filter chosen heuristically) + 2 => --zc 11 -f 0,1,6,7 --fast + 3 => --zc 11 -f 0,7,8,9 --brute-level 1 --brute-lines 3 + 4 => --zc 12 -f 0,7,8,9 --brute-level 1 --brute-lines 4 + 5 => --zc 12 -f 0,1,2,5,6,7,8,9 --brute-level 4 --brute-lines 4 + 6 => --zc 12 -f 0-9 --brute-level 5 --brute-lines 8 + max => (stable alias for the maximum level) Manually specifying a compression option (zc, f, etc.) will override the optimization preset, regardless of the order you write the arguments. @@ -45,7 +45,7 @@ Options: -p, --preserve Preserve file permissions and timestamps if possible - -P, --pretend + -d, --dry-run Do not write any files, only show compression results -s @@ -61,10 +61,14 @@ Options: CAUTION: 'all' will convert APNGs to standard PNGs. - Note that 'bKGD', 'sBIT' and 'hIST' will be forcibly stripped if the color type or bit - depth is changed, regardless of any options set. + Please note that regardless of any options set, some chunks will necessarily be stripped + when invalidated by the optimization: + bKGD, sBIT, hIST: Stripped if the color type or bit depth changes. + iDOT: Always stripped. + caBX: Stripped if it contains C2PA metadata. If explicitly retained by `--keep`, + optimization will be aborted. - The default when --strip is not passed is to keep all metadata. + The default when --strip is not passed is to keep all chunks that remain valid. --keep Strip all metadata chunks except those in the comma-separated list. The special value @@ -79,17 +83,17 @@ Options: but take care as while this is “visually lossless”, it is technically a lossy transformation and may be unsuitable for some applications. - -i, --interlace - Set the PNG interlacing type, where is one of: + -i, --interlace + Set the PNG interlacing mode, where is one of: - 0 => Remove interlacing from all images that are processed - 1 => Apply Adam7 interlacing on all images that are processed - keep => Keep the existing interlacing type of each image + off => Remove interlacing from all images that are processed + on => Apply Adam7 interlacing on all images that are processed + keep => Keep the existing interlacing mode of each image Note that interlacing can add 25-50% to the size of an optimized image. Only use it if you believe the benefits outweigh the costs for your use case. - [default: 0] + [default: off] --scale16 Forcibly reduce images with 16 bits per channel to 8 bits per channel. This is a lossy @@ -119,9 +123,9 @@ Options: Heuristic strategies (try to find the best delta filter for each line) 5 => MinSum Minimum sum of absolute differences - 6 => Entropy Highest Shannon entropy + 6 => Entropy Smallest Shannon entropy 7 => Bigrams Lowest count of distinct bigrams - 8 => BigEnt Highest Shannon entropy of bigrams + 8 => BigEnt Smallest Shannon entropy of bigrams 9 => Brute Smallest compressed size (slow) The default value depends on the optimization level preset. @@ -159,12 +163,12 @@ Options: --fix Do not perform checksum validation of PNG chunks. This may allow some files with errors to - be processed successfully. + be processed successfully. The output will always have correct checksums. --force Write the output even if it is larger than the input - -Z, --zopfli + -z, --zopfli Use the much slower but stronger Zopfli compressor for main compression trials. Recommended use is with '-o max' and '--fast'. @@ -174,6 +178,18 @@ Options: [default: 15] + --ziwi + Stop Zopfli compression after this number of iterations without improvement. Use this in + conjunction with a high value for '--zi' to achieve better compression in reasonable time. + + --brute-level + Set the libdeflate compression level to use with the Brute filter strategy. Sane values + are 1-5. Higher values are not necessarily better. + + --brute-lines + Set the number of lines to compress at once with the Brute filter strategy. Sane values + are 2-16. Higher values are not necessarily better. + --timeout Maximum amount of time, in seconds, to spend on optimizations. Oxipng will check the timeout before each transformation or compression trial, and will stop trying to optimize @@ -181,6 +197,15 @@ Options: are already in progress, so it is currently of limited effectiveness for large files with high compression levels. + --max-raw-size + Maximum size to allow for the input image. If the raw, decompressed image data (or the + file size) of the image exceeds this size, it will be skipped. This is useful for limiting + memory usage or avoiding long processing times on large images. The value may be specified + with a unit suffix such as k, KB, m, MB, etc. + + The decompressed size of an image is roughly equal to width * height * bit-depth / 8. E.g. + a 1920x1080 image with 24-bit color depth would be roughly 6MB. + -t, --threads Set the maximum number of threads to use. Oxipng uses multithreading to evaluate multiple optimizations on the same file in parallel as well as process multiple files in parallel. diff --git a/README.md b/README.md index 7e3a213f..a458adf0 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ preamble to an already existing one: ```yaml repos: - repo: https://github.com/oxipng/oxipng - rev: v9.1.5 + rev: v10.0.0 hooks: - id: oxipng args: ["-o", "4", "--strip", "safe", "--alpha"] @@ -117,7 +117,7 @@ input filename, into the [optimize function](https://docs.rs/oxipng/latest/oxipn It is recommended to disable the "binary" feature when including Oxipng as a library. Currently, there is no simple way to just disable one feature in Cargo, it has to be done by disabling default features and specifying the desired ones, for example: -`oxipng = { version = "9.0", features = ["parallel", "zopfli", "filetime"], default-features = false }` +`oxipng = { version = "10.0", features = ["parallel", "zopfli", "filetime"], default-features = false }` ## Software using Oxipng diff --git a/src/cli.rs b/src/cli.rs index 6c1397e0..61b797b7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -133,10 +133,14 @@ Strip metadata chunks, where is one of: CAUTION: 'all' will convert APNGs to standard PNGs. -Note that 'bKGD', 'sBIT' and 'hIST' will be forcibly stripped if the color type or bit \ -depth is changed, regardless of any options set. +Please note that regardless of any options set, some chunks will necessarily be stripped \ +when invalidated by the optimization: + bKGD, sBIT, hIST: Stripped if the color type or bit depth changes. + iDOT: Always stripped. + caBX: Stripped if it contains C2PA metadata. If explicitly retained by `--keep`, \ + optimization will be aborted. -The default when --strip is not passed is to keep all metadata.", +The default when --strip is not passed is to keep all chunks that remain valid.", DISPLAY_CHUNKS .iter() .map(|c| String::from_utf8_lossy(c)) diff --git a/src/lib.rs b/src/lib.rs index 732615bc..c09432d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,23 +1,4 @@ -#![warn(trivial_casts, trivial_numeric_casts, unused_import_braces)] -#![deny(missing_debug_implementations, missing_copy_implementations)] -#![warn(clippy::expl_impl_clone_on_copy)] -#![warn(clippy::float_cmp_const)] -#![warn(clippy::linkedlist)] -#![warn(clippy::map_flatten)] -#![warn(clippy::match_same_arms)] -#![warn(clippy::mem_forget)] -#![warn(clippy::mut_mut)] -#![warn(clippy::mutex_integer)] -#![warn(clippy::needless_continue)] -#![warn(clippy::path_buf_push_overwrite)] -#![warn(clippy::range_plus_one)] -#![allow(clippy::cognitive_complexity)] -#![allow(clippy::upper_case_acronyms)] -#![cfg_attr( - not(feature = "zopfli"), - allow(irrefutable_let_patterns), - allow(unreachable_patterns) -)] +#![cfg_attr(not(feature = "zopfli"), allow(unreachable_patterns))] #[cfg(feature = "parallel")] extern crate rayon; diff --git a/src/main.rs b/src/main.rs index 7b9fe684..67b1a878 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,3 @@ -#![warn(trivial_casts, trivial_numeric_casts, unused_import_braces)] -#![deny(missing_debug_implementations, missing_copy_implementations)] -#![warn(clippy::expl_impl_clone_on_copy)] -#![warn(clippy::float_cmp_const)] -#![warn(clippy::linkedlist)] -#![warn(clippy::map_flatten)] -#![warn(clippy::match_same_arms)] -#![warn(clippy::mem_forget)] -#![warn(clippy::mut_mut)] -#![warn(clippy::mutex_integer)] -#![warn(clippy::needless_continue)] -#![warn(clippy::path_buf_push_overwrite)] -#![warn(clippy::range_plus_one)] -#![allow(clippy::cognitive_complexity)] - #[cfg(not(feature = "parallel"))] mod rayon;