Git Product home page Git Product logo

oxipng's Introduction

Oxipng

Build Status Version License Docs

Overview

Oxipng is a multithreaded lossless PNG/APNG compression optimizer. It can be used via a command-line interface or as a library in other Rust programs.

Installing

Oxipng for Windows can be downloaded from the Releases link on the GitHub page.

For MacOS or Linux, it is recommended to install from your distro's package repository, if possible. Oxipng is known to be packaged for the environments listed below.

Packaging status

Alternatively, oxipng can be installed from Cargo, via the following command:

cargo install oxipng

Oxipng can be built from source using the latest stable or nightly Rust. This is primarily useful for developing on oxipng.

git clone https://github.com/shssoichiro/oxipng.git
cd oxipng
cargo build --release
cp target/release/oxipng /usr/local/bin

The current minimum supported Rust version is 1.74.0.

Oxipng follows Semantic Versioning.

Usage

Oxipng is a command-line utility. An example usage, suitable for web, may be the following:

oxipng -o 4 --strip safe --alpha *.png

The most commonly used options are as follows:

  • Optimization: -o 0 through -o 6 (or -o max), lower is faster, higher is better compression. The default (-o 2) is quite fast and provides good compression. Higher levels can be notably better but generally have increasingly diminishing returns.
  • Strip: Used to remove metadata info from processed images. Used via --strip [safe,all]. Can save a few kilobytes if you don't need the metadata. "Safe" removes only metadata that will never affect rendering of the image. "All" removes all metadata that is not critical to the image. You can also pass a comma-separated list of specific metadata chunks to remove. -s can be used as a shorthand for --strip safe.
  • Alpha: --alpha can improve compression of images with transparency, by altering the color values of fully transparent pixels. This is generally recommended, but take care as this is technically a lossy transformation and may be unsuitable for some specific applications.

More advanced options can be found by running oxipng --help, or viewed here.

Some options have both short (-a) and long (--alpha) forms. Which form you use is just a matter of preference. Multiple short options can be combined together, e.g.: -savvo6 is equivalent to to --strip safe --alpha --verbose --verbose --opt 6. Note that all options are case-sensitive.

Git integration via pre-commit

Create a .pre-commit-config.yaml file like this, or add the lines after the repos map preamble to an already existing one:

repos:
  - repo: https://github.com/shssoichiro/oxipng
    rev: v9.0.0
    hooks:
      - id: oxipng
        args: ["-o", "4", "--strip", "safe", "--alpha"]

Git integration via Trunk

Trunk is an extendable superlinter which can be used to run oxipng to automatically optimize pngs when committing them into a git repo, or to gate any pngs being added to a git repo on whether they are optimized. The trunk oxipng integration is here.

To enable oxipng via trunk:

# to get the latest version:
trunk check enable oxipng

# to get a specific version:
trunk check enable [email protected]

or modify .trunk/trunk.yaml in your repo to contain:

lint:
  enabled:
    - [email protected]

Then just run:

# to optimize a png:
trunk fmt <file>

# to check if a png is already optimized:
trunk check <file>

You can setup trunk to manage your git hooks and automatically optimize any pngs you commit to git, when you git commit. To enable this, run:

trunk actions enable trunk-fmt-pre-commit

Library Usage

Although originally intended to be used as an executable, oxipng can also be used as a library in other Rust projects. To do so, simply add oxipng as a dependency in your Cargo.toml, then extern crate oxipng in your project. You should then have access to all of the library functions documented here. The simplest method of usage involves creating an Options struct and passing it, along with an input filename, into the optimize function.

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 }

History

Oxipng began as a complete rewrite of the OptiPNG project, which was assumed to be dead as no commit had been made to it since March 2014. (OptiPNG has since released a new version, after Oxipng was first released.) The name has been changed to avoid confusion and potential legal issues.

The core goal of rewriting OptiPNG was to implement multithreading, which would be very difficult to do within the existing C codebase of OptiPNG. This also served as an opportunity to choose a more modern, safer language (Rust).

Note that, while similar, Oxipng is not a drop-in replacement for OptiPNG. If you are migrating from OptiPNG, please check the help before using.

Contributing

Any contributions are welcome and will be accepted via pull request on GitHub. Bug reports can be filed via GitHub issues. Please include as many details as possible. If you have the capability to submit a fix with the bug report, it is preferred that you do so via pull request, however you do not need to be a Rust developer to contribute. Other contributions (such as improving documentation or translations) are also welcome via GitHub.

License

Oxipng is open-source software, distributed under the MIT license.

Benchmarks

