diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 09c27eb..56c3b7f 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -13,7 +13,7 @@ updates:
timezone: "Europe/Paris"
open-pull-requests-limit: 5
commit-message:
- prefix: "cargo"
+ prefix: "[BOT] Cargo :"
labels:
- "dependencies"
- "rust"
@@ -24,26 +24,6 @@ updates:
- "minor"
- "patch"
- # --- Python dependencies (requirements.txt at the repository root) ---
- - package-ecosystem: "pip"
- directory: "/"
- schedule:
- interval: "weekly"
- day: "monday"
- time: "06:00"
- timezone: "Europe/Paris"
- open-pull-requests-limit: 5
- commit-message:
- prefix: "pip"
- labels:
- - "dependencies"
- - "python"
- groups:
- pip-minor-patch:
- update-types:
- - "minor"
- - "patch"
-
# --- Actions used in .github/workflows/ ---
- package-ecosystem: "github-actions"
directory: "/"
@@ -54,7 +34,7 @@ updates:
timezone: "Europe/Paris"
open-pull-requests-limit: 5
commit-message:
- prefix: "ci"
+ prefix: "[BOT] CI :"
labels:
- "dependencies"
- "github-actions"
@@ -73,7 +53,7 @@ updates:
timezone: "Europe/Paris"
open-pull-requests-limit: 3
commit-message:
- prefix: "docker"
+ prefix: "[BOT] Docker :"
labels:
- "dependencies"
- "docker"
diff --git a/.github/workflows/Docker CI.yml b/.github/workflows/Docker CI.yml
index 2b654fc..82c9343 100644
--- a/.github/workflows/Docker CI.yml
+++ b/.github/workflows/Docker CI.yml
@@ -6,15 +6,13 @@ on:
branches: [ main ]
paths:
- 'Dockerfile'
- - 'requirements.txt'
- - 'Filtro_Python/**'
+ - 'Filtro_Rust/**'
- '.github/workflows/Docker CI.yml'
pull_request:
branches: [ main ]
paths:
- 'Dockerfile'
- - 'requirements.txt'
- - 'Filtro_Python/**'
+ - 'Filtro_Rust/**'
- '.github/workflows/Docker CI.yml'
workflow_dispatch:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 9e16a31..0c4495d 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -1,14 +1,12 @@
-# CodeQL security analysis for both implementations of the project:
-# - Python: Filtro_Python/
-# - Rust: Filtro_Rust/
+# CodeQL security analysis for the Rust crate (Filtro_Rust/).
# Results show up under Security > Code scanning.
name: CodeQL
on:
push:
- branches: [ "main" ]
+ branches: [ "main", "development" ]
pull_request:
- branches: [ "main" ]
+ branches: [ "main", "development" ]
schedule:
- cron: '0 5 * * 0'
workflow_dispatch:
@@ -35,9 +33,7 @@ jobs:
fail-fast: false
matrix:
include:
- # Neither language requires a build step for CodeQL extraction
- - language: python
- build-mode: none
+ # Rust does not require a build step for CodeQL extraction
- language: rust
build-mode: none
diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml
deleted file mode 100644
index 1c0ffbf..0000000
--- a/.github/workflows/python-ci.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-# This workflow installs the Python dependencies, checks code quality with Pylint
-# and verifies that the Flask application imports correctly.
-name: Python CI
-
-on:
- push:
- branches: [ main ]
- paths:
- - 'Filtro_Python/**'
- - 'requirements.txt'
- - '.github/workflows/python-ci.yml'
- pull_request:
- branches: [ main ]
- paths:
- - 'Filtro_Python/**'
- - 'requirements.txt'
- - '.github/workflows/python-ci.yml'
- workflow_dispatch:
-
-env:
- # Run JavaScript actions on Node 24 instead of the default Node 20
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
-
-concurrency:
- group: python-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- lint:
- name: Pylint code analysis
- runs-on: ubuntu-latest
- permissions:
- contents: read
-
- strategy:
- fail-fast: false
- matrix:
- python-version: [ '3.12', '3.13' ]
-
- steps:
- - uses: actions/checkout@v7
-
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v7
- with:
- python-version: ${{ matrix.python-version }}
- cache: 'pip'
-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- pip install pylint
-
- - name: Run Pylint
- # The Python code lives in Filtro_Python/, which is not a package:
- # --recursive=y is required for pylint to walk the directory
- run: pylint --recursive=y Filtro_Python --ignore=venv,tests,migrations,static --exit-zero
-
- - name: Check that the application imports
- working-directory: Filtro_Python
- run: python -c "import app; print('Flask app OK:', app.app.name)"
diff --git a/.gitignore b/.gitignore
index 9ad87a3..081e7d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,40 +1,3 @@
-# ---------------------------------------------------------------
-# Python (Filtro_Python)
-# ---------------------------------------------------------------
-__pycache__/
-*.py[cod]
-*$py.class
-*.so
-
-# Virtual environments
-.venv/
-venv/
-ENV/
-env/
-
-# Packaging / build
-build/
-dist/
-*.egg-info/
-.eggs/
-
-# Tests & tooling caches
-.pytest_cache/
-.mypy_cache/
-.ruff_cache/
-.coverage
-.coverage.*
-htmlcov/
-.tox/
-
-# Flask
-instance/
-*.log
-
-# Files generated at runtime by the app
-Filtro_Python/static/uploads/
-Filtro_Python/static/results/
-
# ---------------------------------------------------------------
# Rust (Filtro_Rust)
# ---------------------------------------------------------------
diff --git a/Dockerfile b/Dockerfile
index 930a5f5..27d31dc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,21 +1,26 @@
-# Utilise une image python officielle comme parent
-FROM python:3.14-slim
+# syntax=docker/dockerfile:1
-# Définit le répertoire de travail dans le conteneur
-WORKDIR /app
+# ---- Build stage : compile the `filtro` binary ------------------------------
+FROM rust:1-slim AS build
-# Copie les fichiers nécessaires
-COPY . /app
+WORKDIR /src
+# Only the Rust crate is needed to build
+COPY Filtro_Rust/ ./Filtro_Rust/
-# Installation des dépendances Python
-RUN pip install --no-cache-dir -r requirements.txt
+WORKDIR /src/Filtro_Rust
+RUN cargo build --release --locked
-# Expose le port de Flask (par défaut 5000)
-EXPOSE 5000
+# ---- Runtime stage : minimal image with just the binary --------------------
+FROM debian:stable-slim
-# L'application Flask vit dans Filtro_Python/ et utilise des chemins relatifs
-# (static/uploads, static/results) : on se place donc dans ce dossier
-WORKDIR /app/Filtro_Python
+# Run as a non-root user
+RUN useradd --create-home --uid 10001 filtro
+USER filtro
+WORKDIR /work
-# Commande pour lancer l’application Flask
-CMD ["python", "app.py"]
+COPY --from=build /src/Filtro_Rust/target/release/filtro /usr/local/bin/filtro
+
+# The container *is* the CLI tool:
+# docker run --rm -v "$PWD:/work" filtro photo.jpg -f jaune -o out
+ENTRYPOINT ["filtro"]
+CMD ["--help"]
diff --git a/Filtro_Python/app.py b/Filtro_Python/app.py
deleted file mode 100644
index a142b05..0000000
--- a/Filtro_Python/app.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from flask import Flask, render_template, request
-from PIL import Image
-import os
-
-app = Flask(__name__)
-
-UPLOAD_FOLDER = 'static/uploads/'
-RESULT_FOLDER = 'static/results/'
-os.makedirs(UPLOAD_FOLDER, exist_ok=True)
-os.makedirs(RESULT_FOLDER, exist_ok=True)
-
-def apply_filter(image_path, color_filter, niveau_blanc=180): # Niveau de blanc par défaut
- img = Image.open(image_path)
- largeur_img, hauteur_img = img.size
-
- # --- Parcours de chaque pixel de l'image ---
- for y in range(hauteur_img):
- for x in range(largeur_img):
- r, v, b = img.getpixel((x, y))
- # --- Si le pixel est "clair" ---
- if r > niveau_blanc and v > niveau_blanc and b > niveau_blanc:
- # ---- FILTRE JAUNE ----
- if color_filter == "jaune":
- img.putpixel((x, y), (254, 254, 1))
- # ---- FILTRE VERT ----
- elif color_filter == "vert":
- img.putpixel((x, y), (145, 254, 1))
- # ---- FILTRE ROUGE ----
- elif color_filter == "rouge":
- img.putpixel((x, y), (254, 1, 1))
- # ---- FILTRE NOIR ET BLANC (BLANC) ----
- elif color_filter == "nb":
- img.putpixel((x, y), (254, 254, 254))
- else:
- # ---- FILTRE JAUNE (Foncé) ----
- if color_filter == "jaune":
- img.putpixel((x, y), (b, v, r))
- # ---- FILTRE VERT (Foncé) ----
- elif color_filter == "vert":
- img.putpixel((x, y), (r, b, v))
- # ---- FILTRE ROUGE (Foncé) ----
- elif color_filter == "rouge":
- img.putpixel((x, y), (b, r, v))
- # ---- FILTRE NOIR ET BLANC (NOIR) ----
- elif color_filter == "nb":
- img.putpixel((x, y), (0, 0, 0))
-
- result_filename = f"{color_filter}_{os.path.basename(image_path)}"
- result_path = os.path.join(RESULT_FOLDER, result_filename)
- img.save(result_path)
- return result_filename
-
-@app.route('/', methods=['GET', 'POST'])
-def index():
- uploaded_file = None
- filtered_image = None
- if request.method == 'POST':
- # Upload (si nouvelle image)
- if 'file' in request.files:
- file = request.files['file']
- if file.filename != '':
- file_path = os.path.join(UPLOAD_FOLDER, file.filename)
- file.save(file_path)
- uploaded_file = file.filename
- elif 'action' in request.form:
- # Application d'un filtre
- filter_color = request.form['action']
- image_name = request.form['image_name']
- file_path = os.path.join(UPLOAD_FOLDER, image_name)
- result_filename = apply_filter(file_path, filter_color)
- filtered_image = result_filename
- uploaded_file = image_name
-
- # Affiche la page avec l'image uploadée (si présente) et filtre choisi (si présent)
- return render_template('index.html', uploaded_file=uploaded_file, filtered_image=filtered_image)
-
-if __name__ == '__main__':
- app.run(host="0.0.0.0", port=5000, debug=False)
- # app.run(debug=False) # Pour le développement, pas nécessaire en production
-
diff --git a/Filtro_Python/static/results/vert_ImageIA2.webp b/Filtro_Python/static/results/vert_ImageIA2.webp
deleted file mode 100644
index 69ee8e4..0000000
Binary files a/Filtro_Python/static/results/vert_ImageIA2.webp and /dev/null differ
diff --git a/Filtro_Python/static/style.css b/Filtro_Python/static/style.css
deleted file mode 100644
index f6a8b3a..0000000
--- a/Filtro_Python/static/style.css
+++ /dev/null
@@ -1,87 +0,0 @@
-body {
- background: #3b71ca; /* bleu doux */
- color: #fff;
- font-family: 'Segoe UI', Arial, sans-serif;
- margin: 0;
-}
-
-.navbar {
- background: #254776;
- padding: 15px 0;
- position: sticky;
- top: 0;
- width: 100%;
- z-index: 100;
-}
-
-.nav-container {
- width: 90%;
- margin: auto;
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
-
-.nav-title {
- font-size: 1.8em;
- font-weight: bold;
- color: #ffda47;
- letter-spacing: 1px;
-}
-
-.nav-link {
- color: #fff;
- font-size: 1.1em;
- text-decoration: none;
- border-bottom: 2px solid transparent;
- transition: border-color 0.2s;
-}
-.nav-link:hover { border-bottom: 2px solid #ffda47; }
-
-.container {
- width: 90%;
- max-width: 400px;
- margin: 40px auto 20px auto;
- padding: 24px 20px;
- background: rgba(255,255,255,0.08);
- border-radius: 15px;
- box-shadow: 0 2px 12px rgba(10,45,90,0.11);
- text-align: center;
-}
-
-input[type="file"] {
- margin: 12px 0 18px 0;
-}
-
-button {
- background: #ffda47;
- color: #254776;
- border: none;
- padding: 9px 18px;
- border-radius: 8px;
- margin: 6px 2px;
- font-weight: bold;
- cursor: pointer;
- transition: background 0.16s, color 0.16s;
-}
-button:hover {
- background: #fcd024;
- color: #173860;
-}
-
-.footer {
- background: #14243e;
- color: #fff;
- padding: 32px 10px 22px 10px;
- text-align: center;
- margin-top: 70px;
- border-top: 2px solid #ffda47;
-}
-.footer h3 {
- margin-top: 0;
- color: #ffda47;
-}
-.footer p {
- max-width: 540px;
- margin: 10px auto;
-}
diff --git a/Filtro_Python/static/uploads/ImageIA2.webp b/Filtro_Python/static/uploads/ImageIA2.webp
deleted file mode 100644
index fe567c7..0000000
Binary files a/Filtro_Python/static/uploads/ImageIA2.webp and /dev/null differ
diff --git a/Filtro_Python/templates/index.html b/Filtro_Python/templates/index.html
deleted file mode 100644
index 18cfbf9..0000000
--- a/Filtro_Python/templates/index.html
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
- Filtro - Appliquez votre filtre image !
-
-
-
-
-
-
-
-
-
Upload d'une image
-
-
- {% if uploaded_file %}
-
Image chargée :
-
 }})
