diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 77802e8a..00000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,33 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: 'bug'
-assignees: ''
-
----
-
-## Description:
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-## Steps To Reproduce:
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Write this '...'
-4. See error
-
-## Expected behavior:
-A clear and concise description of what you expected to happen.
-
-## Images
-If applicable, add screenshots or test images to help explain your problem.
-
-## Additional info:
-**Desktop (please complete the following information):**
- - OS: [e.g. Win]
- - Version [e.g. 0.5.0]
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..41c15817
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,60 @@
+name: Bug report
+description: Create a report to help us improve
+title: '[Bug]:
'
+labels: ['bug']
+body:
+ - type: checkboxes
+ attributes:
+ label: Is there an existing issue for this?
+ description: Please search to see if an issue already exists for the bug you encountered.
+ options:
+ - label: I have searched the existing issues
+ required: true
+ - type: textarea
+ attributes:
+ label: Description
+ description: A clear and concise description of what the bug is. (e.g., current behavior)
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Expected behavior
+ description: A clear and concise description of what you expected to happen.
+ validations:
+ required: false
+ - type: dropdown
+ id: version
+ attributes:
+ label: Library Version
+ description: Specify the version of the library where you encountered the issue
+ options:
+ - 0.9.0 (latest)
+ - 0.8.2
+ - 0.8.1
+ - 0.8.0
+ - 0.7.1
+ - 0.7.0
+ - 0.6.0
+ - < 0.5.1 (old)
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. In this environment...
+ 2. With this config...
+ 3. Run '...'
+ 4. See error...
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+
+ Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 43de9e06..00000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: 'enhancement'
-assignees: ''
-
----
-
-**Write a small description of idea**
-
-## Description
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-## Expected Behavior:
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-## Note:
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000..f53fba19
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,44 @@
+name: Feature request
+description: Suggest an idea for this project
+title: '[Feature]: '
+labels: ['enhancement']
+body:
+ - type: textarea
+ attributes:
+ label: Description
+ description: Provide a brief summary of your feature request.
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Problem or Motivation
+ description: |
+ Is your feature request related to a problem or a specific need?
+ Please describe.
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Expected behavior
+ description: |
+ A clear and concise description of the desired feature or solution.
+ Explain what you expect to happen.
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Alternatives Considered
+ description: |
+ Provide information about any alternative solutions or features you've thought about.
+ Discuss different approaches, if any.
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+
+ Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+ validations:
+ required: false
diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml
deleted file mode 100644
index 20805855..00000000
--- a/.github/workflows/combine-prs.yml
+++ /dev/null
@@ -1,152 +0,0 @@
-name: 'Combine PRs'
-
-# Controls when the action will run - in this case triggered manually
-on:
- workflow_dispatch:
- inputs:
- branchPrefix:
- description: 'Branch prefix to find combinable PRs based on'
- required: true
- default: 'dependabot'
- mustBeGreen:
- description: 'Only combine PRs that are green (status is success). Set to false if repo does not run checks'
- type: boolean
- required: true
- default: true
- combineBranchName:
- description: 'Name of the branch to combine PRs into'
- required: true
- default: 'combine-prs-branch'
- ignoreLabel:
- description: 'Exclude PRs with this label'
- required: true
- default: 'nocombine'
-
-# A workflow run is made up of one or more jobs that can run sequentially or in parallel
-jobs:
- # This workflow contains a single job called "combine-prs"
- combine-prs:
- # The type of runner that the job will run on
- runs-on: ubuntu-latest
-
- # Steps represent a sequence of tasks that will be executed as part of the job
- steps:
- - uses: actions/github-script@v6
- id: create-combined-pr
- name: Create Combined PR
- with:
- github-token: ${{secrets.GITHUB_TOKEN}}
- script: |
- const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', {
- owner: context.repo.owner,
- repo: context.repo.repo
- });
- let branchesAndPRStrings = [];
- let baseBranch = null;
- let baseBranchSHA = null;
- for (const pull of pulls) {
- const branch = pull['head']['ref'];
- console.log('Pull for branch: ' + branch);
- if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) {
- console.log('Branch matched prefix: ' + branch);
- let statusOK = true;
- if(${{ github.event.inputs.mustBeGreen }}) {
- console.log('Checking green status: ' + branch);
- const stateQuery = `query($owner: String!, $repo: String!, $pull_number: Int!) {
- repository(owner: $owner, name: $repo) {
- pullRequest(number:$pull_number) {
- commits(last: 1) {
- nodes {
- commit {
- statusCheckRollup {
- state
- }
- }
- }
- }
- }
- }
- }`
- const vars = {
- owner: context.repo.owner,
- repo: context.repo.repo,
- pull_number: pull['number']
- };
- const result = await github.graphql(stateQuery, vars);
- const [{ commit }] = result.repository.pullRequest.commits.nodes;
- const state = commit.statusCheckRollup.state
- console.log('Validating status: ' + state);
- if(state != 'SUCCESS') {
- console.log('Discarding ' + branch + ' with status ' + state);
- statusOK = false;
- }
- }
- console.log('Checking labels: ' + branch);
- const labels = pull['labels'];
- for(const label of labels) {
- const labelName = label['name'];
- console.log('Checking label: ' + labelName);
- if(labelName == '${{ github.event.inputs.ignoreLabel }}') {
- console.log('Discarding ' + branch + ' with label ' + labelName);
- statusOK = false;
- }
- }
- if (statusOK) {
- console.log('Adding branch to array: ' + branch);
- const prString = '#' + pull['number'] + ' ' + pull['title'];
- branchesAndPRStrings.push({ branch, prString });
- baseBranch = pull['base']['ref'];
- baseBranchSHA = pull['base']['sha'];
- }
- }
- }
- if (branchesAndPRStrings.length == 0) {
- core.setFailed('No PRs/branches matched criteria');
- return;
- }
- try {
- await github.rest.git.createRef({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: 'refs/heads/' + '${{ github.event.inputs.combineBranchName }}',
- sha: baseBranchSHA
- });
- } catch (error) {
- console.log(error);
- core.setFailed('Failed to create combined branch - maybe a branch by that name already exists?');
- return;
- }
-
- let combinedPRs = [];
- let mergeFailedPRs = [];
- for(const { branch, prString } of branchesAndPRStrings) {
- try {
- await github.rest.repos.merge({
- owner: context.repo.owner,
- repo: context.repo.repo,
- base: '${{ github.event.inputs.combineBranchName }}',
- head: branch,
- });
- console.log('Merged branch ' + branch);
- combinedPRs.push(prString);
- } catch (error) {
- console.log('Failed to merge branch ' + branch);
- mergeFailedPRs.push(prString);
- }
- }
-
- console.log('Creating combined PR');
- const combinedPRsString = combinedPRs.join('\n');
- let body = '✅ This PR was created by the Combine PRs action by combining the following PRs:\n' + combinedPRsString;
- if(mergeFailedPRs.length > 0) {
- const mergeFailedPRsString = mergeFailedPRs.join('\n');
- body += '\n\n⚠️ The following PRs were left out due to merge conflicts:\n' + mergeFailedPRsString
- }
- await github.rest.pulls.create({
- owner: context.repo.owner,
- repo: context.repo.repo,
- title: 'Combined PR',
- head: '${{ github.event.inputs.combineBranchName }}',
- base: baseBranch,
- body: body
- });
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
deleted file mode 100644
index a2e3cc18..00000000
--- a/.github/workflows/deploy.yml
+++ /dev/null
@@ -1,202 +0,0 @@
-name: Deploy
-
-on:
- push:
- tags: [ "v*.*.*" ]
-
-env:
- CARGO_TERM_COLOR: always
-
-jobs:
- build-win-binaries:
- runs-on: windows-2019
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Install dependencies
- run: |
- choco install -y ninja
- pip install meson
-
- - uses: ilammy/setup-nasm@v1
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- target: x86_64-pc-windows-msvc
- override: true
-
- - name: Rust Cache
- uses: Swatinem/rust-cache@v2.2.1
-
- - name: Build rimage
- shell: cmd
- run: |
- call "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat"
- cargo build --release --target x86_64-pc-windows-msvc --verbose
-
- - name: Get the version
- shell: bash
- id: tagName
- run: |
- VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
- echo "::set-output name=tag::$VERSION"
-
- - name: Build package
- id: package
- shell: bash
- run: |
- ARCHIVE_TARGET="x86_64-pc-windows-msvc"
- ARCHIVE_NAME="rimage-${{ steps.tagName.outputs.tag }}-$ARCHIVE_TARGET"
- ARCHIVE_FILE="${ARCHIVE_NAME}.zip"
- mv LICENSE-MIT LICENSE-MIT.txt
- mv LICENSE-APACHE LICENSE-APACHE.txt
- 7z a ${ARCHIVE_FILE} \
- ./target/x86_64-pc-windows-msvc/release/rimage.exe \
- ./CHANGELOG.md ./LICENSE-MIT.txt ./LICENSE-APACHE.txt ./README.md
- echo "::set-output name=file::${ARCHIVE_FILE}"
- echo "::set-output name=name::${ARCHIVE_NAME}.zip"
-
- - name: Upload artifacts
- uses: actions/upload-artifact@v2
- with:
- name: ${{ steps.package.outputs.name }}
- path: ${{ steps.package.outputs.file }}
-
- build-unix-binaries:
- strategy:
- matrix:
- os: [ubuntu-latest, macos-latest]
- include:
- - os: ubuntu-latest
- target: x86_64-unknown-linux-gnu
- - os: macos-latest
- target: x86_64-apple-darwin
-
- runs-on: ${{ matrix.os }}
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Install dependencies (linux)
- if: matrix.os == 'ubuntu-latest'
- run: |
- DEBIAN_FRONTEND=noninteractive sudo apt-get update
- DEBIAN_FRONTEND=noninteractive sudo apt-get install -y ninja-build nasm meson
-
- - name: Install dependencies (macOS)
- if: matrix.os == 'macOS-latest'
- run: |
- brew install ninja nasm meson
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- target: ${{ matrix.target }}
- override: true
-
- - name: Rust Cache
- uses: Swatinem/rust-cache@v2.2.1
-
- - name: Build rimage
- run: cargo build --release --target ${{ matrix.target }} --verbose
-
- - name: Strip binary
- run: |
- strip target/${{ matrix.target }}/release/rimage
-
- - name: Get the version
- shell: bash
- id: tagName
- run: |
- VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
- echo "::set-output name=tag::$VERSION"
-
- - name: Build package
- id: package
- run: |
- ARCHIVE_TARGET=${{ matrix.target }}
- ARCHIVE_NAME="rimage-${{ steps.tagName.outputs.tag }}-$ARCHIVE_TARGET"
- ARCHIVE_FILE="${ARCHIVE_NAME}.tar.gz"
- mkdir "/tmp/${ARCHIVE_NAME}"
- cp README.md CHANGELOG.md LICENSE-MIT LICENSE-APACHE \
- target/${{ matrix.target }}/release/rimage \
- /tmp/${ARCHIVE_NAME}
- tar -czf ${PWD}/${ARCHIVE_FILE} -C /tmp/ ${ARCHIVE_NAME}
- echo ::set-output "name=file::${ARCHIVE_FILE}"
- echo ::set-output "name=name::${ARCHIVE_NAME}.tar.gz"
-
- - name: Upload artifacts
- uses: actions/upload-artifact@v2
- with:
- name: ${{ steps.package.outputs.name }}
- path: ${{ steps.package.outputs.file }}
-
- deploy:
-
- needs: [build-win-binaries, build-unix-binaries]
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Get version and release description
- id: tagName
- run: |
- VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
- tail -n +3 CHANGELOG.md | sed -e '/^$/,$d' > CHANGELOG.txt
- echo "::set-output name=tag::$VERSION"
-
- - name: Download artifacts
- uses: actions/download-artifact@v3
- with:
- path: ./binaries
-
- - name: Create a release
- uses: softprops/action-gh-release@v1
- with:
- name: v${{ steps.tagName.outputs.tag }}
- body_path: CHANGELOG.txt
- files: |
- ./binaries/**/*.zip
- ./binaries/**/*.tar.gz
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- publish:
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Install dependencies
- run: |
- DEBIAN_FRONTEND=noninteractive sudo apt-get update
- DEBIAN_FRONTEND=noninteractive sudo apt-get install -y ninja-build nasm meson
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- target: x86_64-unknown-linux-musl
- override: true
-
- - name: Install musl
- if: contains(matrix.target, 'linux-musl')
- run: |
- sudo apt-get install musl-tools
-
- - name: Rust Cache
- uses: Swatinem/rust-cache@v2.2.1
-
- - name: Publish to crates.io
- run: |
- cargo login ${{ secrets.CRATES_TOKEN }}
- cargo publish
diff --git a/.github/workflows/rimage.yml b/.github/workflows/rimage.yml
index 4d3da508..dd2cb49f 100644
--- a/.github/workflows/rimage.yml
+++ b/.github/workflows/rimage.yml
@@ -1,68 +1,63 @@
-name: Rimage
+name: rimage
on:
push:
- branches:
- - main
- paths-ignore:
- - '**.md'
+ branches:
+ - main
pull_request:
- paths-ignore:
- - '**.md'
+ branches:
+ - main
jobs:
- build:
- strategy:
- matrix:
- os: [ubuntu-latest, macos-latest, windows-2019]
+ test:
+ name: test
+ runs-on: ubuntu-latest
- name: Test `cargo check/test` on ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
-
- env:
- CARGO_TERM_COLOR: always
+ steps:
+ - name: Checkout 🛎️
+ uses: actions/checkout@v4
+
+ - name: Setup nasm 🧑💻
+ uses: ilammy/setup-nasm@v1
+
+ - name: Setup rust toolchain 🦀
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+
+ - name: Test 🧪
+ uses: actions-rs/cargo@v1
+ with:
+ command: test
+ args: --all-features
+
+ lint:
+ name: lint
+ runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
-
- - name: Install dependencies (linux)
- if: matrix.os == 'ubuntu-latest'
- run: |
- DEBIAN_FRONTEND=noninteractive sudo apt-get update
- DEBIAN_FRONTEND=noninteractive sudo apt-get install -y ninja-build nasm meson
-
- - name: Install dependencies (macOS)
- if: matrix.os == 'macos-latest'
- run: |
- brew install ninja nasm meson
-
- - name: Install dependencies (windows)
- if: matrix.os == 'windows-2019'
- run: |
- choco install -y ninja
- pip install meson
-
- - name: Install nasm (windows)
- if: matrix.os == 'windows-2019'
- uses: ilammy/setup-nasm@v1
-
- - run: |
- rustup set auto-self-update disable
- rustup toolchain install stable --profile minimal
-
- - name: Rust Cache
- uses: Swatinem/rust-cache@v2.2.1
-
- - name: Check/Test (Windows)
- if: matrix.os == 'windows-2019'
- shell: cmd
- run: |
- call "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat"
- cargo check --verbose
- cargo test --verbose
-
- - name: Test (Linux, macOS)
- if: ${{ matrix.os != 'windows-2019' }}
- run: |
- cargo check --verbose
- cargo test --verbose
+ - name: Checkout 🛎️
+ uses: actions/checkout@v4
+
+ - name: Setup nasm 🧑💻
+ uses: ilammy/setup-nasm@v1
+
+ - name: Setup rust toolchain 🦀
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ components: rustfmt, clippy
+
+ - name: Formatting check 🪄
+ uses: actions-rs/cargo@v1
+ with:
+ command: fmt
+ args: --all -- --check
+
+ - name: Clippy check 🔎
+ uses: actions-rs/cargo@v1
+ with:
+ command: clippy
+ args: --all-features -- -D warnings
diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml
deleted file mode 100644
index 9afc3199..00000000
--- a/.github/workflows/rust-clippy.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-# rust-clippy is a tool that runs a bunch of lints to catch common
-# mistakes in your Rust code and help improve your Rust code.
-# More details at https://github.com/rust-lang/rust-clippy
-# and https://rust-lang.github.io/rust-clippy/
-
-name: rust-clippy analyze
-
-on:
- push:
- branches: ["main"]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: ["main"]
- schedule:
- - cron: "18 14 * * 4"
-
-jobs:
- rust-clippy-analyze:
- name: Run rust-clippy analyzing
- runs-on: ubuntu-latest
- permissions:
- contents: read
- security-events: write
- actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
- steps:
- - name: Checkout code
- uses: actions/checkout@v2
-
- - name: Install dependencies (linux)
- run: |
- DEBIAN_FRONTEND=noninteractive sudo apt-get update
- DEBIAN_FRONTEND=noninteractive sudo apt-get install -y ninja-build nasm meson
-
- - name: Install Rust toolchain
- uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1
- with:
- profile: minimal
- toolchain: stable
- components: clippy
- override: true
-
- - name: Install required cargo
- run: cargo install clippy-sarif sarif-fmt
-
- - name: Run rust-clippy
- run: cargo clippy
- --all-features
- --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
- continue-on-error: true
-
- - name: Upload analysis results to GitHub
- uses: github/codeql-action/upload-sarif@v1
- with:
- sarif_file: rust-clippy-results.sarif
- wait-for-processing: true
diff --git a/Benchmarks.md b/Benchmarks.md
deleted file mode 100644
index 52b82ce0..00000000
--- a/Benchmarks.md
+++ /dev/null
@@ -1,117 +0,0 @@
-## Mozjpeg processing
-
-```
-Benchmark 1: squoosh-cli --mozjpeg '{quality:50}' -d ../squoosh ./boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg
- Time (mean ± σ): 4.186 s ± 0.041 s [User: 5.634 s, System: 0.413 s]
- Range (min … max): 4.152 s … 4.284 s 10 runs
-
-Benchmark 2: rimage -q 50 -o ../rimage ./boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg
- Time (mean ± σ): 1.512 s ± 0.036 s [User: 1.461 s, System: 0.051 s]
- Range (min … max): 1.476 s … 1.582 s 10 runs
-
-Summary
- rimage -q 50 -o ../rimage ./boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg ran
- 2.77 ± 0.07 times faster than squoosh-cli --mozjpeg '{quality:50}' -d ../squoosh ./boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg
-```
-
-Original **JPG (1.47MB)**
-
-![Before](./assets/boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg)
-
-Squoosh **JPG (462.86KB)**
-
-![After (Squoosh!)](./assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-squoosh.jpg)
-
-Rimage **JPG (462.86KB)**
-
-![After (Rimage!)](./assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-rimage.jpg)
-
-## Oxipng processing
-
-```
-Benchmark 1: squoosh-cli --oxipng '{level:2}' -d ../squoosh ./edwin-splisser-GC7FLmgU5Z4-unsplash.jpg
- Time (mean ± σ): 28.419 s ± 1.092 s [User: 29.659 s, System: 0.669 s]
- Range (min … max): 27.795 s … 31.432 s 10 runs
-
- Warning: The first benchmarking run for this command was significantly slower than the rest (31.432 s). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.
-
-Benchmark 2: rimage -f oxipng -o ../rimage ./edwin-splisser-GC7FLmgU5Z4-unsplash.jpg
- Time (mean ± σ): 10.862 s ± 0.679 s [User: 13.256 s, System: 0.141 s]
- Range (min … max): 10.358 s … 12.169 s 10 runs
-
- Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
-
-Summary
- rimage -f oxipng -o ../rimage ./edwin-splisser-GC7FLmgU5Z4-unsplash.jpg ran
- 2.62 ± 0.19 times faster than squoosh-cli --oxipng '{level:2}' -d ../squoosh ./edwin-splisser-GC7FLmgU5Z4-unsplash.jpg
-```
-
-Original **JPG (1.37MB)**
-
-![Before](./assets/edwin-splisser-GC7FLmgU5Z4-unsplash.jpg)
-
-Squoosh **PNG (7.83MB)**
-
-![After (squoosh!)](./assets/edwin-splisser-GC7FLmgU5Z4-unsplash-squoosh.png)
-
-Rimage **PNG (7.84MB)**
-
-![After (squoosh!)](./assets/edwin-splisser-GC7FLmgU5Z4-unsplash-rimage.png)
-
-## Webp processing
-
-```
-Benchmark 1: squoosh-cli --webp '{quality:50}' -d ../squoosh ./felix-rottmann-n-Ky-79zeXM-unsplash.jpg
- Time (mean ± σ): 50.785 s ± 2.291 s [User: 52.350 s, System: 1.355 s]
- Range (min … max): 47.623 s … 54.083 s 10 runs
-
-Benchmark 2: rimage -f webp -q 50 -o ../rimage ./felix-rottmann-n-Ky-79zeXM-unsplash.jpg
- Time (mean ± σ): 16.983 s ± 1.002 s [User: 16.759 s, System: 0.214 s]
- Range (min … max): 15.479 s … 18.334 s 10 runs
-
-Summary
- rimage -f webp -q 50 -o ../rimage ./felix-rottmann-n-Ky-79zeXM-unsplash.jpg ran
- 2.99 ± 0.22 times faster than squoosh-cli --webp '{quality:50}' -d ../squoosh ./felix-rottmann-n-Ky-79zeXM-unsplash.jpg
-```
-
-Original **JPG (15.25MB)**
-
-![Before](./assets/felix-rottmann-n-Ky-79zeXM-unsplash.jpg)
-
-Squoosh **WEBP (7.33MB)**
-
-![After (squoosh!)](./assets/felix-rottmann-n-Ky-79zeXM-unsplash-squoosh.webp)
-
-Rimage **WEBP (7.33MB)**
-
-![After (squoosh!)](./assets/felix-rottmann-n-Ky-79zeXM-unsplash-rimage.webp)
-
-## AVIF processing
-
-```
-Benchmark 1: squoosh-cli --avif '{cqLevel:32}' -d ../squoosh ./gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg
- Time (mean ± σ): 21.446 s ± 1.270 s [User: 119.643 s, System: 1.425 s]
- Range (min … max): 18.648 s … 23.048 s 10 runs
-
-Benchmark 2: rimage -f avif -q 50 -o ../rimage ./gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg
- Time (mean ± σ): 6.073 s ± 0.136 s [User: 37.243 s, System: 0.182 s]
- Range (min … max): 5.865 s … 6.281 s 10 runs
-
-Summary
- rimage -f avif -q 50 -o ../rimage ./gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg ran
- 3.53 ± 0.22 times faster than squoosh-cli --avif '{cqLevel:32}' -d ../squoosh ./gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg
-```
-
-Original **JPG (4.71MB)**
-
-![Before](./assets/gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg)
-
-Squoosh **AVIF (596.16KB)**
-
-![After (squoosh!)](./assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-squoosh.avif)
-
-Rimage **AVIF (548.47KB)**
-
-![After (squoosh!)](./assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-rimage.avif)
-
-All images was taken from [Unsplash](https://unsplash.com/)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 892d4846..675e6f8f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,43 +10,43 @@ Rimage is committed to fostering a welcoming community. Everyone participating i
To get started with contributing, you'll need to:
-1. Fork the repository on GitHub
-2. Clone your fork locally
-3. Install Rust and Cargo (if you haven't already)
-4. Also you will need to install cmake, nasm and ninja/meson for avif development
-5. Run `cargo build` to ensure everything builds properly
-> note: On windows use Visual Studio build environment like developer PowerShell for VS 2019
+1. Fork the repository on GitHub.
+2. Clone your fork locally.
+3. Install Rust and Cargo (if you haven't already).
+4. Install cmake for AVIF and JPEG XL development.
+5. Run `cargo build` to ensure everything builds properly.
+ > Note: On Windows, use a Visual Studio build environment like Developer PowerShell for VS 2019/2022.
## Making Changes
Once you have a working environment set up, you can start making changes. Before you start, make sure to:
-1. Create a new branch for your changes
-2. Write tests for any new functionality
-3. Ensure that all tests pass before submitting a pull request
-4. Ensure that your code adheres to the project's style guidelines (run `cargo fmt` to automatically format your code)
-5. Ensure that your code passes Clippy's linter (run `cargo clippy`)
+1. Create a new branch for your changes.
+2. Write tests for any new functionality (optional, but recommended).
+3. Ensure that all tests pass before submitting a pull request.
+4. Ensure that your code adheres to the project's style guidelines (run `cargo fmt` to automatically format your code).
+5. Ensure that your code passes Clippy's linter (run `cargo clippy`).
## Submitting Changes
Once you've made your changes and ensured that all tests pass, you can submit your changes by:
-1. Pushing your changes to your fork
-2. Creating a pull request against the `main` branch of the main repository
-3. Wait for feedback from the maintainers
+1. Pushing your changes to your fork.
+2. Creating a pull request against the `main` branch of the main repository.
+3. Wait for feedback from the maintainers.
## Pull Request Guidelines
When submitting a pull request, please:
-1. Include a clear description of the changes you've made
-2. Include a reference to any relevant issues or pull requests
-3. Ensure that your code is well-documented and easy to understand
-4. Sign your work (for more information, see [the Developer Certificate of Origin](https://developercertificate.org/))
+1. Include a clear description of the changes you've made.
+2. Include a reference to any relevant issues or pull requests.
+3. Ensure that your code is well-documented and easy to understand.
+4. Sign your work (for more information, see [the Developer Certificate of Origin](https://developercertificate.org/)).
## Code Licensing
-All contributions to Rimage are dual licensed under either the Apache License 2.0 or the MIT license. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.
+All contributions to Rimage are dual licensed under either the [Apache License 2.0](LICENSE-APACHE) or the [MIT license](LICENSE-MIT). Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.
## Conclusion
diff --git a/Cargo.lock b/Cargo.lock
index f8773cba..e7cd5f63 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,85 +8,11 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-[[package]]
-name = "ahash"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
-dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "anes"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
-
-[[package]]
-name = "anstream"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
-dependencies = [
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
-dependencies = [
- "anstyle",
- "windows-sys 0.48.0",
-]
-
[[package]]
name = "anyhow"
-version = "1.0.71"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "arbitrary"
@@ -114,17 +40,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -133,9 +48,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "av-metrics"
-version = "0.9.1"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "996ce95bbdb0203e5b91d4a0c9b81c0d67d11c80f884482a0c1ea19e732e3530"
+checksum = "13638b394190295622c0d2493d0c8c39210b92c2110895bfb14c58db213c2b39"
dependencies = [
"crossbeam",
"itertools",
@@ -177,11 +92,17 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
[[package]]
name = "bitstream-io"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d28070975aaf4ef1fd0bd1f29b739c06c2cdd9972e090617fb6dca3b2cb564e"
+checksum = "82704769cb85a22df2c54d6bdd6a158b7931d256cf3248a07d6ecbe9d58b31d7"
[[package]]
name = "bitvec"
@@ -213,9 +134,15 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "bytemuck"
-version = "1.13.1"
+version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
+checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cargo-lock"
@@ -229,26 +156,21 @@ dependencies = [
"url",
]
-[[package]]
-name = "cast"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
-
[[package]]
name = "cc"
-version = "1.0.79"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"jobserver",
+ "libc",
]
[[package]]
name = "cfg-expr"
-version = "0.15.3"
+version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "215c0072ecc28f92eeb0eea38ba63ddfcb65c2828c46311d646f1a3ff5f9841c"
+checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9"
dependencies = [
"smallvec",
"target-lexicon",
@@ -260,83 +182,51 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-[[package]]
-name = "ciborium"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
-dependencies = [
- "ciborium-io",
- "ciborium-ll",
- "serde",
-]
-
-[[package]]
-name = "ciborium-io"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
-
-[[package]]
-name = "ciborium-ll"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
-dependencies = [
- "ciborium-io",
- "half",
-]
-
[[package]]
name = "clap"
-version = "4.4.0"
+version = "4.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d5f1946157a96594eb2d2c10eb7ad9a2b27518cb3000209dec700c35df9197d"
+checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
dependencies = [
- "clap_builder",
+ "bitflags 1.3.2",
"clap_derive",
- "once_cell",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78116e32a042dd73c2901f0dc30790d20ff3447f3e3472fad359e8c3d282bcd6"
-dependencies = [
- "anstream",
- "anstyle",
"clap_lex",
- "strsim",
+ "is-terminal",
+ "once_cell",
+ "termcolor",
"terminal_size",
]
[[package]]
name = "clap_complete"
-version = "4.3.1"
+version = "4.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f6b5c519bab3ea61843a7923d074b04245624bb84a64a8c150f5deb014e388b"
+checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
-version = "4.4.0"
+version = "4.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
+checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
dependencies = [
"heck",
+ "proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.27",
+ "syn 1.0.109",
]
[[package]]
name = "clap_lex"
-version = "0.5.0"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646"
+dependencies = [
+ "os_str_bytes",
+]
[[package]]
name = "cmake"
@@ -348,10 +238,10 @@ dependencies = [
]
[[package]]
-name = "colorchoice"
-version = "1.0.0"
+name = "color_quant"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "console"
@@ -366,12 +256,6 @@ dependencies = [
"windows-sys 0.45.0",
]
-[[package]]
-name = "const_fn_assert"
-version = "0.1.3+deprecated"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c773f3d54b8826857668aafa3bfe14bd179911a5b571b241e192cac1ec1c0b5"
-
[[package]]
name = "crc32fast"
version = "1.3.2"
@@ -381,42 +265,6 @@ dependencies = [
"cfg-if",
]
-[[package]]
-name = "criterion"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
-dependencies = [
- "anes",
- "cast",
- "ciborium",
- "clap",
- "criterion-plot",
- "is-terminal",
- "itertools",
- "num-traits",
- "once_cell",
- "oorandom",
- "plotters",
- "rayon",
- "regex",
- "serde",
- "serde_derive",
- "serde_json",
- "tinytemplate",
- "walkdir",
-]
-
-[[package]]
-name = "criterion-plot"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
-dependencies = [
- "cast",
- "itertools",
-]
-
[[package]]
name = "crossbeam"
version = "0.8.2"
@@ -484,6 +332,78 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "darling"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_builder"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
+dependencies = [
+ "derive_builder_macro",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
+dependencies = [
+ "derive_builder_core",
+ "syn 1.0.109",
+]
+
[[package]]
name = "dunce"
version = "1.0.4"
@@ -492,9 +412,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "either"
-version = "1.8.1"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
@@ -502,30 +422,17 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
-[[package]]
-name = "env_logger"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
-dependencies = [
- "humantime",
- "is-terminal",
- "log",
- "regex",
- "termcolor",
-]
-
[[package]]
name = "equivalent"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
-version = "0.3.1"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
dependencies = [
"errno-dragonfly",
"libc",
@@ -542,15 +449,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "fallible_collections"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "618bf220e692a59c50e7b281149f53c3fe93e0cf0b40c050fc2af8c9ecb28505"
-dependencies = [
- "hashbrown 0.13.2",
-]
-
[[package]]
name = "fdeflate"
version = "0.3.0"
@@ -571,9 +469,9 @@ dependencies = [
[[package]]
name = "filetime"
-version = "0.2.21"
+version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
+checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
@@ -583,14 +481,20 @@ dependencies = [
[[package]]
name = "flate2"
-version = "1.0.26"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
+checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
dependencies = [
"crc32fast",
"miniz_oxide",
]
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
[[package]]
name = "form_urlencoded"
version = "1.2.0"
@@ -623,7 +527,7 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"libc",
"libgit2-sys",
"log",
@@ -638,17 +542,12 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "half"
-version = "1.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
-
-[[package]]
-name = "hashbrown"
-version = "0.13.2"
+version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
dependencies = [
- "ahash",
+ "cfg-if",
+ "crunchy",
]
[[package]]
@@ -665,24 +564,15 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
-name = "humantime"
-version = "2.1.0"
+name = "ident_case"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
@@ -694,6 +584,19 @@ dependencies = [
"unicode-normalization",
]
+[[package]]
+name = "image"
+version = "0.24.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "num-rational",
+ "num-traits",
+]
+
[[package]]
name = "imagequant"
version = "4.2.0"
@@ -721,32 +624,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
- "hashbrown 0.14.0",
+ "hashbrown",
"rayon",
]
-[[package]]
-name = "indicatif"
-version = "0.17.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730"
-dependencies = [
- "console",
- "instant",
- "number_prefix",
- "portable-atomic",
- "unicode-width",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if",
-]
-
[[package]]
name = "interpolate_name"
version = "0.2.3"
@@ -764,20 +645,19 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
- "hermit-abi 0.3.1",
+ "hermit-abi",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "is-terminal"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
- "hermit-abi 0.3.1",
- "io-lifetimes",
- "rustix",
+ "hermit-abi",
+ "rustix 0.38.11",
"windows-sys 0.48.0",
]
@@ -790,12 +670,6 @@ dependencies = [
"either",
]
-[[package]]
-name = "itoa"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
-
[[package]]
name = "ivf"
version = "0.1.1"
@@ -815,12 +689,44 @@ dependencies = [
]
[[package]]
-name = "js-sys"
-version = "0.3.64"
+name = "jpegxl-rs"
+version = "0.8.2+libjxl-0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+checksum = "59bdb84e42cd446f433990a66d3519dc849505f6aa32f5f749b714bd46225876"
dependencies = [
- "wasm-bindgen",
+ "byteorder",
+ "derive_builder",
+ "half",
+ "jpegxl-sys",
+ "thiserror",
+]
+
+[[package]]
+name = "jpegxl-src"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81048fc7b31ebcf4393527f70d211824359a4a266f63fee50a5e5ba5fee18579"
+dependencies = [
+ "cmake",
+]
+
+[[package]]
+name = "jpegxl-sys"
+version = "0.8.2+libjxl-0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b907f540220390b54d9845eb5fe6a8f0453a0ea4f1e2e306cdbdbe8ee027d540"
+dependencies = [
+ "jpegxl-src",
+ "pkg-config",
+]
+
+[[package]]
+name = "kamadak-exif"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077"
+dependencies = [
+ "mutate_once",
]
[[package]]
@@ -835,6 +741,15 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+[[package]]
+name = "libaom-sys"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "610241493fe5178968c3d3b27b6c397dd873244ca17539bb488ade05690fcd71"
+dependencies = [
+ "cmake",
+]
+
[[package]]
name = "libavif-sys"
version = "0.14.3"
@@ -842,9 +757,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c7b9293d221c7d4b4290d4479c491a09b877943208593f1563d8521c4b55930"
dependencies = [
"cmake",
+ "libaom-sys",
"libc",
- "libdav1d-sys",
- "rav1e",
]
[[package]]
@@ -853,12 +767,6 @@ version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
-[[package]]
-name = "libdav1d-sys"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2449c0cf51b69ae65edb12fa071063e5a51034a8b2347269b5a40aefe2071fcf"
-
[[package]]
name = "libdeflate-sys"
version = "0.14.0"
@@ -900,42 +808,20 @@ dependencies = [
]
[[package]]
-name = "libmimalloc-sys"
-version = "0.1.33"
+name = "libwebp-sys"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4ac0e912c8ef1b735e92369695618dc5b1819f5a7bf3f167301a3ba1cea515e"
+checksum = "a5df1e76f0acef0058aa2164ccf74e610e716e7f9eeb3ee2283de7d43659d823"
dependencies = [
"cc",
- "libc",
-]
-
-[[package]]
-name = "libwebp"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7792a82f95b5b2528d9fe1642231f972d2cdd73b91ccbcb6ab214c2cf1a74f4"
-dependencies = [
- "libwebp-sys2",
-]
-
-[[package]]
-name = "libwebp-sys2"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74f9c6964201c51319f16a796dc947a73961646eba49f584187b12de9970d077"
-dependencies = [
- "cc",
- "cfg-if",
- "libc",
- "pkg-config",
- "vcpkg",
+ "glob",
]
[[package]]
name = "libz-sys"
-version = "1.1.9"
+version = "1.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
+checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
dependencies = [
"cc",
"libc",
@@ -949,6 +835,12 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+
[[package]]
name = "log"
version = "0.4.20"
@@ -976,9 +868,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.5.0"
+version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
[[package]]
name = "memoffset"
@@ -989,15 +881,6 @@ dependencies = [
"autocfg",
]
-[[package]]
-name = "mimalloc"
-version = "0.1.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e2894987a3459f3ffb755608bd82188f8ed00d0ae077f1edea29c068d639d98"
-dependencies = [
- "libmimalloc-sys",
-]
-
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -1016,9 +899,9 @@ dependencies = [
[[package]]
name = "mozjpeg"
-version = "0.9.6"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af53d95d79b2239aadc12e06bb2598786f7a7cb563f3257a1857846940d59f27"
+checksum = "d8c7e3afac2fe1a464c056f4ee5627ca2687407e94e45ed252587311a6afd131"
dependencies = [
"arrayvec",
"libc",
@@ -1028,9 +911,9 @@ dependencies = [
[[package]]
name = "mozjpeg-sys"
-version = "1.1.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "626df331e335cf8a26510918010954f4770f3b04714026c1904e42c4507f8a3e"
+checksum = "dab8f5496b7f0e8c593d33dbbbc16c6eefd3a6991d794f56e96bebc5228cfd29"
dependencies = [
"cc",
"dunce",
@@ -1038,6 +921,12 @@ dependencies = [
"nasm-rs",
]
+[[package]]
+name = "mutate_once"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
+
[[package]]
name = "nasm-rs"
version = "0.2.5"
@@ -1071,9 +960,9 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "num-bigint"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
@@ -1091,6 +980,17 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "num-derive"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.31",
+]
+
[[package]]
name = "num-integer"
version = "0.1.45"
@@ -1115,9 +1015,9 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
@@ -1128,16 +1028,10 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi 0.3.1",
+ "hermit-abi",
"libc",
]
-[[package]]
-name = "number_prefix"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
-
[[package]]
name = "once_cell"
version = "1.18.0"
@@ -1145,18 +1039,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
-name = "oorandom"
-version = "11.1.3"
+name = "os_str_bytes"
+version = "6.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
[[package]]
name = "oxipng"
version = "8.0.0"
-source = "git+https://github.com/shssoichiro/oxipng.git?rev=4ae64c56#4ae64c568bbac8bf34ba89bf8772f9b2be70c4e5"
+source = "git+https://github.com/shssoichiro/oxipng.git?rev=02bd47b#02bd47ba297ffdc1210c89b6f98c0d2c4b31d155"
dependencies = [
"bitvec",
- "clap",
"crossbeam-channel",
"filetime",
"indexmap",
@@ -1166,16 +1059,14 @@ dependencies = [
"rgb",
"rustc-hash",
"rustc_version",
- "stderrlog",
- "wild",
"zopfli",
]
[[package]]
name = "paste"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "percent-encoding"
@@ -1189,53 +1080,19 @@ version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
-[[package]]
-name = "plotters"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
-dependencies = [
- "num-traits",
- "plotters-backend",
- "plotters-svg",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "plotters-backend"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
-
-[[package]]
-name = "plotters-svg"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
-dependencies = [
- "plotters-backend",
-]
-
[[package]]
name = "png"
version = "0.17.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
-[[package]]
-name = "portable-atomic"
-version = "1.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794"
-
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -1243,20 +1100,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
-name = "pretty_env_logger"
-version = "0.5.0"
+name = "proc-macro-error"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
- "env_logger",
- "log",
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
]
[[package]]
name = "proc-macro2"
-version = "1.0.63"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
@@ -1269,9 +1140,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
-version = "1.0.30"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -1314,9 +1185,9 @@ dependencies = [
[[package]]
name = "rav1e"
-version = "0.6.3"
+version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "277898094f0d03c6a609e491324102daf5080e71c06b4b25e5acf8b89d26c945"
+checksum = "16c383692a5e7abd9f6d1eddb1a5e0269f859392387883361bb09e5555852ec1"
dependencies = [
"arbitrary",
"arg_enum_proc_macro",
@@ -1330,7 +1201,6 @@ dependencies = [
"clap",
"clap_complete",
"console",
- "const_fn_assert",
"fern",
"interpolate_name",
"itertools",
@@ -1343,7 +1213,7 @@ dependencies = [
"new_debug_unreachable",
"nom",
"noop_proc_macro",
- "num-derive",
+ "num-derive 0.3.3",
"num-traits",
"once_cell",
"paste",
@@ -1400,49 +1270,20 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "regex"
-version = "1.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.3.7"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
+ "bitflags 1.3.2",
]
-[[package]]
-name = "regex-syntax"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
-
[[package]]
name = "resize"
-version = "0.7.4"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87e7bdfff05e26408cf8f82fe896ce3d7624f0c0b06c84b2f1009c50452ead41"
+checksum = "1c0df17280e1b33039ef4c0ce65ea60f26ca77a33b9e7193a8b92ddcd24499d0"
dependencies = [
- "fallible_collections",
+ "rayon",
"rgb",
]
@@ -1459,29 +1300,19 @@ dependencies = [
name = "rimage"
version = "0.8.2"
dependencies = [
- "bytemuck",
- "clap",
- "console",
- "criterion",
- "glob",
"imagequant",
- "indicatif",
+ "jpegxl-rs",
+ "kamadak-exif",
"libavif-sys",
- "libwebp",
- "log",
- "mimalloc",
"mozjpeg",
- "once_cell",
"oxipng",
"png",
- "pretty_env_logger",
"ravif",
- "rayon",
- "regex",
"resize",
"rgb",
"thiserror",
- "tikv-jemallocator",
+ "transpose",
+ "webp",
]
[[package]]
@@ -1523,31 +1354,29 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.37.20"
+version = "0.37.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
+checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.3.8",
"windows-sys 0.48.0",
]
[[package]]
-name = "ryu"
-version = "1.0.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
-
-[[package]]
-name = "same-file"
-version = "1.0.6"
+name = "rustix"
+version = "0.38.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453"
dependencies = [
- "winapi-util",
+ "bitflags 2.4.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.5",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1558,48 +1387,37 @@ checksum = "0b53b0a5db882a8e2fdaae0a43f7b39e7e9082389e978398bdf223a55b581248"
[[package]]
name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
dependencies = [
"serde",
]
[[package]]
name = "serde"
-version = "1.0.164"
+version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.164"
+version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.27",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.99"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
-dependencies = [
- "itoa",
- "ryu",
- "serde",
+ "syn 2.0.31",
]
[[package]]
@@ -1613,9 +1431,9 @@ dependencies = [
[[package]]
name = "signal-hook"
-version = "0.3.15"
+version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
@@ -1632,9 +1450,9 @@ dependencies = [
[[package]]
name = "simd-adler32"
-version = "0.3.5"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simd_helpers"
@@ -1647,21 +1465,15 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.10.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
-name = "stderrlog"
-version = "0.5.4"
+name = "strength_reduce"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69a26bbf6de627d389164afa9783739b56746c6c72c4ed16539f4ff54170327b"
-dependencies = [
- "atty",
- "log",
- "termcolor",
- "thread_local",
-]
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
[[package]]
name = "strsim"
@@ -1682,9 +1494,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.27"
+version = "2.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
+checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
dependencies = [
"proc-macro2",
"quote",
@@ -1700,7 +1512,7 @@ dependencies = [
"cfg-expr",
"heck",
"pkg-config",
- "toml 0.7.5",
+ "toml 0.7.6",
"version-compare",
]
@@ -1712,15 +1524,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target-lexicon"
-version = "0.12.8"
+version = "0.12.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac"
+checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
[[package]]
name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
@@ -1731,28 +1543,28 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
- "rustix",
+ "rustix 0.37.23",
"windows-sys 0.48.0",
]
[[package]]
name = "thiserror"
-version = "1.0.47"
+version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.47"
+version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.27",
+ "syn 2.0.31",
]
[[package]]
@@ -1765,36 +1577,6 @@ dependencies = [
"once_cell",
]
-[[package]]
-name = "tikv-jemalloc-sys"
-version = "0.5.3+5.3.0-patched"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8"
-dependencies = [
- "cc",
- "libc",
-]
-
-[[package]]
-name = "tikv-jemallocator"
-version = "0.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "965fe0c26be5c56c94e38ba547249074803efd52adfb66de62107d95aab3eaca"
-dependencies = [
- "libc",
- "tikv-jemalloc-sys",
-]
-
-[[package]]
-name = "tinytemplate"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
-dependencies = [
- "serde",
- "serde_json",
-]
-
[[package]]
name = "tinyvec"
version = "1.6.0"
@@ -1821,9 +1603,9 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.7.5"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240"
+checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
dependencies = [
"serde",
"serde_spanned",
@@ -1842,9 +1624,9 @@ dependencies = [
[[package]]
name = "toml_edit"
-version = "0.19.11"
+version = "0.19.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7"
+checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
dependencies = [
"indexmap",
"serde",
@@ -1853,6 +1635,16 @@ dependencies = [
"winnow",
]
+[[package]]
+name = "transpose"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6522d49d03727ffb138ae4cbc1283d3774f0d10aa7f9bf52e6784c45daf9b23"
+dependencies = [
+ "num-integer",
+ "strength_reduce",
+]
+
[[package]]
name = "typed-arena"
version = "2.0.2"
@@ -1867,9 +1659,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
-version = "1.0.9"
+version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-normalization"
@@ -1888,30 +1680,24 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "url"
-version = "2.4.0"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
+checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
-[[package]]
-name = "utf8parse"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
-
[[package]]
name = "v_frame"
-version = "0.3.4"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3753f70d50a77f5d381103ba2693a889fed0d84273dd5cbdf4eb8bda720f0c6"
+checksum = "85db69f33d00031c1b07f7292e56317d5aa9475bdbd3d27ef18f3633438a697e"
dependencies = [
"cfg-if",
"noop_proc_macro",
- "num-derive",
+ "num-derive 0.4.0",
"num-traits",
"rust_hawktracer",
]
@@ -1934,16 +1720,6 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
-[[package]]
-name = "walkdir"
-version = "2.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -1971,7 +1747,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.27",
+ "syn 2.0.31",
"wasm-bindgen-shared",
]
@@ -1993,7 +1769,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.27",
+ "syn 2.0.31",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2005,22 +1781,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
-name = "web-sys"
-version = "0.3.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "wild"
-version = "2.1.0"
+name = "webp"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05b116685a6be0c52f5a103334cbff26db643826c7b3735fc0a3ba9871310a74"
+checksum = "12ff0ebb440d1db63b778cb609db8a8abfda825a7841664a76a70b628502c7e1"
dependencies = [
- "glob",
+ "image",
+ "libwebp-sys",
]
[[package]]
@@ -2069,7 +1836,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -2089,17 +1856,17 @@ dependencies = [
[[package]]
name = "windows-targets"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
]
[[package]]
@@ -2110,9 +1877,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
@@ -2122,9 +1889,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
@@ -2134,9 +1901,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
@@ -2146,9 +1913,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
@@ -2158,9 +1925,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -2170,9 +1937,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
@@ -2182,15 +1949,15 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
-version = "0.4.7"
+version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448"
+checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
dependencies = [
"memchr",
]
@@ -2206,9 +1973,9 @@ dependencies = [
[[package]]
name = "y4m"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a72a9921af8237fe25097a1ae31c92a05c1d39b2454653ad48f2f407cf7a0dae"
+checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
[[package]]
name = "zopfli"
diff --git a/Cargo.toml b/Cargo.toml
index 9b434b24..f6e24232 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,80 +1,59 @@
[package]
+authors = ["Vladyslav Vladinov "]
+categories = ["compression", "multimedia::images"]
+description = "A library to convert/optimize/resize images in different formats"
+documentation = "https://docs.rs/rimage"
+homepage = "https://lib.rs/rimage"
+include = ["/README.md", "/Cargo.toml", "/src/**/*.rs"]
+keywords = ["image", "compression", "encoder", "decoder"]
+license = "MIT OR Apache-2.0"
name = "rimage"
-description = "A CLI tool to convert/optimize/resize images in different formats"
-authors = ["Vladyslav Vladinov "]
-
-categories = ["command-line-utilities", "compression"]
-keywords = ["cli", "image", "compression"]
-
-exclude = ["tests/*", ".*"]
+readme = "README.md"
repository = "https://github.com/SalOne22/rimage"
-license = "MIT OR Apache-2.0"
-
version = "0.8.2"
edition = "2021"
-[profile.release]
-lto = true
-codegen-units = 1
+[features]
+default = ["quantization", "resizing", "exif", "oxipng"]
+quantization = ["imagequant"]
+resizing = ["resize"]
+transform = ["transpose"]
+avif = ["libavif-sys", "ravif"]
+exif = ["kamadak-exif", "transform"]
+jxl = ["jpegxl-rs"]
-[profile.profiling]
-inherits = "release"
-debug = 1
[dependencies]
-bytemuck = "1.13.1"
-clap = { version = "4.4.0", features = ["derive"] }
-imagequant = "4.2.0"
-indicatif = "0.17.6"
-log = "0.4.20"
-mozjpeg = "0.9.4"
-oxipng = { git = "https://github.com/shssoichiro/oxipng.git", rev = "4ae64c56" } # For parallel support
-png = "0.17.10"
-pretty_env_logger = "0.5.0"
rgb = "0.8.36"
-resize = "0.7.4"
-libwebp = { version = "0.1.2", features = ["static"] }
-libavif-sys = "0.14.3"
-ravif = "0.11.2"
thiserror = "1.0.47"
-rayon = "1.7.0"
-console = "0.15.7"
-[target.'cfg(not(target_env = "msvc"))'.dependencies]
-tikv-jemallocator = "0.5"
-[target.'cfg(target_env = "msvc")'.dependencies]
-mimalloc = "0.1.37"
-[target.'cfg(windows)'.dependencies]
-glob = "0.3.1"
-
-[dev-dependencies]
-criterion = { version = "0.5.1", features = ["html_reports"] }
-once_cell = "1.18.0"
-regex = "1.9.4"
-
-[[bench]]
-name = "decode_jpg"
-harness = false
-
-[[bench]]
-name = "decode_png"
-harness = false
-
-[[bench]]
-name = "encode_jpg"
-harness = false
-
-[[bench]]
-name = "encode_png"
-harness = false
-
-[[bench]]
-name = "decode_webp"
-harness = false
-
-[[bench]]
-name = "encode_webp"
-harness = false
-
-[[bench]]
-name = "encode_browser_png"
-harness = false
+png = "0.17.10"
+imagequant = { version = "4.2.0", optional = true }
+resize = { version = "0.8.0", optional = true }
+transpose = { version = "0.2.2", optional = true }
+webp = { version = "0.2.5", optional = true }
+ravif = { version = "0.11.2", optional = true }
+kamadak-exif = { version = "0.5.5", optional = true }
+
+[dependencies.mozjpeg]
+version = "0.10.0"
+features = ["parallel", "with_simd"]
+
+[dependencies.oxipng]
+git = "https://github.com/shssoichiro/oxipng.git"
+rev = "02bd47b"
+version = "8.0.0"
+features = ["parallel", "zopfli", "filetime"]
+default-features = false
+optional = true
+
+[dependencies.libavif-sys]
+version = "0.14.3"
+features = ["codec-aom"]
+default-features = false
+optional = true
+
+[dependencies.jpegxl-rs]
+version = "0.8.2"
+default-features = false
+features = ["vendored", "threads"]
+optional = true
\ No newline at end of file
diff --git a/README.md b/README.md
index c1999a00..5d3eb0b8 100644
--- a/README.md
+++ b/README.md
@@ -1,73 +1,83 @@
# Rimage
-[![Build Status](https://img.shields.io/github/actions/workflow/status/SalOne22/rimage/rimage.yml?label=rimage&style=flat-square)](https://github.com/SalOne22/rimage/actions?query=branch%3Amain+)
+[![build status](https://img.shields.io/github/actions/workflow/status/SalOne22/rimage/rimage.yml?label=rimage&style=flat-square)](https://github.com/SalOne22/rimage/actions?query=branch%3Amain+)
[![docs.rs](https://img.shields.io/docsrs/rimage/latest?style=flat-square)](https://docs.rs/rimage)
-[![Version](https://img.shields.io/crates/v/rimage?style=flat-square)](https://crates.io/crates/rimage)
-[![License](https://img.shields.io/crates/l/rimage?style=flat-square)](https://github.com/SalOne22/rimage)
+[![version](https://img.shields.io/crates/v/rimage?style=flat-square)](https://crates.io/crates/rimage)
+[![license](https://img.shields.io/crates/l/rimage?style=flat-square)](https://github.com/SalOne22/rimage)
-This is CLI tool inspired by [squoosh!](https://squoosh.app/)
-Rimage currently supports several codecs - mozjpeg, oxipng, webp and avif. More will be added later.
+A powerful Rust image optimization library inspired by [squoosh!](https://squoosh.app/).
-## Installation
+## Overview
-You can download latest release from [Releases](https://github.com/SalOne22/rimage/releases) tab on GitHub
+Rimage simplifies and enhances your image optimization workflows. Optimize images effortlessly, set quality levels, and apply advanced techniques with ease. Ideal for web apps, mobile apps, and desktop software.
-## Usage
+## Features
-```
-rimage -q 75 *.jpg
+- **Flexible Format Conversion**: Supports modern image formats: JPEG, JPEG XL, PNG, AVIF, WebP.
+- **Quality Control**: Fine-tune image quality with an intuitive interface.
+- **Parallel Optimization**: Optimize multiple images in parallel.
+- **Quantization and Dithering**: Advanced control for experts.
+- **Image Resizing**: Easy resizing with the `resize` crate.
+
+## Installation
+
+Add Rimage to your project with Cargo:
+
+```sh
+cargo add rimage
```
-- Quality: `-q 0` through `-q 100`, higher is better **Default: 75**
-- Output format: `-f png`, currently supported only jpg, png, oxipng, avif and webp **Default: jpg**
-- Output directory: `-o /somewhere`, saves all processed files in this directory, also saves files directory structure
- > Note: On windows cmd, if you path contains spaces, please remove backslash before the closing quote.
- > This is because in cmd a backslash before a quote is recognized as an escape character. More info [here](https://stackoverflow.com/a/75849885)
-- Suffix for output: `-s _updated`, adds suffix in file name ("input.jpg" -> "input_updated.jpg")
-- Info: `-i`, flag used to get info about images (size and data length)
-- Threads: `-t 4`, number of threads to use **Default: number of cpu cores**
-- Quantization: `--quantization 50`, quality of quantization from 0 to 100, higher is better
-- Dithering: `--dithering 0.5`, quality of dithering from 0 to 1, higher is better
-- Resize: `--width 250` or `--height 100`, resizes image to specified width or height
-- Filter: `--filter mitchell`, filter used to resizing
-- Logging: `RUST_LOG=trace`, enables logging output, more information see [here](https://docs.rs/env_logger/latest/env_logger/)
-- Quiet mode: `--quiet` disables progress bar
-- More options will be added later
-
-## To-Do
-
-- Support for JPEG XL
+Or add this to your `Cargo.toml`:
-## Changelog
+```toml
+[dependencies]
+rimage = "0.9.0"
+```
-Read changelog [here](CHANGELOG.md)
+## Usage
-## Benchmarks
+### Decoding
-You can see benchmarks and comparison with `@squoosh/cli` [here](./Benchmarks.md)
+```rs
+use rimage::Decoder;
-## Building from source
+let path = std::path::PathBuf::from(/* Your path */);
-For building rimage from source you will need to run this command:
+let decoder = Decoder::from_path(&path)?;
-```
-cargo install rimage
+let image = decoder.decode()?;
+
+// Handle the image...
```
-This app requires cmake, nasm, ninja and meson installed on system
+### Encoding
-> note: On windows use Visual Studio build environment like developer PowerShell for VS 2019
+```rs
+use rimage::{rgb::RGBA8, Encoder, Image, config::{EncoderConfig, Codec}};
-## Development
+let image_data = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+let image = Image::new(image_data, 100, 50);
-Please read the [contribution guide](CONTRIBUTING.md)
+let config = EncoderConfig::new(Codec::MozJpeg).with_quality(80.0).unwrap();
+let file = std::fs::File::create("output.jpg").expect("Failed to create file");
-## License
+let encoder = Encoder::new(file, image).with_config(config);
+
+encoder.encode()?; // Writes image data to the file.
+```
+
+> For full API documentation, visit [docs.rs](https://docs.rs/rimage) page.
-Rimage is licensed under either the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0), or [the MIT license](https://opensource.org/licenses/MIT).
+## Contributing
-All images are taken from [PNGSuite](http://www.schaik.com/pngsuite/)
+Read the [contribution guide](CONTRIBUTING.md) for build instructions and guidelines.
-## Contribute
+## License
+
+Rimage is dual-licensed under [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) and [MIT License](https://opensource.org/licenses/MIT). You can choose either license for your use.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
+
+## Changelog
+
+View the [Changelog](CHANGELOG.md) for version-specific changes.
diff --git a/SECURITY.md b/SECURITY.md
index d2219811..6bf23cde 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -4,6 +4,9 @@
| Version | Supported |
| ------- | ------------------ |
+| 0.8.x | :white_check_mark: |
+| 0.7.x | :white_check_mark: |
+| 0.6.x | :white_check_mark: |
| 0.5.x | :white_check_mark: |
| 0.4.x | :white_check_mark: |
| < 0.4 | :x: |
diff --git a/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-rimage.jpg b/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-rimage.jpg
deleted file mode 100644
index 7446e3ed..00000000
Binary files a/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-rimage.jpg and /dev/null differ
diff --git a/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-squoosh.jpg b/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-squoosh.jpg
deleted file mode 100644
index 7446e3ed..00000000
Binary files a/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash-squoosh.jpg and /dev/null differ
diff --git a/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg b/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg
deleted file mode 100644
index 882781b5..00000000
Binary files a/assets/boliviainteligente-JUtc5Lu4BCQ-unsplash.jpg and /dev/null differ
diff --git a/assets/edwin-splisser-GC7FLmgU5Z4-unsplash-rimage.png b/assets/edwin-splisser-GC7FLmgU5Z4-unsplash-rimage.png
deleted file mode 100644
index 4d3fb655..00000000
Binary files a/assets/edwin-splisser-GC7FLmgU5Z4-unsplash-rimage.png and /dev/null differ
diff --git a/assets/edwin-splisser-GC7FLmgU5Z4-unsplash-squoosh.png b/assets/edwin-splisser-GC7FLmgU5Z4-unsplash-squoosh.png
deleted file mode 100644
index ee909c97..00000000
Binary files a/assets/edwin-splisser-GC7FLmgU5Z4-unsplash-squoosh.png and /dev/null differ
diff --git a/assets/edwin-splisser-GC7FLmgU5Z4-unsplash.jpg b/assets/edwin-splisser-GC7FLmgU5Z4-unsplash.jpg
deleted file mode 100755
index 85953eba..00000000
Binary files a/assets/edwin-splisser-GC7FLmgU5Z4-unsplash.jpg and /dev/null differ
diff --git a/assets/felix-rottmann-n-Ky-79zeXM-unsplash-rimage.webp b/assets/felix-rottmann-n-Ky-79zeXM-unsplash-rimage.webp
deleted file mode 100644
index 2fc548bc..00000000
Binary files a/assets/felix-rottmann-n-Ky-79zeXM-unsplash-rimage.webp and /dev/null differ
diff --git a/assets/felix-rottmann-n-Ky-79zeXM-unsplash-squoosh.webp b/assets/felix-rottmann-n-Ky-79zeXM-unsplash-squoosh.webp
deleted file mode 100644
index 2fc548bc..00000000
Binary files a/assets/felix-rottmann-n-Ky-79zeXM-unsplash-squoosh.webp and /dev/null differ
diff --git a/assets/felix-rottmann-n-Ky-79zeXM-unsplash.jpg b/assets/felix-rottmann-n-Ky-79zeXM-unsplash.jpg
deleted file mode 100755
index 92a53a3a..00000000
Binary files a/assets/felix-rottmann-n-Ky-79zeXM-unsplash.jpg and /dev/null differ
diff --git a/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-rimage.avif b/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-rimage.avif
deleted file mode 100644
index 21386c2a..00000000
Binary files a/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-rimage.avif and /dev/null differ
diff --git a/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-squoosh.avif b/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-squoosh.avif
deleted file mode 100644
index ddc80a4f..00000000
Binary files a/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash-squoosh.avif and /dev/null differ
diff --git a/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg b/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg
deleted file mode 100755
index d80d54ad..00000000
Binary files a/assets/gustavo-zambelli-SIC3k8IMhhA-unsplash.jpg and /dev/null differ
diff --git a/assets/progress_bar.gif b/assets/progress_bar.gif
deleted file mode 100644
index 827d7877..00000000
Binary files a/assets/progress_bar.gif and /dev/null differ
diff --git a/benches/decode_jpg.rs b/benches/decode_jpg.rs
deleted file mode 100644
index 330092ad..00000000
--- a/benches/decode_jpg.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use std::path::Path;
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-#[allow(deprecated)]
-use rimage::Decoder;
-
-#[allow(deprecated)]
-fn bench_decode_jpg(c: &mut Criterion) {
- c.bench_function("Decoder", |b| {
- b.iter(|| {
- Decoder::from_path(black_box(Path::new("tests/files/basi6a08.jpg")))
- .unwrap()
- .decode()
- })
- });
-}
-
-criterion_group!(benches, bench_decode_jpg);
-criterion_main!(benches);
diff --git a/benches/decode_png.rs b/benches/decode_png.rs
deleted file mode 100644
index 2a86c52e..00000000
--- a/benches/decode_png.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use std::path::Path;
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-#[allow(deprecated)]
-use rimage::Decoder;
-
-#[allow(deprecated)]
-fn bench_decode_png(c: &mut Criterion) {
- c.bench_function("Decoder", |b| {
- b.iter(|| {
- Decoder::from_path(black_box(Path::new("tests/files/basi6a08.png")))
- .unwrap()
- .decode()
- })
- });
-}
-
-criterion_group!(benches, bench_decode_png);
-criterion_main!(benches);
diff --git a/benches/decode_webp.rs b/benches/decode_webp.rs
deleted file mode 100644
index cbb9151f..00000000
--- a/benches/decode_webp.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use std::path::Path;
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use rimage::Decoder;
-
-fn bench_decode_webp(c: &mut Criterion) {
- c.bench_function("decode_webp", |b| {
- b.iter(|| {
- Decoder::from_path(black_box(Path::new("tests/files/basi6a08.webp")))
- .unwrap()
- .decode()
- })
- });
-}
-
-criterion_group!(benches, bench_decode_webp);
-criterion_main!(benches);
diff --git a/benches/encode_browser_png.rs b/benches/encode_browser_png.rs
deleted file mode 100644
index 0d6cfb1b..00000000
--- a/benches/encode_browser_png.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use std::{fs, path::Path};
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use rimage::{image, Decoder};
-
-fn bench_encode_png(c: &mut Criterion) {
- let image = Decoder::from_path(black_box(Path::new("tests/files/basi6a08.png")))
- .unwrap()
- .decode()
- .unwrap();
-
- c.bench_function("encode_browser_png", |b| {
- b.iter(|| {
- let data = rimage::Encoder::new(
- black_box(&rimage::Config::builder(image::Codec::Png).build().unwrap()),
- black_box(image.clone()),
- )
- .encode()
- .unwrap();
- fs::write("en.png", data).unwrap();
- })
- });
- fs::remove_file("en.png").unwrap();
-}
-
-criterion_group!(benches, bench_encode_png);
-criterion_main!(benches);
diff --git a/benches/encode_jpg.rs b/benches/encode_jpg.rs
deleted file mode 100644
index d171f6af..00000000
--- a/benches/encode_jpg.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use std::{fs, path::Path};
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-#[allow(deprecated)]
-use rimage::{image, Decoder};
-
-#[allow(deprecated)]
-fn bench_encode_jpg(c: &mut Criterion) {
- let image = Decoder::from_path(black_box(Path::new("tests/files/basi6a08.jpg")))
- .unwrap()
- .decode()
- .unwrap();
-
- c.bench_function("Encoder", |b| {
- b.iter(|| {
- let data = rimage::Encoder::new(
- black_box(
- &rimage::Config::builder(image::Codec::MozJpeg)
- .build()
- .unwrap(),
- ),
- black_box(image.clone()),
- )
- .encode()
- .unwrap();
- fs::write("en.jpg", data).unwrap();
- })
- });
-
- fs::remove_file("en.jpg").unwrap();
-}
-
-criterion_group!(benches, bench_encode_jpg);
-criterion_main!(benches);
diff --git a/benches/encode_png.rs b/benches/encode_png.rs
deleted file mode 100644
index 949953ab..00000000
--- a/benches/encode_png.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use std::{fs, path::Path};
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-#[allow(deprecated)]
-use rimage::{image, Decoder};
-
-#[allow(deprecated)]
-fn bench_encode_png(c: &mut Criterion) {
- let image = Decoder::from_path(black_box(Path::new("tests/files/basi6a08.png")))
- .unwrap()
- .decode()
- .unwrap();
-
- c.bench_function("Encoder", |b| {
- b.iter(|| {
- let data = rimage::Encoder::new(
- black_box(
- &rimage::Config::builder(image::Codec::Oxipng)
- .build()
- .unwrap(),
- ),
- black_box(image.clone()),
- )
- .encode()
- .unwrap();
- fs::write("en.png", data).unwrap();
- })
- });
-
- fs::remove_file("en.png").unwrap();
-}
-
-criterion_group!(benches, bench_encode_png);
-criterion_main!(benches);
diff --git a/benches/encode_webp.rs b/benches/encode_webp.rs
deleted file mode 100644
index 953c8c2b..00000000
--- a/benches/encode_webp.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use std::{fs, path::Path};
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use rimage::{image, Decoder};
-
-fn bench_encode_webp(c: &mut Criterion) {
- let image = Decoder::from_path(black_box(Path::new("tests/files/basi6a08.webp")))
- .unwrap()
- .decode()
- .unwrap();
-
- c.bench_function("encode_webp", |b| {
- b.iter(|| {
- let data = rimage::Encoder::new(
- black_box(&rimage::Config::builder(image::Codec::WebP).build().unwrap()),
- black_box(image.clone()),
- )
- .encode()
- .unwrap();
- fs::write("en.webp", data).unwrap();
- })
- });
- fs::remove_file("en.webp").unwrap();
-}
-
-criterion_group!(benches, bench_encode_webp);
-criterion_main!(benches);
diff --git a/src/config.rs b/src/config.rs
deleted file mode 100644
index d62994a2..00000000
--- a/src/config.rs
+++ /dev/null
@@ -1,306 +0,0 @@
-use crate::{
- error::ConfigError,
- image::{Codec, ResizeType},
-};
-
-/// Config for image encoding
-///
-/// # Example
-/// ```
-/// use rimage::{Config, image::{Codec, ResizeType}};
-///
-/// // Without resize
-/// let config = Config::builder(Codec::MozJpeg).build().unwrap();
-///
-/// // With resize
-/// let mut config_resize = Config::builder(Codec::MozJpeg)
-/// .target_width(200)
-/// .target_height(200)
-/// .resize_type(ResizeType::Lanczos3)
-/// .build()
-/// .unwrap();
-/// ```
-///
-/// # Default
-/// ```
-/// use rimage::{Config, image::Codec};
-///
-/// let config = Config::default();
-/// assert_eq!(config.quality(), 75.0);
-/// assert_eq!(config.output_format(), Codec::MozJpeg);
-/// ```
-#[derive(Debug, Clone)]
-pub struct Config {
- /// Quality of output image
- quality: f32,
- /// Output format of image
- output_format: Codec,
- /// Target width for output image
- target_width: Option,
- /// Target height for output image
- target_height: Option,
- /// Resize type for output image
- resize_type: Option,
- /// Quantization quality of output image
- quantization_quality: Option,
- /// Dithering level for output image
- dithering_level: Option,
-}
-
-pub struct ConfigBuilder {
- /// Quality of output image
- quality: f32,
- /// Output format of image
- output_format: Codec,
- /// Target width for output image
- target_width: Option,
- /// Target height for output image
- target_height: Option,
- /// Resize type for output image
- resize_type: Option,
- /// Quantization quality of output image
- quantization_quality: Option,
- /// Dithering level for output image
- dithering_level: Option,
-}
-
-impl Config {
- /// Create new config
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::{Codec, ResizeType}};
- ///
- /// // Without resize
- /// let config = Config::builder(Codec::MozJpeg).build().unwrap();
- ///
- /// // With resize
- /// let mut config_resize = Config::builder(Codec::MozJpeg)
- /// .target_width(200)
- /// .target_height(200)
- /// .resize_type(ResizeType::Lanczos3)
- /// .build()
- /// .unwrap();
- /// ```
- pub fn builder(output_format: Codec) -> ConfigBuilder {
- ConfigBuilder {
- quality: 75.0,
- output_format,
- target_width: None,
- target_height: None,
- resize_type: None,
- quantization_quality: None,
- dithering_level: None,
- }
- }
-
- /// Get quality
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::Codec};
- ///
- /// let config = Config::builder(Codec::MozJpeg).build().unwrap();
- /// assert_eq!(config.quality(), 75.0);
- /// ```
- #[inline]
- pub fn quality(&self) -> f32 {
- self.quality
- }
-
- /// Get output format
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::Codec};
- ///
- /// let config = Config::builder(Codec::MozJpeg).build().unwrap();
- /// assert_eq!(config.output_format(), Codec::MozJpeg);
- /// ```
- #[inline]
- pub fn output_format(&self) -> Codec {
- self.output_format
- }
-
- /// Get target width
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::Codec};
- ///
- /// let mut config = Config::builder(Codec::MozJpeg)
- /// .target_width(175)
- /// .target_height(175)
- /// .build()
- /// .unwrap();
- ///
- /// assert_eq!(config.target_width(), Some(175));
- /// ```
- #[inline]
- pub fn target_width(&self) -> Option {
- self.target_width
- }
-
- /// Get target height
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::Codec};
- ///
- /// let mut config = Config::builder(Codec::MozJpeg)
- /// .target_width(175)
- /// .target_height(175)
- /// .build()
- /// .unwrap();
- ///
- /// assert_eq!(config.target_height(), Some(175));
- /// ```
- #[inline]
- pub fn target_height(&self) -> Option {
- self.target_height
- }
-
- /// Get target height
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::Codec, image::ResizeType};
- ///
- /// let mut config = Config::builder(Codec::MozJpeg)
- /// .target_width(175)
- /// .target_height(175)
- /// .resize_type(ResizeType::Triangle)
- /// .build()
- /// .unwrap();
- ///
- /// assert_eq!(config.resize_type(), Some(ResizeType::Triangle));
- /// ```
- #[inline]
- pub fn resize_type(&self) -> Option {
- self.resize_type
- }
-
- /// Get target quantization quality
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::Codec};
- ///
- /// let config = Config::builder(Codec::MozJpeg).build().unwrap();
- /// assert_eq!(config.quantization_quality(), None);
- /// ```
- #[inline]
- pub fn quantization_quality(&self) -> Option {
- self.quantization_quality
- }
-
- /// Get target dithering level
- ///
- /// # Example
- /// ```
- /// use rimage::{Config, image::Codec};
- ///
- /// let config = Config::builder(Codec::MozJpeg).build().unwrap();
- /// assert_eq!(config.dithering_level(), None);
- /// ```
- #[inline]
- pub fn dithering_level(&self) -> Option {
- self.dithering_level
- }
-}
-
-impl ConfigBuilder {
- pub fn build(&mut self) -> Result {
- if !(0.0..=100.0).contains(&self.quality) {
- return Err(ConfigError::QualityOutOfBounds(self.quality));
- }
-
- if let Some(width) = self.target_width {
- if width == 0 {
- return Err(ConfigError::WidthIsZero);
- }
- }
-
- if let Some(height) = self.target_height {
- if height == 0 {
- return Err(ConfigError::HeightIsZero);
- }
- }
-
- if let Some(quality) = self.quantization_quality {
- if !(0..=100).contains(&quality) {
- return Err(ConfigError::QuantizationQualityOutOfBounds(quality));
- }
- }
-
- if let Some(dithering_level) = self.dithering_level {
- if !(0.0..=1.0).contains(&dithering_level) {
- return Err(ConfigError::DitheringLevelOutOfBounds(dithering_level));
- }
- }
-
- Ok(Config {
- quality: self.quality,
- output_format: self.output_format,
- target_width: self.target_width,
- target_height: self.target_height,
- resize_type: self.resize_type,
- quantization_quality: self.quantization_quality,
- dithering_level: self.dithering_level,
- })
- }
-
- #[inline]
- pub fn quality(&mut self, quality: f32) -> &mut Self {
- self.quality = quality;
- self
- }
-
- #[inline]
- pub fn target_width(&mut self, width: usize) -> &mut Self {
- self.target_width = Some(width);
- self
- }
-
- #[inline]
- pub fn target_height(&mut self, height: usize) -> &mut Self {
- self.target_height = Some(height);
- self
- }
-
- #[inline]
- pub fn resize_type(&mut self, resize_type: ResizeType) -> &mut Self {
- self.resize_type = Some(resize_type);
- self
- }
-
- #[inline]
- pub fn quantization_quality(&mut self, quality: u8) -> &mut Self {
- self.quantization_quality = Some(quality);
- self
- }
-
- #[inline]
- pub fn dithering_level(&mut self, dithering_level: f32) -> &mut Self {
- self.dithering_level = Some(dithering_level);
- self
- }
-}
-
-impl Default for Config {
- #[inline]
- fn default() -> Self {
- Self {
- quality: 75.0,
- output_format: Codec::MozJpeg,
- target_width: None,
- target_height: None,
- resize_type: Some(ResizeType::Lanczos3),
- quantization_quality: None,
- dithering_level: None,
- }
- }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/src/config/codec.rs b/src/config/codec.rs
new file mode 100644
index 00000000..a2dd522d
--- /dev/null
+++ b/src/config/codec.rs
@@ -0,0 +1,41 @@
+/// Enum representing various image codecs supported by rimage.
+///
+/// This enum defines the codecs that can be used for encoding and decoding images.
+/// Each variant corresponds to a specific image codec, allowing you to specify the
+/// desired codec when working with images.
+///
+/// # Examples
+///
+/// ```
+/// use rimage::config::Codec;
+///
+/// let jpeg_codec = Codec::MozJpeg;
+/// let png_codec = Codec::Png;
+///
+/// // Use the codec with image processing functions.
+/// // ...
+/// ```
+#[derive(Debug, PartialEq, Eq)]
+pub enum Codec {
+ /// Mozilla JPEG codec for image encoding and decoding.
+ MozJpeg,
+
+ /// PNG codec for image encoding and decoding.
+ Png,
+
+ /// JPEG XL codec for image encoding and decoding.
+ #[cfg(feature = "jxl")]
+ JpegXl,
+
+ /// OxiPNG codec for better PNG image optimization.
+ #[cfg(feature = "oxipng")]
+ OxiPng,
+
+ /// WebP codec for image encoding and decoding with WebP format.
+ #[cfg(feature = "webp")]
+ WebP,
+
+ /// AVIF codec for image encoding and decoding.
+ #[cfg(feature = "avif")]
+ Avif,
+}
diff --git a/src/config/encoder_config.rs b/src/config/encoder_config.rs
new file mode 100644
index 00000000..d48197d1
--- /dev/null
+++ b/src/config/encoder_config.rs
@@ -0,0 +1,333 @@
+use crate::error::InvalidEncoderConfig;
+
+use super::codec::Codec;
+
+#[cfg(feature = "quantization")]
+use super::quantization_config::QuantizationConfig;
+#[cfg(feature = "resizing")]
+use super::resize_config::ResizeConfig;
+
+/// Configuration struct for image encoding.
+///
+/// The [`EncoderConfig`] struct allows you to configure various settings for image encoding,
+/// such as quality, codec, quantization, and resizing. You can create an [`EncoderConfig`] instance
+/// using the provided methods, customize it with the desired settings, and use it to control
+/// the image encoding process.
+///
+/// # Examples
+///
+/// Creating a basic [`EncoderConfig`] with default settings:
+///
+/// ```
+/// # use rimage::config::Codec;
+/// use rimage::config::EncoderConfig;
+///
+/// let config = EncoderConfig::default();
+///
+/// ```
+///
+/// Creating a custom [`EncoderConfig`] with specific settings:
+///
+/// ```
+/// use rimage::config::{EncoderConfig, Codec};
+///
+/// let config = EncoderConfig::new(Codec::Png).with_quality(90.0).unwrap();
+/// ```
+#[derive(Debug)]
+pub struct EncoderConfig {
+ /// The quality level for image encoding, ranging from 0.0 to 100.0.
+ quality: f32,
+
+ /// The codec used for image encoding.
+ codec: Codec,
+
+ /// Optional quantization configuration for fine-tuning image compression.
+ #[cfg(feature = "quantization")]
+ quantization: Option,
+
+ /// Optional resizing configuration for adjusting image dimensions.
+ #[cfg(feature = "resizing")]
+ resize: Option,
+}
+
+impl EncoderConfig {
+ /// Creates a new [`EncoderConfig`] with the specified codec and quality level of 75.0.
+ ///
+ /// # Parameters
+ ///
+ /// - `codec`: The codec used for image encoding.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::{EncoderConfig, Codec};
+ ///
+ /// let config = EncoderConfig::new(Codec::Png);
+ /// ```
+ #[inline]
+ pub fn new(codec: Codec) -> Self {
+ Self {
+ quality: 75.0,
+ codec,
+ #[cfg(feature = "quantization")]
+ quantization: None,
+ #[cfg(feature = "resizing")]
+ resize: None,
+ }
+ }
+
+ /// Sets the quality level for image encoding.
+ ///
+ /// # Parameters
+ ///
+ /// - `quality`: The quality level for image encoding, ranging from 0.0 to 100.0.
+ ///
+ /// # Returns
+ ///
+ /// Returns a modified [`EncoderConfig`] with the specified quality level if it's valid.
+ ///
+ /// # Errors
+ ///
+ /// Returns an [`InvalidEncoderConfig`] error if the quality level is out of bounds (not in the range 0-100).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::{EncoderConfig, Codec};
+ ///
+ /// let config = EncoderConfig::new(Codec::MozJpeg).with_quality(90.0).unwrap();
+ /// ```
+ #[inline]
+ pub fn with_quality(mut self, quality: f32) -> Result {
+ if !(0.0..=100.0).contains(&quality) {
+ return Err(InvalidEncoderConfig::QualityOutOfBounds(quality));
+ }
+
+ self.quality = quality;
+ Ok(self)
+ }
+
+ /// Sets the quantization configuration for image encoding.
+ ///
+ /// Quantization is an optional step in image compression that can affect the image's visual quality.
+ /// Use this method to specify the quantization parameters to fine-tune the compression process.
+ ///
+ /// # Parameters
+ ///
+ /// - `quantization`: A [`QuantizationConfig`] struct containing quantization settings.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::{EncoderConfig, Codec, QuantizationConfig};
+ ///
+ /// let quantization_config = QuantizationConfig::new()
+ /// .with_quality(50).unwrap()
+ /// .with_dithering(0.75).unwrap();
+ ///
+ /// let config = EncoderConfig::new(Codec::Png)
+ /// .with_quantization(quantization_config);
+ /// ```
+ #[inline]
+ #[cfg(feature = "quantization")]
+ pub fn with_quantization(mut self, quantization: QuantizationConfig) -> Self {
+ self.quantization = Some(quantization);
+ self
+ }
+
+ /// Sets the resizing configuration for image encoding.
+ ///
+ /// Resizing is an optional step that allows you to adjust the dimensions of the image before encoding.
+ /// Use this method to specify the resizing parameters to customize the image dimensions.
+ ///
+ /// # Parameters
+ ///
+ /// - `resize`: A [`ResizeConfig`] struct containing resizing settings.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::{EncoderConfig, Codec, ResizeConfig};
+ ///
+ /// let resize_config = ResizeConfig::new(resize::Type::Lanczos3)
+ /// .with_width(800)
+ /// .with_height(600);
+ ///
+ /// let config = EncoderConfig::new(Codec::Png)
+ /// .with_resize(resize_config);
+ /// ```
+ #[inline]
+ #[cfg(feature = "resizing")]
+ pub fn with_resize(mut self, resize: ResizeConfig) -> Self {
+ self.resize = Some(resize);
+ self
+ }
+
+ /// Gets the quality setting for image encoding.
+ ///
+ /// # Returns
+ ///
+ /// Returns the quality level as a floating-point value in the range [0.0, 100.0].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::EncoderConfig;
+ ///
+ /// let config = EncoderConfig::default();
+ /// let quality = config.quality();
+ ///
+ /// assert_eq!(quality, 75.0);
+ /// ```
+ #[inline]
+ pub fn quality(&self) -> f32 {
+ self.quality
+ }
+
+ /// Gets the codec used for image encoding.
+ ///
+ /// # Returns
+ ///
+ /// Returns a reference to the codec enum that specifies the image encoding format.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::{EncoderConfig, Codec};
+ ///
+ /// let config = EncoderConfig::new(Codec::MozJpeg);
+ /// let codec = config.codec();
+ ///
+ /// assert_eq!(codec, &Codec::MozJpeg);
+ /// ```
+ #[inline]
+ pub fn codec(&self) -> &Codec {
+ &self.codec
+ }
+
+ /// Gets the quantization configuration for image encoding, if specified.
+ ///
+ /// # Returns
+ ///
+ /// Returns an `Option` containing a reference to the `QuantizationConfig` if quantization
+ /// settings are configured. Returns `None` if quantization is not configured.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::{EncoderConfig, QuantizationConfig};
+ ///
+ /// let quantization = QuantizationConfig::new().with_quality(90).unwrap();
+ /// let config = EncoderConfig::default().with_quantization(quantization);
+ ///
+ /// if let Some(quantization_config) = config.quantization_config() {
+ /// assert_eq!(quantization_config.quality(), 90);
+ /// }
+ /// ```
+ #[inline]
+ #[cfg(feature = "quantization")]
+ pub fn quantization_config(&self) -> Option<&QuantizationConfig> {
+ self.quantization.as_ref()
+ }
+
+ /// Gets the resize configuration for image encoding, if specified.
+ ///
+ /// # Returns
+ ///
+ /// Returns an `Option` containing a reference to the `ResizeConfig` if resize settings are
+ /// configured. Returns `None` if resizing is not configured.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::{EncoderConfig, ResizeConfig};
+ ///
+ /// let resize = ResizeConfig::new(resize::Type::Lanczos3).with_width(800);
+ /// let config = EncoderConfig::default().with_resize(resize);
+ ///
+ /// if let Some(resize_config) = config.resize_config() {
+ /// assert_eq!(resize_config.width(), Some(800));
+ /// }
+ /// ```
+ #[inline]
+ #[cfg(feature = "resizing")]
+ pub fn resize_config(&self) -> Option<&ResizeConfig> {
+ self.resize.as_ref()
+ }
+}
+
+impl Default for EncoderConfig {
+ /// Creates a default [`EncoderConfig`] with a quality of 75.0 and the MozJPEG codec.
+ fn default() -> Self {
+ Self::new(Codec::MozJpeg)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn new_encoder_config() {
+ let config = EncoderConfig::new(Codec::MozJpeg);
+
+ assert_eq!(config.quality(), 75.0);
+ assert_eq!(config.codec(), &Codec::MozJpeg);
+ #[cfg(feature = "quantization")]
+ assert!(config.quantization_config().is_none());
+ #[cfg(feature = "resizing")]
+ assert!(config.resize_config().is_none());
+ }
+
+ #[test]
+ fn configure_quality() {
+ let config = EncoderConfig::new(Codec::MozJpeg)
+ .with_quality(90.0)
+ .unwrap();
+
+ assert_eq!(config.quality(), 90.0);
+
+ // Test invalid quality level
+ let result = EncoderConfig::new(Codec::MozJpeg).with_quality(120.0);
+
+ assert!(result.is_err());
+ assert_eq!(
+ result.unwrap_err().to_string(),
+ "Quality value 120 is out of bounds (0.0-100.0)."
+ )
+ }
+
+ #[test]
+ #[cfg(feature = "quantization")]
+ fn configure_quantization() {
+ let quantization_config = QuantizationConfig::default();
+
+ let config = EncoderConfig::new(Codec::Png).with_quantization(quantization_config);
+
+ assert_eq!(config.quantization_config(), Some(&quantization_config));
+ }
+
+ #[test]
+ #[cfg(feature = "resizing")]
+ fn configure_resize() {
+ let resize_config = ResizeConfig::default();
+
+ let config = EncoderConfig::new(Codec::Png).with_resize(resize_config);
+
+ assert!(config.resize_config().is_some());
+ }
+
+ #[test]
+ fn default_encoder_config() {
+ let config = EncoderConfig::default();
+
+ assert_eq!(config.quality(), 75.0);
+ assert_eq!(config.codec(), &Codec::MozJpeg);
+ #[cfg(feature = "quantization")]
+ assert!(config.quantization_config().is_none());
+ #[cfg(feature = "resizing")]
+ assert!(config.resize_config().is_none());
+ }
+}
diff --git a/src/config/image_format.rs b/src/config/image_format.rs
new file mode 100644
index 00000000..cbb47e60
--- /dev/null
+++ b/src/config/image_format.rs
@@ -0,0 +1,91 @@
+use std::{ffi::OsStr, path::Path};
+
+use crate::error::ImageFormatError;
+
+/// Enum representing supported image formats.
+#[derive(Debug)]
+pub enum ImageFormat {
+ /// JPEG image format.
+ Jpeg,
+ /// PNG image format.
+ Png,
+ /// JPEG XL image format.
+ #[cfg(feature = "jxl")]
+ JpegXl,
+ /// WebP image format.
+ #[cfg(feature = "webp")]
+ WebP,
+ /// AVIF image format.
+ #[cfg(feature = "avif")]
+ Avif,
+}
+
+impl ImageFormat {
+ /// Attempts to create an [`ImageFormat`] variant from a file extension.
+ ///
+ /// # Parameters
+ ///
+ /// - `ext`: The file extension as an [`OsStr`].
+ ///
+ /// # Returns
+ ///
+ /// Returns a [`Result`] with the parsed [`ImageFormat`] on success or an [`ImageFormatError`] on failure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::{config::ImageFormat, error::ImageFormatError};
+ ///
+ /// let ext = "jpg";
+ ///
+ /// match ImageFormat::from_ext(ext) {
+ /// Ok(format) => println!("Image format: {:?}", format),
+ /// Err(err) => eprintln!("Error parsing image format: {:?}", err),
+ /// }
+ /// ```
+ #[inline]
+ pub fn from_ext(ext: impl AsRef) -> Result {
+ Ok(
+ match ext.as_ref().to_str().ok_or(ImageFormatError::Missing)? {
+ "jpg" | "jpeg" => Self::Jpeg,
+ "png" => Self::Png,
+ #[cfg(feature = "jxl")]
+ "jxl" => Self::JpegXl,
+ #[cfg(feature = "webp")]
+ "webp" => Self::WebP,
+ #[cfg(feature = "avif")]
+ "avif" => Self::Avif,
+ ext => return Err(ImageFormatError::Unknown(ext.to_string())),
+ },
+ )
+ }
+
+ /// Attempts to create an [`ImageFormat`] variant from a file path.
+ ///
+ /// # Parameters
+ ///
+ /// - `path`: The file path from which the extension is extracted.
+ ///
+ /// # Returns
+ ///
+ /// Returns a [`Result`] with the parsed [`ImageFormat`] on success or an [`ImageFormatError`] on failure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::{config::ImageFormat, error::ImageFormatError};
+ /// use std::path::Path;
+ ///
+ /// let file_path = Path::new("image.jpg");
+ /// match ImageFormat::from_path(&file_path) {
+ /// Ok(format) => println!("Image format: {:?}", format),
+ /// Err(err) => eprintln!("Error parsing image format: {:?}", err),
+ /// }
+ /// ```
+ #[inline]
+ pub fn from_path(path: &Path) -> Result {
+ path.extension()
+ .map(Self::from_ext)
+ .ok_or(ImageFormatError::Missing)?
+ }
+}
diff --git a/src/config/mod.rs b/src/config/mod.rs
new file mode 100644
index 00000000..fe514f13
--- /dev/null
+++ b/src/config/mod.rs
@@ -0,0 +1,17 @@
+mod codec;
+mod encoder_config;
+mod image_format;
+
+#[cfg(feature = "quantization")]
+mod quantization_config;
+#[cfg(feature = "resizing")]
+mod resize_config;
+
+pub use codec::Codec;
+pub use encoder_config::EncoderConfig;
+pub use image_format::ImageFormat;
+
+#[cfg(feature = "quantization")]
+pub use quantization_config::QuantizationConfig;
+#[cfg(feature = "resizing")]
+pub use resize_config::ResizeConfig;
diff --git a/src/config/quantization_config.rs b/src/config/quantization_config.rs
new file mode 100644
index 00000000..60229cc1
--- /dev/null
+++ b/src/config/quantization_config.rs
@@ -0,0 +1,214 @@
+use crate::error::InvalidQuantizationConfig;
+
+/// Configuration struct for image quantization.
+///
+/// The [`QuantizationConfig`] struct allows you to configure settings related to image quantization,
+/// which is a process used in image compression. It includes parameters for controlling the quality
+/// of quantization and the level of dithering applied to the image during the process.
+///
+/// # Examples
+///
+/// Creating a basic [`QuantizationConfig`] with default settings:
+///
+/// ```
+/// use rimage::config::QuantizationConfig;
+///
+/// let config = QuantizationConfig::default();
+/// ```
+///
+/// Creating a custom [`QuantizationConfig`] with specific settings:
+///
+/// ```
+/// use rimage::config::QuantizationConfig;
+///
+/// let config = QuantizationConfig::new()
+/// .with_quality(90).unwrap()
+/// .with_dithering(0.75).unwrap();
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct QuantizationConfig {
+ /// The quality level for image quantization, ranging from 0 to 100.
+ quality: u8,
+
+ /// The level of dithering applied during quantization, ranging from 0.0 to 1.0.
+ dithering_level: f32,
+}
+
+impl QuantizationConfig {
+ /// Creates a new [`QuantizationConfig`]. (alias for default)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::QuantizationConfig;
+ ///
+ /// let config = QuantizationConfig::new();
+ /// ```
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Sets the quality level for image quantization.
+ ///
+ /// # Parameters
+ ///
+ /// - `quality`: The quality level for image quantization, ranging from 0 to 100.
+ ///
+ /// # Returns
+ ///
+ /// Returns a modified [`QuantizationConfig`] with the specified quality level if it's valid.
+ ///
+ /// # Errors
+ ///
+ /// Returns an [`InvalidQuantizationConfig`] error if the quality level is out of bounds (not in the range 0-100).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::QuantizationConfig;
+ ///
+ /// let config = QuantizationConfig::new().with_quality(90).unwrap();
+ /// ```
+ pub fn with_quality(mut self, quality: u8) -> Result {
+ if quality > 100 {
+ return Err(InvalidQuantizationConfig::QualityOutOfBounds(quality));
+ }
+
+ self.quality = quality;
+ Ok(self)
+ }
+
+ /// Sets the level of dithering for image quantization.
+ ///
+ /// # Parameters
+ ///
+ /// - `dithering`: The level of dithering applied during quantization, typically ranging from 0.0 to 1.0.
+ ///
+ /// # Returns
+ ///
+ /// Returns a modified [`QuantizationConfig`] with the specified dithering level if it's valid.
+ ///
+ /// # Errors
+ ///
+ /// Returns an [`InvalidQuantizationConfig`] error if the dithering level is out of bounds (not in the range 0.0-1.0).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::QuantizationConfig;
+ ///
+ /// let config = QuantizationConfig::new().with_dithering(0.5).unwrap();
+ /// ```
+ pub fn with_dithering(mut self, dithering: f32) -> Result {
+ if !(0.0..=1.0).contains(&dithering) {
+ return Err(InvalidQuantizationConfig::DitheringOutOfBounds(dithering));
+ }
+
+ self.dithering_level = dithering;
+ Ok(self)
+ }
+
+ /// Gets the quality setting for quantization.
+ ///
+ /// # Returns
+ ///
+ /// Returns the quality level as a unsigned byte value in the range [0, 100].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::QuantizationConfig;
+ ///
+ /// let config = QuantizationConfig::default();
+ /// let quality = config.quality();
+ ///
+ /// assert_eq!(quality, 100);
+ /// ```
+ pub fn quality(&self) -> u8 {
+ self.quality
+ }
+
+ /// Gets the dithering level for quantization.
+ ///
+ /// # Returns
+ ///
+ /// Returns the dithering level as a floating-point value in the range [0.0, 1.0].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::config::QuantizationConfig;
+ ///
+ /// let config = QuantizationConfig::default();
+ /// let dithering_level = config.dithering_level();
+ ///
+ /// assert_eq!(dithering_level, 1.0);
+ /// ```
+ pub fn dithering_level(&self) -> f32 {
+ self.dithering_level
+ }
+}
+
+impl Default for QuantizationConfig {
+ /// Creates a default [`QuantizationConfig`] with a quality of 100 and a dithering level of 1.0.
+ fn default() -> Self {
+ Self {
+ quality: 100,
+ dithering_level: 1.0,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ // Import the necessary dependencies from the code
+ use super::*;
+
+ #[test]
+ fn new_quantization_config() {
+ // Test creating a new QuantizationConfig with default settings
+ let config = QuantizationConfig::new();
+
+ assert_eq!(config.quality(), 100);
+ assert_eq!(config.dithering_level(), 1.0);
+ }
+
+ #[test]
+ fn configure_quality() {
+ // Test configuring quality within bounds
+ let config = QuantizationConfig::new().with_quality(90).unwrap();
+ assert_eq!(config.quality(), 90);
+
+ // Test configuring quality out of bounds
+ let result = QuantizationConfig::new().with_quality(120);
+ assert!(result.is_err());
+ assert_eq!(
+ result.unwrap_err().to_string(),
+ "Quality value 120 is out of bounds (0-100)."
+ )
+ }
+
+ #[test]
+ fn configure_dithering() {
+ // Test configuring dithering within bounds
+ let config = QuantizationConfig::new().with_dithering(0.5).unwrap();
+ assert_eq!(config.dithering_level(), 0.5);
+
+ // Test configuring dithering out of bounds
+ let result = QuantizationConfig::new().with_dithering(1.5);
+ assert!(result.is_err());
+ assert_eq!(
+ result.unwrap_err().to_string(),
+ "Dithering level 1.5 is out of bounds (0.0-1.0)."
+ )
+ }
+
+ #[test]
+ fn default_quantization_config() {
+ // Test the default QuantizationConfig
+ let config = QuantizationConfig::default();
+
+ assert_eq!(config.quality(), 100);
+ assert_eq!(config.dithering_level(), 1.0);
+ }
+}
diff --git a/src/config/resize_config.rs b/src/config/resize_config.rs
new file mode 100644
index 00000000..c1d7bb08
--- /dev/null
+++ b/src/config/resize_config.rs
@@ -0,0 +1,207 @@
+use std::fmt::Debug;
+
+/// Configuration struct for image resizing.
+///
+/// The [`ResizeConfig`] struct allows you to configure settings for image resizing, including
+/// options for specifying the width, height, and the type of resizing filter to be applied.
+/// Use this struct to customize how images are resized to meet your application's needs.
+///
+/// # Examples
+///
+/// Creating a basic [`ResizeConfig`] with default settings:
+///
+/// ```
+/// use rimage::config::ResizeConfig;
+///
+/// let config = ResizeConfig::default();
+/// ```
+///
+/// Creating a custom [`ResizeConfig`] with specific settings:
+///
+/// ```
+/// use rimage::resize;
+/// use rimage::config::ResizeConfig;
+///
+/// let config = ResizeConfig::new(resize::Type::Lanczos3)
+/// .with_width(800)
+/// .with_height(600);
+/// ```
+pub struct ResizeConfig {
+ /// The target width for image resizing. `None` if not specified.
+ width: Option,
+ /// The target height for image resizing. `None` if not specified.
+ height: Option,
+ /// The type of resizing filter to be used.
+ filter_type: resize::Type,
+}
+
+impl ResizeConfig {
+ /// Creates a new [`ResizeConfig`] with the specified resizing filter type.
+ ///
+ /// # Parameters
+ ///
+ /// - `filter_type`: The type of resizing filter to be used (e.g., Lanczos3).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::ResizeConfig;
+ ///
+ /// let config = ResizeConfig::new(resize::Type::Lanczos3);
+ /// ```
+ #[inline]
+ pub fn new(filter_type: resize::Type) -> Self {
+ Self {
+ width: None,
+ height: None,
+ filter_type,
+ }
+ }
+
+ /// Specifies the target width for image resizing.
+ ///
+ /// # Parameters
+ ///
+ /// - `width`: The target width for resizing.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::ResizeConfig;
+ ///
+ /// let config = ResizeConfig::new(resize::Type::Lanczos3)
+ /// .with_width(800);
+ /// ```
+ #[inline]
+ pub fn with_width(mut self, width: usize) -> Self {
+ self.width = Some(width);
+ self
+ }
+
+ /// Specifies the target height for image resizing.
+ ///
+ /// # Parameters
+ ///
+ /// - `height`: The target height for resizing.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::ResizeConfig;
+ ///
+ /// let config = ResizeConfig::new(resize::Type::Lanczos3)
+ /// .with_height(600);
+ /// ```
+ #[inline]
+ pub fn with_height(mut self, height: usize) -> Self {
+ self.height = Some(height);
+ self
+ }
+
+ /// Gets the width setting for image resizing, if specified.
+ ///
+ /// # Returns
+ ///
+ /// Returns an [`Option`] containing the width as a [`usize`] if width is configured.
+ /// Returns [`None`] if the width is not configured, indicating that the image width should
+ /// remain unchanged during resizing.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::ResizeConfig;
+ ///
+ /// let config = ResizeConfig::new(resize::Type::Lanczos3).with_width(800);
+ ///
+ /// if let Some(width) = config.width() {
+ /// assert_eq!(width, 800);
+ /// }
+ /// ```
+ #[inline]
+ pub fn width(&self) -> Option {
+ self.width
+ }
+
+ /// Gets the height setting for image resizing, if specified.
+ ///
+ /// # Returns
+ ///
+ /// Returns an [`Option`] containing the height as a [`usize`] if height is configured.
+ /// Returns [`None`] if the height is not configured, indicating that the image height should
+ /// remain unchanged during resizing.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::ResizeConfig;
+ ///
+ /// let config = ResizeConfig::new(resize::Type::Lanczos3).with_height(600);
+ ///
+ /// if let Some(height) = config.height() {
+ /// assert_eq!(height, 600);
+ /// }
+ /// ```
+ #[inline]
+ pub fn height(&self) -> Option {
+ self.height
+ }
+
+ /// Gets the type of image resizing algorithm used, if specified.
+ ///
+ /// # Returns
+ ///
+ /// Returns a reference to the [`resize::Type`] enum that represents the image resizing algorithm.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::resize;
+ /// use rimage::config::ResizeConfig;
+ ///
+ /// let config = ResizeConfig::new(resize::Type::Lanczos3);
+ /// let resize_type = config.filter_type();
+ /// ```
+ pub fn filter_type(&self) -> resize::Type {
+ match &self.filter_type {
+ resize::Type::Point => resize::Type::Point,
+ resize::Type::Triangle => resize::Type::Triangle,
+ resize::Type::Catrom => resize::Type::Catrom,
+ resize::Type::Mitchell => resize::Type::Mitchell,
+ resize::Type::Lanczos3 => resize::Type::Lanczos3,
+ //FIXME: because resize::Type does not implement Copy or Clone we use fallback Lanczos3
+ resize::Type::Custom(_) => resize::Type::Lanczos3,
+ }
+ }
+}
+
+impl Debug for ResizeConfig {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ResizeConfig")
+ .field("width", &self.width)
+ .field("height", &self.height)
+ .field(
+ "filter_type",
+ &match self.filter_type {
+ resize::Type::Point => "point",
+ resize::Type::Triangle => "triangle",
+ resize::Type::Catrom => "catrom",
+ resize::Type::Mitchell => "mitchell",
+ resize::Type::Lanczos3 => "lanczos3",
+ resize::Type::Custom(_) => "custom",
+ },
+ )
+ .finish()
+ }
+}
+
+impl Default for ResizeConfig {
+ /// Creates a default [`ResizeConfig`] with a default resizing filter type (Lanczos3).
+ fn default() -> Self {
+ Self::new(resize::Type::Lanczos3)
+ }
+}
diff --git a/src/config/tests.rs b/src/config/tests.rs
deleted file mode 100644
index bce0f879..00000000
--- a/src/config/tests.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use super::*;
-
-#[test]
-fn quality_edge_cases() {
- let mut conf = Config::builder(Codec::MozJpeg);
-
- assert!(conf.quality(0.0).build().is_ok());
- assert!(conf.quality(50.0).build().is_ok());
- assert!(conf.quality(100.0).build().is_ok());
- assert!(conf.quality(-10.0).build().is_err());
- assert!(conf.quality(110.0).build().is_err());
-}
-
-#[test]
-fn target_width_edge_cases() {
- let mut conf = Config::builder(Codec::MozJpeg);
-
- assert!(conf.target_width(0).build().is_err());
- assert!(conf.target_width(50).build().is_ok());
- assert!(conf.target_width(usize::MAX).build().is_ok());
-}
-
-#[test]
-fn target_height_edge_cases() {
- let mut conf = Config::builder(Codec::MozJpeg);
-
- assert!(conf.target_height(0).build().is_err());
- assert!(conf.target_height(50).build().is_ok());
- assert!(conf.target_height(usize::MAX).build().is_ok());
-}
-
-#[test]
-fn quantization_quality_edge_cases() {
- let mut conf = Config::builder(Codec::MozJpeg);
-
- assert!(conf.quantization_quality(0).build().is_ok());
- assert!(conf.quantization_quality(50).build().is_ok());
- assert!(conf.quantization_quality(100).build().is_ok());
- assert!(conf.quantization_quality(110).build().is_err());
-}
-
-#[test]
-fn dithering_level_edge_cases() {
- let mut conf = Config::builder(Codec::MozJpeg);
-
- assert!(conf.dithering_level(0.0).build().is_ok());
- assert!(conf.dithering_level(0.5).build().is_ok());
- assert!(conf.dithering_level(1.0).build().is_ok());
- assert!(conf.dithering_level(-1.0).build().is_err());
- assert!(conf.dithering_level(1.1).build().is_err());
-}
diff --git a/src/decoder/file_decoder.rs b/src/decoder/file_decoder.rs
deleted file mode 100644
index 7d23bef2..00000000
--- a/src/decoder/file_decoder.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-use std::{fs, io::Read, panic};
-
-use log::info;
-use rgb::{
- alt::{GRAY8, GRAYA8},
- ComponentBytes, RGB8, RGBA8,
-};
-
-use crate::{
- error::DecodingError,
- image::{ImageData, ImageFormat},
-};
-
-use super::Decode;
-
-pub struct FileDecoder {
- format: ImageFormat,
- file: fs::File,
-}
-
-impl FileDecoder {
- #[inline]
- pub fn new(file: fs::File, format: ImageFormat) -> Self {
- FileDecoder { format, file }
- }
-}
-
-impl Decode for FileDecoder {
- fn decode(self) -> Result {
- match self.format {
- ImageFormat::Jpeg => self.decode_jpeg(),
- ImageFormat::Png => self.decode_png(),
- ImageFormat::WebP => self.decode_webp(),
- ImageFormat::Avif => self.decode_avif(),
- }
- }
-
- fn decode_jpeg(self) -> Result {
- info!("Processing jpeg decoding");
- panic::catch_unwind(move || -> Result {
- let d = mozjpeg::Decompress::with_markers(mozjpeg::ALL_MARKERS).from_file(self.file)?;
-
- let mut image = d.rgba()?;
-
- let data: Vec = image
- .read_scanlines()
- .ok_or(DecodingError::Jpeg("Failed to read scanlines".to_string()))?;
-
- info!("JPEG Color space: {:?}", image.color_space());
- info!("Dimensions: {}x{}", image.width(), image.height());
-
- Ok(ImageData::new(
- image.width(),
- image.height(),
- data.as_bytes(),
- ))
- })
- .unwrap_or(Err(DecodingError::Jpeg(
- "Failed to decode jpeg".to_string(),
- )))
- }
-
- fn decode_png(self) -> Result {
- info!("Processing png decoding");
- let mut d = png::Decoder::new(self.file);
- d.set_transformations(png::Transformations::normalize_to_color8());
-
- let mut reader = d.read_info()?;
- let width = reader.info().width;
- let height = reader.info().height;
-
- let buf_size = width as usize * height as usize * 4;
- let mut buf = vec![0; buf_size];
-
- let info = reader.next_frame(&mut buf)?;
-
- info!("PNG Color type: {:?}", info.color_type);
- info!("Dimensions: {}x{}", width, height);
-
- match info.color_type {
- png::ColorType::Grayscale => Self::expand_pixels(&mut buf, |gray: GRAY8| gray.into()),
- png::ColorType::GrayscaleAlpha => Self::expand_pixels(&mut buf, GRAYA8::into),
- png::ColorType::Rgb => Self::expand_pixels(&mut buf, RGB8::into),
- png::ColorType::Rgba => {}
- png::ColorType::Indexed => {
- return Err(DecodingError::Parsing(
- "Indexed color type is not supported".to_string(),
- ))
- }
- }
-
- Ok(ImageData::new(width as usize, height as usize, &buf))
- }
-
- fn decode_webp(mut self) -> Result {
- let metadata = self.file.metadata()?;
- let mut buf = Vec::with_capacity(metadata.len() as usize);
- self.file.read_to_end(&mut buf)?;
- let (width, height, buf) = libwebp::WebPDecodeRGBA(&buf)?;
-
- Ok(ImageData::new(width as usize, height as usize, &buf))
- }
-
- fn decode_avif(mut self) -> Result {
- use libavif_sys::*;
-
- let image = unsafe { avifImageCreateEmpty() };
- let decoder = unsafe { avifDecoderCreate() };
-
- let metadata = self.file.metadata()?;
- let mut buf = Vec::with_capacity(metadata.len() as usize);
- self.file.read_to_end(&mut buf)?;
-
- let decode_result =
- unsafe { avifDecoderReadMemory(decoder, image, buf.as_ptr(), buf.len()) };
- unsafe { avifDecoderDestroy(decoder) };
-
- let mut result = Err(DecodingError::Avif("Failed to decode avif".to_string()));
-
- if decode_result == AVIF_RESULT_OK {
- let mut rgb: avifRGBImage = Default::default();
- unsafe { avifRGBImageSetDefaults(&mut rgb, image) };
- rgb.depth = 8;
-
- unsafe {
- avifRGBImageAllocatePixels(&mut rgb);
- avifImageYUVToRGB(image, &mut rgb);
- };
-
- let pixels = unsafe {
- std::slice::from_raw_parts(rgb.pixels, (rgb.width * rgb.height * 4) as usize)
- };
-
- result = Ok(ImageData::new(
- rgb.width as usize,
- rgb.height as usize,
- pixels,
- ));
-
- unsafe { avifRGBImageFreePixels(&mut rgb) };
- }
-
- unsafe {
- avifImageDestroy(image);
- };
-
- result
- }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/src/decoder/file_decoder/tests.rs b/src/decoder/file_decoder/tests.rs
deleted file mode 100644
index 8f082f88..00000000
--- a/src/decoder/file_decoder/tests.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-use std::{fs, path, str::FromStr};
-
-use crate::{image::ImageData, test_utils::get_files_by_regex};
-
-use super::*;
-
-fn decode_files(files: &[path::PathBuf], callback: F)
-where
- F: Fn(Result),
-{
- files.iter().for_each(|path| {
- println!("{path:?}");
- let file = fs::File::open(path).unwrap();
- let extension = path
- .extension()
- .unwrap_or_default()
- .to_str()
- .unwrap_or_default();
-
- let format = ImageFormat::from_str(extension).unwrap();
-
- let image = FileDecoder::new(file, format).decode();
-
- callback(image)
- });
-}
-
-#[test]
-fn decode_grayscale() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[^t].+0g\d\d((\.png)|(\.jpg))");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_grayscale_alpha() {
- let files = get_files_by_regex(r"^tests/files/[^x].+4a\d\d\.png");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_rgb() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[^t].+2c\d\d((\.png)|(\.jpg))");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_rgb_transparent() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[t].+2c\d\d((\.png)|(\.jpg))");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_rgba() {
- let files = get_files_by_regex(r"^tests/files/[^x].+6a\d\d\.png$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_indexed() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[^t].+3p\d\d\.png$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_indexed_transparent() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[t].+3p\d\d\.png$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_corrupted() {
- let files = get_files_by_regex(r"^tests/files/x.+\d\d\.png$");
-
- decode_files(&files, |image| assert!(image.is_err()));
-}
-
-#[test]
-fn decode_webp() {
- let files = get_files_by_regex(r"^tests/files/.+.webp$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_avif() {
- let files = get_files_by_regex(r"^tests/files/.+.avif$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
diff --git a/src/decoder/memory_decoder.rs b/src/decoder/memory_decoder.rs
deleted file mode 100644
index 0d9bf372..00000000
--- a/src/decoder/memory_decoder.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use std::panic;
-
-use log::info;
-use rgb::{
- alt::{GRAY8, GRAYA8},
- ComponentBytes, RGB8, RGBA8,
-};
-
-use crate::{
- error::DecodingError,
- image::{ImageData, ImageFormat},
-};
-
-use super::Decode;
-
-pub struct MemoryDecoder<'a> {
- data: &'a [u8],
- format: ImageFormat,
-}
-
-impl<'a> MemoryDecoder<'a> {
- #[inline]
- pub fn new(data: &'a [u8], format: ImageFormat) -> Self {
- MemoryDecoder { data, format }
- }
-}
-
-impl<'a> Decode for MemoryDecoder<'a> {
- fn decode(self) -> Result {
- match self.format {
- ImageFormat::Jpeg => self.decode_jpeg(),
- ImageFormat::Png => self.decode_png(),
- ImageFormat::WebP => self.decode_webp(),
- ImageFormat::Avif => self.decode_avif(),
- }
- }
-
- fn decode_jpeg(self) -> Result {
- info!("Processing jpeg decoding");
- panic::catch_unwind(move || -> Result {
- let d = mozjpeg::Decompress::with_markers(mozjpeg::ALL_MARKERS).from_mem(self.data)?;
-
- let mut image = d.rgba()?;
-
- let data: Vec = image
- .read_scanlines()
- .ok_or(DecodingError::Jpeg("Failed to read scanlines".to_string()))?;
-
- info!("JPEG Color space: {:?}", image.color_space());
- info!("Dimensions: {}x{}", image.width(), image.height());
-
- Ok(ImageData::new(
- image.width(),
- image.height(),
- data.as_bytes(),
- ))
- })
- .unwrap_or(Err(DecodingError::Jpeg(
- "Failed to decode jpeg".to_string(),
- )))
- }
-
- fn decode_png(self) -> Result {
- info!("Processing png decoding");
- let mut d = png::Decoder::new(self.data);
- d.set_transformations(png::Transformations::normalize_to_color8());
-
- let mut reader = d.read_info()?;
- let width = reader.info().width;
- let height = reader.info().height;
-
- let buf_size = width as usize * height as usize * 4;
- let mut buf = vec![0; buf_size];
-
- let info = reader.next_frame(&mut buf)?;
-
- info!("PNG Color type: {:?}", info.color_type);
- info!("Dimensions: {}x{}", width, height);
-
- match info.color_type {
- png::ColorType::Grayscale => Self::expand_pixels(&mut buf, |gray: GRAY8| gray.into()),
- png::ColorType::GrayscaleAlpha => Self::expand_pixels(&mut buf, GRAYA8::into),
- png::ColorType::Rgb => Self::expand_pixels(&mut buf, RGB8::into),
- png::ColorType::Rgba => {}
- png::ColorType::Indexed => {
- return Err(DecodingError::Parsing(
- "Indexed color type is not supported".to_string(),
- ))
- }
- }
-
- Ok(ImageData::new(width as usize, height as usize, &buf))
- }
-
- fn decode_webp(self) -> Result {
- let (width, height, buf) = libwebp::WebPDecodeRGBA(self.data)?;
-
- Ok(ImageData::new(width as usize, height as usize, &buf))
- }
-
- fn decode_avif(self) -> Result {
- use libavif_sys::*;
-
- let image = unsafe { avifImageCreateEmpty() };
- let decoder = unsafe { avifDecoderCreate() };
- let decode_result =
- unsafe { avifDecoderReadMemory(decoder, image, self.data.as_ptr(), self.data.len()) };
- unsafe { avifDecoderDestroy(decoder) };
-
- let mut result = Err(DecodingError::Avif("Failed to decode avif".to_string()));
-
- if decode_result == AVIF_RESULT_OK {
- let mut rgb: avifRGBImage = Default::default();
- unsafe { avifRGBImageSetDefaults(&mut rgb, image) };
- rgb.depth = 8;
-
- unsafe {
- avifRGBImageAllocatePixels(&mut rgb);
- avifImageYUVToRGB(image, &mut rgb);
- };
-
- let pixels = unsafe {
- std::slice::from_raw_parts(rgb.pixels, (rgb.width * rgb.height * 4) as usize)
- };
-
- result = Ok(ImageData::new(
- rgb.width as usize,
- rgb.height as usize,
- pixels,
- ));
-
- unsafe { avifRGBImageFreePixels(&mut rgb) };
- }
-
- unsafe {
- avifImageDestroy(image);
- };
-
- result
- }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/src/decoder/memory_decoder/tests.rs b/src/decoder/memory_decoder/tests.rs
deleted file mode 100644
index 9ec058b8..00000000
--- a/src/decoder/memory_decoder/tests.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-use std::{fs, io::Read, path, str::FromStr};
-
-use crate::{image::ImageData, test_utils::get_files_by_regex};
-
-use super::*;
-
-fn decode_files(files: &[path::PathBuf], callback: F)
-where
- F: Fn(Result),
-{
- files.iter().for_each(|path| {
- println!("{path:?}");
- let mut file = fs::File::open(path).unwrap();
- let metadata = file.metadata().unwrap();
- let mut buf = Vec::with_capacity(metadata.len() as usize);
- file.read_to_end(&mut buf).unwrap();
-
- let format = ImageFormat::from_str(path.extension().unwrap().to_str().unwrap()).unwrap();
-
- let image = MemoryDecoder::new(&buf, format).decode();
-
- callback(image)
- });
-}
-
-#[test]
-fn decode_grayscale() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[^t].+0g\d\d((\.png)|(\.jpg))");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_grayscale_alpha() {
- let files = get_files_by_regex(r"^tests/files/[^x].+4a\d\d\.png");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_rgb() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[^t].+2c\d\d((\.png)|(\.jpg))");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_rgb_transparent() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[t].+2c\d\d((\.png)|(\.jpg))");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_rgba() {
- let files = get_files_by_regex(r"^tests/files/[^x].+6a\d\d\.png$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_indexed() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[^t].+3p\d\d\.png$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_indexed_transparent() {
- let files = get_files_by_regex(r"^tests/files/[^x]&[t].+3p\d\d\.png$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_corrupted() {
- let files = get_files_by_regex(r"^tests/files/x.+\d\d\.png$");
-
- decode_files(&files, |image| assert!(image.is_err()));
-}
-
-#[test]
-fn decode_webp() {
- let files = get_files_by_regex(r"^tests/files/.+.webp$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
-
-#[test]
-fn decode_avif() {
- let files = get_files_by_regex(r"^tests/files/.+.avif$");
-
- decode_files(&files, |image| {
- let image = image.unwrap();
-
- assert_ne!(image.data().len(), 0);
- assert_ne!(image.size(), (0, 0));
- });
-}
diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs
index 9ced839f..a983a0bc 100644
--- a/src/decoder/mod.rs
+++ b/src/decoder/mod.rs
@@ -1,124 +1,311 @@
-use std::{fs, path, str::FromStr};
-
-use rgb::{AsPixels, FromSlice, RGBA8};
+use std::{
+ fs::File,
+ io::{BufRead, BufReader},
+ path::Path,
+};
-use crate::{
- error::DecodingError,
- image::{ImageData, ImageFormat},
+use rgb::{
+ alt::{GRAY8, GRAYA8},
+ AsPixels, FromSlice, RGB8, RGBA8,
};
-use self::{file_decoder::FileDecoder, memory_decoder::MemoryDecoder};
+use crate::{config::ImageFormat, error::DecoderError, Image};
-mod file_decoder;
-mod memory_decoder;
+/// Decoder for reading and decoding images from various formats.
+pub struct Decoder {
+ r: R,
+ format: Option,
+ #[cfg(feature = "transform")]
+ fix_orientation: Option,
+}
-pub trait Decode {
- fn expand_pixels(buf: &mut [u8], to_rgba: impl Fn(T) -> RGBA8)
- where
- [u8]: AsPixels + FromSlice,
- {
- assert!(std::mem::size_of::() <= std::mem::size_of::());
- for i in (0..buf.len() / 4).rev() {
- let src_pix = buf.as_pixels()[i];
- buf.as_rgba_mut()[i] = to_rgba(src_pix);
+impl Decoder {
+ /// Creates a new [`Decoder`] with the specified input reader.
+ ///
+ /// # Parameters
+ ///
+ /// - `r`: The input reader implementing [`BufRead`] from which image data will be read.
+ ///
+ /// # Returns
+ ///
+ /// Returns a [`Decoder`] instance.
+ #[inline]
+ pub fn new(r: R) -> Self {
+ Self {
+ r,
+ format: None,
+ #[cfg(feature = "transform")]
+ fix_orientation: None,
}
}
- fn decode(self) -> Result;
-
- fn decode_jpeg(self) -> Result;
- fn decode_png(self) -> Result;
- fn decode_webp(self) -> Result;
- fn decode_avif(self) -> Result;
-}
+ /// Sets the image format for the decoder.
+ ///
+ /// # Parameters
+ ///
+ /// - `format`: The desired image format as an [`ImageFormat`] enum variant.
+ ///
+ /// # Returns
+ ///
+ /// Returns a modified [`Decoder`] with the specified image format.
+ #[inline]
+ pub fn with_format(mut self, format: ImageFormat) -> Self {
+ self.format = Some(format);
+ self
+ }
-/// Used to build Decoder
-///
-/// # Example
-///
-/// ```
-/// # use rimage::Decoder;
-/// let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
-///
-/// let decoder = Decoder::from_path(&path).unwrap();
-/// ```
-pub struct Decoder;
-
-impl Decoder {
- /// Builds Decoder from file path
+ /// Sets the fixed orientation for image decoding.
+ ///
+ /// This method allows you to specify a fixed orientation for decoding images that may have
+ /// incorrect orientation metadata. The specified orientation will be used during the decoding
+ /// process to ensure the image is correctly oriented.
+ ///
+ /// # Parameters
+ ///
+ /// - `orientation`: An integer value representing the fixed orientation for decoding. It should
+ /// be a value between 1 and 8, inclusive. Use this to correct images with incorrect orientation
+ /// metadata.
+ ///
+ /// # Returns
+ ///
+ /// Returns a modified [`Decoder`] instance with the specified fixed orientation.
///
/// # Example
///
- /// ```
- /// # use rimage::Decoder;
- /// let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
+ /// ```no run
+ /// use your_crate::{Decoder, ImageFormat};
///
- /// let decoder = Decoder::from_path(&path).unwrap();
+ /// let decoder = Decoder::new(/* ... */)
+ /// .with_fixed_orientation(6); // Set a fixed orientation for decoding
+ ///
+ /// let decoded_image = decoder.decode().unwrap();
/// ```
+ #[inline]
+ #[cfg(feature = "transform")]
+ pub fn with_fixed_orientation(mut self, orientation: u32) -> Self {
+ self.fix_orientation = Some(orientation);
+ self
+ }
+
+ /// Decodes the image using the specified format and input data.
///
- /// # Errors
+ /// # Returns
///
- /// Can return error if file format is not supported or file failed to open
- pub fn from_path(path: &path::Path) -> Result, DecodingError> {
- let extension = path
- .extension()
- .unwrap_or_default()
- .to_str()
- .unwrap_or_default()
- .to_lowercase();
+ /// Returns a [`Result`] containing the decoded [`Image`] on success or a [`DecoderError`] on failure.
+ pub fn decode(self) -> Result {
+ #[cfg(feature = "transform")]
+ let orientation = self.fix_orientation;
+
+ #[allow(unused_mut)]
+ let mut image = match self.format {
+ Some(ImageFormat::Jpeg) => self.decode_jpeg(),
+ Some(ImageFormat::Png) => self.decode_png(),
+ #[cfg(feature = "avif")]
+ Some(ImageFormat::Avif) => unsafe { self.decode_avif() },
+ #[cfg(feature = "jxl")]
+ Some(ImageFormat::JpegXl) => self.decode_jpegxl(),
+ #[cfg(feature = "webp")]
+ Some(ImageFormat::WebP) => self.decode_webp(),
+ None => Err(DecoderError::Format(
+ crate::error::ImageFormatError::Missing,
+ )),
+ }?;
+
+ #[cfg(feature = "transform")]
+ if let Some(orientation) = orientation {
+ image.fix_orientation(orientation)
+ }
+
+ Ok(image)
+ }
+
+ fn decode_jpeg(self) -> Result {
+ std::panic::catch_unwind(|| -> Result {
+ let decoder =
+ mozjpeg::Decompress::with_markers(mozjpeg::ALL_MARKERS).from_reader(self.r)?;
+
+ let mut image = decoder.rgba()?;
- let format =
- ImageFormat::from_str(&extension).map_err(|_| DecodingError::Format(extension))?;
+ let pixels = image.read_scanlines()?;
- Ok(Self::from_file(fs::File::open(path)?, format))
+ Ok(Image::new(pixels, image.width(), image.height()))
+ })
+ .map_err(|_| DecoderError::MozJpeg)?
}
- /// Builds Decoder from opened file
+ fn decode_png(self) -> Result {
+ let mut decoder = png::Decoder::new(self.r);
+
+ decoder.set_transformations(png::Transformations::normalize_to_color8());
+
+ let mut reader = decoder.read_info()?;
+
+ let mut buf: Vec = vec![0; reader.output_buffer_size()];
+
+ let info = reader.next_frame(&mut buf)?;
+
+ match info.color_type {
+ png::ColorType::Grayscale => Self::expand_pixels(&mut buf, |gray: GRAY8| gray.into()),
+ png::ColorType::GrayscaleAlpha => Self::expand_pixels(&mut buf, GRAYA8::into),
+ png::ColorType::Rgb => Self::expand_pixels(&mut buf, RGB8::into),
+ png::ColorType::Rgba => {}
+ png::ColorType::Indexed => {
+ unreachable!("Indexed color type is expected to be expanded")
+ }
+ };
+
+ Ok(Image::new(
+ buf.as_rgba().to_owned(),
+ info.width as usize,
+ info.height as usize,
+ ))
+ }
+
+ #[cfg(feature = "avif")]
+ unsafe fn decode_avif(mut self) -> Result {
+ use libavif_sys::*;
+
+ let image = avifImageCreateEmpty();
+ let decoder = avifDecoderCreate();
+
+ let mut buf = Vec::new();
+
+ self.r.read_to_end(&mut buf)?;
+
+ let decode_result = avifDecoderReadMemory(decoder, image, buf.as_ptr(), buf.len());
+ avifDecoderDestroy(decoder);
+
+ if decode_result == AVIF_RESULT_OK {
+ let mut rgb = avifRGBImage::default();
+ avifRGBImageSetDefaults(&mut rgb, image);
+
+ rgb.depth = 8;
+
+ avifRGBImageAllocatePixels(&mut rgb);
+ avifImageYUVToRGB(image, &mut rgb);
+
+ let pixels =
+ std::slice::from_raw_parts(rgb.pixels, (rgb.rowBytes * rgb.height) as usize)
+ .as_rgba()
+ .to_owned();
+
+ let result = Image::new(pixels, rgb.width as usize, rgb.height as usize);
+
+ avifRGBImageFreePixels(&mut rgb);
+
+ Ok(result)
+ } else {
+ Err(DecoderError::Avif(decode_result))
+ }
+ }
+
+ #[cfg(feature = "jxl")]
+ fn decode_jpegxl(mut self) -> Result {
+ let runner = jpegxl_rs::ThreadsRunner::default();
+
+ let decoder = jpegxl_rs::decoder_builder()
+ .parallel_runner(&runner)
+ .pixel_format(jpegxl_rs::decode::PixelFormat {
+ num_channels: 4,
+ endianness: jpegxl_rs::Endianness::Native,
+ align: 0,
+ })
+ .build()?;
+
+ let mut buf = Vec::new();
+
+ self.r.read_to_end(&mut buf)?;
+
+ let (info, pixels) = decoder.decode_with::(&buf)?;
+
+ Ok(Image::new(
+ pixels.as_rgba().to_owned(),
+ info.width as usize,
+ info.height as usize,
+ ))
+ }
+
+ #[cfg(feature = "webp")]
+ fn decode_webp(mut self) -> Result {
+ let mut buf = Vec::new();
+
+ self.r.read_to_end(&mut buf)?;
+
+ let decoder = webp::Decoder::new(&buf);
+
+ let mut image = decoder.decode().ok_or(DecoderError::WebP)?;
+
+ if !image.is_alpha() {
+ Self::expand_pixels(&mut image, RGB8::into);
+ }
+
+ Ok(Image::new(
+ image.as_rgba().to_vec(),
+ image.width() as usize,
+ image.height() as usize,
+ ))
+ }
+
+ fn expand_pixels(buf: &mut [u8], to_rgba: impl Fn(T) -> RGBA8)
+ where
+ [u8]: AsPixels + FromSlice,
+ {
+ assert!(std::mem::size_of::() <= std::mem::size_of::());
+ for i in (0..buf.len() / 4).rev() {
+ let src_pix = buf.as_pixels()[i];
+ buf.as_rgba_mut()[i] = to_rgba(src_pix);
+ }
+ }
+}
+
+impl Decoder> {
+ /// Creates a new [`Decoder`] from a file specified by the given path.
///
- /// # Example
+ /// This method opens the file at the specified path, sets up a `BufReader` for reading, and
+ /// determines the image format based on the file extension.
///
- /// ```
- /// # use rimage::{Decoder, image::ImageFormat};
- /// # let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
- /// let file = std::fs::File::open(path).unwrap();
+ /// # Parameters
///
- /// let decoder = Decoder::from_file(file, ImageFormat::Jpeg);
- /// ```
- #[inline]
- pub fn from_file(file: fs::File, format: ImageFormat) -> GenericDecoder {
- GenericDecoder {
- inner: FileDecoder::new(file, format),
- }
- }
- /// Builds Decoder from bytes
+ /// - `path`: The path to the image file to be decoded.
///
- /// # Example
+ /// # Returns
///
- /// ```
- /// # use std::io::Read;
- /// # use rimage::{Decoder, image::ImageFormat};
- /// # let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
- /// # let mut file = std::fs::File::open(path).unwrap();
- /// let metadata = file.metadata().unwrap();
- /// let mut buf = Vec::with_capacity(metadata.len() as usize);
- /// file.read_to_end(&mut buf).unwrap();
- ///
- /// let decoder = Decoder::from_mem(&buf, ImageFormat::Jpeg);
- /// ```
+ /// Returns a [`Result`] containing the initialized [`Decoder`] instance on success or a [`DecoderError`]
+ /// on failure. The [`Decoder`] is ready to decode the image from the specified file.
+ ///
+ /// # Errors
+ ///
+ /// This method may return a [`DecoderError`] if there are issues with opening the file, determining
+ /// the image format, or other I/O-related errors.
#[inline]
- pub fn from_mem(data: &[u8], format: ImageFormat) -> GenericDecoder {
- GenericDecoder {
- inner: MemoryDecoder::new(data, format),
- }
+ pub fn from_path(path: &Path) -> Result {
+ Ok(Self {
+ r: BufReader::new(File::open(path)?),
+ format: Some(ImageFormat::from_path(path)?),
+ #[cfg(feature = "transform")]
+ fix_orientation: Self::get_orientation(path),
+ })
}
-}
-pub struct GenericDecoder {
- inner: T,
-}
+ #[cfg(feature = "transform")]
+ #[allow(unused_variables)]
+ fn get_orientation(path: &Path) -> Option {
+ #[cfg(not(feature = "exif"))]
+ return None;
+
+ #[cfg(feature = "exif")]
+ {
+ let exif_reader = exif::Reader::new();
+ let mut reader = BufReader::new(File::open(path).ok()?);
+
+ let exif = exif_reader.read_from_container(&mut reader).ok()?;
-impl GenericDecoder {
- pub fn decode(self) -> Result {
- self.inner.decode()
+ let orientation = exif.get_field(exif::Tag::Orientation, exif::In::PRIMARY)?;
+
+ orientation.value.get_uint(0)
+ }
}
}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/decoder/tests.rs b/src/decoder/tests.rs
new file mode 100644
index 00000000..fa4a7e41
--- /dev/null
+++ b/src/decoder/tests.rs
@@ -0,0 +1,127 @@
+use std::{error::Error, fs};
+
+use super::*;
+
+#[test]
+#[cfg(feature = "exif")]
+fn fix_orientation() -> Result<(), Box> {
+ let files = fs::read_dir("tests/files/exif")?;
+
+ for entry in files {
+ let entry = entry?;
+ println!("path: {:?}", entry.path());
+
+ assert!(entry.file_type()?.is_file());
+
+ let decoder = Decoder::from_path(&entry.path())?;
+ let image = decoder.decode()?;
+
+ assert_eq!(image.width(), 48);
+ assert_eq!(image.height(), 80);
+ }
+
+ Ok(())
+}
+
+#[test]
+#[cfg(feature = "avif")]
+fn decode_avif() -> Result<(), Box> {
+ let files = fs::read_dir("tests/files/avif")?;
+
+ for entry in files {
+ let entry = entry?;
+ println!("path: {:?}", entry.path());
+
+ assert!(entry.file_type()?.is_file());
+
+ let decoder = Decoder::from_path(&entry.path())?;
+ let image = decoder.decode()?;
+
+ assert_eq!(image.width(), 48);
+ assert_eq!(image.height(), 80);
+ }
+
+ Ok(())
+}
+
+#[test]
+fn decode_jpeg() -> Result<(), Box> {
+ let files = fs::read_dir("tests/files/jpg")?;
+
+ for entry in files {
+ let entry = entry?;
+ println!("path: {:?}", entry.path());
+
+ assert!(entry.file_type()?.is_file());
+
+ let decoder = Decoder::from_path(&entry.path())?;
+ let image = decoder.decode()?;
+
+ assert!(matches!(image.width(), 48 | 80));
+ assert!(matches!(image.height(), 48 | 80));
+ }
+
+ Ok(())
+}
+
+#[test]
+#[cfg(feature = "jxl")]
+fn decode_jpegxl() -> Result<(), Box> {
+ let files = fs::read_dir("tests/files/jxl")?;
+
+ for entry in files {
+ let entry = entry?;
+ println!("path: {:?}", entry.path());
+
+ assert!(entry.file_type()?.is_file());
+
+ let decoder = Decoder::from_path(&entry.path())?;
+ let image = decoder.decode()?;
+
+ assert_eq!(image.width(), 48);
+ assert_eq!(image.height(), 80);
+ }
+
+ Ok(())
+}
+
+#[test]
+fn decode_png() -> Result<(), Box> {
+ let files = fs::read_dir("tests/files/png")?;
+
+ for entry in files {
+ let entry = entry?;
+ println!("path: {:?}", entry.path());
+
+ assert!(entry.file_type()?.is_file());
+
+ let decoder = Decoder::from_path(&entry.path())?;
+ let image = decoder.decode()?;
+
+ assert_eq!(image.width(), 48);
+ assert_eq!(image.height(), 80);
+ }
+
+ Ok(())
+}
+
+#[test]
+#[cfg(feature = "webp")]
+fn decode_webp() -> Result<(), Box> {
+ let files = fs::read_dir("tests/files/webp")?;
+
+ for entry in files {
+ let entry = entry?;
+ println!("path: {:?}", entry.path());
+
+ assert!(entry.file_type()?.is_file());
+
+ let decoder = Decoder::from_path(&entry.path())?;
+ let image = decoder.decode()?;
+
+ assert_eq!(image.width(), 48);
+ assert_eq!(image.height(), 80);
+ }
+
+ Ok(())
+}
diff --git a/src/encoder.rs b/src/encoder.rs
deleted file mode 100644
index d580a647..00000000
--- a/src/encoder.rs
+++ /dev/null
@@ -1,325 +0,0 @@
-use std::panic;
-
-use log::info;
-use rgb::{AsPixels, ComponentBytes, FromSlice, RGBA};
-
-use crate::{
- error::EncodingError,
- image::{Codec, ImageData, ResizeType},
- Config,
-};
-
-/// Encoder for images
-///
-/// # Example
-/// ```
-/// # use rimage::{Encoder, Config, image::{ImageData, Codec}};
-/// # let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
-/// # let decoder = rimage::Decoder::from_path(&path).unwrap();
-/// # let image = decoder.decode().unwrap();
-/// let config = Config::default();
-///
-/// // image is ImageData
-/// let encoder = Encoder::new(&config, image);
-/// let result = encoder.encode();
-/// assert!(result.is_ok());
-/// ```
-pub struct Encoder<'a> {
- image_data: ImageData,
- config: &'a Config,
-}
-
-impl<'a> Encoder<'a> {
- /// Create new encoder
- ///
- /// # Example
- /// ```
- /// # use rimage::{Encoder, Config, image::{ImageData, Codec}};
- /// # let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
- /// # let decoder = rimage::Decoder::from_path(&path).unwrap();
- /// # let image = decoder.decode().unwrap();
- /// let config = Config::default();
- /// let encoder = Encoder::new(&config, image); // where image is ImageData
- /// ```
- pub fn new(conf: &'a Config, image_data: ImageData) -> Self {
- Encoder {
- image_data,
- config: conf,
- }
- }
- /// Encode image
- ///
- /// # Example
- /// ```
- /// # use rimage::{Encoder, Config, image::{ImageData, Codec}};
- /// # let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
- /// # let decoder = rimage::Decoder::from_path(&path).unwrap();
- /// # let image = decoder.decode().unwrap();
- /// let config = Config::default();
- /// let encoder = Encoder::new(&config, image); // where image is ImageData
- /// let result = encoder.encode();
- /// assert!(result.is_ok());
- /// ```
- ///
- /// # Errors
- ///
- /// Returns [`EncodingError`] if encoding failed
- pub fn encode(mut self) -> Result, EncodingError> {
- if self.config.target_height().is_some() || self.config.target_width().is_some() {
- info!("Resizing image");
- self.resize()?;
- }
-
- if self.config.quantization_quality().is_some() || self.config.dithering_level().is_some() {
- let quantization = self.config.quantization_quality().unwrap_or(100);
- let dithering = self.config.dithering_level().unwrap_or(1.0);
-
- info!(
- "Quantizing to {} with dithering {}",
- quantization, dithering
- );
-
- self.quantize(quantization, dithering)?;
- }
-
- match self.config.output_format() {
- Codec::Png => self.encode_png(),
- Codec::Oxipng => self.encode_oxipng(),
- Codec::MozJpeg => self.encode_mozjpeg(),
- Codec::WebP => self.encode_webp(),
- Codec::Avif => self.encode_avif(),
- }
- }
-
- /// Encode image with quantization
- ///
- /// # Example
- /// ```
- /// # use rimage::{Encoder, Config, image::{ImageData, Codec}};
- /// # let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
- /// # let decoder = rimage::Decoder::from_path(&path).unwrap();
- /// # let image = decoder.decode().unwrap();
- /// let config = Config::default();
- /// let encoder = Encoder::new(&config, image); // where image is ImageData
- /// let result = encoder.encode_quantized(75, 1.0);
- /// assert!(result.is_ok());
- /// ```
- ///
- /// # Errors
- ///
- /// Returns [`EncodingError`] if encoding failed
- pub fn encode_quantized(
- mut self,
- quantization_quality: u8,
- dithering_level: f32,
- ) -> Result, EncodingError> {
- if self.config.target_height().is_some() || self.config.target_width().is_some() {
- self.resize()?;
- }
-
- self.quantize(quantization_quality, dithering_level)?;
-
- match self.config.output_format() {
- Codec::Png => self.encode_png(),
- Codec::Oxipng => self.encode_oxipng(),
- Codec::MozJpeg => self.encode_mozjpeg(),
- Codec::WebP => self.encode_webp(),
- Codec::Avif => self.encode_avif(),
- }
- }
-
- fn resize(&mut self) -> Result<(), EncodingError> {
- let (width, height) = self.image_data.size();
- let aspect_ratio = width as f32 / height as f32;
- info!("Aspect ratio: {}", aspect_ratio);
- info!("Original size: {}x{}", width, height);
-
- // If target width or height is not set, calculate it from the other
- // or use the original size
- let target_width = self.config.target_width().unwrap_or(
- self.config
- .target_height()
- .map(|h| (h as f32 * aspect_ratio) as usize)
- .unwrap_or(width),
- );
- let target_height = self.config.target_height().unwrap_or(
- self.config
- .target_width()
- .map(|w| (w as f32 / aspect_ratio) as usize)
- .unwrap_or(height),
- );
-
- info!("Target size: {}x{}", target_width, target_height);
- info!(
- "Resize type: {:?}",
- self.config
- .resize_type()
- .as_ref()
- .unwrap_or(&ResizeType::Lanczos3)
- );
-
- let mut dest = vec![RGBA::new(0, 0, 0, 0); target_width * target_height];
-
- let mut resizer = resize::new(
- width,
- height,
- target_width,
- target_height,
- resize::Pixel::RGBA8,
- resize::Type::from(
- self.config
- .resize_type()
- .as_ref()
- .unwrap_or(&ResizeType::Lanczos3),
- ),
- )?;
-
- resizer.resize(self.image_data.data().as_rgba(), &mut dest)?;
-
- self.image_data = ImageData::new(target_width, target_height, dest.as_bytes());
-
- Ok(())
- }
-
- fn quantize(
- &mut self,
- quantization_quality: u8,
- dithering_level: f32,
- ) -> Result<(), EncodingError> {
- let mut liq = imagequant::new();
-
- liq.set_speed(5)?;
- liq.set_quality(0, quantization_quality)?;
-
- let mut img = liq.new_image(
- self.image_data.data().as_pixels(),
- self.image_data.size().0,
- self.image_data.size().1,
- 0.0,
- )?;
-
- let mut res = liq.quantize(&mut img)?;
-
- res.set_dithering_level(dithering_level)?;
-
- let (palette, pixels) = res.remapped(&mut img)?;
-
- let mut data = Vec::with_capacity(pixels.len() * 4);
-
- pixels.iter().for_each(|pix| {
- let color = palette[*pix as usize];
- data.extend_from_slice(&[color.r, color.g, color.b, color.a]);
- });
-
- self.image_data = ImageData::new(self.image_data.size().0, self.image_data.size().1, &data);
-
- Ok(())
- }
-
- fn encode_mozjpeg(self) -> Result, EncodingError> {
- info!("Encoding with mozjpeg");
- panic::catch_unwind(|| -> Result, EncodingError> {
- let mut encoder = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBA);
-
- encoder.set_size(self.image_data.size().0, self.image_data.size().1);
- encoder.set_quality(self.config.quality());
- encoder.set_progressive_mode();
- encoder.set_mem_dest();
- encoder.start_compress();
- encoder.write_scanlines(self.image_data.data());
- encoder.finish_compress();
-
- encoder
- .data_to_vec()
- .map_err(|_| EncodingError::Jpeg("Failed to convert data to vec".to_string()))
- })
- .unwrap_or_else(|e| {
- Err(EncodingError::Jpeg(format!(
- "Failed to decode jpeg: {:?}",
- e
- )))
- })
- }
-
- fn encode_png(&self) -> Result, EncodingError> {
- info!("Encoding PNG");
- let mut buf = Vec::new();
-
- {
- let mut encoder = png::Encoder::new(
- &mut buf,
- self.image_data.size().0 as u32,
- self.image_data.size().1 as u32,
- );
-
- encoder.set_color(png::ColorType::Rgba);
- encoder.set_depth(png::BitDepth::Eight);
-
- let mut writer = encoder.write_header()?;
- writer.write_image_data(self.image_data.data())?;
- writer.finish()?;
- }
-
- info!("Encoded {} bytes", buf.len());
-
- Ok(buf)
- }
-
- fn encode_oxipng(&self) -> Result, EncodingError> {
- info!("Encoding with OxiPNG");
- let mut buf = Vec::new();
-
- {
- let mut encoder = png::Encoder::new(
- &mut buf,
- self.image_data.size().0 as u32,
- self.image_data.size().1 as u32,
- );
-
- encoder.set_color(png::ColorType::Rgba);
- encoder.set_depth(png::BitDepth::Eight);
-
- let mut writer = encoder.write_header()?;
- writer.write_image_data(self.image_data.data())?;
- writer.finish()?;
- }
-
- info!("Encoded {} bytes (Not optimized)", buf.len());
-
- Ok(oxipng::optimize_from_memory(
- &buf,
- &oxipng::Options::default(),
- )?)
- }
-
- fn encode_webp(&self) -> Result, EncodingError> {
- info!("Encoding with WebP");
- let (width, height) = self.image_data.size();
-
- let data = libwebp::WebPEncodeRGBA(
- self.image_data.data(),
- width as u32,
- height as u32,
- (width * 4) as u32,
- self.config.quality(),
- )?;
-
- Ok(data.to_owned())
- }
-
- fn encode_avif(&self) -> Result, EncodingError> {
- info!("Encoding with AVIF");
-
- let (width, height) = self.image_data.size();
- let data = ravif::Img::new(self.image_data.data().as_rgba(), width, height);
-
- Ok(ravif::Encoder::new()
- .with_quality(self.config.quality())
- .with_speed(6)
- .encode_rgba(data)?
- .avif_file)
- }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs
new file mode 100644
index 00000000..7290185b
--- /dev/null
+++ b/src/encoder/mod.rs
@@ -0,0 +1,246 @@
+use std::io::Write;
+
+use rgb::ComponentBytes;
+
+use crate::{config::EncoderConfig, error::EncoderError, image::Image};
+
+/// A struct for encoding images using various codecs.
+pub struct Encoder {
+ w: W,
+ data: Image,
+ conf: EncoderConfig,
+}
+
+impl Encoder {
+ /// Creates a new [`Encoder`] instance with the specified writer and image data.
+ ///
+ /// # Parameters
+ ///
+ /// - `w`: The writer to which the encoded image will be written.
+ /// - `data`: The image data to be encoded.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use rimage::rgb::RGBA8;
+ /// use rimage::{Encoder, Image};
+ /// # use std::fs::File;
+ /// # let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ ///
+ /// let image = Image::new(image_data, 800, 600);
+ ///
+ /// let file = File::create("output.jpg").expect("Failed to create file");
+ ///
+ /// let encoder = Encoder::new(file, image); // uses default config
+ /// ```
+ #[inline]
+ pub fn new(w: W, data: Image) -> Self {
+ Self {
+ w,
+ data,
+ conf: EncoderConfig::default(),
+ }
+ }
+
+ /// Configures the encoder with the specified [`EncoderConfig`].
+ ///
+ /// # Parameters
+ ///
+ /// - `conf`: The configuration to use for encoding.
+ ///
+ /// # Returns
+ ///
+ /// Returns a modified [`Encoder`] instance with the updated configuration.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use rimage::rgb::RGBA8;
+ /// use rimage::{Encoder, Image, config::{EncoderConfig, Codec}};
+ /// # use std::fs::File;
+ /// # use std::fs;
+ /// # let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ ///
+ /// let image = Image::new(image_data, 800, 600);
+ ///
+ /// let file = File::create("output.png").expect("Failed to create file");
+ ///
+ /// let config = EncoderConfig::new(Codec::Png)
+ /// .with_quality(90.0)
+ /// .unwrap();
+ ///
+ /// let encoder = Encoder::new(file, image).with_config(config);
+ ///
+ /// # fs::remove_file("output.png").unwrap_or(());
+ /// # Ok::<(), rimage::error::EncoderError>(())
+ /// ```
+ #[inline]
+ pub fn with_config(mut self, conf: EncoderConfig) -> Self {
+ self.conf = conf;
+ self
+ }
+
+ /// Encodes the image using the configured settings.
+ ///
+ /// # Returns
+ ///
+ /// Returns `Ok(())` on successful encoding or an [`EncoderError`] on failure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::rgb::RGBA8;
+ /// use rimage::{Encoder, Image, config::EncoderConfig};
+ /// # use std::fs;
+ /// use std::fs::File;
+ ///
+ /// let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ /// let image = Image::new(image_data, 800, 600);
+ ///
+ /// let file = File::create("output.jpg").expect("Failed to create file");
+ ///
+ /// let config = EncoderConfig::default();
+ ///
+ /// let encoder = Encoder::new(file, image).with_config(config);
+ ///
+ /// encoder.encode()?;
+ /// # fs::remove_file("output.jpg")?;
+ /// # Ok::<(), rimage::error::EncoderError>(())
+ /// ```
+ #[allow(unused_mut)]
+ pub fn encode(mut self) -> Result<(), EncoderError> {
+ #[cfg(feature = "resizing")]
+ if let Some(resize_config) = self.conf.resize_config() {
+ self.data.resize(resize_config)?;
+ }
+
+ #[cfg(feature = "quantization")]
+ if let Some(quantization_config) = self.conf.quantization_config() {
+ self.data.quantize(quantization_config)?;
+ }
+
+ match self.conf.codec() {
+ crate::config::Codec::MozJpeg => self.encode_mozjpeg(),
+ crate::config::Codec::Png => self.encode_png(),
+ #[cfg(feature = "jxl")]
+ crate::config::Codec::JpegXl => self.encode_jpegxl(),
+ #[cfg(feature = "oxipng")]
+ crate::config::Codec::OxiPng => self.encode_oxipng(),
+ #[cfg(feature = "webp")]
+ crate::config::Codec::WebP => self.encode_webp(),
+ #[cfg(feature = "avif")]
+ crate::config::Codec::Avif => self.encode_avif(),
+ }
+ }
+
+ fn encode_mozjpeg(self) -> Result<(), EncoderError> {
+ let width = self.data.width();
+ let height = self.data.height();
+ let quality = self.conf.quality();
+
+ std::panic::catch_unwind(|| -> Result<(), EncoderError> {
+ let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBA);
+
+ comp.set_size(width, height);
+ comp.set_quality(quality);
+ comp.set_progressive_mode();
+
+ let mut comp = comp.start_compress(self.w)?;
+
+ comp.write_scanlines(self.data.data().as_bytes())?;
+
+ comp.finish()?;
+
+ Ok(())
+ })
+ .map_err(|_| EncoderError::General)?
+ }
+
+ fn encode_png(self) -> Result<(), EncoderError> {
+ let width: u32 = self.data.width().try_into()?;
+ let height: u32 = self.data.height().try_into()?;
+
+ let mut encoder = png::Encoder::new(self.w, width, height);
+
+ encoder.set_color(png::ColorType::Rgba);
+ encoder.set_depth(png::BitDepth::Eight);
+
+ let mut writer = encoder.write_header()?;
+
+ writer.write_image_data(self.data.data().as_bytes())?;
+ writer.finish()?;
+
+ Ok(())
+ }
+
+ #[cfg(feature = "jxl")]
+ fn encode_jpegxl(mut self) -> Result<(), EncoderError> {
+ let mut encoder = jpegxl_rs::encoder_builder()
+ .quality(self.conf.quality())
+ .speed(jpegxl_rs::encode::EncoderSpeed::Falcon)
+ .build()?;
+
+ let buf: jpegxl_rs::encode::EncoderResult = encoder.encode(
+ self.data.data().as_bytes(),
+ self.data.width().try_into()?,
+ self.data.height().try_into()?,
+ )?;
+
+ self.w.write_all(&buf)?;
+ self.w.flush()?;
+
+ Ok(())
+ }
+
+ #[cfg(feature = "oxipng")]
+ fn encode_oxipng(mut self) -> Result<(), EncoderError> {
+ let width: u32 = self.data.width().try_into()?;
+ let height: u32 = self.data.height().try_into()?;
+
+ let options = oxipng::Options::default();
+
+ let img = oxipng::RawImage::new(
+ width,
+ height,
+ oxipng::ColorType::RGBA,
+ oxipng::BitDepth::Eight,
+ self.data.data().as_bytes().to_vec(),
+ )?;
+
+ self.w.write_all(&img.create_optimized_png(&options)?)?;
+
+ Ok(())
+ }
+
+ #[cfg(feature = "webp")]
+ fn encode_webp(mut self) -> Result<(), EncoderError> {
+ let width: u32 = self.data.width().try_into()?;
+ let height: u32 = self.data.height().try_into()?;
+
+ let encoder = webp::Encoder::from_rgba(self.data.data().as_bytes(), width, height);
+
+ self.w.write_all(&encoder.encode(self.conf.quality()))?;
+ self.w.flush()?;
+
+ Ok(())
+ }
+
+ #[cfg(feature = "avif")]
+ fn encode_avif(mut self) -> Result<(), EncoderError> {
+ let width = self.data.width();
+ let height = self.data.height();
+
+ let img = ravif::Encoder::new()
+ .with_quality(self.conf.quality())
+ .with_speed(4)
+ .encode_rgba(ravif::Img::new(self.data.data(), width, height))?;
+
+ self.w.write_all(&img.avif_file)?;
+ self.w.flush()?;
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/encoder/tests.rs b/src/encoder/tests.rs
index 4bc58b53..3de5bf93 100644
--- a/src/encoder/tests.rs
+++ b/src/encoder/tests.rs
@@ -1,129 +1,43 @@
-use std::path;
+use std::io::Cursor;
-use once_cell::sync::Lazy;
-
-use crate::{test_utils::get_files_by_regex, Decoder};
+use rgb::RGBA8;
+// Import the necessary dependencies from the code
use super::*;
-
-static FILES: Lazy> =
- Lazy::new(|| get_files_by_regex(r"^tests/files/[^x].+\.png"));
-
-fn encode_files(files: &[path::PathBuf], conf: &Config, callback: F)
-where
- F: Fn(Result, EncodingError>),
-{
- files.iter().for_each(|path| {
- println!("{path:?}");
- let image = Decoder::from_path(path).unwrap().decode().unwrap();
-
- let encoder = Encoder::new(conf, image);
- let result = encoder.encode();
-
- callback(result);
- })
-}
-
-#[test]
-fn encode_jpeg() {
- let conf = Config::builder(Codec::MozJpeg).build().unwrap();
-
- encode_files(&FILES, &conf, |result| {
- assert!(result.is_ok());
- let result = result.unwrap();
- assert!(!result.is_empty());
- });
-}
-
-#[test]
-fn encode_png() {
- let conf = Config::builder(Codec::Png).build().unwrap();
-
- encode_files(&FILES, &conf, |result| {
- assert!(result.is_ok());
- let result = result.unwrap();
- assert!(!result.is_empty());
- });
-}
-
-#[test]
-fn encode_oxipng() {
- let conf = Config::builder(Codec::Oxipng).build().unwrap();
-
- encode_files(&FILES, &conf, |result| {
- assert!(result.is_ok());
- let result = result.unwrap();
- assert!(!result.is_empty());
- });
-}
+use crate::config::Codec;
#[test]
-fn encode_webp() {
- let conf = Config::builder(Codec::WebP).build().unwrap();
+fn encoder_new() {
+ // Create a mock image and writer
+ let image_data = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+ let image = Image::new(image_data.clone(), 100, 50);
+ let writer = Cursor::new(Vec::new());
- encode_files(&FILES, &conf, |result| {
- assert!(result.is_ok());
- let result = result.unwrap();
- assert!(!result.is_empty());
- });
-}
-
-#[test]
-fn encode_avif() {
- let conf = Config::builder(Codec::Avif).build().unwrap();
-
- encode_files(&FILES, &conf, |result| {
- assert!(result.is_ok());
- let result = result.unwrap();
- assert!(!result.is_empty());
- });
-}
-
-#[test]
-fn encode_quantized() {
- let path = path::PathBuf::from("tests/files/basi2c08.png");
-
- let image = Decoder::from_path(&path).unwrap().decode().unwrap();
-
- let conf = Config::builder(Codec::Oxipng).build().unwrap();
+ // Create an Encoder with default config
+ let encoder = Encoder::new(writer.clone(), image);
- let encoder = Encoder::new(&conf, image);
- let result = encoder.encode_quantized(50, 1.0);
-
- assert!(result.is_ok());
- let result = result.unwrap();
- assert!(!result.is_empty());
+ // Verify that the Encoder was created with the correct properties
+ assert_eq!(encoder.w, writer);
+ assert_eq!(encoder.data.data(), &[RGBA8::new(0, 0, 0, 0); 100 * 50]);
+ assert_eq!(encoder.conf.codec(), &Codec::MozJpeg);
}
#[test]
-fn encode_quantized_out_of_bounds() {
- let path = path::PathBuf::from("tests/files/basi2c08.png");
-
- let image = Decoder::from_path(&path).unwrap().decode().unwrap();
-
- let conf = Config::builder(Codec::Oxipng).build().unwrap();
-
- let encoder = Encoder::new(&conf, image);
- let result = encoder.encode_quantized(120, 1.0);
- assert!(result.is_err());
-}
+fn encoder_with_config() {
+ // Create a mock image and writer
+ let image_data = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+ let image = Image::new(image_data.clone(), 100, 50);
+ let writer = Cursor::new(Vec::new());
-#[test]
-fn resize_image() {
- let data = [255; 100 * 100 * 4];
- let image = ImageData::new(100, 100, &data);
-
- let conf = Config::builder(Codec::Oxipng)
- .target_height(50)
- .target_width(50)
- .build()
+ // Create an Encoder with a custom config
+ let config = EncoderConfig::new(Codec::MozJpeg)
+ .with_quality(90.0)
.unwrap();
+ let encoder = Encoder::new(writer.clone(), image).with_config(config);
- let mut encoder = Encoder::new(&conf, image);
-
- let result = encoder.resize();
-
- assert!(result.is_ok());
- assert_eq!(encoder.image_data.size(), (50, 50));
- assert!(encoder.image_data.data().len() < 100 * 100 * 4);
+ // Verify that the Encoder was created with the correct properties
+ assert_eq!(encoder.w, writer);
+ assert_eq!(encoder.data.data(), &[RGBA8::new(0, 0, 0, 0); 100 * 50]);
+ assert_eq!(encoder.conf.codec(), &Codec::MozJpeg);
+ assert_eq!(encoder.conf.quality(), 90.0);
}
diff --git a/src/error.rs b/src/error.rs
index fa247f5d..7ba30d37 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,78 +1,179 @@
-use libwebp::error::WebPSimpleError;
use std::io;
+
use thiserror::Error;
-/// An error that occurred if configuration is invalid
+/// Error type for invalid quantization configuration.
+///
+/// This error is returned when the input values for [`QuantizationConfig`] are out of the valid range.
+#[derive(Error, Debug)]
+pub enum InvalidQuantizationConfig {
+ /// Error indicating that the quality value is out of bounds.
+ #[error("Quality value {0} is out of bounds (0-100).")]
+ QualityOutOfBounds(u8),
+
+ /// Error indicating that the dithering level is out of bounds.
+ #[error("Dithering level {0} is out of bounds (0.0-1.0).")]
+ DitheringOutOfBounds(f32),
+}
+
+/// Error type for invalid encoder configuration.
+///
+/// This error is returned when the input values for [`EncoderConfig`] are out of the valid range.
#[derive(Error, Debug)]
-pub enum ConfigError {
- /// Quality is less than 0 or greater than 100
- #[error("Quality {0} is out of range from 0 to 100")]
+pub enum InvalidEncoderConfig {
+ /// Error indicating that the quality value is out of bounds.
+ #[error("Quality value {0} is out of bounds (0.0-100.0).")]
QualityOutOfBounds(f32),
- /// Width is 0
- #[error("Width cannot be zero")]
- WidthIsZero,
- /// Height is 0
- #[error("Height cannot be zero")]
- HeightIsZero,
- /// Quantization quality is less than 0 or greater than 100
- #[error("Quantization quality {0} is out of range from 0 to 100")]
- QuantizationQualityOutOfBounds(u8),
- /// Dithering level is less than 0 or greater than 1.0
- #[error("Dithering level {0} is out of range from 0 to 1.0")]
- DitheringLevelOutOfBounds(f32),
}
-/// An error that occurred during decoding a image
+/// Enum representing errors related to parsing image formats.
#[derive(Error, Debug)]
-pub enum DecodingError {
- /// A [`io::Error`] if file failed to read, find, etc.
- #[error(transparent)]
- IO(#[from] io::Error),
- /// The format of file is not supported
- #[error("{0} is not supported")]
- Format(String),
- /// A parsing error, color type is not supported, failed to read extension etc.
- #[error("{0}")]
- Parsing(String),
- /// A Jpeg decoding error
- #[error("{0}")]
- Jpeg(String),
- /// A Png decoding error
- #[error(transparent)]
- Png(#[from] png::DecodingError),
- /// A Webp decoding error
- #[error(transparent)]
- Webp(#[from] WebPSimpleError),
- /// A Avif decoding error
- #[error("{0}")]
- Avif(String),
+pub enum ImageFormatError {
+ /// Error indicating an unknown or unsupported image format.
+ #[error("Unknown image format: {0}")]
+ Unknown(String),
+
+ /// Error indicating that the image format is missing or could not be determined.
+ #[error("Can't find image format")]
+ Missing,
}
-/// An error that occurred during encoding a image
+/// Enum representing various error types that can occur during image encoding.
#[derive(Error, Debug)]
-pub enum EncodingError {
- /// A [`io::Error`] if file failed to write, find, etc.
+pub enum EncoderError {
+ /// Error indicating an I/O (input/output) operation failure.
#[error(transparent)]
- IO(#[from] io::Error),
- /// A error that occurred during image resize
+ Io(#[from] std::io::Error),
+
+ /// Error indicating an overflow or conversion error.
#[error(transparent)]
+ Overflow(#[from] std::num::TryFromIntError),
+
+ /// Error indicating a resizing operation failure.
+ #[error(transparent)]
+ #[cfg(feature = "resizing")]
Resize(#[from] resize::Error),
- /// A error that occurred during image quantization
+
+ /// Error indicating a quantization operation failure.
#[error(transparent)]
+ #[cfg(feature = "quantization")]
Quantization(#[from] imagequant::Error),
- /// A Jpeg encoding error
- #[error("{0}")]
- Jpeg(String),
- /// A Png encoding error
+
+ /// Error indicating an encoding failure for the PNG format.
#[error(transparent)]
Png(#[from] png::EncodingError),
- /// A OxiPNG encoding error
+
+ /// Error indicating an encoding failure for the JPEG XL format.
#[error(transparent)]
+ #[cfg(feature = "jxl")]
+ JpegXl(#[from] jpegxl_rs::EncodeError),
+
+ /// Error indicating an error during the encoding of PNG images with oxipng.
+ #[error(transparent)]
+ #[cfg(feature = "oxipng")]
OxiPng(#[from] oxipng::PngError),
- /// A Webp encoding error
+
+ /// Error indicating an error during the encoding of AVIF images with ravif.
+ #[error(transparent)]
+ #[cfg(feature = "avif")]
+ Ravif(#[from] ravif::Error),
+
+ /// General error indicating that something went wrong during image encoding.
+ #[error("Something went wrong")]
+ General,
+}
+
+/// Enum representing errors that can occur during image decoding.
+#[derive(Error, Debug)]
+pub enum DecoderError {
+ /// An I/O error occurred during image decoding.
#[error(transparent)]
- Webp(#[from] WebPSimpleError),
- /// A Avif encoding error
+ Io(#[from] io::Error),
+
+ /// An error occurred while parsing or detecting the image format.
+ #[error(transparent)]
+ Format(#[from] ImageFormatError),
+
+ /// An error occurred when decoding JPEG image format with MozJPEG.
+ #[error("Failed to decode JPEG with MozJPEG")]
+ MozJpeg,
+
+ /// An error occurred during PNG image decoding.
+ #[error(transparent)]
+ Png(#[from] png::DecodingError),
+
+ /// An error occurred when decoding AVIF image format with a specific exit code.
+ #[error("Failed to decode AVIF with exit code: {0}")]
+ #[cfg(feature = "avif")]
+ Avif(u32),
+
+ /// An error occurred during JPEG XL image decoding.
#[error(transparent)]
- Avif(#[from] ravif::Error),
+ #[cfg(feature = "jxl")]
+ JpegXl(#[from] jpegxl_rs::DecodeError),
+
+ /// An error occurred when decoding WebP image format with libwebp.
+ #[error("Failed to decode WebP with libwebp")]
+ #[cfg(feature = "webp")]
+ WebP,
+}
+
+#[cfg(test)]
+mod tests {
+ // Import the necessary dependencies from the code
+ use super::*;
+
+ #[test]
+ fn invalid_quantization_config_errors() {
+ // Test QualityOutOfBounds error
+ let quality_error = InvalidQuantizationConfig::QualityOutOfBounds(120);
+ assert_eq!(
+ format!("{}", quality_error),
+ "Quality value 120 is out of bounds (0-100)."
+ );
+
+ // Test DitheringOutOfBounds error
+ let dithering_error = InvalidQuantizationConfig::DitheringOutOfBounds(1.5);
+ assert_eq!(
+ format!("{}", dithering_error),
+ "Dithering level 1.5 is out of bounds (0.0-1.0)."
+ );
+ }
+
+ #[test]
+ fn invalid_encoder_config_errors() {
+ // Test QualityOutOfBounds error
+ let quality_error = InvalidEncoderConfig::QualityOutOfBounds(120.0);
+ assert_eq!(
+ format!("{}", quality_error),
+ "Quality value 120 is out of bounds (0.0-100.0)."
+ );
+ }
+
+ #[test]
+ fn encoder_error_messages() {
+ // Test Io error message
+ let io_error = EncoderError::Io(std::io::Error::new(std::io::ErrorKind::Other, "IO error"));
+ assert_eq!(format!("{}", io_error), "IO error");
+
+ // Test Resize error message
+ #[cfg(feature = "resizing")]
+ let resize_error = EncoderError::Resize(resize::Error::OutOfMemory);
+ #[cfg(feature = "resizing")]
+ assert_eq!(
+ format!("{}", resize_error),
+ format!("{}", resize::Error::OutOfMemory)
+ );
+
+ // Test Quantization error message
+ #[cfg(feature = "quantization")]
+ let quantization_error = EncoderError::Quantization(imagequant::Error::OutOfMemory);
+ #[cfg(feature = "quantization")]
+ assert_eq!(
+ format!("{}", quantization_error),
+ format!("{}", imagequant::Error::OutOfMemory)
+ );
+
+ // no need to test others 🤷♀️
+ }
}
diff --git a/src/image.rs b/src/image.rs
deleted file mode 100644
index d5d32f37..00000000
--- a/src/image.rs
+++ /dev/null
@@ -1,165 +0,0 @@
-use std::{borrow::Cow, fmt, str::FromStr};
-
-/// Image data
-///
-/// Used to store dimensions and data of an image
-#[derive(Debug, Clone)]
-pub struct ImageData {
- width: usize,
- height: usize,
- data: Box<[u8]>,
-}
-
-impl ImageData {
- /// Creates a new [`ImageData`]
- ///
- /// # Examples
- /// ```
- /// # use rimage::image::ImageData;
- /// let image = ImageData::new(100, 100, &[0; 100 * 100 * 4]); // 100x100 RGBA image
- /// ```
- pub fn new(width: usize, height: usize, data: &[u8]) -> Self {
- Self {
- width,
- height,
- data: data.into(),
- }
- }
- /// Get the width and height of the image
- #[inline]
- pub fn size(&self) -> (usize, usize) {
- (self.width, self.height)
- }
- /// Get image data
- #[inline]
- pub fn data(&self) -> &[u8] {
- &self.data
- }
- /// Get image data as mutable
- #[inline]
- pub fn data_mut(&mut self) -> &mut [u8] {
- &mut self.data
- }
-}
-
-/// Image format for decoder
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ImageFormat {
- /// Jpeg image
- Jpeg,
- /// Png image
- Png,
- /// WebP image
- WebP,
- /// AVIF image
- Avif,
-}
-
-impl std::str::FromStr for ImageFormat {
- type Err = Cow<'static, str>;
-
- fn from_str(s: &str) -> Result {
- match s {
- "mozjpeg" | "jpg" | "jpeg" => Ok(ImageFormat::Jpeg),
- "png" => Ok(ImageFormat::Png),
- "webp" => Ok(ImageFormat::WebP),
- "avif" => Ok(ImageFormat::Avif),
- _ => Err(format!("{} is not a valid input format", s).into()),
- }
- }
-}
-
-impl fmt::Display for ImageFormat {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- ImageFormat::Jpeg => write!(f, "jpg"),
- ImageFormat::Png => write!(f, "png"),
- ImageFormat::WebP => write!(f, "webp"),
- ImageFormat::Avif => write!(f, "avif"),
- }
- }
-}
-
-/// Codec for processing output
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Codec {
- /// MozJpeg codec, outputs .jpg
- MozJpeg,
- /// Browser Png codec, outputs .png
- Png,
- /// OxiPng codec, outputs .png
- Oxipng,
- /// WebP codec, outputs .webp
- WebP,
- /// AVIF codec, outputs .avif
- Avif,
-}
-
-impl std::str::FromStr for Codec {
- type Err = Cow<'static, str>;
-
- fn from_str(s: &str) -> Result {
- match s {
- "mozjpeg" | "jpg" | "jpeg" => Ok(Codec::MozJpeg),
- "png" => Ok(Codec::Png),
- "oxipng" => Ok(Codec::Oxipng),
- "webp" => Ok(Codec::WebP),
- "avif" => Ok(Codec::Avif),
- _ => Err(format!("{} is not a valid output format", s).into()),
- }
- }
-}
-
-impl fmt::Display for Codec {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Codec::MozJpeg => write!(f, "jpg"),
- Codec::Png => write!(f, "png"),
- Codec::Oxipng => write!(f, "png"),
- Codec::WebP => write!(f, "webp"),
- Codec::Avif => write!(f, "avif"),
- }
- }
-}
-
-/// Wrapper around [`resize::Type`]
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ResizeType {
- /// [`resize::Type::Point`]
- Point,
- /// [`resize::Type::Triangle`]
- Triangle,
- /// [`resize::Type::Catrom`]
- CatmullRom,
- /// [`resize::Type::Mitchell`]
- Mitchell,
- /// [`resize::Type::Lanczos3`]
- Lanczos3,
-}
-
-// Implement to [`resize::Type`] for [`ResizeType`]
-impl From<&ResizeType> for resize::Type {
- fn from(resize_type: &ResizeType) -> Self {
- match resize_type {
- ResizeType::Point => resize::Type::Point,
- ResizeType::Triangle => resize::Type::Triangle,
- ResizeType::CatmullRom => resize::Type::Catrom,
- ResizeType::Mitchell => resize::Type::Mitchell,
- ResizeType::Lanczos3 => resize::Type::Lanczos3,
- }
- }
-}
-
-impl FromStr for ResizeType {
- type Err = Box;
- fn from_str(s: &str) -> Result {
- match s {
- "point" => Ok(Self::Point),
- "triangle" => Ok(Self::Triangle),
- "catmull-rom" => Ok(Self::CatmullRom),
- "mitchell" => Ok(Self::Mitchell),
- "lanczos3" => Ok(Self::Lanczos3),
- _ => Err(format!("{} is not a valid resize type", s).into()),
- }
- }
-}
diff --git a/src/image/mod.rs b/src/image/mod.rs
new file mode 100644
index 00000000..ff760887
--- /dev/null
+++ b/src/image/mod.rs
@@ -0,0 +1,341 @@
+use rgb::RGBA8;
+
+#[cfg(feature = "quantization")]
+use crate::config::QuantizationConfig;
+#[cfg(feature = "resizing")]
+use crate::config::ResizeConfig;
+
+/// Struct representing an image with RGBA8 pixel data.
+pub struct Image {
+ data: Vec,
+ width: usize,
+ height: usize,
+}
+
+impl Image {
+ /// Creates a new [`Image`] instance with the given pixel data, width, and height.
+ ///
+ /// # Parameters
+ ///
+ /// - `data`: A vector containing RGBA8 pixel data.
+ /// - `width`: The width of the image in pixels.
+ /// - `height`: The height of the image in pixels.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rimage::Image;
+ /// use rgb::RGBA8;
+ ///
+ /// let pixel_data: Vec = vec![/* pixel data */];
+ /// let image = Image::new(pixel_data, 800, 600);
+ /// ```
+ #[inline]
+ pub fn new(data: Vec, width: usize, height: usize) -> Self {
+ Self {
+ data,
+ width,
+ height,
+ }
+ }
+
+ /// Resizes the image using the specified [`ResizeConfig`].
+ ///
+ /// # Parameters
+ ///
+ /// - `resize_config`: The configuration for resizing the image.
+ ///
+ /// # Returns
+ ///
+ /// Returns `Ok(())` on success or a [`resize::Error`] on failure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use rimage::rgb::RGBA8;
+ /// use rimage::{Image, config::ResizeConfig};
+ ///
+ /// let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ /// let mut image = Image::new(image_data, 800, 600);
+ ///
+ /// let resize_config = ResizeConfig::default()
+ /// .with_width(400);
+ ///
+ /// image.resize(&resize_config)?;
+ /// # Ok::<(), rimage::resize::Error>(())
+ /// ```
+ #[cfg(feature = "resizing")]
+ pub fn resize(&mut self, resize_config: &ResizeConfig) -> Result<(), resize::Error> {
+ let aspect_ratio = self.width as f64 / self.height as f64;
+
+ let width = resize_config.width().unwrap_or(
+ resize_config
+ .height()
+ .map(|h| (h as f64 * aspect_ratio) as usize)
+ .unwrap_or(self.width),
+ );
+ let height = resize_config.height().unwrap_or(
+ resize_config
+ .width()
+ .map(|w| (w as f64 / aspect_ratio) as usize)
+ .unwrap_or(self.height),
+ );
+
+ let mut buf: Vec = vec![RGBA8::new(0, 0, 0, 0); width * height];
+
+ let mut resizer = resize::new(
+ self.width,
+ self.height,
+ width,
+ height,
+ resize::Pixel::RGBA8,
+ resize_config.filter_type(),
+ )?;
+
+ resizer.resize(&self.data, &mut buf)?;
+
+ self.data = buf;
+ self.width = width;
+ self.height = height;
+
+ Ok(())
+ }
+
+ /// Quantizes the image using the specified [`QuantizationConfig`].
+ ///
+ /// # Parameters
+ ///
+ /// - `quantization_config`: The configuration for quantizing the image.
+ ///
+ /// # Returns
+ ///
+ /// Returns `Ok(())` on success or an [`imagequant::Error`] on failure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use rimage::rgb::RGBA8;
+ /// use rimage::{Image, config::QuantizationConfig};
+ ///
+ /// let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ /// let mut image = Image::new(image_data, 800, 600);
+ ///
+ /// let quantization_config = QuantizationConfig::default();
+ /// image.quantize(&quantization_config).unwrap();
+ /// ```
+ #[cfg(feature = "quantization")]
+ pub fn quantize(
+ &mut self,
+ quantization_config: &QuantizationConfig,
+ ) -> Result<(), imagequant::Error> {
+ self.data = {
+ let mut liq = imagequant::new();
+
+ liq.set_speed(5)?;
+ liq.set_quality(0, quantization_config.quality())?;
+
+ let mut img = liq.new_image_borrowed(&self.data, self.width, self.height, 0.0)?;
+
+ let mut res = liq.quantize(&mut img)?;
+
+ res.set_dithering_level(quantization_config.dithering_level())?;
+
+ let (palette, pixels) = res.remapped(&mut img)?;
+
+ pixels.iter().map(|pix| palette[*pix as usize]).collect()
+ };
+
+ Ok(())
+ }
+
+ /// Fixes the orientation of the image based on the given orientation value.
+ ///
+ /// This method applies various transformations to correct the orientation of the image.
+ ///
+ /// # Parameters
+ ///
+ /// - `orientation`: An integer value representing the image orientation. It should be a value
+ /// between 1 and 8, inclusive.
+ ///
+ /// # Example
+ ///
+ /// ```no run
+ /// use rimage::Image;
+ ///
+ /// let mut image = Image::new(/* ... */);
+ /// image.fix_orientation(3); // Fix the orientation of the image
+ /// ```
+ #[cfg(feature = "transform")]
+ pub fn fix_orientation(&mut self, orientation: u32) {
+ if orientation > 8 {
+ return;
+ }
+
+ let orientation = orientation - 1;
+
+ if orientation & 0b100 != 0 {
+ self.flip_diagonally();
+ }
+
+ if orientation & 0b010 != 0 {
+ self.rotate_180();
+ }
+
+ if orientation & 0b001 != 0 {
+ self.flip_horizontally();
+ }
+ }
+
+ /// Flips the image diagonally.
+ ///
+ /// This method performs a diagonal flip (transpose) of the image data and updates the image dimensions accordingly.
+ ///
+ /// # Example
+ ///
+ /// ```no run
+ /// use rimage::Image;
+ ///
+ /// let mut image = Image::new(/* ... */);
+ /// image.flip_diagonally(); // Flip the image diagonally
+ /// ```
+ #[inline]
+ #[cfg(feature = "transform")]
+ fn flip_diagonally(&mut self) {
+ let mut buf = vec![RGBA8::new(0, 0, 0, 0); self.data.len()];
+
+ transpose::transpose(&self.data, &mut buf, self.width, self.height);
+
+ self.data = buf;
+ (self.width, self.height) = (self.height, self.width);
+ }
+
+ /// Flips the image horizontally.
+ ///
+ /// This method performs a horizontal flip of the image data.
+ ///
+ /// # Example
+ ///
+ /// ```no run
+ /// use rimage::Image;
+ ///
+ /// let mut image = Image::new(/* ... */);
+ /// image.flip_horizontally(); // Flip the image horizontally
+ /// ```
+ #[inline]
+ #[cfg(feature = "transform")]
+ pub fn flip_horizontally(&mut self) {
+ for y in 0..self.height {
+ let start = y * self.width;
+
+ self.data[start..start + self.width].reverse();
+ }
+ }
+
+ /// Rotates the image 90 degrees clockwise.
+ ///
+ /// This method rotates the image 90 degrees clockwise by performing a diagonal flip followed by a horizontal flip.
+ ///
+ /// # Example
+ ///
+ /// ```no run
+ /// use rimage::Image;
+ ///
+ /// let mut image = Image::new(/* ... */);
+ /// image.rotate_90(); // Rotate the image 90 degrees clockwise
+ /// ```
+ #[inline]
+ #[cfg(feature = "transform")]
+ pub fn rotate_90(&mut self) {
+ self.flip_diagonally();
+ self.flip_horizontally();
+ }
+
+ /// Rotates the image 180 degrees clockwise.
+ ///
+ /// This method rotates the image 180 degrees clockwise by performing two consecutive 90-degree clockwise rotations.
+ ///
+ /// # Example
+ ///
+ /// ```no run
+ /// use rimage::Image;
+ ///
+ /// let mut image = Image::new(/* ... */);
+ /// image.rotate_180(); // Rotate the image 180 degrees clockwise
+ /// ```
+ #[inline]
+ #[cfg(feature = "transform")]
+ pub fn rotate_180(&mut self) {
+ self.rotate_90();
+ self.rotate_90();
+ }
+
+ /// Gets a reference to the pixel data of the image.
+ ///
+ /// # Returns
+ ///
+ /// Returns a reference to the RGBA8 pixel data.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use rimage::rgb::RGBA8;
+ /// use rimage::Image;
+ ///
+ /// let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ /// let image = Image::new(image_data, 800, 600);
+ /// let data_reference = image.data();
+ /// ```
+ #[inline]
+ pub fn data(&self) -> &[RGBA8] {
+ &self.data
+ }
+
+ /// Gets the width of the image in pixels.
+ ///
+ /// # Returns
+ ///
+ /// Returns the width of the image.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use rimage::rgb::RGBA8;
+ /// use rimage::Image;
+ ///
+ /// let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ /// let image = Image::new(image_data, 800, 600);
+ /// let width = image.width();
+ ///
+ /// assert_eq!(width, 800)
+ /// ```
+ #[inline]
+ pub fn width(&self) -> usize {
+ self.width
+ }
+
+ /// Gets the height of the image in pixels.
+ ///
+ /// # Returns
+ ///
+ /// Returns the height of the image.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use rimage::rgb::RGBA8;
+ /// use rimage::Image;
+ ///
+ /// let image_data = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ /// let image = Image::new(image_data, 800, 600);
+ /// let height = image.height();
+ ///
+ /// assert_eq!(height, 600)
+ /// ```
+ #[inline]
+ pub fn height(&self) -> usize {
+ self.height
+ }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/image/tests.rs b/src/image/tests.rs
new file mode 100644
index 00000000..56d03e84
--- /dev/null
+++ b/src/image/tests.rs
@@ -0,0 +1,166 @@
+use super::*;
+
+#[test]
+fn new_image() {
+ let pixel_data: Vec = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+ let image = Image::new(pixel_data.clone(), 100, 50);
+
+ assert_eq!(image.data(), &pixel_data);
+ assert_eq!(image.width(), 100);
+ assert_eq!(image.height(), 50);
+}
+
+#[test]
+#[cfg(feature = "resizing")]
+fn resize_image_smaller() {
+ let image_data: Vec = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+ let mut image = Image::new(image_data, 100, 50);
+
+ let resize_config = ResizeConfig::default().with_width(50);
+ image.resize(&resize_config).unwrap();
+
+ assert_eq!(image.data(), &[RGBA8::new(0, 0, 0, 0); 50 * 25]);
+ assert_eq!(image.width(), 50);
+ assert_eq!(image.height(), 25);
+}
+
+#[test]
+#[cfg(feature = "resizing")]
+fn resize_image_bigger() {
+ let image_data: Vec = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+ let mut image = Image::new(image_data, 100, 50);
+
+ let resize_config = ResizeConfig::default().with_width(200);
+ image.resize(&resize_config).unwrap();
+
+ assert_eq!(image.data(), &vec![RGBA8::new(0, 0, 0, 0); 200 * 100]);
+ assert_eq!(image.width(), 200);
+ assert_eq!(image.height(), 100);
+}
+
+#[test]
+#[cfg(feature = "resizing")]
+fn resize_image_width_and_height() {
+ let image_data: Vec = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+ let mut image = Image::new(image_data, 100, 50);
+
+ let resize_config = ResizeConfig::default().with_width(200).with_height(150);
+ image.resize(&resize_config).unwrap();
+
+ assert_eq!(image.data(), &[RGBA8::new(0, 0, 0, 0); 200 * 150]);
+ assert_eq!(image.width(), 200);
+ assert_eq!(image.height(), 150);
+}
+
+#[test]
+#[cfg(feature = "quantization")]
+fn quantize_image() {
+ let image_data: Vec = vec![RGBA8::new(0, 0, 0, 0); 800 * 600];
+ let mut image = Image::new(image_data.clone(), 800, 600);
+
+ let quantization_config = QuantizationConfig::default();
+ assert!(image.quantize(&quantization_config).is_ok());
+
+ // Test quantization with quality
+ let quantization_config = QuantizationConfig::default().with_quality(50).unwrap();
+ assert!(image.quantize(&quantization_config).is_ok());
+}
+
+#[test]
+#[cfg(feature = "transform")]
+fn flip_diagonally() {
+ #[rustfmt::skip]
+ let image_data = vec![
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ ];
+
+ #[rustfmt::skip]
+ let test_image_data = vec![
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ ];
+
+ let mut image = Image::new(image_data.clone(), 5, 3);
+
+ image.flip_diagonally();
+
+ assert_ne!(image.data, image_data);
+
+ assert_eq!(image.width, 3);
+ assert_eq!(image.height, 5);
+
+ assert_eq!(image.data, test_image_data);
+}
+
+#[test]
+#[cfg(feature = "transform")]
+fn flip_horizontally() {
+ #[rustfmt::skip]
+ let image_data = vec![
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255),
+ ];
+
+ #[rustfmt::skip]
+ let test_image_data = vec![
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ ];
+
+ let mut image = Image::new(image_data.clone(), 3, 5);
+
+ image.flip_horizontally();
+
+ assert_ne!(image.data, image_data);
+
+ assert_eq!(image.width, 3);
+ assert_eq!(image.height, 5);
+
+ assert_eq!(image.data, test_image_data);
+}
+
+#[test]
+#[cfg(feature = "transform")]
+fn rotate_180() {
+ #[rustfmt::skip]
+ let image_data = vec![
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ ];
+
+ #[rustfmt::skip]
+ let test_image_data = vec![
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ RGBA8::new(0, 0, 0, 255), RGBA8::new(0, 0, 0, 0), RGBA8::new(0, 0, 0, 0),
+ ];
+
+ let mut image = Image::new(image_data.clone(), 3, 5);
+
+ image.rotate_180();
+
+ assert_ne!(image.data, image_data);
+
+ assert_eq!(image.width, 3);
+ assert_eq!(image.height, 5);
+
+ assert_eq!(image.data, test_image_data);
+}
diff --git a/src/lib.rs b/src/lib.rs
index 3084e7e5..9a806737 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,190 +1,85 @@
/*!
-This crate provides a cli tool and library for image processing.
-Similar to [squoosh!](https://squoosh.app/) using same codecs,
-but fully written on rust and with bulk processing support.
+# Rimage
-**Attention: Library in active development stage, all can be changed!**
+Rimage is a powerful Rust image optimization library designed to simplify and enhance your image optimization workflows. With Rimage, you can effortlessly optimize images for various formats, set quality levels, and apply advanced image optimization techniques with ease. Whether you're building a web application, mobile app, or desktop software, Rimage empowers you to deliver visually stunning content with minimal effort.
-Current features:
-- Decoding jpeg, png, webp and avif
-- Encoding with optimizations
+## Features
-# Usage
+1. **Flexible Format Conversion**: Rimage supports all modern image formats, including JPEG, JPEG XL, PNG, AVIF, and WebP.
+2. **Quality Control**: Fine-tune the quality of your images using a simple and intuitive interface.
+3. **Parallel Optimization**: Harness the power of parallel processing to optimize multiple images simultaneously.
+4. **Quantization and Dithering**: For advanced users, Rimage offers control over quantization and dithering.
+5. **Image Resizing**: Resize images with ease using `resize` crate.
-First add this crate to your dependencies:
-```text
-cargo add rimage
-```
+## Decoding
+
+From path:
-or add this to Cargo.toml:
-```toml
-[dependencies]
-rimage = "0.8.0"
```
+use rimage::Decoder;
-After that you can use this crate:
+let path = std::path::PathBuf::from("tests/files/jpg/f1t.jpg");
-## Easy way
-```
-use rimage::{image::Codec, optimize, Config};
-
-// Get file path
-let path = std::path::PathBuf::from("tests/files/basi0g01.jpg"); // Or any other image
-
-// Build config for encoding (Please process errors when build config)
-let config = Config::builder(Codec::MozJpeg).build().unwrap();
-
-// Get encoded image data from encoder
-let data = match optimize(&path, &config) {
- Ok(data) => data,
- Err(e) => {
- eprintln!("Oh no, there is error! {e}");
- std::process::exit(1);
- }
-};
-
-// Write image to file
-std::fs::write("output.jpg", data);
-# std::fs::remove_file("output.jpg").unwrap();
-```
+let decoder = Decoder::from_path(&path)?;
-### Get optimized image from memory
-```
-use std::{io::Read, path, fs};
-use rimage::{image::{Codec, ImageFormat}, optimize_from_memory, Config};
-
-// Get file data from path
-let path = path::PathBuf::from("tests/files/basi0g01.jpg"); // Or any other image
-let mut file = fs::File::open(path).unwrap();
-let metadata = file.metadata().unwrap();
-let mut data = Vec::with_capacity(metadata.len() as usize);
-file.read_to_end(&mut data).unwrap();
-
-// Build config for encoding (Please process errors when build config)
-let config = Config::builder(Codec::MozJpeg).build().unwrap();
-
-// Get encoded image data from encoder
-let data = match optimize_from_memory(&data, ImageFormat::Jpeg, &config) {
- Ok(data) => data,
- Err(e) => {
- eprintln!("Oh no, there is error! {e}");
- std::process::exit(1);
- }
-};
-
-// Write image to file
-fs::write("output.jpg", data);
-# fs::remove_file("output.jpg").unwrap();
-```
+let image = decoder.decode()?;
-## Decoding
+// do something with the image data...
+# Ok::<(), rimage::error::DecoderError>(())
```
-use rimage::Decoder;
-// Create decoder from file path and data
-let path = std::path::PathBuf::from("tests/files/basi0g01.jpg"); // Or any other image
-
-let decoder = match Decoder::from_path(&path) {
- Ok(img) => img,
- Err(e) => {
- eprintln!("Oh no, there is error! {e}");
- std::process::exit(1);
- }
-};
-
-// Decode image to image data
-let image = match decoder.decode() {
- Ok(img) => img,
- Err(e) => {
- eprintln!("Oh no, there is error! {e}");
- std::process::exit(1);
- }
-};
-
-// Get image data
-println!("Size: {:?}", image.size());
-println!("Data length: {:?}", image.data().len());
-
-// Do something with image...
+From memory:
```
+# use std::fs::File;
+use rimage::{Decoder, config::ImageFormat};
-### Decoding from memory
-```
-use std::{io::Read, path, fs};
-use rimage::{Decoder, image::ImageFormat};
-
-// Get file data
-let path = path::PathBuf::from("tests/files/basi0g01.jpg"); // Or any other image
-let mut file = fs::File::open(path).unwrap();
-let metadata = file.metadata().unwrap();
-let mut data = Vec::with_capacity(metadata.len() as usize);
-file.read_to_end(&mut data).unwrap();
-
-// Create decoder from file data and input format
-let decoder = Decoder::from_mem(&data, ImageFormat::Jpeg);
-
-// Decode image to image data
-let image = match decoder.decode() {
- Ok(img) => img,
- Err(e) => {
- eprintln!("Oh no, there is error! {e}");
- std::process::exit(1);
- }
-};
-
-// Get image data
-println!("Size: {:?}", image.size());
-println!("Data length: {:?}", image.data().len());
-
-// Do something with image...
+# let f = File::open("tests/files/jpg/f1t.jpg").unwrap();
+let reader = std::io::BufReader::new(f); // you can use any reader
+
+let decoder = Decoder::new(reader).with_format(ImageFormat::Jpeg);
+
+let image = decoder.decode()?;
+
+// do something with the image data...
+# Ok::<(), rimage::error::DecoderError>(())
```
## Encoding
```
-# use rimage::Decoder;
-use rimage::{Config, Encoder, image::Codec};
-# let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
-# let decoder = Decoder::from_path(&path).unwrap();
-# let image = decoder.decode().unwrap();
-
-// Build config for encoding (Please process errors when build config)
-let config = Config::builder(Codec::MozJpeg).build().unwrap();
-
-let encoder = Encoder::new(&config, image); // where image is image::ImageData
-
-// Get encoded image data from encoder
-let data = match encoder.encode() {
- Ok(data) => data,
- Err(e) => {
- eprintln!("Oh no, there is error! {e}");
- std::process::exit(1);
- }
-};
-
-// Write image to file
-std::fs::write("output.jpg", data);
-# std::fs::remove_file("output.jpg").unwrap();
+use std::fs::File;
+
+use rimage::{rgb::RGBA8, Encoder, Image, config::{EncoderConfig, Codec}};
+
+let image_data = vec![RGBA8::new(0, 0, 0, 0); 100 * 50];
+let image = Image::new(image_data, 100, 50);
+
+let config = EncoderConfig::new(Codec::MozJpeg).with_quality(80.0).unwrap();
+let file = File::create("output.jpg").expect("Failed to create file");
+
+let encoder = Encoder::new(file, image).with_config(config);
+
+encoder.encode()?;
+
+# std::fs::remove_file("output.jpg").unwrap_or(());
+# Ok::<(), rimage::error::EncoderError>(())
```
*/
-#![warn(missing_docs)]
-
-/// Errors that can occur during image processing
-pub mod error;
-/// Image data structs
-pub mod image;
+#![warn(missing_docs)]
-mod config;
+/// Module for configuring image processing settings.
+pub mod config;
mod decoder;
mod encoder;
-mod optimize;
+/// Module for library errors.
+pub mod error;
+mod image;
-pub use config::Config;
pub use decoder::Decoder;
pub use encoder::Encoder;
-pub use optimize::optimize;
-pub use optimize::optimize_from_memory;
+pub use image::Image;
-#[cfg(test)]
-pub mod test_utils;
+#[cfg(feature = "resizing")]
+pub use resize;
+pub use rgb;
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 1d6f143a..00000000
--- a/src/main.rs
+++ /dev/null
@@ -1,327 +0,0 @@
-use std::{fs, io, path, process};
-
-use clap::Parser;
-use console::{Emoji, Style};
-use indicatif::{DecimalBytes, MultiProgress, ProgressDrawTarget};
-use log::{error, info};
-#[cfg(target_env = "msvc")]
-use mimalloc::MiMalloc;
-use rayon::prelude::*;
-use rimage::{error::ConfigError, image::Codec, optimize, Config, Decoder};
-#[cfg(not(target_env = "msvc"))]
-use tikv_jemallocator::Jemalloc;
-
-use crate::{progress_bar::create_spinner, utils::common_path};
-
-#[cfg(not(target_env = "msvc"))]
-#[global_allocator]
-static GLOBAL: Jemalloc = Jemalloc;
-#[cfg(target_env = "msvc")]
-#[global_allocator]
-static GLOBAL: MiMalloc = MiMalloc;
-
-mod progress_bar;
-/// Some utils functions
-mod utils;
-
-#[derive(Parser)]
-#[command(author, about, version, long_about = None)]
-struct Args {
- /// Input file(s)
- input: Vec,
- /// Output directory
- #[arg(short, long, value_name = "DIR", value_hint = clap::ValueHint::DirPath)]
- output: Option,
- /// Quality of the output image (0-100)
- #[arg(short, long, default_value = "75")]
- quality: f32,
- /// Output format of the output image
- #[arg(short, long, default_value = "jpg")]
- format: Codec,
- /// Print image info
- #[arg(short, long)]
- info: bool,
- /// Prefix of the output file
- #[arg(short, long)]
- suffix: Option,
- /// Number of threads to use
- #[arg(short, long)]
- threads: Option,
- /// Target quantization quality from 0 to 100
- #[arg(long)]
- quantization: Option,
- /// Target quantization dithering strength from 0 to 1.0
- #[arg(long)]
- dithering: Option,
- /// Target width in pixels of the output image
- #[arg(long)]
- width: Option,
- /// Target height in pixels of the output image
- #[arg(long)]
- height: Option,
- /// Resize filter to use.
- /// Supported filters: point, triangle, catmull-rom, mitchell, lanczos3
- #[arg(long)]
- filter: Option,
- /// Disable progress bar
- #[arg(long)]
- quiet: bool,
-}
-
-fn main() {
- pretty_env_logger::init();
-
- let args = get_args();
- let m = MultiProgress::new();
-
- let conf = get_config(&args).unwrap_or_else(|err| {
- error!("{err}");
- process::exit(1);
- });
-
- let common_path = common_path(&args.input);
-
- if args.quiet {
- m.set_draw_target(ProgressDrawTarget::hidden());
- }
-
- rayon::ThreadPoolBuilder::new()
- .num_threads(args.threads.unwrap_or(0))
- .build_global()
- .unwrap_or_else(|err| {
- error!("{err}");
- process::exit(1);
- });
-
- info!("Using {} threads", rayon::current_num_threads());
- info!("Using config: {:?}", conf);
- info!("Found common path: {:?}", common_path);
-
- if args.info {
- get_info(args, common_path);
- process::exit(0);
- }
-
- bulk_optimize(args, &conf, common_path, &m);
-}
-
-#[cfg(windows)]
-fn get_args() -> Args {
- let mut args = Args::parse();
- // Get all files from stdin if no input is given
- if args.input.is_empty() {
- info!("Reading input from stdin");
- args.input = io::stdin()
- .lines()
- .map(|res| {
- let input_file = res.unwrap();
- path::PathBuf::from(input_file.trim())
- })
- .collect();
- info!("{} files read from stdin", args.input.len());
- } else {
- // Otherwise use glob pattern
- args.input = args
- .input
- .iter()
- .cloned()
- .flat_map(apply_glob_pattern)
- .collect();
- }
-
- args
-}
-
-#[cfg(windows)]
-fn apply_glob_pattern(path: path::PathBuf) -> Vec {
- let matches = path
- .to_str()
- .and_then(|pattern| glob::glob(pattern).ok())
- .map(|paths| paths.flatten().collect::>());
-
- match matches {
- Some(paths) if !paths.is_empty() => paths,
- _ => vec![path],
- }
-}
-
-#[cfg(not(windows))]
-fn get_args() -> Args {
- let mut args = Args::parse();
- // Get all files from stdin if no input is given
- if args.input.is_empty() {
- info!("Reading input from stdin");
- args.input = io::stdin()
- .lines()
- .map(|res| {
- let input_file = res.unwrap();
- path::PathBuf::from(input_file.trim())
- })
- .collect();
- info!("{} files read from stdin", args.input.len());
- }
-
- args
-}
-
-fn get_info(args: Args, common_path: Option) {
- for path in args.input {
- let d = match Decoder::from_path(&path) {
- Ok(file) => file,
- Err(e) => {
- error!("{} {e}", &path.file_name().unwrap().to_str().unwrap());
- continue;
- }
- };
-
- let img = match d.decode() {
- Ok(img) => img,
- Err(e) => {
- error!("{} {e}", &path.file_name().unwrap().to_str().unwrap());
- continue;
- }
- };
-
- println!("Full path: {:?}", &path);
-
- if let Some(common_path) = &common_path {
- println!(
- "Relative path: {:?}",
- &path.strip_prefix(common_path.parent().unwrap()).unwrap()
- );
- }
-
- println!("{:?}", &path.file_name().unwrap());
- println!("Size: {:?}", img.size());
- println!("Data length: {:?}", img.data().len());
- println!();
- }
-}
-
-fn get_config(args: &Args) -> Result {
- let mut conf = Config::builder(args.format);
-
- conf.quality(args.quality);
-
- if let Some(width) = args.width {
- conf.target_width(width);
- }
- if let Some(height) = args.height {
- conf.target_height(height);
- }
- if let Some(filter) = args.filter {
- conf.resize_type(filter);
- }
- if let Some(quantization) = args.quantization {
- conf.quantization_quality(quantization);
- }
- if let Some(dithering) = args.dithering {
- conf.dithering_level(dithering);
- }
-
- conf.build()
-}
-
-fn bulk_optimize(args: Args, conf: &Config, common_path: Option, m: &MultiProgress) {
- args.input.into_par_iter().for_each(|path| {
- let file_name = match path.file_name() {
- Some(name) => name.to_str().unwrap(),
- None => {
- error!("Path does not contain file name");
- return;
- }
- };
- let spinner = create_spinner(file_name.to_owned(), m);
-
- let cyan = Style::new().cyan();
- let red = Style::new().red();
- let green = Style::new().green();
-
- let file_size_before_optimization = match fs::metadata(&path) {
- Ok(meta) => meta.len(),
- Err(e) => {
- spinner.set_prefix(format!("{}", Emoji("❌", "Failed")));
- spinner.finish_with_message(format!("{file_name} failed: {}", red.apply_to(e)));
- return;
- }
- };
-
- let file_size_after_optimization;
-
- let mut new_path = path.clone();
-
- if let Some(destination_dir) = &args.output {
- let file_name = path::Path::new(new_path.file_name().unwrap());
-
- let relative_path = if let Some(common_path) = &common_path {
- new_path.strip_prefix(common_path).unwrap_or(file_name)
- } else {
- file_name
- };
-
- new_path = destination_dir.join(relative_path);
- }
-
- let ext = args.format.to_string();
- let suffix = args.suffix.clone().unwrap_or_default();
-
- new_path.set_file_name(format!(
- "{}{}",
- path.file_stem().unwrap().to_str().unwrap(),
- suffix,
- ));
- new_path.set_extension(ext);
-
- match fs::create_dir_all(new_path.parent().unwrap()) {
- Ok(_) => (),
- Err(e) => {
- spinner.set_prefix(format!("{}", Emoji("❌", "Failed")));
- spinner.finish_with_message(format!("{file_name} failed: {}", red.apply_to(e)));
- return;
- }
- }
-
- match fs::write(
- &new_path,
- match optimize(&path, conf) {
- Ok(data) => {
- file_size_after_optimization = data.len() as u64;
- data
- }
- Err(e) => {
- spinner.set_prefix(format!("{}", Emoji("❌", "Failed")));
- spinner.finish_with_message(format!("{file_name} failed: {}", red.apply_to(e)));
- return;
- }
- },
- ) {
- Ok(_) => (),
- Err(e) => {
- spinner.set_prefix(format!("{}", Emoji("❌", "Failed")));
- spinner.finish_with_message(format!("{file_name} failed: {}", red.apply_to(e)));
- return;
- }
- };
- info!("Saved to {:?}", new_path);
-
- let diff = file_size_after_optimization as f64 / file_size_before_optimization as f64;
- let abs_percent = diff.abs() * 100.0;
- let percent = if diff > 1.0 {
- abs_percent - 100.0
- } else {
- 100.0 - abs_percent
- };
-
- spinner.set_prefix(format!("{}", Emoji("✅", "Done")));
- spinner.finish_with_message(format!(
- "{file_name} completed {} -> {} {}",
- cyan.apply_to(DecimalBytes(file_size_before_optimization)),
- cyan.apply_to(DecimalBytes(file_size_after_optimization)),
- if file_size_after_optimization > file_size_before_optimization {
- red.apply_to(format!("{} {:.1}%", Emoji("🔺", "^"), percent))
- } else {
- green.apply_to(format!("{} {:.1}%", Emoji("🔻", "v"), percent))
- }
- ));
- });
-}
diff --git a/src/optimize.rs b/src/optimize.rs
deleted file mode 100644
index f8e9efef..00000000
--- a/src/optimize.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use std::{error::Error, fs, path};
-
-use log::info;
-
-use crate::{decoder::Decoder, image::ImageFormat, Config, Encoder};
-
-/// Optimizes one image with provided config
-///
-/// # Example
-/// ```
-/// # use rimage::{Config, optimize, image::Codec};
-/// // Get file path
-/// let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
-///
-/// // Build config for encoding
-/// let config = Config::builder(Codec::MozJpeg).build().unwrap();
-///
-/// // Get encoded image data from encoder
-/// let data = optimize(&path, &config).unwrap();
-///
-/// // Do something with image data...
-/// ```
-///
-/// # Errors
-///
-/// This function can return any error that happens during decoding and encoding process
-pub fn optimize(image_path: &path::Path, config: &Config) -> Result, Box> {
- let file = fs::File::open(image_path)?;
-
- info!("read {} bytes", file.metadata().unwrap().len());
-
- let d = Decoder::from_path(image_path)?;
- let e = Encoder::new(config, d.decode()?);
-
- Ok(e.encode()?)
-}
-
-/// Optimizes one image from memory with provided config
-///
-/// # Example
-/// ```
-/// use std::io::Read;
-/// # use rimage::{optimize_from_memory, image::{ImageFormat, Codec}, Config};
-///
-/// // Get file data
-/// let path = std::path::PathBuf::from("tests/files/basi0g01.jpg");
-/// let mut file = std::fs::File::open(path).unwrap();
-/// let metadata = file.metadata().unwrap();
-/// let mut data = Vec::with_capacity(metadata.len() as usize);
-/// file.read_to_end(&mut data).unwrap();
-///
-/// // Build config for encoding
-/// let config = Config::builder(Codec::MozJpeg).build().unwrap();
-///
-/// // Get encoded image data from encoder
-/// let data = optimize_from_memory(&data, ImageFormat::Jpeg, &config).unwrap();
-///
-/// // Do something with image data...
-/// ```
-///
-/// # Errors
-///
-/// This function can return any error that happens during decoding and encoding process
-pub fn optimize_from_memory(
- data: &[u8],
- input_format: ImageFormat,
- config: &Config,
-) -> Result, Box> {
- let d = Decoder::from_mem(data, input_format);
- let e = Encoder::new(config, d.decode()?);
-
- Ok(e.encode()?)
-}
diff --git a/src/progress_bar.rs b/src/progress_bar.rs
deleted file mode 100644
index c77bb11e..00000000
--- a/src/progress_bar.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use std::{borrow::Cow, time::Duration};
-
-use console::Emoji;
-use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
-
-const TICKS: &str = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ ";
-
-pub fn create_spinner(msg: impl Into>, m: &MultiProgress) -> ProgressBar {
- let spinner_style = ProgressStyle::with_template("{prefix} {spinner} {wide_msg}")
- .expect("Template must be correct")
- .tick_chars(TICKS);
-
- let pb = m.add(ProgressBar::new_spinner());
- pb.set_style(spinner_style);
- pb.set_prefix(format!("{} ", Emoji("🖼️", "Processing...")));
- pb.set_message(msg);
- pb.enable_steady_tick(Duration::from_millis(100));
-
- pb
-}
diff --git a/src/test_utils.rs b/src/test_utils.rs
deleted file mode 100644
index e7ef8009..00000000
--- a/src/test_utils.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use std::path::PathBuf;
-
-pub fn get_files_by_regex(re: &str) -> Vec {
- use std::fs;
-
- use regex::Regex;
-
- fs::read_dir("tests/files/")
- .unwrap()
- .map(|entry| {
- let entry = entry.unwrap();
- entry.path()
- })
- .filter(|path| {
- let re = Regex::new(re).unwrap();
- re.is_match(path.to_str().unwrap_or(""))
- })
- .collect()
-}
diff --git a/src/utils.rs b/src/utils.rs
deleted file mode 100644
index 6d07aa72..00000000
--- a/src/utils.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use std::path::{Path, PathBuf};
-
-/// Gets common path inside a array of paths
-pub fn common_path(paths: &[PathBuf]) -> Option {
- if paths.len() < 2 {
- return None;
- }
-
- let mut iter = paths.iter();
-
- let mut ret = iter.next()?.clone();
-
- for path in iter {
- if let Some(r) = common(ret, path) {
- ret = r;
- } else {
- return None;
- }
- }
-
- Some(ret)
-}
-
-fn common, B: AsRef>(a: A, b: B) -> Option {
- let a = a.as_ref().components();
- let b = b.as_ref().components();
- let mut ret = PathBuf::new();
- let mut found = false;
- for (one, two) in a.zip(b) {
- if one == two {
- ret.push(one);
- found = true;
- } else {
- break;
- }
- }
- if found {
- Some(ret)
- } else {
- None
- }
-}
diff --git a/tests/files/avif/f1t.avif b/tests/files/avif/f1t.avif
new file mode 100644
index 00000000..e78df138
Binary files /dev/null and b/tests/files/avif/f1t.avif differ
diff --git a/tests/files/basi0g01.avif b/tests/files/basi0g01.avif
deleted file mode 100644
index cba0ff14..00000000
Binary files a/tests/files/basi0g01.avif and /dev/null differ
diff --git a/tests/files/basi0g01.jpg b/tests/files/basi0g01.jpg
deleted file mode 100644
index 5303f63f..00000000
Binary files a/tests/files/basi0g01.jpg and /dev/null differ
diff --git a/tests/files/basi0g01.png b/tests/files/basi0g01.png
deleted file mode 100644
index 556fa727..00000000
Binary files a/tests/files/basi0g01.png and /dev/null differ
diff --git a/tests/files/basi0g02.avif b/tests/files/basi0g02.avif
deleted file mode 100644
index ad7b3673..00000000
Binary files a/tests/files/basi0g02.avif and /dev/null differ
diff --git a/tests/files/basi0g02.jpg b/tests/files/basi0g02.jpg
deleted file mode 100644
index f46a7f9a..00000000
Binary files a/tests/files/basi0g02.jpg and /dev/null differ
diff --git a/tests/files/basi0g02.png b/tests/files/basi0g02.png
deleted file mode 100644
index ce09821e..00000000
Binary files a/tests/files/basi0g02.png and /dev/null differ
diff --git a/tests/files/basi0g04.avif b/tests/files/basi0g04.avif
deleted file mode 100644
index 662cc6ab..00000000
Binary files a/tests/files/basi0g04.avif and /dev/null differ
diff --git a/tests/files/basi0g04.jpg b/tests/files/basi0g04.jpg
deleted file mode 100644
index c12181cd..00000000
Binary files a/tests/files/basi0g04.jpg and /dev/null differ
diff --git a/tests/files/basi0g04.png b/tests/files/basi0g04.png
deleted file mode 100644
index 3853273f..00000000
Binary files a/tests/files/basi0g04.png and /dev/null differ
diff --git a/tests/files/basi0g08.avif b/tests/files/basi0g08.avif
deleted file mode 100644
index 198e4380..00000000
Binary files a/tests/files/basi0g08.avif and /dev/null differ
diff --git a/tests/files/basi0g08.jpg b/tests/files/basi0g08.jpg
deleted file mode 100644
index 23afc7bb..00000000
Binary files a/tests/files/basi0g08.jpg and /dev/null differ
diff --git a/tests/files/basi0g08.png b/tests/files/basi0g08.png
deleted file mode 100644
index faed8bec..00000000
Binary files a/tests/files/basi0g08.png and /dev/null differ
diff --git a/tests/files/basi0g08.webp b/tests/files/basi0g08.webp
deleted file mode 100644
index 44d66327..00000000
Binary files a/tests/files/basi0g08.webp and /dev/null differ
diff --git a/tests/files/basi0g16.avif b/tests/files/basi0g16.avif
deleted file mode 100644
index 968a6b0d..00000000
Binary files a/tests/files/basi0g16.avif and /dev/null differ
diff --git a/tests/files/basi0g16.jpg b/tests/files/basi0g16.jpg
deleted file mode 100644
index bfe64318..00000000
Binary files a/tests/files/basi0g16.jpg and /dev/null differ
diff --git a/tests/files/basi0g16.png b/tests/files/basi0g16.png
deleted file mode 100644
index a9f28165..00000000
Binary files a/tests/files/basi0g16.png and /dev/null differ
diff --git a/tests/files/basi0g16.webp b/tests/files/basi0g16.webp
deleted file mode 100644
index 29d6a2b3..00000000
Binary files a/tests/files/basi0g16.webp and /dev/null differ
diff --git a/tests/files/basi2c08.avif b/tests/files/basi2c08.avif
deleted file mode 100644
index c5c04aac..00000000
Binary files a/tests/files/basi2c08.avif and /dev/null differ
diff --git a/tests/files/basi2c08.jpg b/tests/files/basi2c08.jpg
deleted file mode 100644
index 3a7e7c28..00000000
Binary files a/tests/files/basi2c08.jpg and /dev/null differ
diff --git a/tests/files/basi2c08.png b/tests/files/basi2c08.png
deleted file mode 100644
index 2aab44d4..00000000
Binary files a/tests/files/basi2c08.png and /dev/null differ
diff --git a/tests/files/basi2c08.webp b/tests/files/basi2c08.webp
deleted file mode 100644
index c13f60cc..00000000
Binary files a/tests/files/basi2c08.webp and /dev/null differ
diff --git a/tests/files/basi2c16.avif b/tests/files/basi2c16.avif
deleted file mode 100644
index fed0e96d..00000000
Binary files a/tests/files/basi2c16.avif and /dev/null differ
diff --git a/tests/files/basi2c16.jpg b/tests/files/basi2c16.jpg
deleted file mode 100644
index 1f146423..00000000
Binary files a/tests/files/basi2c16.jpg and /dev/null differ
diff --git a/tests/files/basi2c16.png b/tests/files/basi2c16.png
deleted file mode 100644
index cd7e50f9..00000000
Binary files a/tests/files/basi2c16.png and /dev/null differ
diff --git a/tests/files/basi2c16.webp b/tests/files/basi2c16.webp
deleted file mode 100644
index 9a458bac..00000000
Binary files a/tests/files/basi2c16.webp and /dev/null differ
diff --git a/tests/files/basi3p01.avif b/tests/files/basi3p01.avif
deleted file mode 100644
index 7006d5f3..00000000
Binary files a/tests/files/basi3p01.avif and /dev/null differ
diff --git a/tests/files/basi3p01.jpg b/tests/files/basi3p01.jpg
deleted file mode 100644
index b3a6b195..00000000
Binary files a/tests/files/basi3p01.jpg and /dev/null differ
diff --git a/tests/files/basi3p01.png b/tests/files/basi3p01.png
deleted file mode 100644
index 00a7cea6..00000000
Binary files a/tests/files/basi3p01.png and /dev/null differ
diff --git a/tests/files/basi3p01.webp b/tests/files/basi3p01.webp
deleted file mode 100644
index f4b524a1..00000000
Binary files a/tests/files/basi3p01.webp and /dev/null differ
diff --git a/tests/files/basi3p02.avif b/tests/files/basi3p02.avif
deleted file mode 100644
index 823ca800..00000000
Binary files a/tests/files/basi3p02.avif and /dev/null differ
diff --git a/tests/files/basi3p02.jpg b/tests/files/basi3p02.jpg
deleted file mode 100644
index 8ba053a2..00000000
Binary files a/tests/files/basi3p02.jpg and /dev/null differ
diff --git a/tests/files/basi3p02.png b/tests/files/basi3p02.png
deleted file mode 100644
index bb16b44b..00000000
Binary files a/tests/files/basi3p02.png and /dev/null differ
diff --git a/tests/files/basi3p02.webp b/tests/files/basi3p02.webp
deleted file mode 100644
index 70047a12..00000000
Binary files a/tests/files/basi3p02.webp and /dev/null differ
diff --git a/tests/files/basi3p04.avif b/tests/files/basi3p04.avif
deleted file mode 100644
index bc3bc9c3..00000000
Binary files a/tests/files/basi3p04.avif and /dev/null differ
diff --git a/tests/files/basi3p04.jpg b/tests/files/basi3p04.jpg
deleted file mode 100644
index d0d86fa2..00000000
Binary files a/tests/files/basi3p04.jpg and /dev/null differ
diff --git a/tests/files/basi3p04.png b/tests/files/basi3p04.png
deleted file mode 100644
index b4e888e2..00000000
Binary files a/tests/files/basi3p04.png and /dev/null differ
diff --git a/tests/files/basi3p04.webp b/tests/files/basi3p04.webp
deleted file mode 100644
index b30c7a69..00000000
Binary files a/tests/files/basi3p04.webp and /dev/null differ
diff --git a/tests/files/basi3p08.avif b/tests/files/basi3p08.avif
deleted file mode 100644
index 3b636cb1..00000000
Binary files a/tests/files/basi3p08.avif and /dev/null differ
diff --git a/tests/files/basi3p08.jpg b/tests/files/basi3p08.jpg
deleted file mode 100644
index ceba16c1..00000000
Binary files a/tests/files/basi3p08.jpg and /dev/null differ
diff --git a/tests/files/basi3p08.png b/tests/files/basi3p08.png
deleted file mode 100644
index 50a6d1ca..00000000
Binary files a/tests/files/basi3p08.png and /dev/null differ
diff --git a/tests/files/basi3p08.webp b/tests/files/basi3p08.webp
deleted file mode 100644
index c40810c6..00000000
Binary files a/tests/files/basi3p08.webp and /dev/null differ
diff --git a/tests/files/basi4a08.avif b/tests/files/basi4a08.avif
deleted file mode 100644
index 1ce2fc98..00000000
Binary files a/tests/files/basi4a08.avif and /dev/null differ
diff --git a/tests/files/basi4a08.jpg b/tests/files/basi4a08.jpg
deleted file mode 100644
index eaa30cd8..00000000
Binary files a/tests/files/basi4a08.jpg and /dev/null differ
diff --git a/tests/files/basi4a08.png b/tests/files/basi4a08.png
deleted file mode 100644
index 398132be..00000000
Binary files a/tests/files/basi4a08.png and /dev/null differ
diff --git a/tests/files/basi4a08.webp b/tests/files/basi4a08.webp
deleted file mode 100644
index 24d1b3bd..00000000
Binary files a/tests/files/basi4a08.webp and /dev/null differ
diff --git a/tests/files/basi4a16.avif b/tests/files/basi4a16.avif
deleted file mode 100644
index 249d3626..00000000
Binary files a/tests/files/basi4a16.avif and /dev/null differ
diff --git a/tests/files/basi4a16.jpg b/tests/files/basi4a16.jpg
deleted file mode 100644
index 9e90af31..00000000
Binary files a/tests/files/basi4a16.jpg and /dev/null differ
diff --git a/tests/files/basi4a16.png b/tests/files/basi4a16.png
deleted file mode 100644
index 51192e73..00000000
Binary files a/tests/files/basi4a16.png and /dev/null differ
diff --git a/tests/files/basi4a16.webp b/tests/files/basi4a16.webp
deleted file mode 100644
index 7e5671d1..00000000
Binary files a/tests/files/basi4a16.webp and /dev/null differ
diff --git a/tests/files/basi6a08.avif b/tests/files/basi6a08.avif
deleted file mode 100644
index ff20aeb6..00000000
Binary files a/tests/files/basi6a08.avif and /dev/null differ
diff --git a/tests/files/basi6a08.jpg b/tests/files/basi6a08.jpg
deleted file mode 100644
index 1865cfb6..00000000
Binary files a/tests/files/basi6a08.jpg and /dev/null differ
diff --git a/tests/files/basi6a08.png b/tests/files/basi6a08.png
deleted file mode 100644
index aecb32e0..00000000
Binary files a/tests/files/basi6a08.png and /dev/null differ
diff --git a/tests/files/basi6a08.webp b/tests/files/basi6a08.webp
deleted file mode 100644
index 7ab17682..00000000
Binary files a/tests/files/basi6a08.webp and /dev/null differ
diff --git a/tests/files/basi6a16.avif b/tests/files/basi6a16.avif
deleted file mode 100644
index f3087ad8..00000000
Binary files a/tests/files/basi6a16.avif and /dev/null differ
diff --git a/tests/files/basi6a16.jpg b/tests/files/basi6a16.jpg
deleted file mode 100644
index 36022ee6..00000000
Binary files a/tests/files/basi6a16.jpg and /dev/null differ
diff --git a/tests/files/basi6a16.png b/tests/files/basi6a16.png
deleted file mode 100644
index 4181533a..00000000
Binary files a/tests/files/basi6a16.png and /dev/null differ
diff --git a/tests/files/basi6a16.webp b/tests/files/basi6a16.webp
deleted file mode 100644
index a88da975..00000000
Binary files a/tests/files/basi6a16.webp and /dev/null differ
diff --git a/tests/files/basn0g01.jpg b/tests/files/basn0g01.jpg
deleted file mode 100644
index e27b10b8..00000000
Binary files a/tests/files/basn0g01.jpg and /dev/null differ
diff --git a/tests/files/basn0g01.png b/tests/files/basn0g01.png
deleted file mode 100644
index 1d722423..00000000
Binary files a/tests/files/basn0g01.png and /dev/null differ
diff --git a/tests/files/basn0g02.jpg b/tests/files/basn0g02.jpg
deleted file mode 100644
index 45868751..00000000
Binary files a/tests/files/basn0g02.jpg and /dev/null differ
diff --git a/tests/files/basn0g02.png b/tests/files/basn0g02.png
deleted file mode 100644
index 50833241..00000000
Binary files a/tests/files/basn0g02.png and /dev/null differ
diff --git a/tests/files/basn0g04.jpg b/tests/files/basn0g04.jpg
deleted file mode 100644
index 25906389..00000000
Binary files a/tests/files/basn0g04.jpg and /dev/null differ
diff --git a/tests/files/basn0g04.png b/tests/files/basn0g04.png
deleted file mode 100644
index 0bf36878..00000000
Binary files a/tests/files/basn0g04.png and /dev/null differ
diff --git a/tests/files/basn0g08.jpg b/tests/files/basn0g08.jpg
deleted file mode 100644
index a1cd6968..00000000
Binary files a/tests/files/basn0g08.jpg and /dev/null differ
diff --git a/tests/files/basn0g08.png b/tests/files/basn0g08.png
deleted file mode 100644
index 23c82379..00000000
Binary files a/tests/files/basn0g08.png and /dev/null differ
diff --git a/tests/files/basn0g16.jpg b/tests/files/basn0g16.jpg
deleted file mode 100644
index 8657e080..00000000
Binary files a/tests/files/basn0g16.jpg and /dev/null differ
diff --git a/tests/files/basn0g16.png b/tests/files/basn0g16.png
deleted file mode 100644
index e7c82f78..00000000
Binary files a/tests/files/basn0g16.png and /dev/null differ
diff --git a/tests/files/basn2c08.jpg b/tests/files/basn2c08.jpg
deleted file mode 100644
index 7dfe0238..00000000
Binary files a/tests/files/basn2c08.jpg and /dev/null differ
diff --git a/tests/files/basn2c08.png b/tests/files/basn2c08.png
deleted file mode 100644
index db5ad158..00000000
Binary files a/tests/files/basn2c08.png and /dev/null differ
diff --git a/tests/files/basn2c16.jpg b/tests/files/basn2c16.jpg
deleted file mode 100644
index 71b2a069..00000000
Binary files a/tests/files/basn2c16.jpg and /dev/null differ
diff --git a/tests/files/basn2c16.png b/tests/files/basn2c16.png
deleted file mode 100644
index 50c1cb91..00000000
Binary files a/tests/files/basn2c16.png and /dev/null differ
diff --git a/tests/files/basn3p01.jpg b/tests/files/basn3p01.jpg
deleted file mode 100644
index b3a6b195..00000000
Binary files a/tests/files/basn3p01.jpg and /dev/null differ
diff --git a/tests/files/basn3p01.png b/tests/files/basn3p01.png
deleted file mode 100644
index b145c2b8..00000000
Binary files a/tests/files/basn3p01.png and /dev/null differ
diff --git a/tests/files/basn3p02.jpg b/tests/files/basn3p02.jpg
deleted file mode 100644
index 8ba053a2..00000000
Binary files a/tests/files/basn3p02.jpg and /dev/null differ
diff --git a/tests/files/basn3p02.png b/tests/files/basn3p02.png
deleted file mode 100644
index 8985b3d8..00000000
Binary files a/tests/files/basn3p02.png and /dev/null differ
diff --git a/tests/files/basn3p04.jpg b/tests/files/basn3p04.jpg
deleted file mode 100644
index d0d86fa2..00000000
Binary files a/tests/files/basn3p04.jpg and /dev/null differ
diff --git a/tests/files/basn3p04.png b/tests/files/basn3p04.png
deleted file mode 100644
index 0fbf9e82..00000000
Binary files a/tests/files/basn3p04.png and /dev/null differ
diff --git a/tests/files/basn3p08.jpg b/tests/files/basn3p08.jpg
deleted file mode 100644
index ceba16c1..00000000
Binary files a/tests/files/basn3p08.jpg and /dev/null differ
diff --git a/tests/files/basn3p08.png b/tests/files/basn3p08.png
deleted file mode 100644
index 0ddad07e..00000000
Binary files a/tests/files/basn3p08.png and /dev/null differ
diff --git a/tests/files/basn4a08.jpg b/tests/files/basn4a08.jpg
deleted file mode 100644
index eaa30cd8..00000000
Binary files a/tests/files/basn4a08.jpg and /dev/null differ
diff --git a/tests/files/basn4a08.png b/tests/files/basn4a08.png
deleted file mode 100644
index 3e130522..00000000
Binary files a/tests/files/basn4a08.png and /dev/null differ
diff --git a/tests/files/basn4a16.jpg b/tests/files/basn4a16.jpg
deleted file mode 100644
index 9e90af31..00000000
Binary files a/tests/files/basn4a16.jpg and /dev/null differ
diff --git a/tests/files/basn4a16.png b/tests/files/basn4a16.png
deleted file mode 100644
index 8243644d..00000000
Binary files a/tests/files/basn4a16.png and /dev/null differ
diff --git a/tests/files/basn6a08.jpg b/tests/files/basn6a08.jpg
deleted file mode 100644
index 1865cfb6..00000000
Binary files a/tests/files/basn6a08.jpg and /dev/null differ
diff --git a/tests/files/basn6a08.png b/tests/files/basn6a08.png
deleted file mode 100644
index e6087387..00000000
Binary files a/tests/files/basn6a08.png and /dev/null differ
diff --git a/tests/files/basn6a16.jpg b/tests/files/basn6a16.jpg
deleted file mode 100644
index 36022ee6..00000000
Binary files a/tests/files/basn6a16.jpg and /dev/null differ
diff --git a/tests/files/basn6a16.png b/tests/files/basn6a16.png
deleted file mode 100644
index 984a9952..00000000
Binary files a/tests/files/basn6a16.png and /dev/null differ
diff --git a/tests/files/bgai4a08.jpg b/tests/files/bgai4a08.jpg
deleted file mode 100644
index eaa30cd8..00000000
Binary files a/tests/files/bgai4a08.jpg and /dev/null differ
diff --git a/tests/files/bgai4a08.png b/tests/files/bgai4a08.png
deleted file mode 100644
index 398132be..00000000
Binary files a/tests/files/bgai4a08.png and /dev/null differ
diff --git a/tests/files/bgai4a16.jpg b/tests/files/bgai4a16.jpg
deleted file mode 100644
index 9e90af31..00000000
Binary files a/tests/files/bgai4a16.jpg and /dev/null differ
diff --git a/tests/files/bgai4a16.png b/tests/files/bgai4a16.png
deleted file mode 100644
index 51192e73..00000000
Binary files a/tests/files/bgai4a16.png and /dev/null differ
diff --git a/tests/files/bgan6a08.jpg b/tests/files/bgan6a08.jpg
deleted file mode 100644
index 1865cfb6..00000000
Binary files a/tests/files/bgan6a08.jpg and /dev/null differ
diff --git a/tests/files/bgan6a08.png b/tests/files/bgan6a08.png
deleted file mode 100644
index e6087387..00000000
Binary files a/tests/files/bgan6a08.png and /dev/null differ
diff --git a/tests/files/bgan6a16.jpg b/tests/files/bgan6a16.jpg
deleted file mode 100644
index 36022ee6..00000000
Binary files a/tests/files/bgan6a16.jpg and /dev/null differ
diff --git a/tests/files/bgan6a16.png b/tests/files/bgan6a16.png
deleted file mode 100644
index 984a9952..00000000
Binary files a/tests/files/bgan6a16.png and /dev/null differ
diff --git a/tests/files/bgbn4a08.jpg b/tests/files/bgbn4a08.jpg
deleted file mode 100644
index eaa30cd8..00000000
Binary files a/tests/files/bgbn4a08.jpg and /dev/null differ
diff --git a/tests/files/bgbn4a08.png b/tests/files/bgbn4a08.png
deleted file mode 100644
index 7cbefc3b..00000000
Binary files a/tests/files/bgbn4a08.png and /dev/null differ
diff --git a/tests/files/bggn4a16.jpg b/tests/files/bggn4a16.jpg
deleted file mode 100644
index 9e90af31..00000000
Binary files a/tests/files/bggn4a16.jpg and /dev/null differ
diff --git a/tests/files/bggn4a16.png b/tests/files/bggn4a16.png
deleted file mode 100644
index 13fd85ba..00000000
Binary files a/tests/files/bggn4a16.png and /dev/null differ
diff --git a/tests/files/bgwn6a08.jpg b/tests/files/bgwn6a08.jpg
deleted file mode 100644
index 1865cfb6..00000000
Binary files a/tests/files/bgwn6a08.jpg and /dev/null differ
diff --git a/tests/files/bgwn6a08.png b/tests/files/bgwn6a08.png
deleted file mode 100644
index a67ff205..00000000
Binary files a/tests/files/bgwn6a08.png and /dev/null differ
diff --git a/tests/files/bgyn6a16.jpg b/tests/files/bgyn6a16.jpg
deleted file mode 100644
index 36022ee6..00000000
Binary files a/tests/files/bgyn6a16.jpg and /dev/null differ
diff --git a/tests/files/bgyn6a16.png b/tests/files/bgyn6a16.png
deleted file mode 100644
index ae3e9be5..00000000
Binary files a/tests/files/bgyn6a16.png and /dev/null differ
diff --git a/tests/files/ccwn2c08.jpg b/tests/files/ccwn2c08.jpg
deleted file mode 100644
index 5db9ec1a..00000000
Binary files a/tests/files/ccwn2c08.jpg and /dev/null differ
diff --git a/tests/files/ccwn2c08.png b/tests/files/ccwn2c08.png
deleted file mode 100644
index 47c24817..00000000
Binary files a/tests/files/ccwn2c08.png and /dev/null differ
diff --git a/tests/files/ccwn3p08.jpg b/tests/files/ccwn3p08.jpg
deleted file mode 100644
index c7d7872c..00000000
Binary files a/tests/files/ccwn3p08.jpg and /dev/null differ
diff --git a/tests/files/ccwn3p08.png b/tests/files/ccwn3p08.png
deleted file mode 100644
index 8bb2c109..00000000
Binary files a/tests/files/ccwn3p08.png and /dev/null differ
diff --git a/tests/files/cdfn2c08.jpg b/tests/files/cdfn2c08.jpg
deleted file mode 100644
index bae4636d..00000000
Binary files a/tests/files/cdfn2c08.jpg and /dev/null differ
diff --git a/tests/files/cdfn2c08.png b/tests/files/cdfn2c08.png
deleted file mode 100644
index 559e5261..00000000
Binary files a/tests/files/cdfn2c08.png and /dev/null differ
diff --git a/tests/files/cdhn2c08.jpg b/tests/files/cdhn2c08.jpg
deleted file mode 100644
index f56546cb..00000000
Binary files a/tests/files/cdhn2c08.jpg and /dev/null differ
diff --git a/tests/files/cdhn2c08.png b/tests/files/cdhn2c08.png
deleted file mode 100644
index 3e07e8ec..00000000
Binary files a/tests/files/cdhn2c08.png and /dev/null differ
diff --git a/tests/files/cdsn2c08.jpg b/tests/files/cdsn2c08.jpg
deleted file mode 100644
index 95d6cc93..00000000
Binary files a/tests/files/cdsn2c08.jpg and /dev/null differ
diff --git a/tests/files/cdsn2c08.png b/tests/files/cdsn2c08.png
deleted file mode 100644
index 076c32cc..00000000
Binary files a/tests/files/cdsn2c08.png and /dev/null differ
diff --git a/tests/files/cdun2c08.jpg b/tests/files/cdun2c08.jpg
deleted file mode 100644
index 3751b704..00000000
Binary files a/tests/files/cdun2c08.jpg and /dev/null differ
diff --git a/tests/files/cdun2c08.png b/tests/files/cdun2c08.png
deleted file mode 100644
index 846033be..00000000
Binary files a/tests/files/cdun2c08.png and /dev/null differ
diff --git a/tests/files/ch1n3p04.jpg b/tests/files/ch1n3p04.jpg
deleted file mode 100644
index 742facfa..00000000
Binary files a/tests/files/ch1n3p04.jpg and /dev/null differ
diff --git a/tests/files/ch1n3p04.png b/tests/files/ch1n3p04.png
deleted file mode 100644
index 17cd12df..00000000
Binary files a/tests/files/ch1n3p04.png and /dev/null differ
diff --git a/tests/files/ch2n3p08.jpg b/tests/files/ch2n3p08.jpg
deleted file mode 100644
index 4fcf338c..00000000
Binary files a/tests/files/ch2n3p08.jpg and /dev/null differ
diff --git a/tests/files/ch2n3p08.png b/tests/files/ch2n3p08.png
deleted file mode 100644
index 25c17987..00000000
Binary files a/tests/files/ch2n3p08.png and /dev/null differ
diff --git a/tests/files/cm0n0g04.jpg b/tests/files/cm0n0g04.jpg
deleted file mode 100644
index 466a01ae..00000000
Binary files a/tests/files/cm0n0g04.jpg and /dev/null differ
diff --git a/tests/files/cm0n0g04.png b/tests/files/cm0n0g04.png
deleted file mode 100644
index 9fba5db3..00000000
Binary files a/tests/files/cm0n0g04.png and /dev/null differ
diff --git a/tests/files/cm7n0g04.jpg b/tests/files/cm7n0g04.jpg
deleted file mode 100644
index c8efb52f..00000000
Binary files a/tests/files/cm7n0g04.jpg and /dev/null differ
diff --git a/tests/files/cm7n0g04.png b/tests/files/cm7n0g04.png
deleted file mode 100644
index f7dc46e6..00000000
Binary files a/tests/files/cm7n0g04.png and /dev/null differ
diff --git a/tests/files/cm9n0g04.jpg b/tests/files/cm9n0g04.jpg
deleted file mode 100644
index 37d233a6..00000000
Binary files a/tests/files/cm9n0g04.jpg and /dev/null differ
diff --git a/tests/files/cm9n0g04.png b/tests/files/cm9n0g04.png
deleted file mode 100644
index dd70911a..00000000
Binary files a/tests/files/cm9n0g04.png and /dev/null differ
diff --git a/tests/files/cs3n2c16.jpg b/tests/files/cs3n2c16.jpg
deleted file mode 100644
index 9a69a744..00000000
Binary files a/tests/files/cs3n2c16.jpg and /dev/null differ
diff --git a/tests/files/cs3n2c16.png b/tests/files/cs3n2c16.png
deleted file mode 100644
index bf5fd20a..00000000
Binary files a/tests/files/cs3n2c16.png and /dev/null differ
diff --git a/tests/files/cs3n3p08.jpg b/tests/files/cs3n3p08.jpg
deleted file mode 100644
index 46131522..00000000
Binary files a/tests/files/cs3n3p08.jpg and /dev/null differ
diff --git a/tests/files/cs3n3p08.png b/tests/files/cs3n3p08.png
deleted file mode 100644
index f4a66237..00000000
Binary files a/tests/files/cs3n3p08.png and /dev/null differ
diff --git a/tests/files/cs5n2c08.jpg b/tests/files/cs5n2c08.jpg
deleted file mode 100644
index 103e5817..00000000
Binary files a/tests/files/cs5n2c08.jpg and /dev/null differ
diff --git a/tests/files/cs5n2c08.png b/tests/files/cs5n2c08.png
deleted file mode 100644
index 40f947c3..00000000
Binary files a/tests/files/cs5n2c08.png and /dev/null differ
diff --git a/tests/files/cs5n3p08.jpg b/tests/files/cs5n3p08.jpg
deleted file mode 100644
index f8a739aa..00000000
Binary files a/tests/files/cs5n3p08.jpg and /dev/null differ
diff --git a/tests/files/cs5n3p08.png b/tests/files/cs5n3p08.png
deleted file mode 100644
index dfd6e6e6..00000000
Binary files a/tests/files/cs5n3p08.png and /dev/null differ
diff --git a/tests/files/cs8n2c08.jpg b/tests/files/cs8n2c08.jpg
deleted file mode 100644
index a853a0f9..00000000
Binary files a/tests/files/cs8n2c08.jpg and /dev/null differ
diff --git a/tests/files/cs8n2c08.png b/tests/files/cs8n2c08.png
deleted file mode 100644
index 8e01d329..00000000
Binary files a/tests/files/cs8n2c08.png and /dev/null differ
diff --git a/tests/files/cs8n3p08.jpg b/tests/files/cs8n3p08.jpg
deleted file mode 100644
index c5f9d66e..00000000
Binary files a/tests/files/cs8n3p08.jpg and /dev/null differ
diff --git a/tests/files/cs8n3p08.png b/tests/files/cs8n3p08.png
deleted file mode 100644
index a44066eb..00000000
Binary files a/tests/files/cs8n3p08.png and /dev/null differ
diff --git a/tests/files/ct0n0g04.jpg b/tests/files/ct0n0g04.jpg
deleted file mode 100644
index d91517aa..00000000
Binary files a/tests/files/ct0n0g04.jpg and /dev/null differ
diff --git a/tests/files/ct0n0g04.png b/tests/files/ct0n0g04.png
deleted file mode 100644
index 40d1e062..00000000
Binary files a/tests/files/ct0n0g04.png and /dev/null differ
diff --git a/tests/files/ct1n0g04.jpg b/tests/files/ct1n0g04.jpg
deleted file mode 100644
index 641b4cc9..00000000
Binary files a/tests/files/ct1n0g04.jpg and /dev/null differ
diff --git a/tests/files/ct1n0g04.png b/tests/files/ct1n0g04.png
deleted file mode 100644
index 3ba110aa..00000000
Binary files a/tests/files/ct1n0g04.png and /dev/null differ
diff --git a/tests/files/cten0g04.jpg b/tests/files/cten0g04.jpg
deleted file mode 100644
index 2f099bd4..00000000
Binary files a/tests/files/cten0g04.jpg and /dev/null differ
diff --git a/tests/files/cten0g04.png b/tests/files/cten0g04.png
deleted file mode 100644
index a6a56faf..00000000
Binary files a/tests/files/cten0g04.png and /dev/null differ
diff --git a/tests/files/ctfn0g04.jpg b/tests/files/ctfn0g04.jpg
deleted file mode 100644
index 914d17c1..00000000
Binary files a/tests/files/ctfn0g04.jpg and /dev/null differ
diff --git a/tests/files/ctfn0g04.png b/tests/files/ctfn0g04.png
deleted file mode 100644
index 353873eb..00000000
Binary files a/tests/files/ctfn0g04.png and /dev/null differ
diff --git a/tests/files/ctgn0g04.jpg b/tests/files/ctgn0g04.jpg
deleted file mode 100644
index 9a325c05..00000000
Binary files a/tests/files/ctgn0g04.jpg and /dev/null differ
diff --git a/tests/files/ctgn0g04.png b/tests/files/ctgn0g04.png
deleted file mode 100644
index 453f2b0a..00000000
Binary files a/tests/files/ctgn0g04.png and /dev/null differ
diff --git a/tests/files/cthn0g04.jpg b/tests/files/cthn0g04.jpg
deleted file mode 100644
index 53de0cc7..00000000
Binary files a/tests/files/cthn0g04.jpg and /dev/null differ
diff --git a/tests/files/cthn0g04.png b/tests/files/cthn0g04.png
deleted file mode 100644
index 8fce253e..00000000
Binary files a/tests/files/cthn0g04.png and /dev/null differ
diff --git a/tests/files/ctjn0g04.jpg b/tests/files/ctjn0g04.jpg
deleted file mode 100644
index d05a7af6..00000000
Binary files a/tests/files/ctjn0g04.jpg and /dev/null differ
diff --git a/tests/files/ctjn0g04.png b/tests/files/ctjn0g04.png
deleted file mode 100644
index a77b8d2f..00000000
Binary files a/tests/files/ctjn0g04.png and /dev/null differ
diff --git a/tests/files/ctzn0g04.jpg b/tests/files/ctzn0g04.jpg
deleted file mode 100644
index 077ed325..00000000
Binary files a/tests/files/ctzn0g04.jpg and /dev/null differ
diff --git a/tests/files/ctzn0g04.png b/tests/files/ctzn0g04.png
deleted file mode 100644
index b4401c9c..00000000
Binary files a/tests/files/ctzn0g04.png and /dev/null differ
diff --git a/tests/files/encode_test.jpg b/tests/files/encode_test.jpg
deleted file mode 100644
index 00e978d1..00000000
Binary files a/tests/files/encode_test.jpg and /dev/null differ
diff --git a/tests/files/encode_test.png b/tests/files/encode_test.png
deleted file mode 100644
index 9b8958d5..00000000
Binary files a/tests/files/encode_test.png and /dev/null differ
diff --git a/tests/files/exif/f1t.jpg b/tests/files/exif/f1t.jpg
new file mode 100644
index 00000000..1aa26b43
Binary files /dev/null and b/tests/files/exif/f1t.jpg differ
diff --git a/tests/files/exif/f2t.jpg b/tests/files/exif/f2t.jpg
new file mode 100644
index 00000000..15065b08
Binary files /dev/null and b/tests/files/exif/f2t.jpg differ
diff --git a/tests/files/exif/f3t.jpg b/tests/files/exif/f3t.jpg
new file mode 100644
index 00000000..b783b6b8
Binary files /dev/null and b/tests/files/exif/f3t.jpg differ
diff --git a/tests/files/exif/f4t.jpg b/tests/files/exif/f4t.jpg
new file mode 100644
index 00000000..6928bc8d
Binary files /dev/null and b/tests/files/exif/f4t.jpg differ
diff --git a/tests/files/exif/f5t.jpg b/tests/files/exif/f5t.jpg
new file mode 100644
index 00000000..81b224b3
Binary files /dev/null and b/tests/files/exif/f5t.jpg differ
diff --git a/tests/files/exif/f6t.jpg b/tests/files/exif/f6t.jpg
new file mode 100644
index 00000000..e5d22424
Binary files /dev/null and b/tests/files/exif/f6t.jpg differ
diff --git a/tests/files/exif/f7t.jpg b/tests/files/exif/f7t.jpg
new file mode 100644
index 00000000..ff09095e
Binary files /dev/null and b/tests/files/exif/f7t.jpg differ
diff --git a/tests/files/exif/f8t.jpg b/tests/files/exif/f8t.jpg
new file mode 100644
index 00000000..35eca2fe
Binary files /dev/null and b/tests/files/exif/f8t.jpg differ
diff --git a/tests/files/exif2c08.jpg b/tests/files/exif2c08.jpg
deleted file mode 100644
index e4aa4216..00000000
Binary files a/tests/files/exif2c08.jpg and /dev/null differ
diff --git a/tests/files/exif2c08.png b/tests/files/exif2c08.png
deleted file mode 100644
index 56eb7349..00000000
Binary files a/tests/files/exif2c08.png and /dev/null differ
diff --git a/tests/files/f00n0g08.jpg b/tests/files/f00n0g08.jpg
deleted file mode 100644
index b235c9a5..00000000
Binary files a/tests/files/f00n0g08.jpg and /dev/null differ
diff --git a/tests/files/f00n0g08.png b/tests/files/f00n0g08.png
deleted file mode 100644
index 45a00759..00000000
Binary files a/tests/files/f00n0g08.png and /dev/null differ
diff --git a/tests/files/f00n2c08.jpg b/tests/files/f00n2c08.jpg
deleted file mode 100644
index 1b947b65..00000000
Binary files a/tests/files/f00n2c08.jpg and /dev/null differ
diff --git a/tests/files/f00n2c08.png b/tests/files/f00n2c08.png
deleted file mode 100644
index d6a1ffff..00000000
Binary files a/tests/files/f00n2c08.png and /dev/null differ
diff --git a/tests/files/f01n0g08.jpg b/tests/files/f01n0g08.jpg
deleted file mode 100644
index 78aaa006..00000000
Binary files a/tests/files/f01n0g08.jpg and /dev/null differ
diff --git a/tests/files/f01n0g08.png b/tests/files/f01n0g08.png
deleted file mode 100644
index 4a1107b4..00000000
Binary files a/tests/files/f01n0g08.png and /dev/null differ
diff --git a/tests/files/f01n2c08.jpg b/tests/files/f01n2c08.jpg
deleted file mode 100644
index e403b5bc..00000000
Binary files a/tests/files/f01n2c08.jpg and /dev/null differ
diff --git a/tests/files/f01n2c08.png b/tests/files/f01n2c08.png
deleted file mode 100644
index 26fee958..00000000
Binary files a/tests/files/f01n2c08.png and /dev/null differ
diff --git a/tests/files/f02n0g08.jpg b/tests/files/f02n0g08.jpg
deleted file mode 100644
index a17fd83e..00000000
Binary files a/tests/files/f02n0g08.jpg and /dev/null differ
diff --git a/tests/files/f02n0g08.png b/tests/files/f02n0g08.png
deleted file mode 100644
index bfe410c5..00000000
Binary files a/tests/files/f02n0g08.png and /dev/null differ
diff --git a/tests/files/f02n2c08.jpg b/tests/files/f02n2c08.jpg
deleted file mode 100644
index f5049124..00000000
Binary files a/tests/files/f02n2c08.jpg and /dev/null differ
diff --git a/tests/files/f02n2c08.png b/tests/files/f02n2c08.png
deleted file mode 100644
index e590f123..00000000
Binary files a/tests/files/f02n2c08.png and /dev/null differ
diff --git a/tests/files/f03n0g08.jpg b/tests/files/f03n0g08.jpg
deleted file mode 100644
index 4fa33d49..00000000
Binary files a/tests/files/f03n0g08.jpg and /dev/null differ
diff --git a/tests/files/f03n0g08.png b/tests/files/f03n0g08.png
deleted file mode 100644
index ed01e292..00000000
Binary files a/tests/files/f03n0g08.png and /dev/null differ
diff --git a/tests/files/f03n2c08.jpg b/tests/files/f03n2c08.jpg
deleted file mode 100644
index e2ef394f..00000000
Binary files a/tests/files/f03n2c08.jpg and /dev/null differ
diff --git a/tests/files/f03n2c08.png b/tests/files/f03n2c08.png
deleted file mode 100644
index 75811505..00000000
Binary files a/tests/files/f03n2c08.png and /dev/null differ
diff --git a/tests/files/f04n0g08.jpg b/tests/files/f04n0g08.jpg
deleted file mode 100644
index 7c2f9eda..00000000
Binary files a/tests/files/f04n0g08.jpg and /dev/null differ
diff --git a/tests/files/f04n0g08.png b/tests/files/f04n0g08.png
deleted file mode 100644
index 663fdae3..00000000
Binary files a/tests/files/f04n0g08.png and /dev/null differ
diff --git a/tests/files/f04n2c08.jpg b/tests/files/f04n2c08.jpg
deleted file mode 100644
index 598fe89b..00000000
Binary files a/tests/files/f04n2c08.jpg and /dev/null differ
diff --git a/tests/files/f04n2c08.png b/tests/files/f04n2c08.png
deleted file mode 100644
index 3c8b5116..00000000
Binary files a/tests/files/f04n2c08.png and /dev/null differ
diff --git a/tests/files/f99n0g04.jpg b/tests/files/f99n0g04.jpg
deleted file mode 100644
index 36563577..00000000
Binary files a/tests/files/f99n0g04.jpg and /dev/null differ
diff --git a/tests/files/f99n0g04.png b/tests/files/f99n0g04.png
deleted file mode 100644
index 0b521c1d..00000000
Binary files a/tests/files/f99n0g04.png and /dev/null differ
diff --git a/tests/files/g03n0g16.jpg b/tests/files/g03n0g16.jpg
deleted file mode 100644
index e9948eee..00000000
Binary files a/tests/files/g03n0g16.jpg and /dev/null differ
diff --git a/tests/files/g03n0g16.png b/tests/files/g03n0g16.png
deleted file mode 100644
index 41083ca8..00000000
Binary files a/tests/files/g03n0g16.png and /dev/null differ
diff --git a/tests/files/g03n2c08.jpg b/tests/files/g03n2c08.jpg
deleted file mode 100644
index df904676..00000000
Binary files a/tests/files/g03n2c08.jpg and /dev/null differ
diff --git a/tests/files/g03n2c08.png b/tests/files/g03n2c08.png
deleted file mode 100644
index a9354dbe..00000000
Binary files a/tests/files/g03n2c08.png and /dev/null differ
diff --git a/tests/files/g03n3p04.jpg b/tests/files/g03n3p04.jpg
deleted file mode 100644
index 5097fc1a..00000000
Binary files a/tests/files/g03n3p04.jpg and /dev/null differ
diff --git a/tests/files/g03n3p04.png b/tests/files/g03n3p04.png
deleted file mode 100644
index 60396c95..00000000
Binary files a/tests/files/g03n3p04.png and /dev/null differ
diff --git a/tests/files/g04n0g16.jpg b/tests/files/g04n0g16.jpg
deleted file mode 100644
index bd79f449..00000000
Binary files a/tests/files/g04n0g16.jpg and /dev/null differ
diff --git a/tests/files/g04n0g16.png b/tests/files/g04n0g16.png
deleted file mode 100644
index 32395b76..00000000
Binary files a/tests/files/g04n0g16.png and /dev/null differ
diff --git a/tests/files/g04n2c08.jpg b/tests/files/g04n2c08.jpg
deleted file mode 100644
index e7b0f899..00000000
Binary files a/tests/files/g04n2c08.jpg and /dev/null differ
diff --git a/tests/files/g04n2c08.png b/tests/files/g04n2c08.png
deleted file mode 100644
index a652b0ce..00000000
Binary files a/tests/files/g04n2c08.png and /dev/null differ
diff --git a/tests/files/g04n3p04.jpg b/tests/files/g04n3p04.jpg
deleted file mode 100644
index 20365c56..00000000
Binary files a/tests/files/g04n3p04.jpg and /dev/null differ
diff --git a/tests/files/g04n3p04.png b/tests/files/g04n3p04.png
deleted file mode 100644
index 5661cc31..00000000
Binary files a/tests/files/g04n3p04.png and /dev/null differ
diff --git a/tests/files/g05n0g16.jpg b/tests/files/g05n0g16.jpg
deleted file mode 100644
index 5fe67d88..00000000
Binary files a/tests/files/g05n0g16.jpg and /dev/null differ
diff --git a/tests/files/g05n0g16.png b/tests/files/g05n0g16.png
deleted file mode 100644
index 70b37f01..00000000
Binary files a/tests/files/g05n0g16.png and /dev/null differ
diff --git a/tests/files/g05n2c08.jpg b/tests/files/g05n2c08.jpg
deleted file mode 100644
index fc34b130..00000000
Binary files a/tests/files/g05n2c08.jpg and /dev/null differ
diff --git a/tests/files/g05n2c08.png b/tests/files/g05n2c08.png
deleted file mode 100644
index 932c1365..00000000
Binary files a/tests/files/g05n2c08.png and /dev/null differ
diff --git a/tests/files/g05n3p04.jpg b/tests/files/g05n3p04.jpg
deleted file mode 100644
index fe22fe1f..00000000
Binary files a/tests/files/g05n3p04.jpg and /dev/null differ
diff --git a/tests/files/g05n3p04.png b/tests/files/g05n3p04.png
deleted file mode 100644
index 96199305..00000000
Binary files a/tests/files/g05n3p04.png and /dev/null differ
diff --git a/tests/files/g07n0g16.jpg b/tests/files/g07n0g16.jpg
deleted file mode 100644
index faf10761..00000000
Binary files a/tests/files/g07n0g16.jpg and /dev/null differ
diff --git a/tests/files/g07n0g16.png b/tests/files/g07n0g16.png
deleted file mode 100644
index d6a47c2d..00000000
Binary files a/tests/files/g07n0g16.png and /dev/null differ
diff --git a/tests/files/g07n2c08.jpg b/tests/files/g07n2c08.jpg
deleted file mode 100644
index f25b84de..00000000
Binary files a/tests/files/g07n2c08.jpg and /dev/null differ
diff --git a/tests/files/g07n2c08.png b/tests/files/g07n2c08.png
deleted file mode 100644
index 59734646..00000000
Binary files a/tests/files/g07n2c08.png and /dev/null differ
diff --git a/tests/files/g07n3p04.jpg b/tests/files/g07n3p04.jpg
deleted file mode 100644
index c74d34f9..00000000
Binary files a/tests/files/g07n3p04.jpg and /dev/null differ
diff --git a/tests/files/g07n3p04.png b/tests/files/g07n3p04.png
deleted file mode 100644
index c73fb613..00000000
Binary files a/tests/files/g07n3p04.png and /dev/null differ
diff --git a/tests/files/g10n0g16.jpg b/tests/files/g10n0g16.jpg
deleted file mode 100644
index 6f3a1cec..00000000
Binary files a/tests/files/g10n0g16.jpg and /dev/null differ
diff --git a/tests/files/g10n0g16.png b/tests/files/g10n0g16.png
deleted file mode 100644
index 85f2c958..00000000
Binary files a/tests/files/g10n0g16.png and /dev/null differ
diff --git a/tests/files/g10n2c08.jpg b/tests/files/g10n2c08.jpg
deleted file mode 100644
index cb03a1b3..00000000
Binary files a/tests/files/g10n2c08.jpg and /dev/null differ
diff --git a/tests/files/g10n2c08.png b/tests/files/g10n2c08.png
deleted file mode 100644
index b3039970..00000000
Binary files a/tests/files/g10n2c08.png and /dev/null differ
diff --git a/tests/files/g10n3p04.jpg b/tests/files/g10n3p04.jpg
deleted file mode 100644
index 7d9d71c2..00000000
Binary files a/tests/files/g10n3p04.jpg and /dev/null differ
diff --git a/tests/files/g10n3p04.png b/tests/files/g10n3p04.png
deleted file mode 100644
index 1b6a6be2..00000000
Binary files a/tests/files/g10n3p04.png and /dev/null differ
diff --git a/tests/files/g25n0g16.jpg b/tests/files/g25n0g16.jpg
deleted file mode 100644
index 49170153..00000000
Binary files a/tests/files/g25n0g16.jpg and /dev/null differ
diff --git a/tests/files/g25n0g16.png b/tests/files/g25n0g16.png
deleted file mode 100644
index a9f6787c..00000000
Binary files a/tests/files/g25n0g16.png and /dev/null differ
diff --git a/tests/files/g25n2c08.jpg b/tests/files/g25n2c08.jpg
deleted file mode 100644
index f58d2b06..00000000
Binary files a/tests/files/g25n2c08.jpg and /dev/null differ
diff --git a/tests/files/g25n2c08.png b/tests/files/g25n2c08.png
deleted file mode 100644
index 03f505a6..00000000
Binary files a/tests/files/g25n2c08.png and /dev/null differ
diff --git a/tests/files/g25n3p04.jpg b/tests/files/g25n3p04.jpg
deleted file mode 100644
index 1ec0005e..00000000
Binary files a/tests/files/g25n3p04.jpg and /dev/null differ
diff --git a/tests/files/g25n3p04.png b/tests/files/g25n3p04.png
deleted file mode 100644
index 4f943c61..00000000
Binary files a/tests/files/g25n3p04.png and /dev/null differ
diff --git a/tests/files/jpg/f1t.jpg b/tests/files/jpg/f1t.jpg
new file mode 100644
index 00000000..1aa26b43
Binary files /dev/null and b/tests/files/jpg/f1t.jpg differ
diff --git a/tests/files/jxl/f1t.jxl b/tests/files/jxl/f1t.jxl
new file mode 100644
index 00000000..a6c88358
Binary files /dev/null and b/tests/files/jxl/f1t.jxl differ
diff --git a/tests/files/oi1n0g16.jpg b/tests/files/oi1n0g16.jpg
deleted file mode 100644
index c12e17e8..00000000
Binary files a/tests/files/oi1n0g16.jpg and /dev/null differ
diff --git a/tests/files/oi1n0g16.png b/tests/files/oi1n0g16.png
deleted file mode 100644
index e7c82f78..00000000
Binary files a/tests/files/oi1n0g16.png and /dev/null differ
diff --git a/tests/files/oi1n2c16.jpg b/tests/files/oi1n2c16.jpg
deleted file mode 100644
index 8ea78e8e..00000000
Binary files a/tests/files/oi1n2c16.jpg and /dev/null differ
diff --git a/tests/files/oi1n2c16.png b/tests/files/oi1n2c16.png
deleted file mode 100644
index 50c1cb91..00000000
Binary files a/tests/files/oi1n2c16.png and /dev/null differ
diff --git a/tests/files/oi2n0g16.jpg b/tests/files/oi2n0g16.jpg
deleted file mode 100644
index 8d327cf8..00000000
Binary files a/tests/files/oi2n0g16.jpg and /dev/null differ
diff --git a/tests/files/oi2n0g16.png b/tests/files/oi2n0g16.png
deleted file mode 100644
index 14d64c58..00000000
Binary files a/tests/files/oi2n0g16.png and /dev/null differ
diff --git a/tests/files/oi2n2c16.jpg b/tests/files/oi2n2c16.jpg
deleted file mode 100644
index 56f35480..00000000
Binary files a/tests/files/oi2n2c16.jpg and /dev/null differ
diff --git a/tests/files/oi2n2c16.png b/tests/files/oi2n2c16.png
deleted file mode 100644
index 4c2e3e33..00000000
Binary files a/tests/files/oi2n2c16.png and /dev/null differ
diff --git a/tests/files/oi4n0g16.jpg b/tests/files/oi4n0g16.jpg
deleted file mode 100644
index c387e2eb..00000000
Binary files a/tests/files/oi4n0g16.jpg and /dev/null differ
diff --git a/tests/files/oi4n0g16.png b/tests/files/oi4n0g16.png
deleted file mode 100644
index 69e73ede..00000000
Binary files a/tests/files/oi4n0g16.png and /dev/null differ
diff --git a/tests/files/oi4n2c16.jpg b/tests/files/oi4n2c16.jpg
deleted file mode 100644
index 0e44b7c5..00000000
Binary files a/tests/files/oi4n2c16.jpg and /dev/null differ
diff --git a/tests/files/oi4n2c16.png b/tests/files/oi4n2c16.png
deleted file mode 100644
index 93691e37..00000000
Binary files a/tests/files/oi4n2c16.png and /dev/null differ
diff --git a/tests/files/oi9n0g16.jpg b/tests/files/oi9n0g16.jpg
deleted file mode 100644
index 1cecc141..00000000
Binary files a/tests/files/oi9n0g16.jpg and /dev/null differ
diff --git a/tests/files/oi9n0g16.png b/tests/files/oi9n0g16.png
deleted file mode 100644
index 92484135..00000000
Binary files a/tests/files/oi9n0g16.png and /dev/null differ
diff --git a/tests/files/oi9n2c16.jpg b/tests/files/oi9n2c16.jpg
deleted file mode 100644
index 9d9ba1cc..00000000
Binary files a/tests/files/oi9n2c16.jpg and /dev/null differ
diff --git a/tests/files/oi9n2c16.png b/tests/files/oi9n2c16.png
deleted file mode 100644
index f0512e49..00000000
Binary files a/tests/files/oi9n2c16.png and /dev/null differ
diff --git a/tests/files/png/f1t.png b/tests/files/png/f1t.png
new file mode 100644
index 00000000..45a9e433
Binary files /dev/null and b/tests/files/png/f1t.png differ
diff --git a/tests/files/pp0n2c16.jpg b/tests/files/pp0n2c16.jpg
deleted file mode 100644
index 6f466893..00000000
Binary files a/tests/files/pp0n2c16.jpg and /dev/null differ
diff --git a/tests/files/pp0n2c16.png b/tests/files/pp0n2c16.png
deleted file mode 100644
index 8f2aad73..00000000
Binary files a/tests/files/pp0n2c16.png and /dev/null differ
diff --git a/tests/files/pp0n6a08.jpg b/tests/files/pp0n6a08.jpg
deleted file mode 100644
index 06ed8a9c..00000000
Binary files a/tests/files/pp0n6a08.jpg and /dev/null differ
diff --git a/tests/files/pp0n6a08.png b/tests/files/pp0n6a08.png
deleted file mode 100644
index 4ed7a30e..00000000
Binary files a/tests/files/pp0n6a08.png and /dev/null differ
diff --git a/tests/files/ps1n0g08.jpg b/tests/files/ps1n0g08.jpg
deleted file mode 100644
index 3e11f668..00000000
Binary files a/tests/files/ps1n0g08.jpg and /dev/null differ
diff --git a/tests/files/ps1n0g08.png b/tests/files/ps1n0g08.png
deleted file mode 100644
index 99625fa4..00000000
Binary files a/tests/files/ps1n0g08.png and /dev/null differ
diff --git a/tests/files/ps1n2c16.jpg b/tests/files/ps1n2c16.jpg
deleted file mode 100644
index b49032d7..00000000
Binary files a/tests/files/ps1n2c16.jpg and /dev/null differ
diff --git a/tests/files/ps1n2c16.png b/tests/files/ps1n2c16.png
deleted file mode 100644
index 0c7a6b38..00000000
Binary files a/tests/files/ps1n2c16.png and /dev/null differ
diff --git a/tests/files/ps2n0g08.jpg b/tests/files/ps2n0g08.jpg
deleted file mode 100644
index f24ed72c..00000000
Binary files a/tests/files/ps2n0g08.jpg and /dev/null differ
diff --git a/tests/files/ps2n0g08.png b/tests/files/ps2n0g08.png
deleted file mode 100644
index 90b29796..00000000
Binary files a/tests/files/ps2n0g08.png and /dev/null differ
diff --git a/tests/files/ps2n2c16.jpg b/tests/files/ps2n2c16.jpg
deleted file mode 100644
index 5723f060..00000000
Binary files a/tests/files/ps2n2c16.jpg and /dev/null differ
diff --git a/tests/files/ps2n2c16.png b/tests/files/ps2n2c16.png
deleted file mode 100644
index a4a181e4..00000000
Binary files a/tests/files/ps2n2c16.png and /dev/null differ
diff --git a/tests/files/s01i3p01.jpg b/tests/files/s01i3p01.jpg
deleted file mode 100644
index 962f3822..00000000
Binary files a/tests/files/s01i3p01.jpg and /dev/null differ
diff --git a/tests/files/s01i3p01.png b/tests/files/s01i3p01.png
deleted file mode 100644
index 6c0fad1f..00000000
Binary files a/tests/files/s01i3p01.png and /dev/null differ
diff --git a/tests/files/s01n3p01.jpg b/tests/files/s01n3p01.jpg
deleted file mode 100644
index 962f3822..00000000
Binary files a/tests/files/s01n3p01.jpg and /dev/null differ
diff --git a/tests/files/s01n3p01.png b/tests/files/s01n3p01.png
deleted file mode 100644
index cb2c8c78..00000000
Binary files a/tests/files/s01n3p01.png and /dev/null differ
diff --git a/tests/files/s02i3p01.jpg b/tests/files/s02i3p01.jpg
deleted file mode 100644
index 33ef7ef5..00000000
Binary files a/tests/files/s02i3p01.jpg and /dev/null differ
diff --git a/tests/files/s02i3p01.png b/tests/files/s02i3p01.png
deleted file mode 100644
index 2defaed9..00000000
Binary files a/tests/files/s02i3p01.png and /dev/null differ
diff --git a/tests/files/s02n3p01.jpg b/tests/files/s02n3p01.jpg
deleted file mode 100644
index 33ef7ef5..00000000
Binary files a/tests/files/s02n3p01.jpg and /dev/null differ
diff --git a/tests/files/s02n3p01.png b/tests/files/s02n3p01.png
deleted file mode 100644
index 2b1b6696..00000000
Binary files a/tests/files/s02n3p01.png and /dev/null differ
diff --git a/tests/files/s03i3p01.jpg b/tests/files/s03i3p01.jpg
deleted file mode 100644
index d345eaad..00000000
Binary files a/tests/files/s03i3p01.jpg and /dev/null differ
diff --git a/tests/files/s03i3p01.png b/tests/files/s03i3p01.png
deleted file mode 100644
index c23fdc46..00000000
Binary files a/tests/files/s03i3p01.png and /dev/null differ
diff --git a/tests/files/s03n3p01.jpg b/tests/files/s03n3p01.jpg
deleted file mode 100644
index d345eaad..00000000
Binary files a/tests/files/s03n3p01.jpg and /dev/null differ
diff --git a/tests/files/s03n3p01.png b/tests/files/s03n3p01.png
deleted file mode 100644
index 6d96ee4f..00000000
Binary files a/tests/files/s03n3p01.png and /dev/null differ
diff --git a/tests/files/s04i3p01.jpg b/tests/files/s04i3p01.jpg
deleted file mode 100644
index 069865e2..00000000
Binary files a/tests/files/s04i3p01.jpg and /dev/null differ
diff --git a/tests/files/s04i3p01.png b/tests/files/s04i3p01.png
deleted file mode 100644
index 0e710c2c..00000000
Binary files a/tests/files/s04i3p01.png and /dev/null differ
diff --git a/tests/files/s04n3p01.jpg b/tests/files/s04n3p01.jpg
deleted file mode 100644
index 069865e2..00000000
Binary files a/tests/files/s04n3p01.jpg and /dev/null differ
diff --git a/tests/files/s04n3p01.png b/tests/files/s04n3p01.png
deleted file mode 100644
index 956396c4..00000000
Binary files a/tests/files/s04n3p01.png and /dev/null differ
diff --git a/tests/files/s05i3p02.jpg b/tests/files/s05i3p02.jpg
deleted file mode 100644
index e3a3bac1..00000000
Binary files a/tests/files/s05i3p02.jpg and /dev/null differ
diff --git a/tests/files/s05i3p02.png b/tests/files/s05i3p02.png
deleted file mode 100644
index d14cbd35..00000000
Binary files a/tests/files/s05i3p02.png and /dev/null differ
diff --git a/tests/files/s05n3p02.jpg b/tests/files/s05n3p02.jpg
deleted file mode 100644
index e3a3bac1..00000000
Binary files a/tests/files/s05n3p02.jpg and /dev/null differ
diff --git a/tests/files/s05n3p02.png b/tests/files/s05n3p02.png
deleted file mode 100644
index bf940f05..00000000
Binary files a/tests/files/s05n3p02.png and /dev/null differ
diff --git a/tests/files/s06i3p02.jpg b/tests/files/s06i3p02.jpg
deleted file mode 100644
index 49dfd0f2..00000000
Binary files a/tests/files/s06i3p02.jpg and /dev/null differ
diff --git a/tests/files/s06i3p02.png b/tests/files/s06i3p02.png
deleted file mode 100644
index 456ada32..00000000
Binary files a/tests/files/s06i3p02.png and /dev/null differ
diff --git a/tests/files/s06n3p02.jpg b/tests/files/s06n3p02.jpg
deleted file mode 100644
index 49dfd0f2..00000000
Binary files a/tests/files/s06n3p02.jpg and /dev/null differ
diff --git a/tests/files/s06n3p02.png b/tests/files/s06n3p02.png
deleted file mode 100644
index 501064dc..00000000
Binary files a/tests/files/s06n3p02.png and /dev/null differ
diff --git a/tests/files/s07i3p02.jpg b/tests/files/s07i3p02.jpg
deleted file mode 100644
index 7ad19605..00000000
Binary files a/tests/files/s07i3p02.jpg and /dev/null differ
diff --git a/tests/files/s07i3p02.png b/tests/files/s07i3p02.png
deleted file mode 100644
index 44b66bab..00000000
Binary files a/tests/files/s07i3p02.png and /dev/null differ
diff --git a/tests/files/s07n3p02.jpg b/tests/files/s07n3p02.jpg
deleted file mode 100644
index 7ad19605..00000000
Binary files a/tests/files/s07n3p02.jpg and /dev/null differ
diff --git a/tests/files/s07n3p02.png b/tests/files/s07n3p02.png
deleted file mode 100644
index 6a582593..00000000
Binary files a/tests/files/s07n3p02.png and /dev/null differ
diff --git a/tests/files/s08i3p02.jpg b/tests/files/s08i3p02.jpg
deleted file mode 100644
index 65fe11da..00000000
Binary files a/tests/files/s08i3p02.jpg and /dev/null differ
diff --git a/tests/files/s08i3p02.png b/tests/files/s08i3p02.png
deleted file mode 100644
index acf74f3f..00000000
Binary files a/tests/files/s08i3p02.png and /dev/null differ
diff --git a/tests/files/s08n3p02.jpg b/tests/files/s08n3p02.jpg
deleted file mode 100644
index 65fe11da..00000000
Binary files a/tests/files/s08n3p02.jpg and /dev/null differ
diff --git a/tests/files/s08n3p02.png b/tests/files/s08n3p02.png
deleted file mode 100644
index b7094e1b..00000000
Binary files a/tests/files/s08n3p02.png and /dev/null differ
diff --git a/tests/files/s09i3p02.jpg b/tests/files/s09i3p02.jpg
deleted file mode 100644
index ed6ef23d..00000000
Binary files a/tests/files/s09i3p02.jpg and /dev/null differ
diff --git a/tests/files/s09i3p02.png b/tests/files/s09i3p02.png
deleted file mode 100644
index 0bfae8e4..00000000
Binary files a/tests/files/s09i3p02.png and /dev/null differ
diff --git a/tests/files/s09n3p02.jpg b/tests/files/s09n3p02.jpg
deleted file mode 100644
index ed6ef23d..00000000
Binary files a/tests/files/s09n3p02.jpg and /dev/null differ
diff --git a/tests/files/s09n3p02.png b/tests/files/s09n3p02.png
deleted file mode 100644
index 711ab824..00000000
Binary files a/tests/files/s09n3p02.png and /dev/null differ
diff --git a/tests/files/s32i3p04.jpg b/tests/files/s32i3p04.jpg
deleted file mode 100644
index 8aeb59d1..00000000
Binary files a/tests/files/s32i3p04.jpg and /dev/null differ
diff --git a/tests/files/s32i3p04.png b/tests/files/s32i3p04.png
deleted file mode 100644
index 0841910b..00000000
Binary files a/tests/files/s32i3p04.png and /dev/null differ
diff --git a/tests/files/s32n3p04.jpg b/tests/files/s32n3p04.jpg
deleted file mode 100644
index 8aeb59d1..00000000
Binary files a/tests/files/s32n3p04.jpg and /dev/null differ
diff --git a/tests/files/s32n3p04.png b/tests/files/s32n3p04.png
deleted file mode 100644
index fa58e3e3..00000000
Binary files a/tests/files/s32n3p04.png and /dev/null differ
diff --git a/tests/files/s33i3p04.jpg b/tests/files/s33i3p04.jpg
deleted file mode 100644
index 5f762746..00000000
Binary files a/tests/files/s33i3p04.jpg and /dev/null differ
diff --git a/tests/files/s33i3p04.png b/tests/files/s33i3p04.png
deleted file mode 100644
index ab0dc14a..00000000
Binary files a/tests/files/s33i3p04.png and /dev/null differ
diff --git a/tests/files/s33n3p04.jpg b/tests/files/s33n3p04.jpg
deleted file mode 100644
index 5f762746..00000000
Binary files a/tests/files/s33n3p04.jpg and /dev/null differ
diff --git a/tests/files/s33n3p04.png b/tests/files/s33n3p04.png
deleted file mode 100644
index 764f1a3d..00000000
Binary files a/tests/files/s33n3p04.png and /dev/null differ
diff --git a/tests/files/s34i3p04.jpg b/tests/files/s34i3p04.jpg
deleted file mode 100644
index a8ffda04..00000000
Binary files a/tests/files/s34i3p04.jpg and /dev/null differ
diff --git a/tests/files/s34i3p04.png b/tests/files/s34i3p04.png
deleted file mode 100644
index bd99039b..00000000
Binary files a/tests/files/s34i3p04.png and /dev/null differ
diff --git a/tests/files/s34n3p04.jpg b/tests/files/s34n3p04.jpg
deleted file mode 100644
index a8ffda04..00000000
Binary files a/tests/files/s34n3p04.jpg and /dev/null differ
diff --git a/tests/files/s34n3p04.png b/tests/files/s34n3p04.png
deleted file mode 100644
index 9cbc68b3..00000000
Binary files a/tests/files/s34n3p04.png and /dev/null differ
diff --git a/tests/files/s35i3p04.jpg b/tests/files/s35i3p04.jpg
deleted file mode 100644
index 48ad662f..00000000
Binary files a/tests/files/s35i3p04.jpg and /dev/null differ
diff --git a/tests/files/s35i3p04.png b/tests/files/s35i3p04.png
deleted file mode 100644
index e2a5e0a6..00000000
Binary files a/tests/files/s35i3p04.png and /dev/null differ
diff --git a/tests/files/s35n3p04.jpg b/tests/files/s35n3p04.jpg
deleted file mode 100644
index 48ad662f..00000000
Binary files a/tests/files/s35n3p04.jpg and /dev/null differ
diff --git a/tests/files/s35n3p04.png b/tests/files/s35n3p04.png
deleted file mode 100644
index 90b892eb..00000000
Binary files a/tests/files/s35n3p04.png and /dev/null differ
diff --git a/tests/files/s36i3p04.jpg b/tests/files/s36i3p04.jpg
deleted file mode 100644
index a75b9f61..00000000
Binary files a/tests/files/s36i3p04.jpg and /dev/null differ
diff --git a/tests/files/s36i3p04.png b/tests/files/s36i3p04.png
deleted file mode 100644
index eb61b6f9..00000000
Binary files a/tests/files/s36i3p04.png and /dev/null differ
diff --git a/tests/files/s36n3p04.jpg b/tests/files/s36n3p04.jpg
deleted file mode 100644
index a75b9f61..00000000
Binary files a/tests/files/s36n3p04.jpg and /dev/null differ
diff --git a/tests/files/s36n3p04.png b/tests/files/s36n3p04.png
deleted file mode 100644
index b38d1797..00000000
Binary files a/tests/files/s36n3p04.png and /dev/null differ
diff --git a/tests/files/s37i3p04.jpg b/tests/files/s37i3p04.jpg
deleted file mode 100644
index 4352a499..00000000
Binary files a/tests/files/s37i3p04.jpg and /dev/null differ
diff --git a/tests/files/s37i3p04.png b/tests/files/s37i3p04.png
deleted file mode 100644
index 6e2b1e9b..00000000
Binary files a/tests/files/s37i3p04.png and /dev/null differ
diff --git a/tests/files/s37n3p04.jpg b/tests/files/s37n3p04.jpg
deleted file mode 100644
index 4352a499..00000000
Binary files a/tests/files/s37n3p04.jpg and /dev/null differ
diff --git a/tests/files/s37n3p04.png b/tests/files/s37n3p04.png
deleted file mode 100644
index 4d3054da..00000000
Binary files a/tests/files/s37n3p04.png and /dev/null differ
diff --git a/tests/files/s38i3p04.jpg b/tests/files/s38i3p04.jpg
deleted file mode 100644
index e0d1f4c2..00000000
Binary files a/tests/files/s38i3p04.jpg and /dev/null differ
diff --git a/tests/files/s38i3p04.png b/tests/files/s38i3p04.png
deleted file mode 100644
index a0a8a140..00000000
Binary files a/tests/files/s38i3p04.png and /dev/null differ
diff --git a/tests/files/s38n3p04.jpg b/tests/files/s38n3p04.jpg
deleted file mode 100644
index e0d1f4c2..00000000
Binary files a/tests/files/s38n3p04.jpg and /dev/null differ
diff --git a/tests/files/s38n3p04.png b/tests/files/s38n3p04.png
deleted file mode 100644
index 1233ed04..00000000
Binary files a/tests/files/s38n3p04.png and /dev/null differ
diff --git a/tests/files/s39i3p04.jpg b/tests/files/s39i3p04.jpg
deleted file mode 100644
index 1437f7a8..00000000
Binary files a/tests/files/s39i3p04.jpg and /dev/null differ
diff --git a/tests/files/s39i3p04.png b/tests/files/s39i3p04.png
deleted file mode 100644
index 04fee93e..00000000
Binary files a/tests/files/s39i3p04.png and /dev/null differ
diff --git a/tests/files/s39n3p04.jpg b/tests/files/s39n3p04.jpg
deleted file mode 100644
index 1437f7a8..00000000
Binary files a/tests/files/s39n3p04.jpg and /dev/null differ
diff --git a/tests/files/s39n3p04.png b/tests/files/s39n3p04.png
deleted file mode 100644
index c750100d..00000000
Binary files a/tests/files/s39n3p04.png and /dev/null differ
diff --git a/tests/files/s40i3p04.jpg b/tests/files/s40i3p04.jpg
deleted file mode 100644
index 4862b152..00000000
Binary files a/tests/files/s40i3p04.jpg and /dev/null differ
diff --git a/tests/files/s40i3p04.png b/tests/files/s40i3p04.png
deleted file mode 100644
index 68f358b8..00000000
Binary files a/tests/files/s40i3p04.png and /dev/null differ
diff --git a/tests/files/s40n3p04.jpg b/tests/files/s40n3p04.jpg
deleted file mode 100644
index 4862b152..00000000
Binary files a/tests/files/s40n3p04.jpg and /dev/null differ
diff --git a/tests/files/s40n3p04.png b/tests/files/s40n3p04.png
deleted file mode 100644
index 864b6b96..00000000
Binary files a/tests/files/s40n3p04.png and /dev/null differ
diff --git a/tests/files/tbbn0g04.jpg b/tests/files/tbbn0g04.jpg
deleted file mode 100644
index ac120016..00000000
Binary files a/tests/files/tbbn0g04.jpg and /dev/null differ
diff --git a/tests/files/tbbn0g04.png b/tests/files/tbbn0g04.png
deleted file mode 100644
index 39a7050d..00000000
Binary files a/tests/files/tbbn0g04.png and /dev/null differ
diff --git a/tests/files/tbbn2c16.jpg b/tests/files/tbbn2c16.jpg
deleted file mode 100644
index 21d9404b..00000000
Binary files a/tests/files/tbbn2c16.jpg and /dev/null differ
diff --git a/tests/files/tbbn2c16.png b/tests/files/tbbn2c16.png
deleted file mode 100644
index dd3168e5..00000000
Binary files a/tests/files/tbbn2c16.png and /dev/null differ
diff --git a/tests/files/tbbn3p08.jpg b/tests/files/tbbn3p08.jpg
deleted file mode 100644
index 2d1505cd..00000000
Binary files a/tests/files/tbbn3p08.jpg and /dev/null differ
diff --git a/tests/files/tbbn3p08.png b/tests/files/tbbn3p08.png
deleted file mode 100644
index 0ede3574..00000000
Binary files a/tests/files/tbbn3p08.png and /dev/null differ
diff --git a/tests/files/tbgn2c16.jpg b/tests/files/tbgn2c16.jpg
deleted file mode 100644
index 011c65bb..00000000
Binary files a/tests/files/tbgn2c16.jpg and /dev/null differ
diff --git a/tests/files/tbgn2c16.png b/tests/files/tbgn2c16.png
deleted file mode 100644
index 85cec395..00000000
Binary files a/tests/files/tbgn2c16.png and /dev/null differ
diff --git a/tests/files/tbgn3p08.jpg b/tests/files/tbgn3p08.jpg
deleted file mode 100644
index 2d1505cd..00000000
Binary files a/tests/files/tbgn3p08.jpg and /dev/null differ
diff --git a/tests/files/tbgn3p08.png b/tests/files/tbgn3p08.png
deleted file mode 100644
index 8cf2e6fb..00000000
Binary files a/tests/files/tbgn3p08.png and /dev/null differ
diff --git a/tests/files/tbrn2c08.jpg b/tests/files/tbrn2c08.jpg
deleted file mode 100644
index 7394f718..00000000
Binary files a/tests/files/tbrn2c08.jpg and /dev/null differ
diff --git a/tests/files/tbrn2c08.png b/tests/files/tbrn2c08.png
deleted file mode 100644
index 5cca0d62..00000000
Binary files a/tests/files/tbrn2c08.png and /dev/null differ
diff --git a/tests/files/tbwn0g16.jpg b/tests/files/tbwn0g16.jpg
deleted file mode 100644
index b0178b75..00000000
Binary files a/tests/files/tbwn0g16.jpg and /dev/null differ
diff --git a/tests/files/tbwn0g16.png b/tests/files/tbwn0g16.png
deleted file mode 100644
index 99bdeed2..00000000
Binary files a/tests/files/tbwn0g16.png and /dev/null differ
diff --git a/tests/files/tbwn3p08.jpg b/tests/files/tbwn3p08.jpg
deleted file mode 100644
index 2d1505cd..00000000
Binary files a/tests/files/tbwn3p08.jpg and /dev/null differ
diff --git a/tests/files/tbwn3p08.png b/tests/files/tbwn3p08.png
deleted file mode 100644
index eacab7a1..00000000
Binary files a/tests/files/tbwn3p08.png and /dev/null differ
diff --git a/tests/files/tbyn3p08.jpg b/tests/files/tbyn3p08.jpg
deleted file mode 100644
index 2d1505cd..00000000
Binary files a/tests/files/tbyn3p08.jpg and /dev/null differ
diff --git a/tests/files/tbyn3p08.png b/tests/files/tbyn3p08.png
deleted file mode 100644
index 656db098..00000000
Binary files a/tests/files/tbyn3p08.png and /dev/null differ
diff --git a/tests/files/test.bmp b/tests/files/test.bmp
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/files/test_corrupted.jpg b/tests/files/test_corrupted.jpg
deleted file mode 100644
index 48abba19..00000000
Binary files a/tests/files/test_corrupted.jpg and /dev/null differ
diff --git a/tests/files/tm3n3p02.jpg b/tests/files/tm3n3p02.jpg
deleted file mode 100644
index 98c7ea79..00000000
Binary files a/tests/files/tm3n3p02.jpg and /dev/null differ
diff --git a/tests/files/tm3n3p02.png b/tests/files/tm3n3p02.png
deleted file mode 100644
index fb3ef1d0..00000000
Binary files a/tests/files/tm3n3p02.png and /dev/null differ
diff --git a/tests/files/tp0n0g08.jpg b/tests/files/tp0n0g08.jpg
deleted file mode 100644
index 1aae8876..00000000
Binary files a/tests/files/tp0n0g08.jpg and /dev/null differ
diff --git a/tests/files/tp0n0g08.png b/tests/files/tp0n0g08.png
deleted file mode 100644
index 333465fc..00000000
Binary files a/tests/files/tp0n0g08.png and /dev/null differ
diff --git a/tests/files/tp0n2c08.jpg b/tests/files/tp0n2c08.jpg
deleted file mode 100644
index 52069f01..00000000
Binary files a/tests/files/tp0n2c08.jpg and /dev/null differ
diff --git a/tests/files/tp0n2c08.png b/tests/files/tp0n2c08.png
deleted file mode 100644
index fc6e42cb..00000000
Binary files a/tests/files/tp0n2c08.png and /dev/null differ
diff --git a/tests/files/tp0n3p08.jpg b/tests/files/tp0n3p08.jpg
deleted file mode 100644
index 274577b9..00000000
Binary files a/tests/files/tp0n3p08.jpg and /dev/null differ
diff --git a/tests/files/tp0n3p08.png b/tests/files/tp0n3p08.png
deleted file mode 100644
index 69a69e58..00000000
Binary files a/tests/files/tp0n3p08.png and /dev/null differ
diff --git a/tests/files/tp1n3p08.jpg b/tests/files/tp1n3p08.jpg
deleted file mode 100644
index 2d1505cd..00000000
Binary files a/tests/files/tp1n3p08.jpg and /dev/null differ
diff --git a/tests/files/tp1n3p08.png b/tests/files/tp1n3p08.png
deleted file mode 100644
index a6c9f35a..00000000
Binary files a/tests/files/tp1n3p08.png and /dev/null differ
diff --git a/tests/files/webp/f1t.webp b/tests/files/webp/f1t.webp
new file mode 100644
index 00000000..d1432037
Binary files /dev/null and b/tests/files/webp/f1t.webp differ
diff --git a/tests/files/xc1n0g08.png b/tests/files/xc1n0g08.png
deleted file mode 100644
index 94042273..00000000
Binary files a/tests/files/xc1n0g08.png and /dev/null differ
diff --git a/tests/files/xc9n2c08.png b/tests/files/xc9n2c08.png
deleted file mode 100644
index b11c2a7b..00000000
Binary files a/tests/files/xc9n2c08.png and /dev/null differ
diff --git a/tests/files/xcrn0g04.png b/tests/files/xcrn0g04.png
deleted file mode 100644
index 48abba19..00000000
Binary files a/tests/files/xcrn0g04.png and /dev/null differ
diff --git a/tests/files/xcsn0g01.png b/tests/files/xcsn0g01.png
deleted file mode 100644
index 9863a262..00000000
Binary files a/tests/files/xcsn0g01.png and /dev/null differ
diff --git a/tests/files/xd0n2c08.png b/tests/files/xd0n2c08.png
deleted file mode 100644
index 2f001610..00000000
Binary files a/tests/files/xd0n2c08.png and /dev/null differ
diff --git a/tests/files/xd3n2c08.png b/tests/files/xd3n2c08.png
deleted file mode 100644
index 9e4a3ff7..00000000
Binary files a/tests/files/xd3n2c08.png and /dev/null differ
diff --git a/tests/files/xd9n2c08.png b/tests/files/xd9n2c08.png
deleted file mode 100644
index 2c3b91aa..00000000
Binary files a/tests/files/xd9n2c08.png and /dev/null differ
diff --git a/tests/files/xdtn0g01.png b/tests/files/xdtn0g01.png
deleted file mode 100644
index 1a81abef..00000000
Binary files a/tests/files/xdtn0g01.png and /dev/null differ
diff --git a/tests/files/xhdn0g08.png b/tests/files/xhdn0g08.png
deleted file mode 100644
index fcb8737f..00000000
Binary files a/tests/files/xhdn0g08.png and /dev/null differ
diff --git a/tests/files/xlfn0g04.png b/tests/files/xlfn0g04.png
deleted file mode 100644
index d9ec53ed..00000000
Binary files a/tests/files/xlfn0g04.png and /dev/null differ
diff --git a/tests/files/xs1n0g01.png b/tests/files/xs1n0g01.png
deleted file mode 100644
index 1817c514..00000000
Binary files a/tests/files/xs1n0g01.png and /dev/null differ
diff --git a/tests/files/xs2n0g01.png b/tests/files/xs2n0g01.png
deleted file mode 100644
index b8147f2a..00000000
Binary files a/tests/files/xs2n0g01.png and /dev/null differ
diff --git a/tests/files/xs4n0g01.png b/tests/files/xs4n0g01.png
deleted file mode 100644
index 45237a1d..00000000
Binary files a/tests/files/xs4n0g01.png and /dev/null differ
diff --git a/tests/files/xs7n0g01.png b/tests/files/xs7n0g01.png
deleted file mode 100644
index 3f307f14..00000000
Binary files a/tests/files/xs7n0g01.png and /dev/null differ
diff --git a/tests/files/z00n2c08.jpg b/tests/files/z00n2c08.jpg
deleted file mode 100644
index 44aaabe2..00000000
Binary files a/tests/files/z00n2c08.jpg and /dev/null differ
diff --git a/tests/files/z00n2c08.png b/tests/files/z00n2c08.png
deleted file mode 100644
index 7669eb83..00000000
Binary files a/tests/files/z00n2c08.png and /dev/null differ
diff --git a/tests/files/z03n2c08.jpg b/tests/files/z03n2c08.jpg
deleted file mode 100644
index 1107c133..00000000
Binary files a/tests/files/z03n2c08.jpg and /dev/null differ
diff --git a/tests/files/z03n2c08.png b/tests/files/z03n2c08.png
deleted file mode 100644
index bfb10de8..00000000
Binary files a/tests/files/z03n2c08.png and /dev/null differ
diff --git a/tests/files/z06n2c08.jpg b/tests/files/z06n2c08.jpg
deleted file mode 100644
index 4c711a6a..00000000
Binary files a/tests/files/z06n2c08.jpg and /dev/null differ
diff --git a/tests/files/z06n2c08.png b/tests/files/z06n2c08.png
deleted file mode 100644
index b90ebc10..00000000
Binary files a/tests/files/z06n2c08.png and /dev/null differ
diff --git a/tests/files/z09n2c08.jpg b/tests/files/z09n2c08.jpg
deleted file mode 100644
index a65efa03..00000000
Binary files a/tests/files/z09n2c08.jpg and /dev/null differ
diff --git a/tests/files/z09n2c08.png b/tests/files/z09n2c08.png
deleted file mode 100644
index 5f191a78..00000000
Binary files a/tests/files/z09n2c08.png and /dev/null differ
diff --git a/tests/integration_test.rs b/tests/integration_test.rs
deleted file mode 100644
index 97412279..00000000
--- a/tests/integration_test.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-use regex::Regex;
-use rimage::{image::Codec, Config, Decoder, Encoder};
-use std::path::PathBuf;
-use std::{fs, path};
-
-#[test]
-fn test_image_processing_jpeg() {
- let path = PathBuf::from("tests/files/basi0g01.jpg");
-
- let decoder = Decoder::from_path(&path).unwrap();
- let image = decoder.decode().unwrap();
-
- let config = Config::builder(Codec::MozJpeg).build().unwrap();
- let encoder = Encoder::new(&config, image);
- let encoded_data = encoder.encode().unwrap();
-
- let output_path = "tests/files/out.jpg";
- fs::write(output_path, encoded_data).unwrap();
-
- let output_data = fs::read(output_path).unwrap();
- assert!(!output_data.is_empty());
-
- fs::remove_file(output_path).unwrap();
-}
-
-#[test]
-fn test_image_processing_png() {
- let path = PathBuf::from("tests/files/basi0g01.png");
-
- let decoder = Decoder::from_path(&path).unwrap();
- let image = decoder.decode().unwrap();
-
- let config = Config::builder(Codec::Png).build().unwrap();
- let encoder = Encoder::new(&config, image);
- let encoded_data = encoder.encode().unwrap();
-
- let output_path = "tests/files/out.png";
- fs::write(output_path, encoded_data).unwrap();
-
- let output_data = fs::read(output_path).unwrap();
- assert!(!output_data.is_empty());
-
- fs::remove_file(output_path).unwrap();
-}
-
-#[test]
-fn test_image_processing_oxipng() {
- let path = PathBuf::from("tests/files/basi0g01.png");
-
- let decoder = Decoder::from_path(&path).unwrap();
- let image = decoder.decode().unwrap();
-
- let config = Config::builder(Codec::Oxipng).build().unwrap();
- let encoder = Encoder::new(&config, image);
- let encoded_data = encoder.encode().unwrap();
-
- let output_path = "tests/files/oxiout.png";
- fs::write(output_path, encoded_data).unwrap();
-
- let output_data = fs::read(output_path).unwrap();
- assert!(!output_data.is_empty());
-
- fs::remove_file(output_path).unwrap();
-}
-
-#[test]
-fn test_bulk_image_processing_jpeg() {
- let files: Vec = fs::read_dir("tests/files/")
- .unwrap()
- .map(|entry| {
- let entry = entry.unwrap();
- entry.path()
- })
- .filter(|path| {
- let re = Regex::new(r"^tests/files/[^x].+\.png").unwrap();
- re.is_match(path.to_str().unwrap_or(""))
- })
- .collect();
-
- for path in files {
- let decoder = Decoder::from_path(&path).unwrap();
- let image = decoder.decode().unwrap();
-
- let config = Config::builder(Codec::MozJpeg).build().unwrap();
- let encoder = Encoder::new(&config, image);
- let encoded_data = encoder.encode().unwrap();
-
- let mut output_path = path::PathBuf::from("tests/files/");
- output_path.push(path.file_name().unwrap());
- output_path.set_extension("out.jpg");
- fs::write(&output_path, encoded_data).unwrap();
-
- let output_data = fs::read(&output_path).unwrap();
- assert!(!output_data.is_empty());
-
- fs::remove_file(&output_path).unwrap();
- }
-}
-
-#[test]
-fn test_bulk_image_processing_png() {
- let files: Vec = fs::read_dir("tests/files/")
- .unwrap()
- .map(|entry| {
- let entry = entry.unwrap();
- entry.path()
- })
- .filter(|path| {
- let re = Regex::new(r"^tests/files/[^x].+\.png").unwrap();
- re.is_match(path.to_str().unwrap_or(""))
- })
- .collect();
-
- for path in files {
- let decoder = Decoder::from_path(&path).unwrap();
- let image = decoder.decode().unwrap();
-
- let config = Config::builder(Codec::Png).build().unwrap();
- let encoder = Encoder::new(&config, image);
- let encoded_data = encoder.encode().unwrap();
-
- let mut output_path = path::PathBuf::from("tests/files/");
- output_path.push(path.file_name().unwrap());
- output_path.set_extension("out.png");
- fs::write(&output_path, encoded_data).unwrap();
-
- let output_data = fs::read(&output_path).unwrap();
- assert!(!output_data.is_empty());
-
- fs::remove_file(&output_path).unwrap();
- }
-}
-
-#[test]
-fn test_bulk_image_processing_oxipng() {
- let files: Vec = fs::read_dir("tests/files/")
- .unwrap()
- .map(|entry| {
- let entry = entry.unwrap();
- entry.path()
- })
- .filter(|path| {
- let re = Regex::new(r"^tests/files/[^x].+\.png").unwrap();
- re.is_match(path.to_str().unwrap_or(""))
- })
- .collect();
-
- for path in files {
- let decoder = Decoder::from_path(&path).unwrap();
- let image = decoder.decode().unwrap();
-
- let config = Config::builder(Codec::Oxipng).build().unwrap();
- let encoder = Encoder::new(&config, image);
- let encoded_data = encoder.encode().unwrap();
-
- let mut output_path = path::PathBuf::from("tests/files/");
- output_path.push(path.file_name().unwrap());
- output_path.set_extension("oxiout.png");
- fs::write(&output_path, encoded_data).unwrap();
-
- let output_data = fs::read(&output_path).unwrap();
- assert!(!output_data.is_empty());
-
- fs::remove_file(&output_path).unwrap();
- }
-}