Tested OxiPNG 9.0.0 (commit c16519b38b0519988db625913be919d4f0e42f5d, compiled on rustc 1.74.0-nightly (7b4d9e155 2023-09-28)) against OptiPNG version 0.7.7, as packaged by Debian unstable, on a Linux 6.5.0-2-amd64 kernel, Intel Core i7-12700 CPU (8 performance cores, 4 efficiency cores, 20 threads), DDR5-5200 RAM in dual channel configuration.


Benchmark 1: ./target/release/oxipng -P ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):      59.6 ms ±   7.7 ms    [User: 77.4 ms, System: 3.6 ms]
  Range (min … max):    53.3 ms …  89.9 ms    32 runs

Benchmark 2: optipng -simulate ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):     132.4 ms ±   0.8 ms    [User: 132.5 ms, System: 0.6 ms]
  Range (min … max):   131.8 ms … 134.4 ms    22 runs

Summary
  ./target/release/oxipng -P ./tests/files/rgb_16_should_be_grayscale_8.png ran
    2.22 ± 0.29 times faster than optipng -simulate ./tests/files/rgb_16_should_be_grayscale_8.png

Benchmark 1: ./target/release/oxipng -o4 -P ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):      88.7 ms ±   4.3 ms    [User: 270.3 ms, System: 11.0 ms]
  Range (min … max):    86.8 ms … 109.4 ms    26 runs

Benchmark 2: optipng -o 4 -simulate ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):     444.9 ms ±   0.3 ms    [User: 444.8 ms, System: 0.7 ms]
  Range (min … max):   444.4 ms … 445.6 ms    10 runs

Summary
  ./target/release/oxipng -o4 -P ./tests/files/rgb_16_should_be_grayscale_8.png ran
    5.01 ± 0.25 times faster than optipng -o 4 -simulate ./tests/files/rgb_16_should_be_grayscale_8.png

Older benchmark

Tested oxipng 5.0.0 (compiled on rustc 1.55.0-nightly (7a16cfcff 2021-07-11)) against OptiPNG version 0.7.7 on AMD Ryzen 7 4800H with Radeon Graphics with 16 logical cores


Benchmark #1: ./target/release/oxipng -P ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):     128.8 ms ±  14.2 ms    [User: 296.0 ms, System: 14.3 ms]
  Range (min … max):    98.8 ms … 152.3 ms    21 runs

Benchmark #2: optipng -simulate ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):     254.2 ms ±  16.0 ms    [User: 252.8 ms, System: 1.2 ms]
  Range (min … max):   208.4 ms … 263.8 ms    14 runs

Summary
  './target/release/oxipng -P ./tests/files/rgb_16_should_be_grayscale_8.png' ran
    1.97 ± 0.25 times faster than 'optipng -simulate ./tests/files/rgb_16_should_be_grayscale_8.png'



Benchmark #1: ./target/release/oxipng -o4 -P ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):     141.4 ms ±  14.9 ms    [User: 611.7 ms, System: 21.1 ms]
  Range (min … max):   100.2 ms … 160.4 ms    23 runs

Benchmark #2: optipng -o 4 -simulate ./tests/files/rgb_16_should_be_grayscale_8.png
  Time (mean ± σ):     730.0 ms ±  25.9 ms    [User: 728.0 ms, System: 1.2 ms]
  Range (min … max):   713.3 ms … 768.2 ms    10 runs

Summary
  './target/release/oxipng -o4 -P ./tests/files/rgb_16_should_be_grayscale_8.png' ran
    5.16 ± 0.58 times faster than 'optipng -o 4 -simulate ./tests/files/rgb_16_should_be_grayscale_8.png'

oxipng's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oxipng's Issues

oxipng crash when optimising 4bpp indexed image

$ RUST_BACKTRACE=1 oxipng -o5 credits_2.png
Processing: credits_2.png
    345x98 pixels, PNG format
    4 bits/pixel, 16 colors in palette
    IDAT size = 2209 bytes
    File size = 2366 bytes
