Commit graph

391 commits

Author SHA1 Message Date
Andrew
60911977f6 Add read_be_u16 and tidy parse_next_chunk 2025-01-26 00:16:24 +01:00
Andrew
6c7ab5d79f Add warning when interlacing was not enabled as requested 2025-01-16 12:37:33 +01:00
Kornel
ffbd927d82 Remove obsolete flag warning 2025-01-02 09:40:52 +13:00
Kornel
a72bc945fc Clippy 2025-01-02 09:15:24 +13:00
Kornel
8270c6a034 Skip signed files or remove C2PA metadata 2024-12-26 19:03:03 +01:00
Kornel
910e779b09 Use ExitCode in main 2024-12-26 19:03:03 +01:00
Frank Elsinga
8740f475d7
docs: added the defaults for some Options (#661)
There are some parts of the `Options`-struct that miss the defaults.
I also made sure that `Interlacing` is a link
2024-12-20 17:08:13 +01:00
andrews05
262082f546
Fix manpage for --zi (#653)
I don't know if this is a bug in the mangen but it seems to require
explicitly setting the value name to display correctly.
Before:
```
       --zi [default: 15]
              Set the number of iterations to use for Zopfli compression.
```

After:
```
       --zi=iterations [default: 15]
              Set the number of iterations to use for Zopfli compression.
```

This does not affect MANUAL.txt or the built-in help.
2024-12-02 12:34:52 +01:00
Kornel
f8c84c258d API docs 2024-11-30 13:28:05 +13:00
andrews05
ca05d99be4
Assume None if the line is all zeros (#650)
#648 may have been a bit hasty - I realised afterward that there's a
simpler way to achieve the same thing, and include the Brute filter as
well.

This reverts #648 and instead just picks None up front if the line is
all zeros. This is guaranteed to be the chosen filter for MinSum,
Entropy, Bigrams and BigEnt. It's almost certainly true for Brute as
well but this is harder to prove. I've tested this across hundreds of
images and found no change in output.
2024-11-28 17:21:10 +01:00
andrews05
1efacace9f
Break when best possible filter result found 2024-11-27 00:52:21 +13:00
andrews05
1936861b91
Workaround weird regression with &[u8] and zopfli (#595)
Fixes #579. In lack of a proper understanding of what's going on here
and why the issue is happening, this workaround will do for now.
2024-11-26 12:41:18 +01:00
Alejandro González
59e0509a58
Fix new Clippy lint 2024-11-19 21:36:06 +01:00
Alejandro González
44619fd24d
Fix new Clippy lint about manual div_ceil implementation
This numeric method was added on Rust 1.73, which is below our MSRV.
2024-09-28 15:42:02 +02:00
Alejandro González
7a29741f18
Run cargo fmt 2024-09-28 15:41:54 +02:00
Galaxy4594
0d60e8b7c6
Expose zc level 0 (uncompressed) 🚀 (#641)
Allow the creation of PNGs with uncompressed deflate streams via level 0
of libdeflate. If you want a glorified BMP with delta filters, this
change will make your dreams come true 🙂.
2024-09-05 00:32:28 +02:00
Matthew House
0f24120c9a
Add option for Zopfli iteration count (#640)
This PR extends the `--zopfli` argument with an optional iteration
count. In my case, I have a bunch of very small images (a few kB or
less), and I often like to use hundreds of iterations to squeeze off the
last several bytes. (I know that this crate isn't intended for
brute-force optimization, but I've found that some of its
transformations and filter strategies can be more creative than
`zopflipng`.) But this is also useful in the opposite direction, for
allowing Zopfli compression on large images where 15 iterations would be
prohibitive.
2024-08-07 21:09:47 +02:00
Alejandro González
0506418157 Remove some now unnecessary Clippy lints allowances 2024-07-11 15:20:53 +12:00
Alejandro González
a8846b897d Make CI Clippy static analysis checks more robust
I have identified two potential improvements for how we perform static analysis
on our code in our CI pipeline:

- The `giraffate/clippy-action` we currently use has not been updated to Node
  20, and GitHub has repeatedly indicated that they will phase out actions that
  do not support the latest Node versions. Despite my efforts to help with the
  update by submitting a pull request upstream, it has been ignored for months
  despite its perceived ease of review, raising concerns about the ongoing
  maintenance of the action. This situation suggests we should explore
  alternative methods for integrating Clippy with GitHub's UI.
- As evidenced by PR 632, thoroughly testing Rust crates for every possible
  feature combination is often overlooked due to the tedious nature of the task.
  Our current CI setup only checks two feature combinations, which is far from
  comprehensive.

To address the first improvement, these changes drop `clippy-action` entirely in
favor of utilizing GitHub's native CodeQL SARIF (Static Analysis Results
Interchange Format) file integration. Since Clippy cannot directly output lints
in SARIF, `clippy-sarif` is used to convert Clippy's JSON output to SARIF.
Additionally, `sarif-fmt` is added to turn SARIF into a human-friendly display
format in the workflow run logs.

For the second improvement, let's use `cargo hack` with the `--feature-powerset`
flag to run Clippy for every possible feature combination. This approach strikes
a good balance between CI runtime and thoroughness, as the number of feature
combinations grows superlinearly with the number of features: running `cargo
nextest` for every powerset element would lead to excessively long CI times.
2024-07-11 15:20:53 +12:00
andrews05
f7b837af69
Allow sanity-checks without parallel (#634)
This adds the use of the rayon shim when parallel is not enabled. See
#632.
2024-07-10 21:38:34 +02:00
Kornel
a2dedcfed2 Bump RGB crate 2024-07-10 08:31:47 +12:00
andrews05
3759ca85ec
Keep fcTL after PLTE (#626)
Fixes #625

---------

Co-authored-by: Alejandro González <me@alegon.dev>
2024-06-23 01:38:02 +02:00
andrews05
90ceef9796
Use MatchOptions::default() to disable case-sensitivity (#622)
Fixes #621
2024-06-04 18:08:07 +02:00
andrews05
e2c5b45f4b
Remove default value for interlace to ensure it is kept when nx is used (#620)
Fixes #619.

Interlacing still defaults to 0 when `--nx` is not specified and can
still be explicitly set even if it is (e.g. `-i 0 --nx` will still
deinterlace).
2024-06-02 18:46:44 +02:00
Torbjörn Lönnemark
a70e523241 Document that no metadata is stripped by default 2024-04-18 08:18:01 +12:00
andrews05
5a66e77581
Update dependencies and bump MSRV (#606)
Updates all dependencies to latest versions and bumps MSRV to 1.74 (to
match clap).
Some changes required for `env_logger` and `image`.
2024-04-13 14:58:19 +02:00
Andrew
8a59f5aa1a Change 'Reducing' to 'Transformed' 2024-04-13 08:12:16 +12:00
Andrew
268499b0db Allow passing a format description to the evaluator 2024-04-13 08:12:16 +12:00
andrews05
2d3555fe0f
Add modified zeng palette sorting method (#602)
This PR adds the modified zeng ("mzeng") palette sorting method, in
addition to the existing luma and battiato methods. Speed is very
similar to the battiato method with slightly better results on average.

Resulting sizes from two different image sets (all indexed or able to be
indexed):
| | master | PR |
|-|-|-|
| Set 1 | 29,647,156 | 29,555,697 |
| Set 2 | 23,732,133 | 23,570,862 |

Additionally, I've added a new "first colour" heuristic for both the
mzeng and battiato methods: We use the most popular colour overall, but
only if it covers at least 15% of the image. This provided 13k savings
on Set 2 vs the edge colour heuristic (which is still used in the luma
sort).
2024-04-06 01:16:29 +02:00
Andrew
fca76a7afb Always run palette reduction even if there's only one entry 2024-03-29 09:15:27 +13:00
Andrew
1ddab42edb Always fix palette even if it would be larger 2024-03-29 09:15:27 +13:00
andrews05
eae53362b2 Add comment regarding avoiding panics 2024-03-29 09:07:24 +13:00
Kornel
a3590df20c Optimize co-occurence matrix 2024-03-29 09:07:24 +13:00
Kornel
36de54318d Faster, garbage-resilient most_popular_edge_color 2024-03-29 09:07:24 +13:00
Kornel
d063119d9c Avoid split_at panic in ScanLines 2024-03-29 09:07:24 +13:00
andrews05
f4e631bce7
Feature/manpage (#596)
This PR adds a build script to generate a man page using clap_mangen, as
per this example:
https://github.com/sondr3/clap-man-example/blob/main/build.rs

I'm not sure what to actually do with the man file from here, I guess
it's up to the packaging process to do something with it?
See
https://github.com/shssoichiro/oxipng/issues/69#issuecomment-1963352536

Note I couldn't see a way to include the `DISPLAY` chunk names from the
constant as we did before. They're now just hardcoded into the help and
will require manually updating if the list changes.

Closes #526

---------

Co-authored-by: Alejandro González <me@alegon.dev>
2024-03-18 12:28:52 +01:00
Andrew
2c3321b68a
Fix clippy lints 2024-03-18 12:15:02 +01:00
Andrew
3e3c027cd0 Skip extra evaluation when identical 2024-03-11 17:38:48 +13:00
Andrew
703ebb4813 Try both gray and indexed low depths 2024-03-11 17:38:48 +13:00
Andrew
a426fabe13 Fix secondary palette sort
This was only working if the input image was already indexed
2024-02-21 08:13:03 +13:00
Alejandro González
0608ab9cab
Fix some new nightly Clippy lints and run rustfmt with prettier config 2024-02-20 19:12:35 +01:00
andrews05
63efc76f3e
Add --keep display equivalent to --strip safe (#584)
This PR reverts #580 and introduces `--keep display` instead.

See discussion in #581.
2023-12-13 10:47:29 +01:00
andrews05
f6c24409f1
If only one filter selected use it for evaluations (#582)
The filters used during reduction evaluations are normally set to None &
Bigrams, regardless of any options. This PR makes a slight change so
that if only one filter is specified in the options, this filter will be
used for reduction evaluations too.

This resolves an odd situation affecting lower levels (when --fast is
enabled) where you may try to force the filter to a specific value but
it actually ends up different because a reduction evaluation was
smaller. It's particularly helpful if you're wanting it to be as fast as
possible by using `-o0 -f0` which will now exclusively use None instead
of trying the slower Bigrams as well.

As another use, you could try to brute force oxipng by iterating each
filter separately, though this may not actually achieve anything 😂

[edit] I also pulled the options out into a separate file, though this
wasn't relevant to the filter change.
2023-12-10 13:15:45 +01:00
andrews05
97af04a539
Allow strip and keep to be used together (#580)
This is a minor change that allows using both `--strip` and `--keep` at
the same time.

E.g. `--strip safe --keep eXIf` will strip chunks while preserving both
the ones that aren't "safe" to remove *and* eXIf. Essentially it's a
convenience to allow extending the default list used by `--strip safe`.

Specifying chunk names for both options is not permitted, e.g. `--strip
eXIf --keep eXIf` will error.

Use of `--strip all` with `--keep` is redundant, but is permitted.
2023-11-18 13:06:49 +01:00
Alejandro González
a6152b06c4
Fix and simplify output file permission copying code (#578)
A detailed read to the [Rust documentation for the `fs::Permissions`
struct](https://doc.rust-lang.org/stable/std/fs/struct.Permissions.html)
and a little digging into its implementation in the standard library
have shown that this code didn't work as expected in any platform, and
was a bit weird to begin with:

- It first read the permissions from the input file metadata.
- Then it fetched the output file metadata.
- After that, it changed the permissions for that output file metadata.
- It then performed a sanity check that the output file had the expected
permissions.

Barring the fact that the sanity check in step 4 is not needed, the
overall approach is wrong because setting the permissions in a file
metadata struct does not actually persist those changes anywere; it's
just an in-memory change only, so these operations were useless. The
Rust documentation explicitly mentions that the `set_readonly` method
"does not modify the files attributes" [sic], but it's easy to miss that
warning and not realize that it also applies to the methods offered by
the `PermissionsExt` trait. The code only appeared to work because in
most cases the default permissions for new files happen to match the
input file permissions, so the sanity check passed.

To fix this, use the `set_permissions` method on `File` to actually set
the output file permissions to be the same as the input file
permissions, which is both much simpler and robust.

These changes were tested in the context of issue #576, and fix #576.
2023-11-16 00:15:36 +01:00
andrews05
93c3e7dfae
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>
2023-10-10 00:39:37 +02:00
Andrew
6763eea1e3 Combine palette reduction and sorting 2023-10-10 11:03:08 +13:00
Alejandro González
904beeec6b
Update zopfli to v0.8.0 (#560)
This update brings several parameterization and usage flexibility
improvements on Zopfli, allowing users to choose to limit execution time
by a number of iterations without improvement, and exposing a more
advanced `ZlibEncoder` struct to tune compression block sizes and
DEFLATE block types. Some minor microoptimizations were also made.

For now, I don't expect this PR to substantially affect how OxiPNG
compresses images using its Zopfli mode, but the additional parameter
customization may come in handy for future work improving how Zopfli is
used in OxiPNG.
2023-10-09 23:14:22 +02:00
Andrew
8eb5a3f419 Remove libdeflate slack space no longer needed 2023-09-27 09:16:24 +13:00
andrews05
25d0685bdf
Remove backup and check options (#546)
Tidy up the API by removing a couple of options we don't really need.
Backup was discussed in #542
Check was discussed in  #439

@shssoichiro Just say if you prefer to keep either of these 🙂
2023-09-26 09:52:51 +02:00