-
- {% endif %}
-
- {% if filtered_image %}
-
Image filtrée :
-
 }})
-
-
-
- {% endif %}
-
-
-
-
-
-
diff --git a/Filtro_Rust/Cargo.lock b/Filtro_Rust/Cargo.lock
index 7de80e2..e28cc0f 100644
--- a/Filtro_Rust/Cargo.lock
+++ b/Filtro_Rust/Cargo.lock
@@ -2,13 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "Filtro_Rust"
-version = "0.1.0"
-dependencies = [
- "image",
-]
-
[[package]]
name = "adler2"
version = "2.0.1"
@@ -16,148 +9,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
-name = "aligned"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
-dependencies = [
- "as-slice",
-]
-
-[[package]]
-name = "aligned-vec"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
-dependencies = [
- "equator",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
-
-[[package]]
-name = "arbitrary"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
-
-[[package]]
-name = "arg_enum_proc_macro"
-version = "0.3.4"
+name = "anstream"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
]
[[package]]
-name = "arrayvec"
-version = "0.7.6"
+name = "anstyle"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
-name = "as-slice"
-version = "0.2.1"
+name = "anstyle-parse"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
- "stable_deref_trait",
+ "utf8parse",
]
[[package]]
-name = "autocfg"
-version = "1.5.0"
+name = "anstyle-query"
+version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
-
-[[package]]
-name = "av-scenechange"
-version = "0.14.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
- "aligned",
- "anyhow",
- "arg_enum_proc_macro",
- "arrayvec",
- "log",
- "num-rational",
- "num-traits",
- "pastey",
- "rayon",
- "thiserror",
- "v_frame",
- "y4m",
+ "windows-sys",
]
[[package]]
-name = "av1-grain"
-version = "0.2.5"
+name = "anstyle-wincon"
+version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
- "anyhow",
- "arrayvec",
- "log",
- "nom",
- "num-rational",
- "v_frame",
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys",
]
[[package]]
-name = "avif-serialize"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
-dependencies = [
- "arrayvec",
-]
-
-[[package]]
-name = "bit_field"
-version = "0.10.3"
+name = "autocfg"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bitflags"
-version = "2.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
-
-[[package]]
-name = "bitstream-io"
-version = "4.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
-dependencies = [
- "core2",
-]
-
-[[package]]
-name = "built"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
-
-[[package]]
-name = "bumpalo"
-version = "3.19.1"
+version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "bytemuck"
-version = "1.24.0"
+version = "1.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
+checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
[[package]]
name = "byteorder-lite"
@@ -165,18 +82,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
-[[package]]
-name = "cc"
-version = "1.2.55"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
-dependencies = [
- "find-msvc-tools",
- "jobserver",
- "libc",
- "shlex",
-]
-
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -184,120 +89,77 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
-name = "color_quant"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
-
-[[package]]
-name = "core2"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "crc32fast"
-version = "1.5.0"
+name = "clap"
+version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
dependencies = [
- "cfg-if",
+ "clap_builder",
+ "clap_derive",
]
[[package]]
-name = "crossbeam-deque"
-version = "0.8.6"
+name = "clap_builder"
+version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
]
[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
+name = "clap_derive"
+version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
dependencies = [
- "crossbeam-utils",
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 3.0.4",
]
[[package]]
-name = "crossbeam-utils"
-version = "0.8.21"
+name = "clap_lex"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
-name = "crunchy"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
-
-[[package]]
-name = "either"
-version = "1.15.0"
+name = "color_quant"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
-name = "equator"
-version = "0.4.2"
+name = "colorchoice"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
-dependencies = [
- "equator-macro",
-]
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
-name = "equator-macro"
-version = "0.4.2"
+name = "crc32fast"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
+checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "cfg-if",
]
[[package]]
-name = "exr"
-version = "1.74.0"
+name = "crunchy"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
-dependencies = [
- "bit_field",
- "half",
- "lebe",
- "miniz_oxide",
- "rayon-core",
- "smallvec",
- "zune-inflate",
-]
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "fax"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
-dependencies = [
- "fax_derive",
-]
-
-[[package]]
-name = "fax_derive"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]]
name = "fdeflate"
@@ -309,38 +171,30 @@ dependencies = [
]
[[package]]
-name = "find-msvc-tools"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
-
-[[package]]
-name = "flate2"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
+name = "filtro"
+version = "0.1.0"
dependencies = [
- "crc32fast",
- "miniz_oxide",
+ "clap",
+ "image",
+ "thiserror",
]
[[package]]
-name = "getrandom"
-version = "0.3.4"
+name = "flate2"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [
- "cfg-if",
- "libc",
- "r-efi",
- "wasip2",
+ "crc32fast",
+ "miniz_oxide 0.9.1",
+ "zlib-rs",
]
[[package]]
name = "gif"
-version = "0.14.1"
+version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
+checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159"
dependencies = [
"color_quant",
"weezl",
@@ -357,6 +211,12 @@ dependencies = [
"zerocopy",
]
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
[[package]]
name = "image"
version = "0.25.10"
@@ -366,16 +226,11 @@ dependencies = [
"bytemuck",
"byteorder-lite",
"color_quant",
- "exr",
"gif",
"image-webp",
"moxcms",
"num-traits",
"png",
- "qoi",
- "ravif",
- "rayon",
- "rgb",
"tiff",
"zune-core",
"zune-jpeg",
@@ -392,99 +247,26 @@ dependencies = [
]
[[package]]
-name = "imgref"
-version = "1.12.0"
+name = "is_terminal_polyfill"
+version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
-name = "interpolate_name"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "itertools"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "jobserver"
-version = "0.1.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
-dependencies = [
- "getrandom",
- "libc",
-]
-
-[[package]]
-name = "lebe"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
-
-[[package]]
-name = "libc"
-version = "0.2.180"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
-
-[[package]]
-name = "libfuzzer-sys"
-version = "0.4.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
-dependencies = [
- "arbitrary",
- "cc",
-]
-
-[[package]]
-name = "log"
-version = "0.4.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
-
-[[package]]
-name = "loop9"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
-dependencies = [
- "imgref",
-]
-
-[[package]]
-name = "maybe-rayon"
-version = "0.1.1"
+name = "miniz_oxide"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
- "cfg-if",
- "rayon",
+ "adler2",
+ "simd-adler32",
]
-[[package]]
-name = "memchr"
-version = "2.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
-
[[package]]
name = "miniz_oxide"
-version = "0.8.9"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
dependencies = [
"adler2",
"simd-adler32",
@@ -500,68 +282,6 @@ dependencies = [
"pxfm",
]
-[[package]]
-name = "new_debug_unreachable"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
-
-[[package]]
-name = "nom"
-version = "8.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "noop_proc_macro"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
-
-[[package]]
-name = "num-bigint"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
-dependencies = [
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-derive"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.46"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
-dependencies = [
- "num-bigint",
- "num-integer",
- "num-traits",
-]
-
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -572,90 +292,38 @@ dependencies = [
]
[[package]]
-name = "once_cell"
-version = "1.21.3"
+name = "once_cell_polyfill"
+version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
-
-[[package]]
-name = "paste"
-version = "1.0.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
-
-[[package]]
-name = "pastey"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "png"
-version = "0.18.0"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
+checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [
"bitflags",
"crc32fast",
"fdeflate",
"flate2",
- "miniz_oxide",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
-dependencies = [
- "zerocopy",
+ "miniz_oxide 0.8.9",
]
[[package]]
name = "proc-macro2"
-version = "1.0.106"
+version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
-[[package]]
-name = "profiling"
-version = "1.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
-dependencies = [
- "profiling-procmacros",
-]
-
-[[package]]
-name = "profiling-procmacros"
-version = "1.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
-dependencies = [
- "quote",
- "syn",
-]
-
[[package]]
name = "pxfm"
-version = "0.1.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "qoi"
-version = "0.4.1"
+version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
-dependencies = [
- "bytemuck",
-]
+checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]]
name = "quick-error"
@@ -665,168 +333,41 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
-version = "1.0.44"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
-name = "r-efi"
-version = "5.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
-
-[[package]]
-name = "rand"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
-dependencies = [
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "rav1e"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
-dependencies = [
- "aligned-vec",
- "arbitrary",
- "arg_enum_proc_macro",
- "arrayvec",
- "av-scenechange",
- "av1-grain",
- "bitstream-io",
- "built",
- "cfg-if",
- "interpolate_name",
- "itertools",
- "libc",
- "libfuzzer-sys",
- "log",
- "maybe-rayon",
- "new_debug_unreachable",
- "noop_proc_macro",
- "num-derive",
- "num-traits",
- "paste",
- "profiling",
- "rand",
- "rand_chacha",
- "simd_helpers",
- "thiserror",
- "v_frame",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "ravif"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45"
-dependencies = [
- "avif-serialize",
- "imgref",
- "loop9",
- "quick-error",
- "rav1e",
- "rayon",
- "rgb",
-]
-
-[[package]]
-name = "rayon"
-version = "1.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
-dependencies = [
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
-dependencies = [
- "crossbeam-deque",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "rgb"
-version = "0.8.52"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
-
-[[package]]
-name = "rustversion"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
-
-[[package]]
-name = "shlex"
-version = "1.3.0"
+name = "simd-adler32"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
-name = "simd-adler32"
-version = "0.3.8"
+name = "strsim"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
-name = "simd_helpers"
-version = "0.1.0"
+name = "syn"
+version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
+ "proc-macro2",
"quote",
+ "unicode-ident",
]
-[[package]]
-name = "smallvec"
-version = "1.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
-
-[[package]]
-name = "stable_deref_trait"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
-
[[package]]
name = "syn"
-version = "2.0.114"
+version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
+checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
dependencies = [
"proc-macro2",
"quote",
@@ -835,22 +376,22 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.18"
+version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.18"
+version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 3.0.4",
]
[[package]]
@@ -869,74 +410,15 @@ dependencies = [
[[package]]
name = "unicode-ident"
-version = "1.0.22"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
-name = "v_frame"
-version = "0.3.9"
+name = "utf8parse"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
-dependencies = [
- "aligned-vec",
- "num-traits",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "wasip2"
-version = "1.0.2+wasi-0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
-dependencies = [
- "wit-bindgen",
-]
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.108"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
-dependencies = [
- "cfg-if",
- "once_cell",
- "rustversion",
- "wasm-bindgen-macro",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.108"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.108"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
-dependencies = [
- "bumpalo",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.108"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
-dependencies = [
- "unicode-ident",
-]
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "weezl"
@@ -945,57 +427,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
-name = "wit-bindgen"
-version = "0.51.0"
+name = "windows-link"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
-name = "y4m"
-version = "0.8.0"
+name = "windows-sys"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
[[package]]
name = "zerocopy"
-version = "0.8.36"
+version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dafd85c832c1b68bbb4ec0c72c7f6f4fc5179627d2bc7c26b30e4c0cc11e76cc"
+checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.8.36"
+version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cb7e4e8436d9db52fbd6625dbf2f45243ab84994a72882ec8227b99e72b439a"
+checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
-name = "zune-core"
-version = "0.5.1"
+name = "zlib-rs"
+version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
+checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
[[package]]
-name = "zune-inflate"
-version = "0.2.54"
+name = "zune-core"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
-dependencies = [
- "simd-adler32",
-]
+checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b"
[[package]]
name = "zune-jpeg"
-version = "0.5.12"
+version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe"
+checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
diff --git a/Filtro_Rust/Cargo.toml b/Filtro_Rust/Cargo.toml
index 90a92a6..14443c8 100644
--- a/Filtro_Rust/Cargo.toml
+++ b/Filtro_Rust/Cargo.toml
@@ -1,7 +1,34 @@
[package]
-name = "Filtro_Rust"
+name = "filtro"
version = "0.1.0"
edition = "2024"
+rust-version = "1.88"
+
+[[bin]]
+name = "filtro"
+path = "src/main.rs"
+
+[lib]
+name = "filtro"
+path = "src/lib.rs"
[dependencies]
-image = "0.25.9"
+image = { version = "0.25", default-features = false, features = [
+ "png",
+ "jpeg",
+ "bmp",
+ "tiff",
+ "webp",
+ "gif",
+] }
+thiserror = "2"
+clap = { version = "4.6", features = ["derive"] }
+
+[dev-dependencies]
+image = { version = "0.25", default-features = false, features = ["png"] }
+
+[profile.release]
+opt-level = 3
+lto = "thin"
+codegen-units = 1
+strip = true
diff --git a/Filtro_Rust/README.md b/Filtro_Rust/README.md
new file mode 100644
index 0000000..2d9773b
--- /dev/null
+++ b/Filtro_Rust/README.md
@@ -0,0 +1,140 @@
+# Filtro (Rust)
+
+Outil de filtrage d'images en ligne de commande, écrit en Rust (édition 2024,
+`rust-version` 1.88) : un cœur stable, et des filtres qui s'ajoutent sans jamais
+toucher au cœur.
+
+## Architecture — dossiers et MVC
+
+Le crate expose **une bibliothèque** (`filtro`, le *Model*) et **un binaire**
+(`filtro`, qui porte le *Controller* et la *View*).
+
+```
+Filtro_Rust/
+├── Cargo.toml
+├── README.md
+├── src/
+│ ├── lib.rs racine de la bibliothèque : modules + façade `pub use`
+│ ├── main.rs entrée du binaire : assemble controller + view
+│ │
+│ ├── model/ ── M — le cœur métier (aucune dépendance CLI) ──
+│ │ ├── error.rs type d'erreur unique (FiltroError)
+│ │ ├── pixel.rs types d'image partagés (Rgba8, Dimensions, PixelBuffer)
+│ │ ├── resolution.rs (privé) lecture des ppp dans les métadonnées
+│ │ ├── codec.rs (privé) pont vers le crate `image`
+│ │ ├── pipeline/
+│ │ │ ├── analyzer.rs étape 1 — décode et décrit l'image
+│ │ │ ├── constructor.rs étape 2 — prépare l'image de travail
+│ │ │ ├── formatter.rs étape 3 — décline formats et tailles
+│ │ │ ├── renderer.rs étape 4 — applique les filtres et encode
+│ │ │ └── orchestrator.rs enchaîne les 4 étapes (Pipeline, PipelineBuilder)
+│ │ └── filter/
+│ │ ├── contract.rs CONTRAT des filtres (Filter, FilterChain, …)
+│ │ └── registry.rs configuration et instanciation des filtres
+│ │
+│ ├── filters/ ── LES FILTRES, hors du cœur ──
+│ │ ├── mod.rs register_all()
+│ │ ├── color.rs jaune, vert, rouge
+│ │ └── threshold.rs noir-blanc
+│ │
+│ ├── controller/ ── C — traduit la CLI en configuration du cœur ──
+│ │ ├── mod.rs run() : parse → pipeline → view ; register_filters()
+│ │ └── cli.rs arguments clap + analyse des formats / tailles
+│ │
+│ ├── view/ ── V — tout ce qui s'écrit sur le terminal ──
+│ │ ├── report.rs résumé d'un traitement terminé
+│ │ ├── catalog.rs affichage de `--list-filters`
+│ │ └── mod.rs rapport d'erreur + tailles lisibles
+│ │
+│ └── test_support.rs (cfg(test)) fabriques d'objets pour les tests
+└── tests/
+ └── pipeline.rs test d'intégration bout en bout
+```
+
+`controller/` et `view/` appartiennent **au binaire uniquement** : la
+bibliothèque reste un domaine pur, réutilisable depuis un autre programme.
+
+## Le pipeline
+
+```
+Utilisateur ── image ──▶ Analyzer ──┐
+ ├──▶ Constructor ──▶ Formatter ──▶ Renderer ──▶ sorties
+Utilisateur ── filtres ─▶ Registre ─┘ (applique la chaîne)
+```
+
+| Étape | Fichier | Entrée | Sortie |
+|-------|---------|--------|--------|
+| Analyzer | `src/model/pipeline/analyzer.rs` | octets | `ImageAnalysis` (description + pixels RGBA8) |
+| Constructor | `src/model/pipeline/constructor.rs` | analyse + `FilterRequirements` | `PreparedImage` |
+| Formatter | `src/model/pipeline/formatter.rs` | image préparée | `FormatSet` (format × taille) |
+| Renderer | `src/model/pipeline/renderer.rs` | variantes + `FilterChain` | `RenderOutput` (octets encodés) |
+
+Autour : `src/model/pixel.rs` (types d'image), `src/model/error.rs`,
+`src/model/pipeline/orchestrator.rs` (orchestration), `src/model/codec.rs` (seul
+fichier qui connaît le crate `image`), `src/model/resolution.rs` (lecture des ppp).
+
+## Cœur et filtres
+
+Le cœur ne contient aucun filtre. Il expose un contrat :
+
+* `src/model/filter/contract.rs` — `Filter` (transforme des pixels),
+ `FilterRequirements` (ce que le filtre attend de l'image), `FilterChain`
+ (composition) ;
+* `src/model/filter/registry.rs` — `FilterParams`, `FilterFactory`,
+ `FilterRegistry`, `FilterRequest` (analyse de `id:clé=valeur`).
+
+Les filtres vivent dans `src/filters/`, module que le cœur ne référence jamais.
+Ajouter un filtre :
+
+1. implémenter `Filter` et `FilterFactory` dans un fichier de `src/filters/` ;
+2. ajouter une ligne à `filters::register_all`.
+
+Rien d'autre : ni le pipeline, ni le contrôleur ne changent.
+
+## Les filtres livrés
+
+| Identifiant | Effet |
+|-------------|-------|
+| `jaune` | blanc → jaune, canaux permutés en `bvr` |
+| `vert` | blanc → vert, canaux permutés en `rbv` |
+| `rouge` | blanc → rouge, canaux permutés en `brv` |
+| `noir-blanc` | seuillage binaire |
+
+Paramètres communs aux filtres de couleur : `niveau` (seuil de blanc, défaut
+180), `couleur` (remplacement du blanc, hexadécimal), `permutation` (mot de
+trois lettres parmi `r`, `v`, `b`). `noir-blanc` accepte `niveau`, `mode`
+(`canaux` ou `luminance`), `clair` et `sombre`.
+
+La virgule sépare les paramètres : les couleurs s'écrivent donc en hexadécimal
+(`fefe01`), jamais en `254,254,1`.
+
+## Utilisation
+
+```bash
+cargo run --release -- photo.jpg -f jaune
+cargo run --release -- photo.jpg -f noir-blanc:niveau=128,mode=luminance
+cargo run --release -- photo.jpg -f rouge -f noir-blanc # chaînage
+cargo run --release -- photo.jpg --formats png,jpeg,webp \
+ --size vignette:256 --size web:1200 --quality 90 -o sortie
+
+cargo run -- --list-filters
+```
+
+Sans filtre, `filtro` se comporte en convertisseur de formats.
+
+## Tests
+
+```bash
+cargo test # unitaires par module + intégration bout en bout
+```
+
+`tests/pipeline.rs` définit un filtre `luminosite` **en dehors du cœur** : il
+vérifie que l'API publique (`filtro::…`) suffit à écrire un filtre et à traiter
+une image de bout en bout.
+
+## Notes
+
+* `#![forbid(unsafe_code)]`, aucun `unwrap()` hors des tests, `cargo clippy` propre.
+* Garde-fou contre les bombes de décompression (`Analyzer::with_max_pixels`).
+* Résolution lue dans le chunk `pHYs` (PNG) et le segment `JFIF` (JPEG).
+* Dépendances : `image` 0.25, `thiserror` 2, `clap` 4.6.
diff --git a/Filtro_Rust/src/controller/cli.rs b/Filtro_Rust/src/controller/cli.rs
new file mode 100644
index 0000000..3c09f2d
--- /dev/null
+++ b/Filtro_Rust/src/controller/cli.rs
@@ -0,0 +1,77 @@
+//! Définition des arguments de la ligne de commande et analyse des valeurs
+//! composées (formats, déclinaisons dimensionnelles).
+
+use std::path::PathBuf;
+
+use clap::Parser;
+use filtro::{FiltroError, OutputFormat, Result, SizeVariant};
+
+#[derive(Parser, Debug)]
+#[command(
+ name = "filtro",
+ version,
+ about = "Outil de filtrage d'images en ligne de commande",
+ long_about = None
+)]
+pub struct Cli {
+ /// Image à traiter.
+ #[arg(value_name = "IMAGE", required_unless_present = "list_filters")]
+ pub input: Option,
+
+ /// Filtre à appliquer, répétable : `-f id` ou `-f id:clé=valeur,clé=valeur`.
+ #[arg(short = 'f', long = "filter", value_name = "EXPRESSION")]
+ pub filters: Vec,
+
+ /// Formats de sortie, séparés par des virgules (png, jpeg, webp, bmp, tiff).
+ #[arg(long, value_name = "FORMATS", default_value = "png")]
+ pub formats: String,
+
+ /// Déclinaison dimensionnelle, répétable : `--size vignette:256`.
+ #[arg(long = "size", value_name = "ÉTIQUETTE:PIXELS")]
+ pub sizes: Vec,
+
+ /// Répertoire de sortie.
+ #[arg(
+ short = 'o',
+ long = "out",
+ value_name = "RÉPERTOIRE",
+ default_value = "sortie"
+ )]
+ pub out: PathBuf,
+
+ /// Qualité des formats avec perte (1–100).
+ #[arg(long, value_name = "1-100", default_value_t = 85)]
+ pub quality: u8,
+
+ /// Borne le plus grand côté de l'image de travail.
+ #[arg(long, value_name = "PIXELS")]
+ pub max_dimension: Option,
+
+ /// Liste les filtres disponibles puis quitte.
+ #[arg(long)]
+ pub list_filters: bool,
+}
+
+/// Analyse la liste de formats séparés par des virgules.
+pub fn parse_formats(raw: &str) -> Result> {
+ let formats: Vec = raw
+ .split(',')
+ .filter(|token| !token.trim().is_empty())
+ .map(OutputFormat::parse)
+ .collect::>()?;
+ if formats.is_empty() {
+ return Err(FiltroError::Config(
+ "la liste de formats est vide".to_owned(),
+ ));
+ }
+ Ok(formats)
+}
+
+/// Analyse les déclinaisons dimensionnelles ; `Original` est toujours en tête.
+pub fn parse_sizes(raw: &[String]) -> Result> {
+ let mut sizes = vec![SizeVariant::Original];
+ for expression in raw {
+ sizes.push(SizeVariant::parse(expression)?);
+ }
+ Ok(sizes)
+}
diff --git a/Filtro_Rust/src/controller/mod.rs b/Filtro_Rust/src/controller/mod.rs
new file mode 100644
index 0000000..e865a20
--- /dev/null
+++ b/Filtro_Rust/src/controller/mod.rs
@@ -0,0 +1,74 @@
+//! Couche **Controller** — traduit la ligne de commande en configuration du
+//! cœur, lance le pipeline, puis confie l'affichage à la couche [`view`](crate::view).
+
+mod cli;
+
+use std::process::ExitCode;
+
+use clap::Parser;
+use filtro::{FilterChain, FilterRegistry, FilterRequest, FiltroError, Pipeline, Result};
+
+use crate::view;
+use cli::{Cli, parse_formats, parse_sizes};
+
+/// **Unique point de branchement des filtres.**
+///
+/// Les filtres vivent hors du cœur, dans le module `filtro::filters`.
+fn register_filters(registry: &mut FilterRegistry) -> Result<()> {
+ filtro::filters::register_all(registry)
+}
+
+/// Point d'entrée de l'outil : renvoie le code de sortie du processus.
+pub fn run() -> ExitCode {
+ match execute() {
+ Ok(()) => ExitCode::SUCCESS,
+ Err(error) => {
+ view::report_error(&error);
+ ExitCode::FAILURE
+ }
+ }
+}
+
+fn execute() -> Result<()> {
+ let cli = Cli::parse();
+
+ let mut registry = FilterRegistry::new();
+ register_filters(&mut registry)?;
+
+ if cli.list_filters {
+ view::print_filters(®istry);
+ return Ok(());
+ }
+
+ let input = cli
+ .input
+ .ok_or_else(|| FiltroError::Config("aucune image fournie".to_owned()))?;
+
+ let requests: Vec = cli
+ .filters
+ .iter()
+ .map(|expression| FilterRequest::parse(expression))
+ .collect::>()?;
+
+ let chain: FilterChain = if requests.is_empty() {
+ FilterChain::empty()
+ } else {
+ registry.build_chain(&requests)?
+ };
+
+ let formats = parse_formats(&cli.formats)?;
+ let sizes = parse_sizes(&cli.sizes)?;
+
+ let pipeline = Pipeline::builder()
+ .formats(formats)
+ .sizes(sizes)
+ .quality(cli.quality)
+ .max_dimension(cli.max_dimension)
+ .build()?;
+
+ let output = pipeline.run_path(&input, &chain)?;
+ let written = output.write_to_dir(&cli.out)?;
+
+ view::print_summary(&output, &written);
+ Ok(())
+}
diff --git a/Filtro_Rust/src/filters/color.rs b/Filtro_Rust/src/filters/color.rs
new file mode 100644
index 0000000..d267b9b
--- /dev/null
+++ b/Filtro_Rust/src/filters/color.rs
@@ -0,0 +1,289 @@
+//! Filtres de couleur : `jaune`, `vert`, `rouge`.
+//!
+//! Les trois filtres font la même chose à deux constantes près :
+//!
+//! ```text
+//! si r > niveau_blanc et v > niveau_blanc et b > niveau_blanc :
+//! pixel <- (254, 254, 1) // le blanc devient une couleur
+//! sinon :
+//! pixel <- (b, v, r) // les autres pixels sont permutés
+//! ```
+//!
+//! Un seul type Rust couvre donc les trois filtres : seules la couleur de
+//! remplacement et la permutation par défaut changent.
+
+use crate::Result;
+use crate::{Filter, FilterContext};
+use crate::{FilterFactory, FilterParams, ParamSpec};
+use crate::{PixelBuffer, Rgba8};
+
+// ---------------------------------------------------------------------------
+// Permutation des canaux
+// ---------------------------------------------------------------------------
+
+/// Un canal source.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum Channel {
+ R,
+ V,
+ B,
+}
+
+/// Réaffectation des trois canaux, décrite par un mot de trois lettres.
+///
+/// `bvr` signifie : « le rouge de sortie prend le bleu d'entrée, le vert garde
+/// le vert, le bleu prend le rouge » — soit le pixel `(b, v, r)`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ChannelOrder([Channel; 3]);
+
+impl ChannelOrder {
+ /// Aucune permutation (`rvb`).
+ pub const IDENTITY: Self = Self([Channel::R, Channel::V, Channel::B]);
+
+ /// Analyse un mot de trois lettres parmi `r`, `v`/`g`, `b`.
+ pub fn parse(raw: &str) -> Option {
+ let letters: Vec = raw
+ .trim()
+ .to_ascii_lowercase()
+ .chars()
+ .map(|c| match c {
+ 'r' => Some(Channel::R),
+ 'v' | 'g' => Some(Channel::V),
+ 'b' => Some(Channel::B),
+ _ => None,
+ })
+ .collect::