Reducing image to 1x4 bits/pixel, Grayscale
Trying: 336 combinations
thread '<unnamed>' panicked at 'index 34104 out of range for slice of length 34006', ../src/libcore/slice.rs:549
stack backtrace:
thread '<unnamed>' panicked at 'index 34104 out of range for slice of length 34006', ../src/libcore/slice.rs:549
thread '<unnamed>' panicked at 'index 34104 out of range for slice of length 34006', ../src/libcore/slice.rs:549
thread '<unnamed>' panicked at 'index 34104 out of range for slice of length 34006', ../src/libcore/slice.rs:549
thread '<unnamed>' panicked at 'index 34104 out of range for slice of length 34006', ../src/libcore/slice.rs:549
thread '<unnamed>' panicked at 'index 34104 out of range for slice of length 34006', ../src/libcore/slice.rs:549
   1:     0x564a40bd2caf - <unknown>
   2:     0x564a40bd7d1b - <unknown>
   3:     0x564a40bd6a5a - <unknown>
   4:     0x564a40bd70ac - <unknown>
   5:     0x564a40bd6f01 - <unknown>
   6:     0x564a40bd6e2a - <unknown>
   7:     0x564a40bd6d9e - <unknown>
   8:     0x564a40c115bf - <unknown>
   9:     0x564a40c116a3 - <unknown>
  10:     0x564a40b7e46f - <unknown>
  11:     0x564a40b8181e - <unknown>
  12:     0x564a40b98058 - <unknown>
  13:     0x564a40b73ee6 - <unknown>
  14:     0x564a40b9a860 - <unknown>
  15:     0x564a40b742af - <unknown>
  16:     0x564a40bdf52b - <unknown>
  17:     0x564a40bdf40e - <unknown>
  18:     0x564a40b98494 - <unknown>
  19:     0x564a40b767bb - <unknown>
  20:     0x564a40bd54c4 - <unknown>
  21:     0x7f3d2bfa2483 - start_thread
  22:     0x7f3d2bacb6dc - clone
  23:                0x0 - <unknown>
stack backtrace:
   1:     0x564a40bd2caf - <unknown>
   2:     0x564a40bd7d1b - <unknown>
   3:     0x564a40bd6a5a - <unknown>
   4:     0x564a40bd70ac - <unknown>
   5:     0x564a40bd6f01 - <unknown>
   6:     0x564a40bd6e2a - <unknown>
   7:     0x564a40bd6d9e - <unknown>
   8:     0x564a40c115bf - <unknown>
   9:     0x564a40c116a3 - <unknown>
  10:     0x564a40b7e46f - <unknown>
  11:     0x564a40b8181e - <unknown>
  12:     0x564a40b98058 - <unknown>
  13:     0x564a40b73ee6 - <unknown>
  14:     0x564a40b9a860 - <unknown>
  15:     0x564a40b742af - <unknown>
  16:     0x564a40bdf52b - <unknown>
  17:     0x564a40bdf40e - <unknown>
  18:     0x564a40b98494 - <unknown>
  19:     0x564a40b767bb - <unknown>
  20:     0x564a40bd54c4 - <unknown>
  21:     0x7f3d2bfa2483 - start_thread
  22:     0x7f3d2bacb6dc - clone
  23:                0x0 - <unknown>
stack backtrace:
   1:     0x564a40bd2caf - <unknown>
   2:     0x564a40bd7d1b - <unknown>
   3:     0x564a40bd6a5a - <unknown>
   4:     0x564a40bd70ac - <unknown>
   5:     0x564a40bd6f01 - <unknown>
   6:     0x564a40bd6e2a - <unknown>
   7:     0x564a40bd6d9e - <unknown>
   8:     0x564a40c115bf - <unknown>
   9:     0x564a40c116a3 - <unknown>
  10:     0x564a40b7e46f - <unknown>
  11:     0x564a40b8181e - <unknown>
  12:     0x564a40b98058 - <unknown>
  13:     0x564a40b73ee6 - <unknown>
  14:     0x564a40b9a860 - <unknown>
  15:     0x564a40b742af - <unknown>
  16:     0x564a40bdf52b - <unknown>
  17:     0x564a40bdf40e - <unknown>
  18:     0x564a40b98494 - <unknown>
  19:     0x564a40b767bb - <unknown>
  20:     0x564a40bd54c4 - <unknown>
  21:     0x7f3d2bfa2483 - start_thread
  22:     0x7f3d2bacb6dc - clone
  23:                0x0 - <unknown>
stack backtrace:
   1:     0x564a40bd2caf - <unknown>
   2:     0x564a40bd7d1b - <unknown>
   3:     0x564a40bd6a5a - <unknown>
   4:     0x564a40bd70ac - <unknown>
   5:     0x564a40bd6f01 - <unknown>
   6:     0x564a40bd6e2a - <unknown>
   7:     0x564a40bd6d9e - <unknown>
   8:     0x564a40c115bf - <unknown>
   9:     0x564a40c116a3 - <unknown>
  10:     0x564a40b7e46f - <unknown>
  11:     0x564a40b8181e - <unknown>
  12:     0x564a40b98058 - <unknown>
  13:     0x564a40b73ee6 - <unknown>
  14:     0x564a40b9a860 - <unknown>
  15:     0x564a40b742af - <unknown>
  16:     0x564a40bdf52b - <unknown>
  17:     0x564a40bdf40e - <unknown>
  18:     0x564a40b98494 - <unknown>
  19:     0x564a40b767bb - <unknown>
  20:     0x564a40bd54c4 - <unknown>
  21:     0x7f3d2bfa2483 - start_thread
  22:     0x7f3d2bacb6dc - clone
  23:                0x0 - <unknown>
