Overhaul help (#563)
This PR brings a big overhaul to oxipng's help, with new long form
descriptions of many options.
The full output (--help) is added as a text file MANUAL.txt. Critiques
welcome.
The short output (-h) is simplified and appears as follows:
```
Losslessly improve compression of PNG files
Usage: oxipng [OPTIONS] <files>...
Arguments:
<files>... File(s) to compress (use '-' for stdin)
Options:
-o, --opt <level> Optimization level (0-6, or max) [default: 2]
-r, --recursive Recurse input directories, optimizing all PNG files
--dir <directory> Write output file(s) to <directory>
--out <file> Write output file to <file>
--stdout Write output to stdout
-p, --preserve Preserve file permissions and timestamps if possible
-P, --pretend Do not write any files, only show compression results
-s Strip safely-removable chunks, same as '--strip safe'
--strip <mode> Strip metadata (safe, all, or comma-separated list)
CAUTION: 'all' will convert APNGs to standard PNGs
--keep <list> Strip all metadata except in the comma-separated list
-a, --alpha Perform additional alpha channel optimization
-i, --interlace <type> Set PNG interlacing type (0, 1, keep) [default: 0]
--scale16 Forcibly reduce 16-bit images to 8-bit (lossy)
-v, --verbose... Run in verbose mode (use twice to increase verbosity)
-q, --quiet Run in quiet mode
-f, --filters <list> Filters to try (0-9; see '--help' for details)
--fast Use fast filter evaluation
--zc <level> Deflate compression level (1-12)
--nb Do not change bit depth
--nc Do not change color type
--np Do not change color palette
--ng Do not change to or from grayscale
--nx Do not perform any transformations
--nz Do not recompress unless transformations occur
--fix Disable checksum validation
--force Write the output even if it is larger than the input
-Z, --zopfli Use the much slower but stronger Zopfli compressor
--timeout <secs> Maximum amount of time to spend on optimizations
-t, --threads <num> Set number of threads to use [default: num CPU cores]
-h, --help Print help (see more with '--help')
-V, --version Print version
Run `oxipng --help` to see full details of all options
```
---------
Co-authored-by: Alejandro González <me@alegon.dev>
This commit is contained in:
parent
0b3302316b
commit
93c3e7dfae
6 changed files with 346 additions and 56 deletions
5
.github/workflows/deploy.yml
vendored
5
.github/workflows/deploy.yml
vendored
|
|
@ -48,13 +48,16 @@ jobs:
|
||||||
name: Oxipng binary (${{ matrix.target }})
|
name: Oxipng binary (${{ matrix.target }})
|
||||||
path: target
|
path: target
|
||||||
|
|
||||||
|
- name: Generate up to date manual
|
||||||
|
run: scripts/manual.sh
|
||||||
|
|
||||||
- name: Build archives
|
- name: Build archives
|
||||||
working-directory: target
|
working-directory: target
|
||||||
run: |
|
run: |
|
||||||
ARCHIVE_NAME="oxipng-${{ steps.oxipngMeta.outputs.version }}-${{ matrix.target }}"
|
ARCHIVE_NAME="oxipng-${{ steps.oxipngMeta.outputs.version }}-${{ matrix.target }}"
|
||||||
|
|
||||||
mkdir "$ARCHIVE_NAME"
|
mkdir "$ARCHIVE_NAME"
|
||||||
cp ../CHANGELOG.md ../README.md "$ARCHIVE_NAME"
|
cp ../CHANGELOG.md ../README.md ../MANUAL.txt "$ARCHIVE_NAME"
|
||||||
|
|
||||||
case '${{ matrix.target }}' in
|
case '${{ matrix.target }}' in
|
||||||
*-windows-*)
|
*-windows-*)
|
||||||
|
|
|
||||||
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -125,6 +125,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"strsim",
|
"strsim",
|
||||||
|
"terminal_size",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -596,6 +597,16 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "terminal_size"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
|
||||||
|
dependencies = [
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typed-arena"
|
name = "typed-arena"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ version = "1.7.0"
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
optional = true
|
optional = true
|
||||||
version = "4.3.8"
|
version = "4.3.8"
|
||||||
|
features = ["wrap_help"]
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.glob]
|
[target.'cfg(windows)'.dependencies.glob]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
|
||||||
178
MANUAL.txt
Normal file
178
MANUAL.txt
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
oxipng 9.0.0
|
||||||
|
Losslessly improve compression of PNG files
|
||||||
|
|
||||||
|
Usage: oxipng [OPTIONS] <files>...
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<files>...
|
||||||
|
File(s) to compress (use '-' for stdin)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-o, --opt <level>
|
||||||
|
Set the optimization level preset. The default level 2 is quite fast and provides good
|
||||||
|
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)
|
||||||
|
|
||||||
|
Manually specifying a compression option (zc, f, etc.) will override the optimization
|
||||||
|
preset, regardless of the order you write the arguments.
|
||||||
|
|
||||||
|
[default: 2]
|
||||||
|
|
||||||
|
-r, --recursive
|
||||||
|
When directories are given as input, traverse the directory trees and optimize all PNG
|
||||||
|
files found (files with “.png” or “.apng” extension).
|
||||||
|
|
||||||
|
--dir <directory>
|
||||||
|
Write output file(s) to <directory>. If the directory does not exist, it will be created.
|
||||||
|
Note that this will not preserve the directory structure of the input files when used with
|
||||||
|
'--recursive'.
|
||||||
|
|
||||||
|
--out <file>
|
||||||
|
Write output file to <file>
|
||||||
|
|
||||||
|
--stdout
|
||||||
|
Write output to stdout
|
||||||
|
|
||||||
|
-p, --preserve
|
||||||
|
Preserve file permissions and timestamps if possible
|
||||||
|
|
||||||
|
-P, --pretend
|
||||||
|
Do not write any files, only show compression results
|
||||||
|
|
||||||
|
-s
|
||||||
|
Strip safely-removable chunks, same as '--strip safe'
|
||||||
|
|
||||||
|
--strip <mode>
|
||||||
|
Strip metadata chunks, where <mode> is one of:
|
||||||
|
|
||||||
|
safe => Strip all non-critical chunks, except for the following:
|
||||||
|
cICP, iCCP, sRGB, pHYs, acTL, fcTL, fdAT
|
||||||
|
all => Strip all non-critical chunks
|
||||||
|
<list> => Strip chunks in the comma-separated list, e.g. 'bKGD,cHRM'
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
--keep <list>
|
||||||
|
Strip all metadata except in the comma-separated list
|
||||||
|
|
||||||
|
-a, --alpha
|
||||||
|
Perform additional optimization on images with an alpha channel, by altering the color
|
||||||
|
values of fully transparent pixels. This is generally recommended for better compression,
|
||||||
|
but take care as while this is “visually lossless”, it is technically a lossy
|
||||||
|
transformation and may be unsuitable for some applications.
|
||||||
|
|
||||||
|
-i, --interlace <type>
|
||||||
|
Set the PNG interlacing type, where <type> 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
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
--scale16
|
||||||
|
Forcibly reduce images with 16 bits per channel to 8 bits per channel. This is a lossy
|
||||||
|
operation but can provide significant savings when you have no need for higher depth.
|
||||||
|
Reduction is performed by scaling the values such that, e.g. 0x00FF is reduced to 0x01
|
||||||
|
rather than 0x00.
|
||||||
|
|
||||||
|
Without this flag, 16-bit images will only be reduced in depth if it can be done
|
||||||
|
losslessly.
|
||||||
|
|
||||||
|
-v, --verbose...
|
||||||
|
Run in verbose mode (use twice to increase verbosity)
|
||||||
|
|
||||||
|
-q, --quiet
|
||||||
|
Run in quiet mode
|
||||||
|
|
||||||
|
-f, --filters <list>
|
||||||
|
Perform compression trials with each of the given filter types. You can specify a
|
||||||
|
comma-separated list, or a range of values. E.g. '-f 0-3' is the same as '-f 0,1,2,3'.
|
||||||
|
|
||||||
|
PNG delta filters (apply the same filter to every line)
|
||||||
|
0 => None (recommended to always include this filter)
|
||||||
|
1 => Sub
|
||||||
|
2 => Up
|
||||||
|
3 => Average
|
||||||
|
4 => Paeth
|
||||||
|
Heuristic strategies (try to find the best delta filter for each line)
|
||||||
|
5 => MinSum Minimum sum of absolute differences
|
||||||
|
6 => Entropy Highest Shannon entropy
|
||||||
|
7 => Bigrams Lowest count of distinct bigrams
|
||||||
|
8 => BigEnt Highest Shannon entropy of bigrams
|
||||||
|
9 => Brute Smallest compressed size (slow)
|
||||||
|
|
||||||
|
The default value depends on the optimization level preset.
|
||||||
|
|
||||||
|
--fast
|
||||||
|
Perform a fast compression evaluation of each enabled filter, followed by a single main
|
||||||
|
compression trial of the best result. Recommended if you have more filters enabled than
|
||||||
|
CPU cores.
|
||||||
|
|
||||||
|
--zc <level>
|
||||||
|
Deflate compression level (1-12) for main compression trials. The levels here are defined
|
||||||
|
by the libdeflate compression library.
|
||||||
|
|
||||||
|
The default value depends on the optimization level preset.
|
||||||
|
|
||||||
|
--nb
|
||||||
|
Do not change bit depth
|
||||||
|
|
||||||
|
--nc
|
||||||
|
Do not change color type
|
||||||
|
|
||||||
|
--np
|
||||||
|
Do not change color palette
|
||||||
|
|
||||||
|
--ng
|
||||||
|
Do not change to or from grayscale
|
||||||
|
|
||||||
|
--nx
|
||||||
|
Do not perform any transformations and do not deinterlace by default.
|
||||||
|
|
||||||
|
--nz
|
||||||
|
Do not recompress IDAT unless required due to transformations. Recompression of other
|
||||||
|
compressed chunks (such as iCCP) will also be disabled. Note that the combination of
|
||||||
|
'--nx' and '--nz' will fully disable all optimization.
|
||||||
|
|
||||||
|
--fix
|
||||||
|
Do not perform checksum validation of PNG chunks. This may allow some files with errors to
|
||||||
|
be processed successfully.
|
||||||
|
|
||||||
|
--force
|
||||||
|
Write the output even if it is larger than the input
|
||||||
|
|
||||||
|
-Z, --zopfli
|
||||||
|
Use the much slower but stronger Zopfli compressor for main compression trials.
|
||||||
|
Recommended use is with '-o max' and '--fast'.
|
||||||
|
|
||||||
|
--timeout <secs>
|
||||||
|
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
|
||||||
|
the file if the timeout is exceeded. Note that this does not cut short any operations that
|
||||||
|
are already in progress, so it is currently of limited effectiveness for large files with
|
||||||
|
high compression levels.
|
||||||
|
|
||||||
|
-t, --threads <num>
|
||||||
|
Set number of threads to use [default: num CPU cores]
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
Print help (see a summary with '-h')
|
||||||
|
|
||||||
|
-V, --version
|
||||||
|
Print version
|
||||||
5
scripts/manual.sh
Executable file
5
scripts/manual.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
./target/debug/oxipng -V > MANUAL.txt
|
||||||
|
./target/debug/oxipng --help >> MANUAL.txt
|
||||||
202
src/main.rs
202
src/main.rs
|
|
@ -35,13 +35,18 @@ use std::process::exit;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// Note: clap 'wrap_help' is enabled to automatically wrap lines according to terminal width.
|
||||||
|
// To keep things tidy though, short help descriptions should be no more than 54 characters,
|
||||||
|
// so that they can fit on a single line in an 80 character terminal.
|
||||||
|
// Long help descriptions are soft wrapped here at 90 characters (column 91) but this does not
|
||||||
|
// affect output, it simply matches what is rendered when help is output to a file.
|
||||||
let matches = Command::new("oxipng")
|
let matches = Command::new("oxipng")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.author("Joshua Holmer <jholmer.in@gmail.com>")
|
.author("Joshua Holmer <jholmer.in@gmail.com>")
|
||||||
.about("Losslessly improves compression of PNG files")
|
.about("Losslessly improve compression of PNG files")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("files")
|
Arg::new("files")
|
||||||
.help("File(s) to compress (use \"-\" for stdin)")
|
.help("File(s) to compress (use '-' for stdin)")
|
||||||
.index(1)
|
.index(1)
|
||||||
.num_args(1..)
|
.num_args(1..)
|
||||||
.use_value_delimiter(false)
|
.use_value_delimiter(false)
|
||||||
|
|
@ -50,11 +55,29 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("optimization")
|
Arg::new("optimization")
|
||||||
.help("Optimization level - Default: 2")
|
.help("Optimization level (0-6, or max)")
|
||||||
|
.long_help("\
|
||||||
|
Set the optimization level preset. The default level 2 is quite fast and provides good \
|
||||||
|
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)
|
||||||
|
|
||||||
|
Manually specifying a compression option (zc, f, etc.) will override the optimization \
|
||||||
|
preset, regardless of the order you write the arguments.")
|
||||||
.short('o')
|
.short('o')
|
||||||
.long("opt")
|
.long("opt")
|
||||||
.value_name("level")
|
.value_name("level")
|
||||||
.value_parser(["0", "1", "2", "3", "4", "5", "6", "max"]),
|
.default_value("2")
|
||||||
|
.value_parser(["0", "1", "2", "3", "4", "5", "6", "max"])
|
||||||
|
.hide_possible_values(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("backup")
|
Arg::new("backup")
|
||||||
|
|
@ -66,7 +89,10 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("recursive")
|
Arg::new("recursive")
|
||||||
.help("Recurse into subdirectories and optimize all *.png/*.apng files")
|
.help("Recurse input directories, optimizing all PNG files")
|
||||||
|
.long_help("\
|
||||||
|
When directories are given as input, traverse the directory trees and optimize all PNG \
|
||||||
|
files found (files with “.png” or “.apng” extension).")
|
||||||
.short('r')
|
.short('r')
|
||||||
.long("recursive")
|
.long("recursive")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
|
|
@ -74,6 +100,10 @@ fn main() {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("output_dir")
|
Arg::new("output_dir")
|
||||||
.help("Write output file(s) to <directory>")
|
.help("Write output file(s) to <directory>")
|
||||||
|
.long_help("\
|
||||||
|
Write output file(s) to <directory>. If the directory does not exist, it will be created. \
|
||||||
|
Note that this will not preserve the directory structure of the input files when used with \
|
||||||
|
'--recursive'.")
|
||||||
.long("dir")
|
.long("dir")
|
||||||
.value_name("directory")
|
.value_name("directory")
|
||||||
.value_parser(value_parser!(PathBuf))
|
.value_parser(value_parser!(PathBuf))
|
||||||
|
|
@ -99,35 +129,52 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("preserve")
|
Arg::new("preserve")
|
||||||
.help("Preserve file attributes if possible")
|
.help("Preserve file permissions and timestamps if possible")
|
||||||
.short('p')
|
.short('p')
|
||||||
.long("preserve")
|
.long("preserve")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("pretend")
|
Arg::new("pretend")
|
||||||
.help("Do not write any files, only calculate compression gains")
|
.help("Do not write any files, only show compression results")
|
||||||
.short('P')
|
.short('P')
|
||||||
.long("pretend")
|
.long("pretend")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("strip-safe")
|
Arg::new("strip-safe")
|
||||||
.help("Strip safely-removable metadata objects")
|
.help("Strip safely-removable chunks, same as '--strip safe'")
|
||||||
.short('s')
|
.short('s')
|
||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::SetTrue)
|
||||||
.conflicts_with("strip"),
|
.conflicts_with("strip"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("strip")
|
Arg::new("strip")
|
||||||
.help("Strip metadata objects ['safe', 'all', or comma-separated list]\nCAUTION: stripping 'all' will convert APNGs to standard PNGs")
|
.help("Strip metadata (safe, all, or comma-separated list)\nCAUTION: 'all' will convert APNGs to standard PNGs")
|
||||||
|
.long_help(format!("\
|
||||||
|
Strip metadata chunks, where <mode> is one of:
|
||||||
|
|
||||||
|
safe => Strip all non-critical chunks, except for the following:
|
||||||
|
{}
|
||||||
|
all => Strip all non-critical chunks
|
||||||
|
<list> => Strip chunks in the comma-separated list, e.g. 'bKGD,cHRM'
|
||||||
|
|
||||||
|
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.",
|
||||||
|
StripChunks::KEEP_SAFE
|
||||||
|
.iter()
|
||||||
|
.map(|c| String::from_utf8_lossy(c))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")))
|
||||||
.long("strip")
|
.long("strip")
|
||||||
.value_name("mode")
|
.value_name("mode")
|
||||||
.conflicts_with("strip-safe"),
|
.conflicts_with("strip-safe"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("keep")
|
Arg::new("keep")
|
||||||
.help("Strip all optional metadata except objects in the comma-separated list")
|
.help("Strip all metadata except in the comma-separated list")
|
||||||
.long("keep")
|
.long("keep")
|
||||||
.value_name("list")
|
.value_name("list")
|
||||||
.conflicts_with("strip")
|
.conflicts_with("strip")
|
||||||
|
|
@ -135,28 +182,52 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("alpha")
|
Arg::new("alpha")
|
||||||
.help("Perform additional alpha optimizations")
|
.help("Perform additional alpha channel optimization")
|
||||||
|
.long_help("\
|
||||||
|
Perform additional optimization on images with an alpha channel, by altering the color \
|
||||||
|
values of fully transparent pixels. This is generally recommended for better compression, \
|
||||||
|
but take care as while this is “visually lossless”, it is technically a lossy \
|
||||||
|
transformation and may be unsuitable for some applications.")
|
||||||
.short('a')
|
.short('a')
|
||||||
.long("alpha")
|
.long("alpha")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("interlace")
|
Arg::new("interlace")
|
||||||
.help("PNG interlace type - Default: 0")
|
.help("Set PNG interlacing type (0, 1, keep)")
|
||||||
|
.long_help("\
|
||||||
|
Set the PNG interlacing type, where <type> 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
|
||||||
|
|
||||||
|
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.")
|
||||||
.short('i')
|
.short('i')
|
||||||
.long("interlace")
|
.long("interlace")
|
||||||
.value_name("type")
|
.value_name("type")
|
||||||
.value_parser(["0", "1", "keep"]),
|
.default_value("0")
|
||||||
|
.value_parser(["0", "1", "keep"])
|
||||||
|
.hide_possible_values(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("scale16")
|
Arg::new("scale16")
|
||||||
.help("Forcibly reduce 16-bit images to 8-bit")
|
.help("Forcibly reduce 16-bit images to 8-bit (lossy)")
|
||||||
|
.long_help("\
|
||||||
|
Forcibly reduce images with 16 bits per channel to 8 bits per channel. This is a lossy \
|
||||||
|
operation but can provide significant savings when you have no need for higher depth. \
|
||||||
|
Reduction is performed by scaling the values such that, e.g. 0x00FF is reduced to 0x01 \
|
||||||
|
rather than 0x00.
|
||||||
|
|
||||||
|
Without this flag, 16-bit images will only be reduced in depth if it can be done \
|
||||||
|
losslessly.")
|
||||||
.long("scale16")
|
.long("scale16")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("verbose")
|
Arg::new("verbose")
|
||||||
.help("Run in verbose mode (use multiple times to increase verbosity)")
|
.help("Run in verbose mode (use twice to increase verbosity)")
|
||||||
.short('v')
|
.short('v')
|
||||||
.long("verbose")
|
.long("verbose")
|
||||||
.action(ArgAction::Count)
|
.action(ArgAction::Count)
|
||||||
|
|
@ -172,9 +243,28 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("filters")
|
Arg::new("filters")
|
||||||
.help(format!("PNG delta filters (0-{})", RowFilter::LAST))
|
.help(format!("Filters to try (0-{}; see '--help' for details)", RowFilter::LAST))
|
||||||
|
.long_help("\
|
||||||
|
Perform compression trials with each of the given filter types. You can specify a \
|
||||||
|
comma-separated list, or a range of values. E.g. '-f 0-3' is the same as '-f 0,1,2,3'.
|
||||||
|
|
||||||
|
PNG delta filters (apply the same filter to every line)
|
||||||
|
0 => None (recommended to always include this filter)
|
||||||
|
1 => Sub
|
||||||
|
2 => Up
|
||||||
|
3 => Average
|
||||||
|
4 => Paeth
|
||||||
|
Heuristic strategies (try to find the best delta filter for each line)
|
||||||
|
5 => MinSum Minimum sum of absolute differences
|
||||||
|
6 => Entropy Highest Shannon entropy
|
||||||
|
7 => Bigrams Lowest count of distinct bigrams
|
||||||
|
8 => BigEnt Highest Shannon entropy of bigrams
|
||||||
|
9 => Brute Smallest compressed size (slow)
|
||||||
|
|
||||||
|
The default value depends on the optimization level preset.")
|
||||||
.short('f')
|
.short('f')
|
||||||
.long("filters")
|
.long("filters")
|
||||||
|
.value_name("list")
|
||||||
.value_parser(|x: &str| {
|
.value_parser(|x: &str| {
|
||||||
parse_numeric_range_opts(x, 0, RowFilter::LAST)
|
parse_numeric_range_opts(x, 0, RowFilter::LAST)
|
||||||
.map_err(|_| "Invalid option for filters")
|
.map_err(|_| "Invalid option for filters")
|
||||||
|
|
@ -182,13 +272,22 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("fast")
|
Arg::new("fast")
|
||||||
.help("Use fast filter evaluation (helpful when you have more filters enabled than CPU cores)")
|
.help("Use fast filter evaluation")
|
||||||
|
.long_help("\
|
||||||
|
Perform a fast compression evaluation of each enabled filter, followed by a single main \
|
||||||
|
compression trial of the best result. Recommended if you have more filters enabled than \
|
||||||
|
CPU cores.")
|
||||||
.long("fast")
|
.long("fast")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("compression")
|
Arg::new("compression")
|
||||||
.help("zlib compression level (1-12)")
|
.help("Deflate compression level (1-12)")
|
||||||
|
.long_help("\
|
||||||
|
Deflate compression level (1-12) for main compression trials. The levels here are defined \
|
||||||
|
by the libdeflate compression library.
|
||||||
|
|
||||||
|
The default value depends on the optimization level preset.")
|
||||||
.long("zc")
|
.long("zc")
|
||||||
.value_name("level")
|
.value_name("level")
|
||||||
.value_parser(1..=12)
|
.value_parser(1..=12)
|
||||||
|
|
@ -196,43 +295,52 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("no-bit-reduction")
|
Arg::new("no-bit-reduction")
|
||||||
.help("No bit depth reduction")
|
.help("Do not change bit depth")
|
||||||
.long("nb")
|
.long("nb")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("no-color-reduction")
|
Arg::new("no-color-reduction")
|
||||||
.help("No color type reduction")
|
.help("Do not change color type")
|
||||||
.long("nc")
|
.long("nc")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("no-palette-reduction")
|
Arg::new("no-palette-reduction")
|
||||||
.help("No palette reduction")
|
.help("Do not change color palette")
|
||||||
.long("np")
|
.long("np")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("no-grayscale-reduction")
|
Arg::new("no-grayscale-reduction")
|
||||||
.help("No grayscale reduction")
|
.help("Do not change to or from grayscale")
|
||||||
.long("ng")
|
.long("ng")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("no-reductions")
|
Arg::new("no-reductions")
|
||||||
.help("No reductions or deinterlacing")
|
.help("Do not perform any transformations")
|
||||||
|
.long_help("\
|
||||||
|
Do not perform any transformations and do not deinterlace by default.")
|
||||||
.long("nx")
|
.long("nx")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("no-recoding")
|
Arg::new("no-recoding")
|
||||||
.help("No recoding of IDAT or other compressed chunks unless necessary")
|
.help("Do not recompress unless transformations occur")
|
||||||
|
.long_help("\
|
||||||
|
Do not recompress IDAT unless required due to transformations. Recompression of other \
|
||||||
|
compressed chunks (such as iCCP) will also be disabled. Note that the combination of \
|
||||||
|
'--nx' and '--nz' will fully disable all optimization.")
|
||||||
.long("nz")
|
.long("nz")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("fix")
|
Arg::new("fix")
|
||||||
.help("Enable error recovery")
|
.help("Disable checksum validation")
|
||||||
|
.long_help("\
|
||||||
|
Do not perform checksum validation of PNG chunks. This may allow some files with errors to \
|
||||||
|
be processed successfully.")
|
||||||
.long("fix")
|
.long("fix")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
|
|
@ -244,53 +352,37 @@ fn main() {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("zopfli")
|
Arg::new("zopfli")
|
||||||
.help("Use the slow but stronger Zopfli compressor (recommended use is with all filters and `--fast` enabled)")
|
.help("Use the much slower but stronger Zopfli compressor")
|
||||||
|
.long_help("\
|
||||||
|
Use the much slower but stronger Zopfli compressor for main compression trials. \
|
||||||
|
Recommended use is with '-o max' and '--fast'.")
|
||||||
.short('Z')
|
.short('Z')
|
||||||
.long("zopfli")
|
.long("zopfli")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("timeout")
|
Arg::new("timeout")
|
||||||
.help("Maximum amount of time, in seconds, to spend on optimizations (currently of limited use due to the shift away from zlib)")
|
.help("Maximum amount of time to spend on optimizations")
|
||||||
|
.long_help("\
|
||||||
|
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 \
|
||||||
|
the file if the timeout is exceeded. Note that this does not cut short any operations that \
|
||||||
|
are already in progress, so it is currently of limited effectiveness for large files with \
|
||||||
|
high compression levels.")
|
||||||
.value_name("secs")
|
.value_name("secs")
|
||||||
.long("timeout")
|
.long("timeout")
|
||||||
.value_parser(value_parser!(u64)),
|
.value_parser(value_parser!(u64)),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("threads")
|
Arg::new("threads")
|
||||||
.help("Set number of threads to use - Default: num CPU cores")
|
.help("Set number of threads to use [default: num CPU cores]")
|
||||||
.long("threads")
|
.long("threads")
|
||||||
.short('t')
|
.short('t')
|
||||||
.value_name("num")
|
.value_name("num")
|
||||||
.value_parser(value_parser!(usize)),
|
.value_parser(value_parser!(usize)),
|
||||||
)
|
)
|
||||||
.after_help(
|
.after_help("Run `oxipng --help` to see full details of all options")
|
||||||
"Optimization levels:
|
.after_long_help("")
|
||||||
-o 0 => --zc 5 --fast (1 trial, determined heuristically)
|
|
||||||
-o 1 => --zc 10 --fast (1 trial, determined heuristically)
|
|
||||||
-o 2 => --zc 11 -f 0,1,6,7 --fast (1 trial, determined by fast evaluation)
|
|
||||||
-o 3 => --zc 11 -f 0,7,8,9 (4 trials)
|
|
||||||
-o 4 => --zc 12 -f 0,7,8,9 (4 trials; same as `-o 3` for zopfli)
|
|
||||||
-o 5 => --zc 12 -f 0,1,2,5,6,7,8,9 (8 trials)
|
|
||||||
-o 6 => --zc 12 -f 0-9 (10 trials)
|
|
||||||
-o max => (stable alias for the max compression)
|
|
||||||
|
|
||||||
Manually specifying a compression option (zc, f, etc.) will override the optimization preset,
|
|
||||||
regardless of the order you write the arguments.
|
|
||||||
|
|
||||||
PNG delta filters:
|
|
||||||
0 => None
|
|
||||||
1 => Sub
|
|
||||||
2 => Up
|
|
||||||
3 => Average
|
|
||||||
4 => Paeth
|
|
||||||
Heuristic filter selection strategies:
|
|
||||||
5 => MinSum Minimum sum of absolute differences
|
|
||||||
6 => Entropy Highest Shannon entropy
|
|
||||||
7 => Bigrams Lowest count of distinct bigrams
|
|
||||||
8 => BigEnt Highest Shannon entropy of bigrams
|
|
||||||
9 => Brute Smallest compressed size (slow)",
|
|
||||||
)
|
|
||||||
.get_matches_from(std::env::args());
|
.get_matches_from(std::env::args());
|
||||||
|
|
||||||
if matches.get_flag("backup") {
|
if matches.get_flag("backup") {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue