Compare commits

...

35 commits

Author SHA1 Message Date
dependabot[bot]
0466546981
chore(deps): bump the rust-dependencies group with 2 updates (#818)
Some checks failed
docker / build (push) Has been cancelled
oxipng / CI (push) Has been cancelled
oxipng / MSRV check (push) Has been cancelled
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-27 17:18:56 +00:00
Alejandro González
4e80dd009a
ci: cherry-pick changes from PR #815
That PR got hard to review and confidently merge, so I'm cherry-picking
some of the good ideas from it in a format that feels more comfortable
to me. Thanks @vlakoff for the original PR!

Co-authored-by: vlakoff <544424+vlakoff@users.noreply.github.com>
2026-05-08 23:37:53 +02:00
dependabot[bot]
028294cc8c
chore(deps): bump dawidd6/action-download-artifact from 20 to 21 (#816)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-08 20:25:30 +00:00
andrews05
628e241e23
v10.1.1 (#813)
Some checks failed
docker / build (push) Has been cancelled
oxipng / CI (push) Has been cancelled
oxipng / MSRV check (push) Has been cancelled
deploy / Deploy release (push) Has been cancelled
2026-04-22 20:29:48 +00:00
vlakoff
e167b04b85
Warn when Zopfli --ziwi exceeds --zi (#812) 2026-04-20 02:48:32 +00:00
dependabot[bot]
fbae35922d
chore(deps): bump dawidd6/action-download-artifact from 19 to 20 (#808)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 18:49:43 +00:00
dependabot[bot]
55b9758575
chore(deps): bump indexmap from 2.13.0 to 2.13.1 in the rust-dependencies group (#809)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 18:49:21 +00:00
dependabot[bot]
010b76eada
chore(deps): bump softprops/action-gh-release from 2 to 3 (#811)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 18:48:59 +00:00
AFCMS
d239592b58
Migrate Docker CI to newer docker/github-builder (#810) 2026-04-15 18:46:51 +00:00
andrews05
0cf8b782ff
Use build-std to reduce binary size (#807) 2026-04-06 21:18:00 +00:00
andrews05
25e147f6a0
Prevent conversion to indexed when palette reduction is off (#802) 2026-04-06 21:14:22 +00:00
andrews05
a75343800c
Increase icc decompression buffer size (#803) 2026-04-06 20:50:47 +00:00
andrews05
7d52fd66d0
Improve bigrams performance (#804) 2026-03-31 15:57:18 +00:00
dependabot[bot]
5bbd826ae3
chore(deps): bump the rust-dependencies group with 2 updates (#805)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 12:39:01 +00:00
dependabot[bot]
e4a7d0fd99
chore(deps): bump clap from 4.5.60 to 4.6.0 in the rust-dependencies group (#797)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-25 15:56:34 +00:00
dependabot[bot]
a1a18ffd21
chore(deps): bump dawidd6/action-download-artifact from 16 to 19 (#800)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-25 11:13:31 +00:00
dependabot[bot]
587fd03d0e
chore(deps): bump docker/setup-buildx-action from 3 to 4 (#795)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 23:42:42 +00:00
dependabot[bot]
ce52ab8748
chore(deps): bump docker/metadata-action from 5 to 6 (#794)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 23:42:28 +00:00
dependabot[bot]
494edba245
chore(deps): bump docker/login-action from 3 to 4 (#793)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 23:42:18 +00:00
dependabot[bot]
2e861b95db
chore(deps): bump docker/setup-qemu-action from 3 to 4 (#792)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 23:42:08 +00:00
dependabot[bot]
7834e4709e
chore(deps): bump docker/build-push-action from 6 to 7 (#791)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 23:41:42 +00:00
dependabot[bot]
8284676f50
chore(deps): bump rgb from 0.8.52 to 0.8.53 in the rust-dependencies group (#789)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alejandro González <me@alegon.dev>
2026-03-03 20:33:52 +00:00
dependabot[bot]
2a79fa4c6c
chore(deps): bump dawidd6/action-download-artifact from 15 to 16 (#788)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 19:19:33 +00:00
dependabot[bot]
00b9e33508
chore(deps): bump actions/upload-artifact from 6 to 7 (#787)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 16:15:36 +00:00
dependabot[bot]
2585529089
chore(deps): bump actions/attest-build-provenance from 3 to 4 (#786)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 16:14:54 +00:00
Demetris
6a99181fec
Make five small changes to the updated README.md (#785) 2026-02-26 20:59:09 +00:00
dependabot[bot]
ea9caac249
chore(deps): bump dawidd6/action-download-artifact from 14 to 15 (#779)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-23 16:51:30 +00:00
dependabot[bot]
764d057c32
chore(deps): bump clap from 4.5.58 to 4.5.60 in the rust-dependencies group (#780)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-23 16:51:09 +00:00
andrews05
4a537c087d
Update Readme (#767) 2026-02-22 13:39:54 +00:00
Jakub Onderka
015d31e6a1
Remove crossbeam-channel dependency (#777) 2026-02-22 13:37:38 +00:00
dependabot[bot]
854510a864
chore(deps): bump the rust-dependencies group with 2 updates (#776)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-16 15:27:09 +00:00
dependabot[bot]
8466e390b8
chore(deps): bump the rust-dependencies group with 2 updates (#774)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 23:10:41 +00:00
dependabot[bot]
baaafee4e4
chore(deps): bump clap from 4.5.54 to 4.5.56 in the rust-dependencies group (#772)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-03 13:04:45 +00:00
dependabot[bot]
9169f99674
chore(deps): bump dawidd6/action-download-artifact from 12 to 14 (#771)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-03 13:04:16 +00:00
dependabot[bot]
47d5628a68
chore(deps): bump serde_json from 1.0.147 to 1.0.149 in the rust-dependencies group (#770)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-26 21:15:59 +00:00
17 changed files with 255 additions and 365 deletions

View file

@ -1,4 +1,7 @@
scripts
.github
.editorconfig .editorconfig
.pre-commit-hooks.yaml .gitignore
/.git/
/.github/
/.pre-commit-hooks.yaml
/scripts/
/target/

View file

@ -3,7 +3,12 @@ name: deploy
on: on:
push: push:
tags: tags:
- "v*.*.*" - v*.*.*
concurrency:
# If we ever end up with two concurrent jobs for releasing the same tag, the former should be cancelled
group: release-${{ github.ref }}
cancel-in-progress: true
permissions: permissions:
actions: read actions: read
@ -54,15 +59,15 @@ jobs:
- name: Get the Oxipng version - name: Get the Oxipng version
id: oxipngMeta id: oxipngMeta
run: run: >
echo "version=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "oxipng").version')" echo "version=$(cargo metadata --format-version 1 --no-deps |
>> "$GITHUB_OUTPUT" jq -r '.packages[] | select(.name == "oxipng").version')" >> "$GITHUB_OUTPUT"
- name: Retrieve ${{ matrix.target }} binary - name: Retrieve ${{ matrix.target }} binary
uses: dawidd6/action-download-artifact@v12 uses: dawidd6/action-download-artifact@v21
with: with:
workflow: oxipng.yml workflow: oxipng.yml
commit: ${{ env.GITHUB_SHA }} commit: ${{ github.sha }}
name: Oxipng binary (${{ matrix.target }}) name: Oxipng binary (${{ matrix.target }})
path: target path: target
@ -123,7 +128,7 @@ jobs:
run: tail -n +3 CHANGELOG.md | sed -e '/^$/,$d' > RELEASE_NOTES.txt run: tail -n +3 CHANGELOG.md | sed -e '/^$/,$d' > RELEASE_NOTES.txt
- name: Create release - name: Create release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
with: with:
name: v${{ steps.oxipngMeta.outputs.version }} name: v${{ steps.oxipngMeta.outputs.version }}
body_path: RELEASE_NOTES.txt body_path: RELEASE_NOTES.txt

View file

@ -2,9 +2,9 @@ name: docker
on: on:
push: push:
branches: branches:
- 'master' - master
tags: tags:
- 'v*.*.*' - v*.*.*
pull_request: pull_request:
types: types:
- opened - opened
@ -17,76 +17,44 @@ on:
type: boolean type: boolean
default: true default: true
env: concurrency:
REGISTRY: ghcr.io # If we ever end up with two concurrent Docker build jobs for the same commit
# ghcr.io/OWNER/REPO # and same versioning context, the former should be cancelled
IMAGE_NAME: ${{ github.repository }} group: docker-${{ github.sha }}-${{ github.ref_type }}-${{ github.head_ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
runs-on: ubuntu-latest uses: docker/github-builder/.github/workflows/build.yml@v1
permissions: permissions:
contents: read contents: read
packages: write packages: write
id-token: write id-token: write
attestations: write attestations: write
with:
output: image
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
cache: ${{ github.event_name != 'workflow_dispatch' || inputs.use_cache }}
# We don't sign the image because ghcr.io doesn't support OCI Referrers API yet, and the fallback relies on pushing new tags on each build
# https://github.com/docker/github-builder/issues/109#issuecomment-3885082486
# https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md#backwards-compatibility
sign: false
sbom: true
meta-images: ghcr.io/${{ github.repository }}
meta-tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{raw}}
set-meta-annotations: true
set-meta-labels: true
labels: |
org.opencontainers.image.title=Oxipng
annotations: |
org.opencontainers.image.title=Oxipng
steps: secrets:
- name: Checkout repository registry-auths: |
uses: actions/checkout@v6 - registry: ghcr.io
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# For some reason the title have to be set manually
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
labels: |
org.opencontainers.image.title=Oxipng
annotations: |
org.opencontainers.image.title=Oxipng
# Build and push Docker image with Buildx (don't push on PR)
# Cache isn't used for tags and on workflow_dispatch if specified
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
no-cache: ${{ (github.event_name == 'workflow_dispatch' && !inputs.use_cache) || startsWith(github.ref, 'refs/tags/') }}
# Attest the build provenance
# TODO: enable push to registry when referrers API will be supported by ghcr.io
- name: Attest Build Provenance
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build-and-push.outputs.digest }}
push-to-registry: false

View file

@ -8,6 +8,11 @@ on:
- synchronize - synchronize
workflow_dispatch: workflow_dispatch:
concurrency:
# If we ever end up with two concurrent CI jobs for the same commit, the former should be cancelled
group: ci-${{ github.sha }}
cancel-in-progress: true
jobs: jobs:
ci: ci:
name: CI name: CI
@ -55,7 +60,6 @@ jobs:
env: env:
CARGO_BUILD_TARGET: ${{ matrix.target }} CARGO_BUILD_TARGET: ${{ matrix.target }}
RUSTFLAGS: '-Zlocation-detail=none'
steps: steps:
- name: Checkout source - name: Checkout source
@ -74,7 +78,7 @@ jobs:
with: with:
toolchain: nightly toolchain: nightly
target: ${{ env.CARGO_BUILD_TARGET }} target: ${{ env.CARGO_BUILD_TARGET }}
components: clippy, rustfmt components: clippy, rustfmt, rust-src
cache-bin: false cache-bin: false
cache-shared-key: cache cache-shared-key: cache
@ -125,10 +129,12 @@ jobs:
run: cargo doc --release --no-deps run: cargo doc --release --no-deps
- name: Build CLI binary - name: Build CLI binary
run: cargo build --release run: cargo build --release -Zbuild-std
env:
RUSTFLAGS: "-Zlocation-detail=none -Zunstable-options -Cpanic=immediate-abort"
- name: Upload CLI binary as artifact - name: Upload CLI binary as artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
with: with:
name: Oxipng binary (${{ matrix.target }}) name: Oxipng binary (${{ matrix.target }})
path: | path: |

8
.gitignore vendored
View file

@ -1,6 +1,6 @@
target
*.bk
.DS_Store .DS_Store
*.bk
*.out.png *.out.png
/.idea /.idea/
/node_modules /node_modules/
/target/

View file

@ -1,3 +1,11 @@
## Version 10.1.1
- [Performance] Improve Bigrams performance, giving notably faster results at lower levels.
- [Improvement] Change `--np` flag to also prevent conversion to indexed from other color types.
- [Improvement] Improve support for recompressing ICC profiles with high compression ratios.
- [Misc] Add warning when `--ziwi` exceeds `--zi`.
- [Build] Further reduce size of binaries.
## Version 10.1.0 ## Version 10.1.0
- [Feature] Add `--json` option for machine-readable output. - [Feature] Add `--json` option for machine-readable output.
@ -7,7 +15,7 @@
## Version 10.0.0 ## Version 10.0.0
- [Breaking] CLI: Change short zopfli flag from `-Z` to `-z`. - [Breaking] CLI: Change short zopfli flag from `-Z` to `-z`.
- [Breaking] CLI: Change `--pretend`/`-P` to `--dry-run`/`-D`. - [Breaking] CLI: Change `--pretend`/`-P` to `--dry-run`/`-d`.
- [Breaking] CLI: Change `--interlace` options from `1`/`0` to `on`/`off`. - [Breaking] CLI: Change `--interlace` options from `1`/`0` to `on`/`off`.
- [Breaking] API: Change `Options.interlace: Option<Interlacing>` to `Options.interlace: Option<bool>`. - [Breaking] API: Change `Options.interlace: Option<Interlacing>` to `Options.interlace: Option<bool>`.
- [Breaking] API: Change `Options.filter: IndexSet<RowFilter>` to `Options.filters: IndexSet<FilterStrategy>`. - [Breaking] API: Change `Options.filter: IndexSet<RowFilter>` to `Options.filters: IndexSet<FilterStrategy>`.

295
Cargo.lock generated
View file

@ -10,9 +10,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.20" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@ -25,37 +25,37 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.11" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.7" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.1.4" version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [ dependencies = [
"windows-sys 0.60.2", "windows-sys",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.10" version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"once_cell_polyfill", "once_cell_polyfill",
"windows-sys 0.60.2", "windows-sys",
] ]
[[package]] [[package]]
@ -66,9 +66,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.4" version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]] [[package]]
name = "bitvec" name = "bitvec"
@ -84,15 +84,15 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.19.0" version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.23.2" version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
[[package]] [[package]]
name = "byteorder-lite" name = "byteorder-lite"
@ -102,9 +102,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.38" version = "1.2.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"shlex", "shlex",
@ -112,24 +112,24 @@ dependencies = [
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.3" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.54" version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
] ]
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.54" version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -140,15 +140,15 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.5" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.4" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
@ -159,15 +159,6 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.6" version = "0.8.6"
@ -201,18 +192,18 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.3" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef"
dependencies = [ dependencies = [
"log", "log",
] ]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.11.8" version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -233,7 +224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.0", "windows-sys",
] ]
[[package]] [[package]]
@ -247,15 +238,15 @@ dependencies = [
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.2" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.1.2" version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
@ -275,9 +266,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.1" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]] [[package]]
name = "image" name = "image"
@ -294,9 +285,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -305,27 +296,27 @@ dependencies = [
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.1" version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.16" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.175" version = "0.2.185"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
[[package]] [[package]]
name = "libdeflate-sys" name = "libdeflate-sys"
version = "1.25.0" version = "1.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23bd6304ebf75390d8a99b88bdf2a266f62647838140cb64af8e6702f6e3fddc" checksum = "72753e0008ea87963d2f0770042d0df7abe51fafbb8dcaf618ac440f2f1fec0a"
dependencies = [ dependencies = [
"cc", "cc",
"pkg-config", "pkg-config",
@ -333,30 +324,30 @@ dependencies = [
[[package]] [[package]]
name = "libdeflater" name = "libdeflater"
version = "1.25.0" version = "1.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d4880e6d634d3d029d65fa016038e788cc728a17b782684726fb34ee140caf" checksum = "d1ee41cf6fb1bb6030dfb59ffb7bc01ab26aade44142084c87f0fc7a1658fe71"
dependencies = [ dependencies = [
"libdeflate-sys", "libdeflate-sys",
] ]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.11.0" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.29" version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.6" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
@ -370,9 +361,9 @@ dependencies = [
[[package]] [[package]]
name = "moxcms" name = "moxcms"
version = "0.7.5" version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
dependencies = [ dependencies = [
"num-traits", "num-traits",
"pxfm", "pxfm",
@ -389,17 +380,16 @@ dependencies = [
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
version = "1.70.1" version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]] [[package]]
name = "oxipng" name = "oxipng"
version = "10.1.0" version = "10.1.1"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"clap", "clap",
"crossbeam-channel",
"env_logger", "env_logger",
"glob", "glob",
"image", "image",
@ -422,15 +412,15 @@ checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.32" version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]] [[package]]
name = "png" name = "png"
version = "0.18.0" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
@ -441,27 +431,24 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.103" version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "pxfm" name = "pxfm"
version = "0.1.24" version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83f9b339b02259ada5c0f4a389b7fb472f933aa17ce176fd2ad98f28bb401fde" checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.42" version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -474,9 +461,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.11.0" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
dependencies = [ dependencies = [
"either", "either",
"rayon-core", "rayon-core",
@ -494,30 +481,30 @@ dependencies = [
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.52" version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
] ]
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "2.1.1" version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.1.2" version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.61.0", "windows-sys",
] ]
[[package]] [[package]]
@ -551,9 +538,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.147" version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4" checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -570,9 +557,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.7" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]] [[package]]
name = "strsim" name = "strsim"
@ -582,9 +569,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.111" version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -599,19 +586,19 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "terminal_size" name = "terminal_size"
version = "0.4.3" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
dependencies = [ dependencies = [
"rustix", "rustix",
"windows-sys 0.60.2", "windows-sys",
] ]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.22" version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
@ -621,99 +608,19 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.1.3" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.60.2" version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [ dependencies = [
"windows-targets", "windows-link",
] ]
[[package]]
name = "windows-sys"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
dependencies = [
"windows-link 0.2.0",
]
[[package]]
name = "windows-targets"
version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [
"windows-link 0.1.3",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]] [[package]]
name = "wyz" name = "wyz"
version = "0.5.1" version = "0.5.1"
@ -725,9 +632,9 @@ dependencies = [
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "0.1.10" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4af59da1029247450b54ba43e0b62c8e376582464bbe5504dd525fe521e7e8fd" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]] [[package]]
name = "zopfli" name = "zopfli"

View file

@ -20,7 +20,7 @@ homepage = "https://github.com/oxipng/oxipng"
license = "MIT" license = "MIT"
name = "oxipng" name = "oxipng"
repository = "https://github.com/oxipng/oxipng" repository = "https://github.com/oxipng/oxipng"
version = "10.1.0" version = "10.1.1"
rust-version = "1.85.1" rust-version = "1.85.1"
[badges] [badges]
@ -39,29 +39,28 @@ required-features = ["zopfli"]
[dependencies] [dependencies]
bitvec = "1.0.1" bitvec = "1.0.1"
clap = { version = "4.5.53", optional = true, features = ["wrap_help"] } clap = { version = "4.6.0", optional = true, features = ["wrap_help"] }
crossbeam-channel = { version = "0.5.15", optional = true } env_logger = { version = "0.11.10", optional = true, default-features = false, features = ["auto-color"] }
env_logger = { version = "0.11.8", optional = true, default-features = false, features = ["auto-color"] }
image = { version = "0.25.9", optional = true, default-features = false, features = ["png"] } image = { version = "0.25.9", optional = true, default-features = false, features = ["png"] }
indexmap = "2.13.0" indexmap = "2.14.0"
libdeflater = "1.25.0" libdeflater = "1.25.2"
log = "0.4.29" log = "0.4.30"
parse-size = { version = "1.1.0", optional = true } parse-size = { version = "1.1.0", optional = true }
rayon = { version = "1.11.0", optional = true } rayon = { version = "1.11.0", optional = true }
rgb = "0.8.52" rgb = "0.8.53"
rustc-hash = "2.1.1" rustc-hash = "2.1.2"
zopfli = { version = "0.8.3", optional = true, default-features = false, features = ["std", "zlib"] } zopfli = { version = "0.8.3", optional = true, default-features = false, features = ["std", "zlib"] }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
glob = { version = "0.3.3", optional = true } glob = { version = "0.3.3", optional = true }
[dev-dependencies] [dev-dependencies]
serde_json = "1.0.147" serde_json = "1.0.150"
[features] [features]
binary = ["dep:clap", "dep:glob", "dep:env_logger", "dep:parse-size"] binary = ["dep:clap", "dep:glob", "dep:env_logger", "dep:parse-size"]
default = ["binary", "parallel", "zopfli"] default = ["binary", "parallel", "zopfli"]
parallel = ["dep:rayon", "indexmap/rayon", "dep:crossbeam-channel"] parallel = ["dep:rayon", "indexmap/rayon"]
freestanding = ["libdeflater/freestanding"] freestanding = ["libdeflater/freestanding"]
sanity-checks = ["dep:image"] sanity-checks = ["dep:image"]
zopfli = ["dep:zopfli"] zopfli = ["dep:zopfli"]

View file

@ -8,7 +8,7 @@
## Overview ## Overview
Oxipng is a multithreaded lossless PNG/APNG compression optimizer. It can be used via a command-line 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. interface or as a library in other Rust programs. It is fast and highly effective.
## Installing ## Installing
@ -76,7 +76,14 @@ All options are case-sensitive.
\* Note that oxipng is not a brute-force optimizer. This means that while higher optimization levels \* Note that oxipng is not a brute-force optimizer. This means that while higher optimization levels
are almost always better or equal to lower levels, this is not guaranteed and it is possible in are almost always better or equal to lower levels, this is not guaranteed and it is possible in
rare circumstances that a lower level may give a marginally smaller output. Similarly, using Zopfli rare circumstances that a lower level may give a marginally smaller output. Similarly, using Zopfli
compression (`-z`) is not guaranteed to always be better than without. compression (`-z`) is not guaranteed to always be better than default compression.
## APNG support
Oxipng currently only supports limited optimization of animated PNGs (APNGs). It can perform
alpha-optimization, refiltering and recompression of all frames, but all transformations will be
disabled. For best results, it is recommended to use another tool such as
[apngopt](https://sourceforge.net/projects/apng/files/APNG_Optimizer/) before running Oxipng.
## Git integration via [pre-commit] ## Git integration via [pre-commit]
@ -131,17 +138,13 @@ and specifying the desired ones, for example:
## History ## History
Oxipng began as a complete rewrite of the OptiPNG project, Oxipng began in 2015 as a rewrite of the OptiPNG project. The core goal was to implement
which was assumed to be dead as no commit had been made to it since March 2014. multithreading, which would have been very difficult to do within the existing C codebase of OptiPNG.
(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). 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. However, Oxipng has evolved considerably since then. While some of the options remain similar to
If you are migrating from OptiPNG, please check the [help](MANUAL.txt) before using. OptiPNG, the architecture and capabilities are now quite different. It is not a drop-in
replacement - if you are migrating from OptiPNG, please check the [help](MANUAL.txt) before use.
## Contributing ## Contributing
@ -151,42 +154,11 @@ to submit a fix with the bug report, it is preferred that you do so via pull req
however you do not need to be a Rust developer to contribute. 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. Other contributions (such as improving documentation or translations) are also welcome via GitHub.
## Benchmarks
An independent benchmark is linked here with permission by the author:\
[oxipng and friends: A comparison of PNG optimization tools](https://op111.net/posts/2025/09/png-compression-oxipng-optipng-fileoptimizer-cwebp/)
## License ## License
Oxipng is open-source software, distributed under the MIT 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
```

View file

@ -298,6 +298,8 @@ The default value depends on the optimization level preset.")
.arg( .arg(
Arg::new("no-palette-reduction") Arg::new("no-palette-reduction")
.help("Do not change color palette") .help("Do not change color palette")
.long_help("\
Do not convert to indexed and do not modify an existing color palette.")
.long("np") .long("np")
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )

View file

@ -8,12 +8,12 @@ use std::sync::{
atomic::{AtomicUsize, Ordering::*}, atomic::{AtomicUsize, Ordering::*},
}; };
#[cfg(feature = "parallel")]
use crossbeam_channel::{Receiver, Sender, unbounded};
use deflate::Deflater; use deflate::Deflater;
use indexmap::IndexSet; use indexmap::IndexSet;
use log::trace; use log::trace;
use rayon::prelude::*; use rayon::prelude::*;
#[cfg(feature = "parallel")]
use std::sync::mpsc::{Receiver, Sender, channel};
#[cfg(not(feature = "parallel"))] #[cfg(not(feature = "parallel"))]
use crate::rayon; use crate::rayon;
@ -73,7 +73,7 @@ impl Evaluator {
final_round: bool, final_round: bool,
) -> Self { ) -> Self {
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
let eval_channel = unbounded(); let eval_channel = channel();
Self { Self {
deadline, deadline,
filters, filters,

View file

@ -263,11 +263,11 @@ pub fn extract_icc(iccp: &Chunk, max_size: Option<usize>) -> Option<Vec<u8>> {
if compression_method != 0 { if compression_method != 0 {
return None; // The profile is supposed to be compressed (method 0) return None; // The profile is supposed to be compressed (method 0)
} }
// The decompressed size is unknown so we have to guess the required buffer size // Libdeflate works with a fixed size buffer. Since the decompressed size is unknown we have to
let mut out_size = compressed_data.len() * 2 + 1000; // guess the required buffer size. We allow a fairly generous 10x factor with a minimum of 1000.
if let Some(max) = max_size { let mut out_size = (compressed_data.len() * 10).max(1000);
out_size = out_size.min(max); // For sanity, impose a default limit of 1MB.
} out_size = out_size.min(max_size.unwrap_or(1_000_000));
match inflate(compressed_data, out_size) { match inflate(compressed_data, out_size) {
Ok(icc) => Some(icc), Ok(icc) => Some(icc),
Err(e) => { Err(e) => {

View file

@ -430,6 +430,16 @@ fn parse_opts_into_struct(
let iterations_without_improvement = *matches let iterations_without_improvement = *matches
.get_one::<NonZeroU64>("iterations-without-improvement") .get_one::<NonZeroU64>("iterations-without-improvement")
.unwrap_or(&NonZeroU64::MAX); .unwrap_or(&NonZeroU64::MAX);
if iterations_without_improvement > iteration_count
&& iterations_without_improvement != NonZeroU64::MAX
{
warn!(
"--ziwi ({}) is higher than --zi ({}) and will never be reached.",
iterations_without_improvement, iteration_count
);
}
opts.deflater = Deflater::Zopfli(ZopfliOptions { opts.deflater = Deflater::Zopfli(ZopfliOptions {
iteration_count, iteration_count,
iterations_without_improvement, iterations_without_improvement,

View file

@ -1,9 +1,7 @@
use std::{fs, path::Path, sync::Arc}; use std::{fs, path::Path, sync::Arc};
use bitvec::bitarr;
use libdeflater::{CompressionLvl, Compressor}; use libdeflater::{CompressionLvl, Compressor};
use log::warn; use log::warn;
use rgb::ComponentSlice;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::{ use crate::{
@ -192,7 +190,7 @@ impl PngData {
ColorType::Indexed { palette } => { ColorType::Indexed { palette } => {
let mut palette_data = Vec::with_capacity(palette.len() * 3); let mut palette_data = Vec::with_capacity(palette.len() * 3);
for px in palette { for px in palette {
palette_data.extend_from_slice(px.rgb().as_slice()); palette_data.extend_from_slice(px.rgb().as_ref());
} }
write_png_block(b"PLTE", &palette_data, &mut output); write_png_block(b"PLTE", &palette_data, &mut output);
if let Some(last_trns) = palette.iter().rposition(|px| px.a != 255) { if let Some(last_trns) = palette.iter().rposition(|px| px.a != 255) {
@ -374,6 +372,12 @@ impl PngImage {
let mut f_buf = Vec::new(); let mut f_buf = Vec::new();
// For heuristic strategies, keep track of the actual filter used for each line // For heuristic strategies, keep track of the actual filter used for each line
let mut filters_used = Vec::new(); let mut filters_used = Vec::new();
// Pre-allocate buffers for the Bigrams strategy to avoid per-line allocations
let (mut bigram_seen, mut bigram_touched) = if matches!(strategy, FilterStrategy::Bigrams) {
(vec![false; 0x10000], Vec::<u16>::new())
} else {
(Vec::new(), Vec::new())
};
for (i, line) in self.scan_lines(false).enumerate() { for (i, line) in self.scan_lines(false).enumerate() {
if prev_pass != line.pass || line.data.len() != prev_line.len() { if prev_pass != line.pass || line.data.len() != prev_line.len() {
prev_line = vec![0; line.data.len()]; prev_line = vec![0; line.data.len()];
@ -465,18 +469,29 @@ impl PngImage {
let mut best_size = usize::MAX; let mut best_size = usize::MAX;
for f in try_filters { for f in try_filters {
f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes); f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes);
let mut set = bitarr![0; 0x10000]; let mut count = 0;
for pair in f_buf.windows(2) { for pair in f_buf.windows(2) {
let bigram = ((pair[0] as usize) << 8) | pair[1] as usize; let bigram = ((pair[0] as usize) << 8) | pair[1] as usize;
set.set(bigram, true); if !bigram_seen[bigram] {
count += 1;
if count >= best_size {
break;
}
bigram_seen[bigram] = true;
bigram_touched.push(bigram as u16);
}
} }
let size = set.count_ones(); if count < best_size {
if size < best_size { best_size = count;
best_size = size;
std::mem::swap(&mut best_line, &mut f_buf); std::mem::swap(&mut best_line, &mut f_buf);
best_line_raw.clone_from(&line_data); best_line_raw.clone_from(&line_data);
best_filter = *f; best_filter = *f;
} }
// Clear only the entries that were touched
for &idx in &bigram_touched {
bigram_seen[idx as usize] = false;
}
bigram_touched.clear();
} }
} }
FilterStrategy::BigEnt => { FilterStrategy::BigEnt => {

View file

@ -1,7 +1,7 @@
use std::hash::{BuildHasherDefault, Hash}; use std::hash::{BuildHasherDefault, Hash};
use indexmap::IndexSet; use indexmap::IndexSet;
use rgb::{ComponentMap, ComponentSlice, FromSlice, RGB, RGBA, alt::Gray}; use rgb::{ComponentMap, FromSlice, RGB, RGBA, alt::Gray};
use rustc_hash::FxHasher; use rustc_hash::FxHasher;
use crate::{ use crate::{
@ -193,7 +193,7 @@ pub fn indexed_to_channels(
let mut data = Vec::with_capacity(out_size); let mut data = Vec::with_capacity(out_size);
for b in &png.data { for b in &png.data {
let color = palette.get(*b as usize).unwrap_or(&black); let color = palette.get(*b as usize).unwrap_or(&black);
data.extend_from_slice(&color.as_slice()[ch_start..=ch_end]); data.extend_from_slice(&color.as_ref()[ch_start..=ch_end]);
} }
Some(PngImage { Some(PngImage {

View file

@ -118,7 +118,7 @@ pub(crate) fn perform_reductions(
// Attempt to reduce to indexed // Attempt to reduce to indexed
// Keep the existing `png` var in case it is grayscale - we can test both for depth reduction later // Keep the existing `png` var in case it is grayscale - we can test both for depth reduction later
let mut indexed = None; let mut indexed = None;
if opts.color_type_reduction && !deadline.passed() { if opts.color_type_reduction && opts.palette_reduction && !deadline.passed() {
if let Some(reduced) = reduced_to_indexed(&png, opts.grayscale_reduction) { if let Some(reduced) = reduced_to_indexed(&png, opts.grayscale_reduction) {
// Make sure the palette gets sorted (but don't bother evaluating both results) // Make sure the palette gets sorted (but don't bother evaluating both results)
let new = Arc::new(sorted_palette(&reduced).unwrap_or(reduced)); let new = Arc::new(sorted_palette(&reduced).unwrap_or(reduced));

View file

@ -1,13 +1,12 @@
use indexmap::indexset;
use oxipng::{internal_tests::*, *};
use serde_json::Value;
use std::{ use std::{
fs::remove_file, fs::remove_file,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
}; };
use indexmap::indexset;
use oxipng::{internal_tests::*, *};
use serde_json::Value;
const GRAY: u8 = 0; const GRAY: u8 = 0;
const RGB: u8 = 2; const RGB: u8 = 2;
const INDEXED: u8 = 3; const INDEXED: u8 = 3;
@ -97,13 +96,9 @@ fn test_it_converts(
#[test] #[test]
fn verbose_mode() { fn verbose_mode() {
use std::cell::RefCell;
#[cfg(not(feature = "parallel"))]
use std::sync::mpsc::{Sender, channel as unbounded};
#[cfg(feature = "parallel")]
use crossbeam_channel::{Sender, unbounded};
use log::{Level, LevelFilter, Log, Metadata, Record, set_logger, set_max_level}; use log::{Level, LevelFilter, Log, Metadata, Record, set_logger, set_max_level};
use std::cell::RefCell;
use std::sync::mpsc::{Sender, channel};
// Rust runs tests in parallel by default. // Rust runs tests in parallel by default.
// We want to make sure that we verify only logs from our test. // We want to make sure that we verify only logs from our test.
@ -143,7 +138,7 @@ fn verbose_mode() {
let input = PathBuf::from("tests/files/verbose_mode.png"); let input = PathBuf::from("tests/files/verbose_mode.png");
let (output, opts) = get_opts(&input); let (output, opts) = get_opts(&input);
let (sender, receiver) = unbounded(); let (sender, receiver) = channel();
let thread_init = move || { let thread_init = move || {
// Initialise logs storage for all threads within our test. // Initialise logs storage for all threads within our test.