stack backtrace:
   1:     0x564a40bd2caf - <unknown>
   2:     0x564a40bd7d1b - <unknown>
   3:     0x564a40bd6a5a - <unknown>
   4:     0x564a40bd70ac - <unknown>
   5:     0x564a40bd6f01 - <unknown>
   6:     0x564a40bd6e2a - <unknown>
   7:     0x564a40bd6d9e - <unknown>
   8:     0x564a40c115bf - <unknown>
   9:     0x564a40c116a3 - <unknown>
  10:     0x564a40b7e46f - <unknown>
  11:     0x564a40b8181e - <unknown>
  12:     0x564a40b98058 - <unknown>
  13:     0x564a40b73ee6 - <unknown>
  14:     0x564a40b9a860 - <unknown>
  15:     0x564a40b742af - <unknown>
  16:     0x564a40bdf52b - <unknown>
  17:     0x564a40bdf40e - <unknown>
  18:     0x564a40b98494 - <unknown>
  19:     0x564a40b767bb - <unknown>
  20:     0x564a40bd54c4 - <unknown>
  21:     0x7f3d2bfa2483 - start_thread
  22:     0x7f3d2bacb6dc - clone
  23:                0x0 - <unknown>
stack backtrace:
   1:     0x564a40bd2caf - <unknown>
   2:     0x564a40bd7d1b - <unknown>
   3:     0x564a40bd6a5a - <unknown>
   4:     0x564a40bd70ac - <unknown>
   5:     0x564a40bd6f01 - <unknown>
   6:     0x564a40bd6e2a - <unknown>
   7:     0x564a40bd6d9e - <unknown>
   8:     0x564a40c115bf - <unknown>
   9:     0x564a40c116a3 - <unknown>
  10:     0x564a40b7e46f - <unknown>
  11:     0x564a40b8181e - <unknown>
  12:     0x564a40b98058 - <unknown>
  13:     0x564a40b73ee6 - <unknown>
  14:     0x564a40b9a860 - <unknown>
  15:     0x564a40b742af - <unknown>
  16:     0x564a40bdf52b - <unknown>
  17:     0x564a40bdf40e - <unknown>
  18:     0x564a40b98494 - <unknown>
  19:     0x564a40b767bb - <unknown>
  20:     0x564a40bd54c4 - <unknown>
  21:     0x7f3d2bfa2483 - start_thread
  22:     0x7f3d2bacb6dc - clone
  23:                0x0 - <unknown>
thread 'main' panicked at 'WaitGroup explicitly poisoned!', /home/fratti/.cargo/registry/src/github.com-1ecc6299db9ec823/scoped-pool-0.1.9/src/lib.rs:360
stack backtrace:
   1:     0x564a40bd2caf - <unknown>
   2:     0x564a40bd7d1b - <unknown>
   3:     0x564a40bd6a5a - <unknown>
   4:     0x564a40bd70ac - <unknown>
   5:     0x564a40b7427f - <unknown>
   6:     0x564a40b72fa4 - <unknown>
   7:     0x564a40b93929 - <unknown>
   8:     0x564a40b8f843 - <unknown>
   9:     0x564a40ae06d6 - <unknown>
  10:     0x564a40adeb5c - <unknown>
  11:     0x564a40bd6d08 - <unknown>
  12:     0x564a40bdf52b - <unknown>
  13:     0x564a40bdf40e - <unknown>
  14:     0x564a40bd5ff8 - <unknown>
  15:     0x7f3d2ba04740 - __libc_start_main
  16:     0x564a40ad87f8 - <unknown>
  17:                0x0 - <unknown>

File: https://fratti.ch/bugdemos/oxipng/credits_2.png

$ oxipng --version
oxipng 0.9.0
$ rustc --version
rustc 1.12.0-nightly (b5ad2779e 2016-07-16)

Certain images do not render correctly in OS X Preview tool after oxipng

I encountered 2 images that had the bottom half of the image rendered as pure black after being converted by oxipng. The images were all converted from RGBA8 -> RGB8, and were all taken with cmd+shift+4 on OS X 10.11. The images display correctly in other viewers--the black only appears in Preview.

I am attempting to recreate a pre-compression image that exhibits this, but it does not occur most of the time and I am having difficulty reproducing it. Here are the 2 post-compression images that exhibit the issue:

screen shot 2016-03-11 at 11 34 10 am

screen shot 2016-03-11 at 11 34 41 am

libpng warns about invalid chunks

I recently got these warnings after recompressing a set of sprites with oxipng 0.12.0:

libpng warning: sBIT: invalid
libpng warning: bKGD: invalid

The files were compressed with oxipng -o6 -Z sample$N-good.png --out sample$N-bad.png

Some of the offending files:
Bad: sample1-bad
Good: sample1-good
Bad: sample2-bad
Good: sample2-good
Bad: sample3-bad
Good: sample3-good
Bad: sample4-bad
Good: sample4-good
Bad: sample5-bad
Good: sample5-good
Bad: sample6-bad
Good: sample6-good
(I hope GitHub didn't mangle those)

Fix "FIXME" sections in code

There are a couple of areas in the code that are very messy and need refactoring or optimization. I'd like to eliminate all of the FIXMEs.

Chosen filter type is non-deterministic and not always best

Two separate runs on same file:

    Processing: tests/files/rgb_should_be_palette.png
    500x400 pixels, PNG format
    3x8 bits/pixel, RGB
    IDAT size = 17406 bytes
    File size = 18320 bytes
Reducing image to 8 bits/pixel, 71 colors in palette
Trying: 4 combinations
Found better combination:
    zc = 9  zm = 9  zs = 1  f = 0        14704 bytes
Output: tests/files/rgb_should_be_palette.out.png
    IDAT size = 14704 bytes (2702 bytes decrease)
    file size = 15843 bytes (2477 bytes = 13.52% decrease)

    Processing: tests/files/rgb_should_be_palette.png
    500x400 pixels, PNG format
    3x8 bits/pixel, RGB
    IDAT size = 17406 bytes
    File size = 18320 bytes
Reducing image to 8 bits/pixel, 71 colors in palette
Trying: 8 combinations
Found better combination:
    zc = 9  zm = 9  zs = 0  f = 5        14808 bytes
Output: tests/files/rgb_should_be_palette.out.png
    IDAT size = 14808 bytes (2598 bytes decrease)
    file size = 15947 bytes (2373 bytes = 12.95% decrease)

Broken image

And here I'm again.

% oxipng -o 6 coords-dom-01-f.png
% oxipng -V
oxipng 0.14.3

coords-dom-01-f

Broken image

Here is an input file which became broken(black) after optimizing.

Using: oxipng -o 6 color-prop-05-t.png

color-prop-05-t

PNG optimizer strategies from TruePNG

TruePNG is a very good PNG optimizer, unfortunately it only runs on Windows (and under wine).

Some of the strategies is uses are described:

TruePNG is actually one of the most advanced PNG optimizer available. it does an exhaustive checks for reductions and should be able to transform image with more efficiency than other tools. those transformations are called "image reductions".

https://css-ig.net/articles/truepng.php

TruePNG binary:
http://x128.ho.ua/pngutils.html

Meta issues:

  • #23 Add option and functionality to allow for lossy alpha reductions
  • #72 Implement possible size optimizations from Color Type from TruePNG
  • #73 Implement possible size optimizations from Bit Depth from TruePNG
  • #74 Implement possible size optimizations from Palette sorting from TruePNG
  • #75 Implement possible size optimizations from Palette transparency from TruePNG
  • #76 Implement lossy optimization methods from TruePNG
  • #77 Implement gamma optimization option from TruePNG

Implement multithreading

One of the core features of Oxipng over Optipng is multithreading the compression iterations. This needs to be implemented. Existing tests should continue to pass but no new tests need to be implemented.

Crash on compressing with interlacing enabled

1x1

oxipng is crashing with the uploaded file:
https://cloud.githubusercontent.com/assets/322387/15561653/543759a2-233b-11e6-8624-5809f6551000.png

oxipng -o 4 -i 1 --strip safe 1x1.png

Processing: 1x1.png
    1x1 pixels, PNG format
    2x8 bits/pixel, GrayscaleAlpha
    IDAT size = 11 bytes
    File size = 68 bytes
Trying: 48 combinations
thread '<main>' panicked at 'index 2 out of range for slice of length 0', ../src/libcore/slice.rs:527
stack backtrace:
   1:     0x7f1403ffb060 - sys::backtrace::tracing::imp::write::h3675b4f0ca767761Xcv
   2:     0x7f1403ffdf1b - panicking::default_handler::_$u7b$$u7b$closure$u7d$$u7d$::closure.44519
   3:     0x7f1403ffdb88 - panicking::default_handler::h18faf4fbd296d909lSz
   4:     0x7f1403feee4c - sys_common::unwind::begin_unwind_inner::hfb5d07d6e405c6bbg1t
   5:     0x7f1403fef298 - sys_common::unwind::begin_unwind_fmt::h8b491a76ae84af35m0t
   6:     0x7f1403ffa4d1 - rust_begin_unwind
   7:     0x7f140402d4af - panicking::panic_fmt::h98b8cbb286f5298alcM
   8:     0x7f140402d723 - slice::slice_index_len_fail::hc4dead8ea7162edaCWP
   9:     0x7f1403f37959 - png::filter_line::h754e21cc4a65cb1cHWb
  10:     0x7f1403f2576e - png::PngData::filter_image::h279709035897e51bJkb
  11:     0x7f1403f1d9fb - optimize_png::h380f5a051c02ba65L0c
  12:     0x7f1403f195a3 - optimize::ha508f8c49b7cb7eb8Oc
  13:     0x7f1403f138a9 - handle_optimization::h785d9e26eeb741aftja
  14:     0x7f1403f0c8ab - main::h2ac802ddff177ad7yaa
  15:     0x7f1403ffd7e4 - sys_common::unwind::try::try_fn::h14622312129452522850
  16:     0x7f1403ffa45b - __rust_try
  17:     0x7f1403ffd27b - rt::lang_start::h0ba42f7a8c46a626rKz
  18:     0x7f140311aeac - __libc_start_main
  19:     0x7f1403f09e58 - <unknown>

It's OK with just oxipng -o 4 --strip safe 1x1.png though. I've got some other crashes with different images as well, would you like separate bug reports for those as well?

Should --strip really strip the gAMA chunk by default?

Currently, --strip strips the gAMA chunk, which, while a small bit of metadata, significantly affects the way some applications display images (notably browsers).

Test image with arbitrarily added gAMA:

Without gAMA:

People may be lead to perform --strip to get rid of things such as zTXT chunks, without knowing that gAMA is a thing. (Granted, many applications don't handle gAMA anyway, but browsers do.)

Error code on decompress: -2

Application returns an error on any png file I'm trying to optimize.

% oxipng color-prop-02-f.png
Processing: color-prop-02-f.png
Error code on decompress: -2
% oxipng -V
oxipng 0.14.0

Create benchmark suite

I'd like to have some benchmarks to compare this with optipng. I'd like to compare compression, speed, and memory usage--cargo bench is probably out of the question, so maybe this can be done in Python or similar.

Ideally this would be reusable to help optimize oxipng as well. Maybe have the benchmarks run against oxipng only by default, and a flag to have it run the comparison benchmarks.

Implement preserve `-p` option

Oxipng has a command-line option to preserve original file attributes. This needs to be implemented, with at least one test case.

oxipng corrupts certain images

I used the following script to run oxipng on a large number of PNG files from my mpv screenshot directory (~650 files), comparing the output of oxipng with the original file using graphicsmagick's compare command. The oxipng version used was 3f42eae.

#!/usr/bin/env bash
OXI=~/Projekte/oxipng/target/release/oxipng
NUM=0
if [ ! -d "/tmp/oxifaults" ]; then
    mkdir "/tmp/oxifaults"
fi
while IFS= read -rd '' f; do
    rm -f "/tmp/oxitest.png"
    $OXI -F --no-clobber --out "/tmp/oxitest.png" "$f" >> /dev/null 2>&1
    gm compare -maximum-error 0 -metric MSE "$f" "/tmp/oxitest.png" >> /dev/null 2>&1
    if [ $? -ne 0 ]; then
        cp "/tmp/oxitest.png" "/tmp/oxifaults/$NUM-oxi.png"
        echo "$f -> $NUM-oxi.png"
        ((NUM++))
    fi
done < <(find "$1" -type f -iname '*.png' -print0)

A number of images where the output did not correspond to the input were detected.

  1. Original -> Oxipng
  2. Original -> Oxipng
  3. Original -> Oxipng
  4. Original -> Oxipng
  5. Original -> Oxipng
  6. Original -> Oxipng
  7. Original -> Oxipng
  8. Original -> Oxipng
  9. Original -> Oxipng
  10. Original -> Oxipng

(For reasons beyond my comprehension, KonoSuba screenshots seemed to be more likely to get corrupted than others; I'm going to assume oxipng has bad taste in anime.)

As one can see, subtitles, for some reason, often trigger this.

rustc version is rustc 1.9.0-nightly (998a6720b 2016-03-07).

Image shouldn't reduce to palette if the palette header would increase total size

You may feel differently, and that's fine. But personally I was shocked to find out that after executing oxipng -i 0 -o 6 -Z --strip all *.png, some of my png files were bigger than they were before! I knew I hadn't specified --force. I had to dig into the specific png files and dig into the source code to figure out it was the -i 0 option that was causing it. I just specified -i 0 in case any of the png files were interlaced and removing it made them smaller.

I'm advocating that a larger png file should never be written unless --force is used.

The only reason I can guess for the current behavior is that it covers the case of someone wanting to use oxipng as an interlace-changing tool. If someone wants this, I think they simply need to use -i 1 --force. Since this is primarily a png optimizing tool, this makes sense to me.

Get oxipng into linux distributions and Homebrew

As cargo install is not recommended to be used for installing end-user binaries, we should move to get oxipng listed in Linux distributions and Homebrew.

We may not have immediate control over some of these.

  • Homebrew
  • Ubuntu
  • Debian
  • Gentoo
  • Arch
  • Centos
  • Fedora

This will also require a plan for pushing updates to these distributions.

Make `--strip` option more versatile

Currently --strip is all or nothing. I'd like to have the ability to specify a list of specific data blocks to remove (and forbid stripping of critical blocks), and two presets: safe and all. safe would remove all auxiliary data blocks that do not potentially affect the rendering of the image. all would maintain the current behavior and remove all auxiliary data blocks.

Blocks that should never be stripped: IHDR, IDAT, tRNS, PLTE, IEND.

Blocks that should be preserved with safe: All of the above, plus cHRM, gAMA, iCCP, sBIT, sRGB, bKGD, hIST, pHYs, sPLT

Implement possible size optimizations from Bit Depth from TruePNG

https://css-ig.net/articles/truepng.php

The bitdepth should match to the colortype, with some exceptions in paletted mode. encoders have the choice to use 8, 4, 2 or 1 bit by pixel. most of the time, the best choice is to select the lower bitdepth, that should decrease the filesize. this feature is included in most of optimizers.

most of encoders, including TruePNG, choose the minimal bitdepth possible considering the number of colors in the image. sometimes, it is better to choose the 8 bits / pixels, even for an image that have 16 colors or less. TruePNG can check that, and it reduces filesize that way.

  • Test 8 bits/pixel even if image could support less

Implement interlacing

Oxipng should be able to change an interlaced image to non-interlaced and vice versa. This is available via a command line option but unimplemented in the code. This needs to be implemented, with at least one test case from non-interlaced to interlaced, and at least one test case from interlaced to non-interlaced.

-Z makes desktop unresponsive.

I took oxipng-v0.15.1-i686-pc-windows-msvc.zip release and tried oxipng with -Z option (zopfli), default thread count (i7-4770, 4+HT x 1.5 = 12 threads) and default process priority (Normal) on Windows 7 x64.

The program does not hang, but takes too long and system barely responds to anything, having trouble even redrawing anything on the desktop, except mouse pointer. After waiting around 8 minutes running on a single 200 KB file, i managed to kill it with Task Manager.

I continued testing after applying the lowest priority setting using a helpful menu from https://habrahabr.ru/post/317802/. Still takes pretty long even on small files, and seems to take longer than with single thread ("-t 1"), but no more troubles.

I think either x1.5 threads default is overkill for -Z, or the program needs to run with lower base priority implicitly.

Also a suggestion: interpret floating point like "-t 1.0" as multiplier to number of CPU cores.

oxipng corrupts an all transparent png file

$ wget -qO space_16x16.png https://image.ibb.co/n9SLyv/space_16x16.png

$ oxipng space_16x16.png 
Processing: space_16x16.png
    16x16 pixels, PNG format
    4x8 bits/pixel, RGBA
    IDAT size = 18 bytes
    File size = 133 bytes
Reducing image to 2x8 bits/pixel, Grayscale + Alpha
Trying: 8 combinations
    IDAT size = 18 bytes (0 bytes decrease)
    file size = 129 bytes (4 bytes = 3.01% decrease)
Output: space_16x16.png

$ oxipng space_16x16.png 
Processing: space_16x16.png
thread 'main' panicked at 'index 1056 out of range for slice of length 1040', /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libcore/slice.rs:578
note: Run with `RUST_BACKTRACE=1` for a backtrace.

$ RUST_BACKTRACE=1 oxipng space_16x16.png 
Processing: space_16x16.png
thread 'main' panicked at 'index 1056 out of range for slice of length 1040', /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libcore/slice.rs:578
stack backtrace:
   1:     0x555c7f1be15c - std::sys::imp::backtrace::tracing::imp::write::h1d59ca58eb86a1e2
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
   2:     0x555c7f1c1e6e - std::panicking::default_hook::{{closure}}::hc8550e2dc230bf9b
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:351
   3:     0x555c7f1c1a74 - std::panicking::default_hook::he85ae9e5c9867198
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:367
   4:     0x555c7f1c230b - std::panicking::rust_panic_with_hook::h319375f6b98710b0
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:555
   5:     0x555c7f1c21a4 - std::panicking::begin_panic::hbfd2ece4b73bdca6
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:517
   6:     0x555c7f1c20c9 - std::panicking::begin_panic_fmt::h8409f0145843dd1f
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:501
   7:     0x555c7f1c2057 - rust_begin_unwind
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:477
   8:     0x555c7f1ee2bd - core::panicking::panic_fmt::hfaaf47fca562bd04
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libcore/panicking.rs:69
   9:     0x555c7f1ee3b9 - core::slice::slice_index_len_fail::h1479b49feeda4eb6
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libcore/slice.rs:578
  10:     0x555c7f0c8df8 - <oxipng::png::ScanLines<'a> as core::iter::iterator::Iterator>::next::h0c866b7002f03a5d
  11:     0x555c7f0cab17 - oxipng::png::PngData::unfilter_image::hd29ee8bdcb3c42ba
  12:     0x555c7f0ca124 - oxipng::png::PngData::from_slice::hcb8961728318e794
  13:     0x555c7f0c9018 - oxipng::png::PngData::new::h3d5f02c762088b4b
  14:     0x555c7f0d93ad - oxipng::optimize::h1ce3773fa9751aac
  15:     0x555c7f0b0794 - oxipng::handle_optimization::hf840bd1282ba2ff4
  16:     0x555c7f0ad099 - oxipng::main::h448741f3952fecea
  17:     0x555c7f1c919a - __rust_maybe_catch_panic
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libpanic_unwind/lib.rs:98
  18:     0x555c7f1c2a76 - std::rt::lang_start::h0637c2e100ff36fc
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:436
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panic.rs:361
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/rt.rs:57
  19:     0x7f87ebfb53f0 - __libc_start_main
  20:     0x555c7f0a3f19 - _start
  21:                0x0 - <unknown>

Implement fix `-f` option

Oxipng has a --fix (-f) command line flag to attempt to repair broken PNG files. This needs to be implemented, with at least one test case, to mimic optipng's flag.

Broken image

This image became broken after oxipng -o 6 coords-coord-01-t.png.

I can't open it anymore.

coords-coord-01-t

Reducing color type produces incorrect output

See the files in the test directory for example. This is why most of the tests fail. Tests without color reduction pass and result in normal output.

Example:

Input:

rgb_should_be_palette

Reduced from RGB to Indexed:

test_rgb

Add option and functionality to allow for lossy alpha reductions

Several other PNG optimisers such as zopflipng have the option to perform "lossy" alpha optimisations. Pixels which are fully transparent may still have RGB data associated; the "lossy" part is to discard said RGB data (which means it's perceptually lossless). An advanced form of this is to set the fully transparent pixels to an RGB value that allows easier compression by the block filters and DEFLATE, depending on the visible pixel data in the image.

Implement palette reduction

i.e. eliminating duplicate and unused entries from the palette

The process involves:

  • Search the palette for duplicate entries and eliminate them
  • Remove any entries from the palette that are never used in the image
  • Update the image data to reference the correct indices in the palette
  • Also update the alpha table, if present
  • Write some tests

This needs to be done with BitVec since indexed PNGs support bit depth < 8

Add Windows builds to rust everywhere

This is a followup to #34. @japaric, @TPS suggested that you could help. Do you have experience with deploying rust-everywhere for Windows on Travis CI? The Travis docs seem to indicate that Windows isn't a supported platform. Is the workaround then to use Appveyor for Windows builds?

Implement possible size optimizations from Color Type from TruePNG

https://css-ig.net/articles/truepng.php

Colortype reductions
TruePNG does some other trials that are not done automatically (or not done at all) by other tools:

  • TruePNG can test grayscale if image is graylevel
  • it tests tRNS chunk instead of alpha channel if applicable
  • it reduces truecolor+alpha to grayscale+alpha
  • it can choose truecolor, even if image contains less than 256 colors (or less)
  • TruePNG can encode to palette mode even if image is graylevel

Write tests for bit depth reduction

Test cases are outlined in tests/oxipng.rs. They should verify that the bit depth is reduced and that the output pixels are identical to the input (lossless reduction). This includes creating images that are capable of being reduced losslessly (as in the tests/files directory).

Shortest path to using this as an external library?

Heyo!

Firstly, thanks for providing a competitor to OptiPNG! And written in a safer language, no less. :D

I've been working on a side project for doing behind-the-scenes compression/optimization of images being uploaded to a front-end service, and really wanted to write it in Rust. Looking for image optimization libraries in Rust, your project came up.. but alas, it's not a library. :(

Is there a short path between what you have right now and me being able to use it as a library?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.