From 17208922365b995953168c4981cec5781a98d4b4 Mon Sep 17 00:00:00 2001 From: Alexandre GOURAUD <148047039+Alexandre-git-SDV@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:54:22 +0000 Subject: [PATCH 1/3] [REF] Deletion of the python files + update of the README files --- .github/workflows/python-ci.yml | 62 ---------- .gitignore | 37 ------ Dockerfile | 35 +++--- Filtro_Python/app.py | 80 ------------- .../static/results/vert_ImageIA2.webp | Bin 82650 -> 0 bytes Filtro_Python/static/style.css | 87 -------------- Filtro_Python/static/uploads/ImageIA2.webp | Bin 199226 -> 0 bytes Filtro_Python/templates/index.html | 60 ---------- Filtro_Rust/src/red.rs | 71 ------------ README.fr.md | 107 +++++++++++------- README.md | 107 +++++++++++------- index.html | 2 +- requirements.txt | 4 - 13 files changed, 148 insertions(+), 504 deletions(-) delete mode 100644 .github/workflows/python-ci.yml delete mode 100644 Filtro_Python/app.py delete mode 100644 Filtro_Python/static/results/vert_ImageIA2.webp delete mode 100644 Filtro_Python/static/style.css delete mode 100644 Filtro_Python/static/uploads/ImageIA2.webp delete mode 100644 Filtro_Python/templates/index.html delete mode 100644 Filtro_Rust/src/red.rs delete mode 100644 requirements.txt 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 69ee8e44af78cabac08fa827114e3539fea2fef7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82650 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 z 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 bja*vi0(Ti#F6jC-MZS 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 - - - - Filtro - Appliquez votre filtre image ! - - - - - - - -
-

Upload d'une image

-
- - -
- - {% if uploaded_file %} -

Image chargée :

-

-
- - - - - -
- {% endif %} - - {% if filtered_image %} -

Image filtrée :

-
- - - - {% endif %} -
- - -
-

À propos

-

Auteurs : Alexandre GOURAUD, Alla-Eddine BOUKABOU

-

- Filtro est une application web permettant d’uploader une image et d’y appliquer des filtres quasi instantanément (jaune, vert, rouge ou noir et blanc). -

-

- Projet réalisé dans le cadre de notre Projet d'étude.
- © 2025 Filtro Team. -

-
- - diff --git a/Filtro_Rust/src/red.rs b/Filtro_Rust/src/red.rs deleted file mode 100644 index 1d4b3b7..0000000 --- a/Filtro_Rust/src/red.rs +++ /dev/null @@ -1,71 +0,0 @@ -use image::{Rgb, RgbImage}; -use rfd::FileDialog; - -fn main() { - // Niveau blanc - let niveau_blanc: u8 = 180; - - println!("Veuillez choisir votre image"); - - // Ouverture de la boîte de dialogue - let fichier_image = FileDialog::new() - .set_title("Sélectionner un fichier") - .pick_file(); - - let fichier_image = match fichier_image { - Some(path) => path, - None => { - println!("Aucun fichier sélectionné."); - return; - } - }; - - println!("Démarrage du traitement de : {:?}", fichier_image); - println!("niveau_blanc {}", niveau_blanc); - - // Chargement de l'image - let mut img: RgbImage = image::open(&fichier_image) - .expect("Impossible d'ouvrir l'image") - .to_rgb8(); - - let (largeur_img, hauteur_img) = img.dimensions(); - - filtre_rouge(&mut img, largeur_img, hauteur_img, niveau_blanc); - - img.save("Image Rouge.jpg") - .expect("Impossible de sauvegarder l'image"); - - println!("Traitement image rouge terminé"); -} - -fn filtre_rouge( - img: &mut RgbImage, - largeur_img: u32, - hauteur_img: u32, - niveau_blanc: u8, -) { - println!("Vous avez choisi le filtre Rouge"); - - for y in 0..hauteur_img { - for x in 0..largeur_img { - let pixel = img.get_pixel(x, y); - - let r = pixel[0]; - let v = pixel[1]; - let b = pixel[2]; - - let nouveau_pixel = if r > niveau_blanc - && v > niveau_blanc - && b > niveau_blanc - { - // Blanc -> Rouge - Rgb([254, 1, 1]) - } else { - // Permutation des couleurs (b, r, v) - Rgb([b, r, v]) - }; - - img.put_pixel(x, y, nouveau_pixel); - } - } -} \ No newline at end of file diff --git a/README.fr.md b/README.fr.md index 84da1f7..c70d485 100644 --- a/README.fr.md +++ b/README.fr.md @@ -1,89 +1,110 @@ # Filtro -Filtro est une application de traitement d’image qui transforme des photos classiques en œuvres visuelles inspirées du style **Pop Art d’Andy Warhol**. +Filtro est un outil de traitement d'image qui transforme des photos ordinaires en +œuvres visuelles inspirées du style **Pop Art d'Andy Warhol** : aplats de couleur +saturés et contraste franc. -L’objectif du projet est de proposer un outil simple et rapide permettant d’appliquer des effets graphiques colorés, contrastés et artistiques afin de créer des portraits uniques. +Il se présente comme un **outil en ligne de commande écrit en Rust** (dans +[`Filtro_Rust/`](Filtro_Rust/)) : un pipeline stable en quatre étapes et un jeu +de filtres qui s'ajoutent sans jamais toucher au cœur. ## ✨ Fonctionnalités -* 🖼️ Importation d’images -* 🎨 Application d’effets Pop Art inspirés d’Andy Warhol -* 🌈 Génération de palettes de couleurs vives -* ⚡ Traitement rapide des images -* 🖥️ Interface simple et intuitive -* 🌐 Application accessible depuis un navigateur - -## 🚀 Démonstration - -Essayez Filtro en ligne : - -https://alexandre-git-sdv.github.io/Filtro/ +* 🎨 Filtres de couleur Pop Art — `jaune`, `vert`, `rouge`, `noir-blanc` +* 🔗 Chaînage de filtres (`-f rouge -f noir-blanc`) +* 🖼️ Sortie multi-format en une passe — PNG, JPEG, WebP, BMP, TIFF +* 📐 Déclinaisons dimensionnelles (vignettes, tailles web) générées ensemble +* 🔄 Simple convertisseur de formats quand aucun filtre n'est donné +* ⚡ Traitement rapide en un seul passage ; `#![forbid(unsafe_code)]` ## 🛠️ Technologies utilisées -* **Python** — traitement d’image et logique applicative -* **Rust** — composants optimisés (selon les modules du projet) -* **HTML / CSS / JavaScript** — interface utilisateur -* **Docker** — environnement de déploiement +* **Rust** (édition 2024, `rust-version` 1.88) — pipeline et CLI +* [`image`](https://crates.io/crates/image) — décodage et encodage +* [`clap`](https://crates.io/crates/clap) — analyse des arguments +* [`thiserror`](https://crates.io/crates/thiserror) — types d'erreur ## 📦 Installation locale ### Prérequis -* Python 3.x +* Une chaîne d'outils Rust ≥ 1.88 ([rustup.rs](https://rustup.rs)) * Git -### Cloner le projet +### Cloner et compiler ```bash git clone https://github.com/Alexandre-git-SDV/Filtro.git -cd Filtro +cd Filtro/Filtro_Rust +cargo build --release ``` -### Installer les dépendances +Le binaire se trouve alors dans `Filtro_Rust/target/release/filtro`. -```bash -pip install -r requirements.txt -``` +## 🚀 Utilisation -### Lancer l’application +À lancer depuis `Filtro_Rust/` (ou en appelant directement le binaire compilé) : ```bash -python app.py +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 ``` -L’application sera ensuite disponible localement selon la configuration du serveur. +| Filtre | Effet | +|--------|-------| +| `jaune` | blanc → jaune, canaux rouge et bleu échangés | +| `vert` | blanc → vert | +| `rouge` | blanc → rouge | +| `noir-blanc` | seuillage binaire (par canaux ou par luminance) | + +Les filtres de couleur acceptent `niveau` (seuil de blanc, défaut 180), `couleur` +(couleur de remplacement en hexadécimal) et `permutation`. `noir-blanc` accepte +`niveau`, `mode` (`canaux` / `luminance`), `clair` et `sombre`. Les paramètres +sont séparés par des virgules : les couleurs s'écrivent donc en hexadécimal +(`fefe01`), jamais en `254,254,1`. ## 📂 Structure du projet ```text Filtro/ -├── app.py # Application principale -├── requirements.txt # Dépendances Python -├── dockerfile # Configuration Docker -├── static/ # Ressources statiques -├── templates/ # Templates de l’interface -└── README.md # Documentation +└── Filtro_Rust/ # l'outil CLI — voir Filtro_Rust/README.md + ├── Cargo.toml + ├── src/ + │ ├── lib.rs # bibliothèque « filtro » (le Model) + façade publique + │ ├── main.rs # point d'entrée du binaire + │ ├── model/ # M — cœur : types d'image, pipeline en 4 étapes, contrat des filtres + │ ├── filters/ # les filtres, hors du cœur + │ ├── controller/ # C — analyse de la CLI et orchestration + │ └── view/ # V — affichage terminal + └── tests/pipeline.rs # test d'intégration bout en bout ``` +Le pipeline : `Analyzer → Constructor → Formatter → Renderer`. On ajoute un filtre +en implémentant `Filter` + `FilterFactory` dans `src/filters/` puis en +l'enregistrant en une ligne — le cœur ne change pas. Détails complets dans +[`Filtro_Rust/README.md`](Filtro_Rust/README.md). + ## 🎯 Objectif du projet -Filtro explore la rencontre entre programmation et création artistique en utilisant le traitement d’image pour transformer des photos ordinaires en compositions graphiques originales. +Filtro explore la rencontre entre programmation et création artistique en +utilisant le traitement d'image pour transformer des photos ordinaires en +compositions graphiques originales. ## 🤝 Contribution Les contributions sont les bienvenues : 1. Forkez le projet -2. Créez une branche : - -```bash -git checkout -b feature/nouvelle-fonctionnalite -``` - -3. Faites vos modifications +2. Créez une branche : `git checkout -b feature/nouvelle-fonctionnalite` +3. Faites vos modifications (`cargo test` et `cargo clippy` doivent rester au vert) 4. Créez une Pull Request ## 📄 Licence -Ce projet est open source. Consultez le dépôt pour plus d’informations sur la licence utilisée. +Ce projet est open source. Consultez le dépôt pour plus d'informations sur la +licence utilisée. diff --git a/README.md b/README.md index 19f2584..558144e 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,109 @@ # Filtro -Filtro is an image processing application that transforms regular photos into visual artworks inspired by the **Pop Art style of Andy Warhol**. +Filtro is an image-processing tool that turns ordinary photos into visual +artworks inspired by the **Pop Art style of Andy Warhol**: flat, saturated +colours and hard contrast. -The goal of the project is to provide a simple and fast tool for applying colorful, high-contrast artistic effects to create unique portraits. +It ships as a **command-line tool written in Rust** (under +[`Filtro_Rust/`](Filtro_Rust/)): a stable four-stage pipeline and a set of +filters that can be added without ever touching the core. ## ✨ Features -* 🖼️ Image import -* 🎨 Pop Art effects inspired by Andy Warhol -* 🌈 Generation of vibrant color palettes -* ⚡ Fast image processing -* 🖥️ Simple and intuitive interface -* 🌐 Browser-based application - -## 🚀 Demo - -Try Filtro online: - -https://alexandre-git-sdv.github.io/Filtro/ +* 🎨 Pop Art colour filters — `jaune`, `vert`, `rouge`, `noir-blanc` +* 🔗 Filter chaining (`-f rouge -f noir-blanc`) +* 🖼️ Multi-format output in one run — PNG, JPEG, WebP, BMP, TIFF +* 📐 Size variants (thumbnails, web sizes) generated together +* 🔄 Runs as a plain format converter when no filter is given +* ⚡ Fast, single-pass processing; `#![forbid(unsafe_code)]` ## 🛠️ Technologies Used -* **Python** — image processing and application logic -* **Rust** — optimized components (depending on project modules) -* **HTML / CSS / JavaScript** — user interface -* **Docker** — deployment environment +* **Rust** (edition 2024, `rust-version` 1.88) — pipeline and CLI +* [`image`](https://crates.io/crates/image) — decoding and encoding +* [`clap`](https://crates.io/crates/clap) — argument parsing +* [`thiserror`](https://crates.io/crates/thiserror) — error types ## 📦 Local Installation ### Prerequisites -* Python 3.x +* A Rust toolchain ≥ 1.88 ([rustup.rs](https://rustup.rs)) * Git -### Clone the repository +### Clone and build ```bash git clone https://github.com/Alexandre-git-SDV/Filtro.git -cd Filtro +cd Filtro/Filtro_Rust +cargo build --release ``` -### Install dependencies +The binary is then at `Filtro_Rust/target/release/filtro`. -```bash -pip install -r requirements.txt -``` +## 🚀 Usage -### Run the application +Run from `Filtro_Rust/` (or call the built binary directly): ```bash -python app.py +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 # chaining +cargo run --release -- photo.jpg --formats png,jpeg,webp \ + --size vignette:256 --size web:1200 --quality 90 -o out + +cargo run -- --list-filters ``` -The application will then be available locally depending on your server configuration. +| Filter | Effect | +|--------|--------| +| `jaune` | white → yellow, red/blue channels swapped | +| `vert` | white → green | +| `rouge` | white → red | +| `noir-blanc` | binary threshold (per-channel or luminance) | + +Colour filters accept `niveau` (white threshold, default 180), `couleur` (hex +replacement colour) and `permutation`. `noir-blanc` accepts `niveau`, `mode` +(`canaux` / `luminance`), `clair` and `sombre`. Parameters are comma-separated, +so colours are written in hex (`fefe01`), never as `254,254,1`. ## 📂 Project Structure ```text Filtro/ -├── app.py # Main application -├── requirements.txt # Python dependencies -├── dockerfile # Docker configuration -├── static/ # Static assets -├── templates/ # Interface templates -└── README.md # Documentation +└── Filtro_Rust/ # the CLI tool — see Filtro_Rust/README.md + ├── Cargo.toml + ├── src/ + │ ├── lib.rs # library "filtro" (the Model) + public façade + │ ├── main.rs # binary entry point + │ ├── model/ # M — core: pixel types, 4-stage pipeline, filter contract + │ ├── filters/ # the filters, outside the core + │ ├── controller/ # C — CLI parsing and orchestration + │ └── view/ # V — terminal output + └── tests/pipeline.rs # end-to-end integration test ``` +The pipeline: `Analyzer → Constructor → Formatter → Renderer`. Filters are added +by implementing `Filter` + `FilterFactory` in `src/filters/` and registering them +in one line — the core never changes. Full details in +[`Filtro_Rust/README.md`](Filtro_Rust/README.md). + ## 🎯 Project Purpose -Filtro explores the connection between programming and digital art by using image processing techniques to transform ordinary photos into original graphic compositions. +Filtro explores the connection between programming and digital art by using +image-processing techniques to transform ordinary photos into original graphic +compositions. ## 🤝 Contributing Contributions are welcome: 1. Fork the project -2. Create a new branch: - -```bash -git checkout -b feature/new-feature -``` - -3. Make your changes +2. Create a new branch: `git checkout -b feature/new-feature` +3. Make your changes (`cargo test` and `cargo clippy` should stay green) 4. Submit a Pull Request ## 📄 License -This project is open source. Check the repository for more information about the license. - +This project is open source. Check the repository for more information about the +license. diff --git a/index.html b/index.html index c11eaaa..dfe4fdf 100644 --- a/index.html +++ b/index.html @@ -75,7 +75,7 @@

À propos

const sourceCanvas = document.getElementById('source-canvas'); const workCanvas = document.getElementById('work-canvas'); - // --- Reproduit exactement la logique de apply_filter() côté Python --- + // --- Même logique de filtrage que l'outil CLI (seuil de blanc + permutation des canaux) --- function applyFilter(imageData, colorFilter) { const data = imageData.data; // [r,g,b,a, r,g,b,a, ...] for (let i = 0; i < data.length; i += 4) { diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index fff3f71..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -requests -# This file is used to install the required packages for the project. -Flask -Pillow From eee4f6923cec11789bff85b189558879f45289de Mon Sep 17 00:00:00 2001 From: Alexandre GOURAUD <148047039+Alexandre-git-SDV@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:55:19 +0000 Subject: [PATCH 2/3] [CI] Deletion of the python workflow + update of the workflows --- .github/dependabot.yml | 26 +- .github/workflows/Docker CI.yml | 6 +- .github/workflows/codeql.yml | 12 +- Filtro_Rust/Cargo.lock | 832 ++++++-------------------------- Filtro_Rust/Cargo.toml | 31 +- 5 files changed, 195 insertions(+), 712 deletions(-) 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/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 From 65ab53f1968cc788a33c42ed7491b4633a3f7fa8 Mon Sep 17 00:00:00 2001 From: Alexandre GOURAUD <148047039+Alexandre-git-SDV@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:07:36 +0000 Subject: [PATCH 3/3] [IMP] Refacto of the cli app filtro rust --- Filtro_Rust/README.md | 140 +++++++ Filtro_Rust/src/controller/cli.rs | 77 ++++ Filtro_Rust/src/controller/mod.rs | 74 ++++ Filtro_Rust/src/filters/color.rs | 289 +++++++++++++ Filtro_Rust/src/filters/mod.rs | 63 +++ Filtro_Rust/src/filters/threshold.rs | 191 +++++++++ Filtro_Rust/src/lib.rs | 76 ++++ Filtro_Rust/src/main.rs | 29 +- Filtro_Rust/src/model/codec.rs | 117 ++++++ Filtro_Rust/src/model/error.rs | 89 ++++ Filtro_Rust/src/model/filter/contract.rs | 173 ++++++++ Filtro_Rust/src/model/filter/mod.rs | 13 + Filtro_Rust/src/model/filter/registry.rs | 386 ++++++++++++++++++ Filtro_Rust/src/model/mod.rs | 21 + Filtro_Rust/src/model/pipeline/analyzer.rs | 298 ++++++++++++++ Filtro_Rust/src/model/pipeline/constructor.rs | 154 +++++++ Filtro_Rust/src/model/pipeline/formatter.rs | 362 ++++++++++++++++ Filtro_Rust/src/model/pipeline/mod.rs | 17 + .../src/model/pipeline/orchestrator.rs | 145 +++++++ Filtro_Rust/src/model/pipeline/renderer.rs | 193 +++++++++ Filtro_Rust/src/model/pixel.rs | 342 ++++++++++++++++ Filtro_Rust/src/model/resolution.rs | 120 ++++++ Filtro_Rust/src/test_support.rs | 74 ++++ Filtro_Rust/src/view/catalog.rs | 29 ++ Filtro_Rust/src/view/mod.rs | 33 ++ Filtro_Rust/src/view/report.rs | 40 ++ Filtro_Rust/tests/pipeline.rs | 204 +++++++++ 27 files changed, 3727 insertions(+), 22 deletions(-) create mode 100644 Filtro_Rust/README.md create mode 100644 Filtro_Rust/src/controller/cli.rs create mode 100644 Filtro_Rust/src/controller/mod.rs create mode 100644 Filtro_Rust/src/filters/color.rs create mode 100644 Filtro_Rust/src/filters/mod.rs create mode 100644 Filtro_Rust/src/filters/threshold.rs create mode 100644 Filtro_Rust/src/lib.rs create mode 100644 Filtro_Rust/src/model/codec.rs create mode 100644 Filtro_Rust/src/model/error.rs create mode 100644 Filtro_Rust/src/model/filter/contract.rs create mode 100644 Filtro_Rust/src/model/filter/mod.rs create mode 100644 Filtro_Rust/src/model/filter/registry.rs create mode 100644 Filtro_Rust/src/model/mod.rs create mode 100644 Filtro_Rust/src/model/pipeline/analyzer.rs create mode 100644 Filtro_Rust/src/model/pipeline/constructor.rs create mode 100644 Filtro_Rust/src/model/pipeline/formatter.rs create mode 100644 Filtro_Rust/src/model/pipeline/mod.rs create mode 100644 Filtro_Rust/src/model/pipeline/orchestrator.rs create mode 100644 Filtro_Rust/src/model/pipeline/renderer.rs create mode 100644 Filtro_Rust/src/model/pixel.rs create mode 100644 Filtro_Rust/src/model/resolution.rs create mode 100644 Filtro_Rust/src/test_support.rs create mode 100644 Filtro_Rust/src/view/catalog.rs create mode 100644 Filtro_Rust/src/view/mod.rs create mode 100644 Filtro_Rust/src/view/report.rs create mode 100644 Filtro_Rust/tests/pipeline.rs 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::>()?; + let channels: [Channel; 3] = letters.try_into().ok()?; + Some(Self(channels)) + } + + /// Applique la permutation ; l'alpha est toujours préservé. + pub fn apply(self, pixel: Rgba8) -> Rgba8 { + let take = |channel: Channel| match channel { + Channel::R => pixel.r, + Channel::V => pixel.g, + Channel::B => pixel.b, + }; + Rgba8::new(take(self.0[0]), take(self.0[1]), take(self.0[2]), pixel.a) + } +} + +// --------------------------------------------------------------------------- +// Le filtre +// --------------------------------------------------------------------------- + +/// Remplace les pixels clairs par une couleur, permute les autres. +#[derive(Debug, Clone)] +pub struct ColorFilter { + id: &'static str, + /// Seuil au-delà duquel un pixel est considéré comme blanc (`niveau_blanc`). + level: u8, + /// Couleur qui remplace le blanc. + color: Rgba8, + /// Permutation appliquée aux pixels non blancs. + order: ChannelOrder, +} + +impl ColorFilter { + /// Vrai si les trois composantes dépassent le seuil. + fn is_white(&self, pixel: Rgba8) -> bool { + pixel.r > self.level && pixel.g > self.level && pixel.b > self.level + } +} + +impl Filter for ColorFilter { + fn id(&self) -> &str { + self.id + } + + fn apply(&self, canvas: &mut PixelBuffer, _ctx: &FilterContext<'_>) -> Result<()> { + canvas.map_pixels(|_, _, pixel| { + if self.is_white(pixel) { + Rgba8 { + a: pixel.a, + ..self.color + } + } else { + self.order.apply(pixel) + } + }); + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// Fabrique +// --------------------------------------------------------------------------- + +/// Fabrique commune aux trois filtres de couleur. +/// +/// Chaque filtre n'est qu'un jeu de valeurs par défaut. +#[derive(Debug, Clone, Copy)] +pub struct ColorFilterFactory { + id: &'static str, + description: &'static str, + color: Rgba8, + order: &'static str, + params: &'static [ParamSpec], +} + +impl FilterFactory for ColorFilterFactory { + fn id(&self) -> &'static str { + self.id + } + + fn description(&self) -> &'static str { + self.description + } + + fn parameters(&self) -> &'static [ParamSpec] { + self.params + } + + fn build(&self, params: &FilterParams) -> Result> { + let level = params.u8("niveau", 180)?; + let color = params.color("couleur", self.color)?; + let raw_order = params.text("permutation", self.order); + let order = + ChannelOrder::parse(raw_order).ok_or_else(|| crate::FiltroError::InvalidParameter { + filter: self.id.to_owned(), + name: "permutation".to_owned(), + reason: format!("« {raw_order} » n'est pas un mot de trois lettres parmi r, v, b"), + })?; + + Ok(Box::new(ColorFilter { + id: self.id, + level, + color, + order, + })) + } +} + +/// Construit la liste de paramètres d'un filtre de couleur. +macro_rules! color_params { + ($couleur:expr, $permutation:expr) => { + &[ + ParamSpec { + name: "niveau", + default: "180", + help: "seuil (0-255) au-delà duquel un pixel est vu comme blanc", + }, + ParamSpec { + name: "couleur", + default: $couleur, + help: "couleur hexadécimale remplaçant le blanc", + }, + ParamSpec { + name: "permutation", + default: $permutation, + help: "réaffectation des canaux des autres pixels (ex. bvr)", + }, + ] + }; +} + +/// Filtre `jaune` — blanc → jaune, canaux permutés en `bvr`. +pub fn yellow() -> ColorFilterFactory { + ColorFilterFactory { + id: "jaune", + description: "Remplace le blanc par du jaune et échange rouge et bleu", + color: Rgba8::new(254, 254, 1, 255), + order: "bvr", + params: color_params!("fefe01", "bvr"), + } +} + +/// Filtre `vert` — blanc → vert, canaux permutés en `rbv`. +pub fn green() -> ColorFilterFactory { + ColorFilterFactory { + id: "vert", + description: "Remplace le blanc par du vert et échange vert et bleu", + color: Rgba8::new(145, 254, 1, 255), + order: "rbv", + params: color_params!("91fe01", "rbv"), + } +} + +/// Filtre `rouge` — blanc → rouge, canaux permutés en `brv`. +pub fn red() -> ColorFilterFactory { + ColorFilterFactory { + id: "rouge", + description: "Remplace le blanc par du rouge et fait tourner les canaux", + color: Rgba8::new(254, 1, 1, 255), + order: "brv", + params: color_params!("fe0101", "brv"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_support::apply_to_pixel; + use crate::{FilterRegistry, FilterRequest}; + + #[test] + fn permutation_bvr_echange_rouge_et_bleu() { + let order = ChannelOrder::parse("bvr").unwrap(); + let out = order.apply(Rgba8::new(10, 20, 30, 200)); + assert_eq!(out, Rgba8::new(30, 20, 10, 200)); + } + + #[test] + fn permutation_invalide() { + assert!(ChannelOrder::parse("bv").is_none()); + assert!(ChannelOrder::parse("xyz").is_none()); + assert_eq!(ChannelOrder::parse("rgb"), Some(ChannelOrder::IDENTITY)); + } + + fn filtre(expression: &str) -> Box { + let mut registry = FilterRegistry::new(); + registry.register(yellow()).unwrap(); + registry.register(green()).unwrap(); + registry.register(red()).unwrap(); + registry + .build(&FilterRequest::parse(expression).unwrap()) + .unwrap() + } + + #[test] + fn le_blanc_devient_jaune() { + let sortie = apply_to_pixel(&*filtre("jaune"), Rgba8::new(200, 210, 220, 255)); + assert_eq!(sortie, Rgba8::new(254, 254, 1, 255)); + } + + #[test] + fn les_autres_pixels_sont_permutes() { + // 100 < 180 : le pixel n'est pas « blanc », il est donc permuté. + let sortie = apply_to_pixel(&*filtre("jaune"), Rgba8::new(10, 100, 30, 255)); + assert_eq!(sortie, Rgba8::new(30, 100, 10, 255)); + } + + #[test] + fn le_seuil_est_reglable() { + let clair = Rgba8::new(200, 200, 200, 255); + // Avec un seuil à 220, ce pixel n'est plus considéré comme blanc. + let sortie = apply_to_pixel(&*filtre("jaune:niveau=220"), clair); + assert_eq!(sortie, clair); // permutation bvr sur un gris = identique + } + + #[test] + fn la_couleur_est_reglable() { + let sortie = apply_to_pixel(&*filtre("rouge:couleur=00ccff"), Rgba8::WHITE); + assert_eq!(sortie, Rgba8::new(0, 204, 255, 255)); + } + + #[test] + fn l_alpha_est_preserve() { + let sortie = apply_to_pixel(&*filtre("vert"), Rgba8::new(250, 250, 250, 64)); + assert_eq!(sortie.a, 64); + } + + #[test] + fn permutation_invalide_rejetee_a_la_construction() { + let mut registry = FilterRegistry::new(); + registry.register(yellow()).unwrap(); + let request = FilterRequest::parse("jaune:permutation=xx").unwrap(); + assert!(registry.build(&request).is_err()); + } +} diff --git a/Filtro_Rust/src/filters/mod.rs b/Filtro_Rust/src/filters/mod.rs new file mode 100644 index 0000000..bc10c3c --- /dev/null +++ b/Filtro_Rust/src/filters/mod.rs @@ -0,0 +1,63 @@ +//! Les filtres — **hors du cœur**. +//! +//! Ce module n'utilise que l'API publique de la bibliothèque +//! ([`Filter`](crate::Filter), [`FilterFactory`](crate::FilterFactory)). +//! Aucune étape du pipeline ne le connaît : il pourrait être déplacé dans un +//! crate séparé sans modifier une seule ligne d'Analyzer, Constructor, +//! Formatter ou Renderer. +//! +//! Filtres livrés avec le projet : +//! +//! | 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 | + +pub mod color; +pub mod threshold; + +use crate::FilterRegistry; +use crate::Result; + +/// Enregistre tous les filtres fournis avec le projet. +/// +/// C'est l'unique point de couplage entre l'application et les filtres : +/// ajouter un filtre se résume à une ligne ici. +pub fn register_all(registry: &mut FilterRegistry) -> Result<()> { + registry.register(color::yellow())?; + registry.register(color::green())?; + registry.register(color::red())?; + registry.register(threshold::BlackAndWhiteFactory)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tous_les_filtres_s_enregistrent() { + let mut registry = FilterRegistry::new(); + register_all(&mut registry).unwrap(); + assert_eq!(registry.len(), 4); + for id in ["jaune", "vert", "rouge", "noir-blanc"] { + assert!(registry.get(id).is_some(), "filtre « {id} » absent"); + } + } + + #[test] + fn chaque_filtre_se_construit_sans_parametre() { + let mut registry = FilterRegistry::new(); + register_all(&mut registry).unwrap(); + for factory in registry.factories() { + let params = crate::FilterParams::new(factory.id()); + assert!( + factory.build(¶ms).is_ok(), + "le filtre « {} » exige un paramètre", + factory.id() + ); + } + } +} diff --git a/Filtro_Rust/src/filters/threshold.rs b/Filtro_Rust/src/filters/threshold.rs new file mode 100644 index 0000000..95dfbe2 --- /dev/null +++ b/Filtro_Rust/src/filters/threshold.rs @@ -0,0 +1,191 @@ +//! Filtre `noir-blanc` — seuillage binaire. +//! +//! ```text +//! si r > niveau_blanc et v > niveau_blanc et b > niveau_blanc : +//! pixel <- (255, 255, 255) +//! sinon : +//! pixel <- (0, 0, 0) +//! ``` +//! +//! Le mode par défaut compare les trois composantes séparément. Le paramètre +//! `mode=luminance` propose la variante habituelle en traitement d'image : +//! comparer la luminance perçue du pixel. + +use crate::{Filter, FilterContext}; +use crate::{FilterFactory, FilterParams, ParamSpec}; +use crate::{FiltroError, Result}; +use crate::{PixelBuffer, Rgba8}; + +/// Manière de décider si un pixel est clair. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ThresholdMode { + /// Les trois composantes doivent dépasser le seuil (mode par défaut). + Channels, + /// La luminance perçue doit dépasser le seuil. + Luminance, +} + +impl ThresholdMode { + fn parse(raw: &str) -> Option { + match raw.trim().to_ascii_lowercase().as_str() { + "canaux" | "channels" => Some(Self::Channels), + "luminance" => Some(Self::Luminance), + _ => None, + } + } + + fn is_light(self, pixel: Rgba8, level: u8) -> bool { + match self { + Self::Channels => pixel.r > level && pixel.g > level && pixel.b > level, + Self::Luminance => pixel.luminance() > level, + } + } +} + +/// Seuillage binaire : chaque pixel devient clair ou sombre. +#[derive(Debug, Clone)] +pub struct BlackAndWhiteFilter { + level: u8, + mode: ThresholdMode, + light: Rgba8, + dark: Rgba8, +} + +impl Filter for BlackAndWhiteFilter { + fn id(&self) -> &str { + "noir-blanc" + } + + fn apply(&self, canvas: &mut PixelBuffer, _ctx: &FilterContext<'_>) -> Result<()> { + canvas.map_pixels(|_, _, pixel| { + let chosen = if self.mode.is_light(pixel, self.level) { + self.light + } else { + self.dark + }; + Rgba8 { + a: pixel.a, + ..chosen + } + }); + Ok(()) + } +} + +/// Fabrique du filtre `noir-blanc`. +#[derive(Debug, Clone, Copy)] +pub struct BlackAndWhiteFactory; + +const PARAMS: &[ParamSpec] = &[ + ParamSpec { + name: "niveau", + default: "180", + help: "seuil (0-255) séparant le clair du sombre", + }, + ParamSpec { + name: "mode", + default: "canaux", + help: "canaux (comparaison composante par composante) ou luminance", + }, + ParamSpec { + name: "clair", + default: "ffffff", + help: "couleur hexadécimale des pixels au-dessus du seuil", + }, + ParamSpec { + name: "sombre", + default: "000000", + help: "couleur hexadécimale des pixels en dessous du seuil", + }, +]; + +impl FilterFactory for BlackAndWhiteFactory { + fn id(&self) -> &'static str { + "noir-blanc" + } + + fn description(&self) -> &'static str { + "Seuillage binaire : chaque pixel devient clair ou sombre" + } + + fn parameters(&self) -> &'static [ParamSpec] { + PARAMS + } + + fn build(&self, params: &FilterParams) -> Result> { + let raw_mode = params.text("mode", "canaux"); + let mode = ThresholdMode::parse(raw_mode).ok_or_else(|| FiltroError::InvalidParameter { + filter: self.id().to_owned(), + name: "mode".to_owned(), + reason: format!("« {raw_mode} » inconnu (attendu : canaux ou luminance)"), + })?; + + Ok(Box::new(BlackAndWhiteFilter { + level: params.u8("niveau", 180)?, + mode, + light: params.color("clair", Rgba8::WHITE)?, + dark: params.color("sombre", Rgba8::BLACK)?, + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_support::apply_to_pixel; + use crate::{FilterRegistry, FilterRequest}; + + fn filtre(expression: &str) -> Box { + let mut registry = FilterRegistry::new(); + registry.register(BlackAndWhiteFactory).unwrap(); + registry + .build(&FilterRequest::parse(expression).unwrap()) + .unwrap() + } + + #[test] + fn seuillage_par_canaux() { + let f = filtre("noir-blanc"); + assert_eq!( + apply_to_pixel(&*f, Rgba8::new(200, 200, 200, 255)), + Rgba8::WHITE + ); + assert_eq!( + apply_to_pixel(&*f, Rgba8::new(200, 100, 200, 255)), + Rgba8::BLACK + ); + } + + #[test] + fn seuillage_par_luminance() { + // Vert vif : luminance ≈ 191, au-dessus du seuil de 180… + let vert = Rgba8::new(100, 255, 100, 255); + assert_eq!( + apply_to_pixel(&*filtre("noir-blanc:mode=luminance"), vert), + Rgba8::WHITE + ); + // …alors qu'en mode « canaux » le rouge à 100 le fait basculer au noir. + assert_eq!(apply_to_pixel(&*filtre("noir-blanc"), vert), Rgba8::BLACK); + } + + #[test] + fn couleurs_personnalisees() { + let f = filtre("noir-blanc:clair=ffcc00,sombre=001133"); + assert_eq!( + apply_to_pixel(&*f, Rgba8::WHITE), + Rgba8::new(255, 204, 0, 255) + ); + assert_eq!( + apply_to_pixel(&*f, Rgba8::BLACK), + Rgba8::new(0, 17, 51, 255) + ); + } + + #[test] + fn mode_inconnu_rejete() { + let mut registry = FilterRegistry::new(); + registry.register(BlackAndWhiteFactory).unwrap(); + let request = FilterRequest::parse("noir-blanc:mode=sepia").unwrap(); + assert!(registry.build(&request).is_err()); + } +} diff --git a/Filtro_Rust/src/lib.rs b/Filtro_Rust/src/lib.rs new file mode 100644 index 0000000..c69e133 --- /dev/null +++ b/Filtro_Rust/src/lib.rs @@ -0,0 +1,76 @@ +//! # Filtro +//! +//! Traitement d'images organisé en quatre étapes stables, plus un jeu de +//! filtres qui vit à l'extérieur de ces étapes. +//! +//! ## Architecture (MVC) +//! +//! | Couche | Emplacement | Rôle | +//! |--------|-------------|------| +//! | **Model** | [`model`] (cette bibliothèque) | le cœur métier : pipeline, contrat des filtres, types d'image | +//! | **View** | `src/view/` (binaire) | rendu terminal des résultats et du catalogue de filtres | +//! | **Controller** | `src/controller/` (binaire) | traduction des arguments CLI en configuration du cœur | +//! +//! Les filtres livrés avec le projet sont regroupés dans [`filters`] : ce module +//! n'utilise que l'API publique ci-dessous et aucune étape du pipeline ne le +//! référence. +//! +//! ## Le pipeline +//! +//! | Étape | Rôle | Entrée | Sortie | +//! |-------|------|--------|--------| +//! | [`Analyzer`] | décode et décrit l'image | octets | [`ImageAnalysis`] | +//! | [`Constructor`] | prépare l'image de travail | analyse + exigences des filtres | [`PreparedImage`] | +//! | [`Formatter`] | décline formats et tailles | image préparée | [`FormatSet`] | +//! | [`Renderer`] | applique les filtres et encode | variantes + chaîne | [`RenderOutput`] | +//! +//! ```no_run +//! use filtro::{FilterRegistry, FilterRequest, OutputFormat, Pipeline}; +//! +//! # fn main() -> filtro::Result<()> { +//! let mut registry = FilterRegistry::new(); +//! filtro::filters::register_all(&mut registry)?; +//! +//! let requests = [FilterRequest::parse("jaune:niveau=200")?]; +//! let chain = registry.build_chain(&requests)?; +//! +//! let pipeline = Pipeline::builder() +//! .formats(vec![OutputFormat::Png, OutputFormat::Jpeg]) +//! .quality(90) +//! .build()?; +//! +//! pipeline.run_path("photo.jpg", &chain)?.write_to_dir("sortie")?; +//! # Ok(()) +//! # } +//! ``` + +#![forbid(unsafe_code)] +#![warn(missing_debug_implementations)] + +// --- Le cœur (Model) ------------------------------------------------------ +pub mod model; + +// --- Les filtres, hors du cœur ------------------------------------------ +pub mod filters; + +#[cfg(test)] +mod test_support; + +// --- Façade publique ---------------------------------------------------- +// L'API exposée est stable : seuls les chemins internes ont bougé. +pub use crate::model::error::{FiltroError, Result}; +pub use crate::model::filter::contract::{Filter, FilterChain, FilterContext, FilterRequirements}; +pub use crate::model::filter::registry::{ + FilterFactory, FilterParams, FilterRegistry, FilterRequest, ParamSpec, +}; +pub use crate::model::pipeline::analyzer::{ + Analyzer, ColorStats, ImageAnalysis, ImageMetadata, Origin, SourceFormat, +}; +pub use crate::model::pipeline::constructor::{ConstructionPolicy, Constructor, PreparedImage}; +pub use crate::model::pipeline::formatter::{ + FormatOptions, FormatSet, FormattedImage, Formatter, OutputFormat, OutputTarget, SizeVariant, +}; +pub use crate::model::pipeline::orchestrator::{Pipeline, PipelineBuilder}; +pub use crate::model::pipeline::renderer::{RenderOutput, RenderedArtifact, Renderer}; +pub use crate::model::pixel::{Dimensions, PixelBuffer, Rgba8}; +pub use crate::model::resolution::Resolution; diff --git a/Filtro_Rust/src/main.rs b/Filtro_Rust/src/main.rs index fa9bca8..16d516b 100644 --- a/Filtro_Rust/src/main.rs +++ b/Filtro_Rust/src/main.rs @@ -1,24 +1,9 @@ -use std::io::Cursor; -use image::ImageReader; +//! Binaire `filtro` — assemble les couches **Controller** et **View** autour de +//! la bibliothèque [`filtro`] (la couche **Model**). -fn main() -> Result<(), Box> { - // Charger une image depuis un fichier - let img = ImageReader::open("/workspaces/Filtro/Filtro_Rust/src/Image.webp")?.decode()?; +mod controller; +mod view; - // Sauvegarde directe - img.save("empty.jpg")?; - - // Écrire l'image dans un buffer mémoire - let mut bytes: Vec = Vec::new(); - img.write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Png)?; - - // Relire l'image depuis le buffer - let img2 = ImageReader::new(Cursor::new(bytes)) - .with_guessed_format()? - .decode()?; - - // Sauvegarde pour vérifier - img2.save("from_bytes.png")?; - - Ok(()) -} \ No newline at end of file +fn main() -> std::process::ExitCode { + controller::run() +} diff --git a/Filtro_Rust/src/model/codec.rs b/Filtro_Rust/src/model/codec.rs new file mode 100644 index 0000000..98f69df --- /dev/null +++ b/Filtro_Rust/src/model/codec.rs @@ -0,0 +1,117 @@ +//! Pont interne entre le cœur et la bibliothèque de codecs. +//! +//! Ce module est le **seul** endroit du crate qui connaît le type +//! `image::RgbaImage`. Les filtres et les étapes du pipeline ne manipulent que +//! [`PixelBuffer`] : changer de bibliothèque de décodage n'impacterait que ce +//! fichier. + +use std::io::Cursor; + +use image::codecs::bmp::BmpEncoder; +use image::codecs::jpeg::JpegEncoder; +use image::codecs::png::{CompressionType, FilterType as PngFilterType, PngEncoder}; +use image::codecs::tiff::TiffEncoder; +use image::codecs::webp::WebPEncoder; +use image::imageops::FilterType as ResizeFilter; +use image::{ExtendedColorType, ImageEncoder, ImageFormat, RgbaImage}; + +use crate::model::error::{FiltroError, Result}; +use crate::model::pipeline::formatter::{FormatOptions, OutputFormat}; +use crate::model::pixel::{Dimensions, PixelBuffer}; + +/// Décode des octets bruts vers un tampon RGBA8 et renvoie le format détecté. +pub(crate) fn decode(bytes: &[u8], origin: &str) -> Result<(PixelBuffer, Option)> { + let format = image::guess_format(bytes).ok(); + let decoded = image::load_from_memory(bytes).map_err(|source| FiltroError::Decode { + origin: origin.to_owned(), + source, + })?; + let rgba = decoded.into_rgba8(); + let dimensions = Dimensions::new(rgba.width(), rgba.height())?; + let buffer = PixelBuffer::from_raw(dimensions, rgba.into_raw())?; + Ok((buffer, format)) +} + +/// Ré-échantillonne un tampon (Lanczos3). +/// +/// Attention : il s'agit du ré-échantillonnage interne du pipeline, sans aucun +/// rapport avec les filtres applicatifs du projet. +pub(crate) fn resize(buffer: &PixelBuffer, target: Dimensions) -> Result { + if buffer.dimensions() == target { + return Ok(buffer.clone()); + } + let source = to_rgba_image(buffer)?; + let resized = + image::imageops::resize(&source, target.width, target.height, ResizeFilter::Lanczos3); + PixelBuffer::from_raw(target, resized.into_raw()) +} + +/// Encode un tampon vers les octets d'un fichier image. +pub(crate) fn encode( + buffer: &PixelBuffer, + format: OutputFormat, + options: &FormatOptions, +) -> Result> { + let width = buffer.width(); + let height = buffer.height(); + let mut out: Vec = Vec::new(); + let wrap = |source: image::ImageError| FiltroError::Encode { + format: format.to_string(), + source, + }; + + match format { + OutputFormat::Png => { + let encoder = PngEncoder::new_with_quality( + &mut out, + CompressionType::Default, + PngFilterType::Adaptive, + ); + encoder + .write_image(buffer.as_bytes(), width, height, ExtendedColorType::Rgba8) + .map_err(wrap)?; + } + OutputFormat::Jpeg => { + // JPEG ne porte pas d'alpha : le Formatter a déjà aplati l'image. + let rgb = to_rgb_bytes(buffer); + JpegEncoder::new_with_quality(&mut out, options.quality) + .encode(&rgb, width, height, ExtendedColorType::Rgb8) + .map_err(wrap)?; + } + OutputFormat::Bmp => { + let rgb = to_rgb_bytes(buffer); + let mut cursor = Cursor::new(&mut out); + BmpEncoder::new(&mut cursor) + .encode(&rgb, width, height, ExtendedColorType::Rgb8) + .map_err(wrap)?; + } + OutputFormat::Tiff => { + let cursor = Cursor::new(&mut out); + TiffEncoder::new(cursor) + .write_image(buffer.as_bytes(), width, height, ExtendedColorType::Rgba8) + .map_err(wrap)?; + } + OutputFormat::WebP => { + // Encodeur sans perte : le paramètre `quality` est ignoré ici. + WebPEncoder::new_lossless(&mut out) + .write_image(buffer.as_bytes(), width, height, ExtendedColorType::Rgba8) + .map_err(wrap)?; + } + } + + Ok(out) +} + +fn to_rgba_image(buffer: &PixelBuffer) -> Result { + RgbaImage::from_raw(buffer.width(), buffer.height(), buffer.as_bytes().to_vec()).ok_or_else( + || FiltroError::InvalidImage("tampon incompatible avec les dimensions".to_owned()), + ) +} + +fn to_rgb_bytes(buffer: &PixelBuffer) -> Vec { + let mut rgb = Vec::with_capacity(buffer.as_bytes().len() / 4 * 3); + for chunk in buffer.as_bytes().as_chunks::<4>().0 { + rgb.extend_from_slice(&chunk[..3]); + } + rgb +} diff --git a/Filtro_Rust/src/model/error.rs b/Filtro_Rust/src/model/error.rs new file mode 100644 index 0000000..c032d2f --- /dev/null +++ b/Filtro_Rust/src/model/error.rs @@ -0,0 +1,89 @@ +//! Type d'erreur unique du cœur applicatif. +//! +//! Aucune fonction publique de ce crate ne panique sur un chemin critique : +//! toute défaillance remonte sous forme de [`FiltroError`]. + +use std::path::PathBuf; + +use thiserror::Error; + +/// Alias de `Result` utilisé partout dans le crate. +pub type Result = std::result::Result; + +/// Erreurs pouvant survenir dans la chaîne Analyzer → Constructor → Formatter → Renderer. +#[derive(Debug, Error)] +pub enum FiltroError { + /// Lecture ou écriture sur le système de fichiers impossible. + #[error("erreur d'entrée/sortie sur « {path} »")] + Io { + path: PathBuf, + #[source] + source: std::io::Error, + }, + + /// Le décodage de l'image source a échoué. + #[error("décodage impossible pour « {origin} »")] + Decode { + origin: String, + #[source] + source: image::ImageError, + }, + + /// L'encodage vers un format de sortie a échoué. + #[error("encodage impossible vers le format {format}")] + Encode { + format: String, + #[source] + source: image::ImageError, + }, + + /// Données de pixels incohérentes (dimensions nulles, tampon mal dimensionné…). + #[error("image invalide : {0}")] + InvalidImage(String), + + /// Aucun filtre enregistré ne porte cet identifiant. + #[error("filtre inconnu : « {name} » (disponibles : {available})")] + UnknownFilter { name: String, available: String }, + + /// Paramètre présent mais inutilisable. + #[error("paramètre « {name} » invalide pour le filtre « {filter} » : {reason}")] + InvalidParameter { + filter: String, + name: String, + reason: String, + }, + + /// Paramètre obligatoire absent. + #[error("paramètre obligatoire « {name} » manquant pour le filtre « {filter} »")] + MissingParameter { filter: String, name: String }, + + /// Un filtre a refusé de traiter l'image. + #[error("échec du filtre « {filter} » : {reason}")] + FilterFailed { filter: String, reason: String }, + + /// Format de sortie non reconnu. + #[error("format de sortie inconnu : « {0} »")] + UnknownFormat(String), + + /// Configuration incohérente fournie par l'appelant (CLI, API…). + #[error("configuration invalide : {0}")] + Config(String), +} + +impl FiltroError { + /// Raccourci pour construire une erreur d'entrée/sortie. + pub fn io(path: impl Into, source: std::io::Error) -> Self { + Self::Io { + path: path.into(), + source, + } + } + + /// Raccourci pour signaler l'échec d'un filtre depuis une implémentation externe. + pub fn filter_failed(filter: impl Into, reason: impl Into) -> Self { + Self::FilterFailed { + filter: filter.into(), + reason: reason.into(), + } + } +} diff --git a/Filtro_Rust/src/model/filter/contract.rs b/Filtro_Rust/src/model/filter/contract.rs new file mode 100644 index 0000000..c09d30b --- /dev/null +++ b/Filtro_Rust/src/model/filter/contract.rs @@ -0,0 +1,173 @@ +//! **Le contrat des filtres** — et rien d'autre. +//! +//! Ce fichier ne contient aucun filtre. Il décrit ce qu'un filtre *est*, du +//! point de vue du cœur : +//! +//! * il porte un identifiant ([`Filter::id`]) ; +//! * il peut déclarer ce qu'il attend de l'image ([`FilterRequirements`]) ; +//! * il transforme des pixels ([`Filter::apply`]). +//! +//! L'instanciation depuis la ligne de commande est décrite dans +//! [`crate::model::filter::registry`]. + +use std::fmt; + +use crate::model::error::Result; +use crate::model::pipeline::analyzer::ImageMetadata; +use crate::model::pipeline::formatter::OutputTarget; +use crate::model::pixel::PixelBuffer; + +/// Ce qu'un filtre exige de l'image qu'on lui remet. +/// +/// C'est le **seul** canal par lequel un filtre influence la préparation : +/// le Constructor lit ces données, jamais le filtre lui-même. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct FilterRequirements { + /// Le filtre a besoin d'un canal alpha significatif. + pub needs_alpha: bool, + /// Plus grand côté toléré ; le Constructor réduit l'image si nécessaire. + pub max_dimension: Option, +} + +impl FilterRequirements { + /// Combine deux jeux d'exigences en retenant la contrainte la plus stricte. + pub fn merge(self, other: Self) -> Self { + Self { + needs_alpha: self.needs_alpha || other.needs_alpha, + max_dimension: match (self.max_dimension, other.max_dimension) { + (Some(a), Some(b)) => Some(a.min(b)), + (Some(v), None) | (None, Some(v)) => Some(v), + (None, None) => None, + }, + } + } +} + +/// Ce que le filtre sait du traitement en cours. +/// +/// Le Renderer appelant les filtres une fois par variante, un filtre peut +/// s'adapter au format ou aux dimensions de sortie. +#[derive(Debug, Clone, Copy)] +pub struct FilterContext<'a> { + /// Description de l'image source. + pub metadata: &'a ImageMetadata, + /// Variante en cours de rendu. + pub target: &'a OutputTarget, + /// Rang du filtre dans la chaîne (0 = premier). + pub position: usize, +} + +/// Un filtre configuré, prêt à traiter des pixels. +/// +/// `Send + Sync` pour autoriser un rendu parallèle des variantes. +pub trait Filter: Send + Sync { + /// Identifiant du filtre, tel qu'écrit sur la ligne de commande. + fn id(&self) -> &str; + + /// Exigences sur l'image d'entrée. Par défaut : aucune. + fn requirements(&self) -> FilterRequirements { + FilterRequirements::default() + } + + /// Transforme les pixels sur place. + /// + /// Un filtre qui change les dimensions (recadrage, rotation) remplace le + /// contenu de `canvas` par un nouveau [`PixelBuffer`]. + fn apply(&self, canvas: &mut PixelBuffer, ctx: &FilterContext<'_>) -> Result<()>; +} + +/// Suite ordonnée de filtres, appliquée telle quelle par le Renderer. +#[derive(Default)] +pub struct FilterChain { + filters: Vec>, +} + +impl FilterChain { + /// Chaîne vide : le pipeline se comporte alors en convertisseur de formats. + pub fn empty() -> Self { + Self::default() + } + + pub fn new(filters: Vec>) -> Self { + Self { filters } + } + + pub fn is_empty(&self) -> bool { + self.filters.is_empty() + } + + pub fn len(&self) -> usize { + self.filters.len() + } + + /// Identifiants des filtres, dans l'ordre d'application. + pub fn ids(&self) -> Vec<&str> { + self.filters.iter().map(|f| f.id()).collect() + } + + /// Exigences cumulées, lues par le Constructor. + pub fn requirements(&self) -> FilterRequirements { + self.filters + .iter() + .fold(FilterRequirements::default(), |acc, f| { + acc.merge(f.requirements()) + }) + } + + /// Applique tous les filtres, dans l'ordre. + pub fn apply( + &self, + canvas: &mut PixelBuffer, + metadata: &ImageMetadata, + target: &OutputTarget, + ) -> Result<()> { + for (position, filter) in self.filters.iter().enumerate() { + let ctx = FilterContext { + metadata, + target, + position, + }; + filter.apply(canvas, &ctx)?; + } + Ok(()) + } +} + +impl fmt::Debug for FilterChain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterChain") + .field("ids", &self.ids()) + .finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fusion_des_exigences() { + let a = FilterRequirements { + needs_alpha: true, + max_dimension: Some(4096), + }; + let b = FilterRequirements { + needs_alpha: false, + max_dimension: Some(2048), + }; + assert_eq!( + a.merge(b), + FilterRequirements { + needs_alpha: true, + max_dimension: Some(2048), + } + ); + } + + #[test] + fn chaine_vide() { + let chain = FilterChain::empty(); + assert!(chain.is_empty()); + assert_eq!(chain.requirements(), FilterRequirements::default()); + } +} diff --git a/Filtro_Rust/src/model/filter/mod.rs b/Filtro_Rust/src/model/filter/mod.rs new file mode 100644 index 0000000..952b562 --- /dev/null +++ b/Filtro_Rust/src/model/filter/mod.rs @@ -0,0 +1,13 @@ +//! Le **contrat** des filtres et leur annuaire — aucun filtre concret ici. +//! +//! * [`contract`] décrit ce qu'un filtre *est* du point de vue du cœur +//! ([`Filter`](contract::Filter), [`FilterChain`](contract::FilterChain), +//! [`FilterRequirements`](contract::FilterRequirements)) ; +//! * [`registry`] configure et instancie les filtres à partir d'une expression +//! textuelle ([`FilterRegistry`](registry::FilterRegistry), +//! [`FilterRequest`](registry::FilterRequest)). +//! +//! Les filtres livrés avec le projet vivent dans [`crate::filters`], hors du cœur. + +pub mod contract; +pub mod registry; diff --git a/Filtro_Rust/src/model/filter/registry.rs b/Filtro_Rust/src/model/filter/registry.rs new file mode 100644 index 0000000..ac953dc --- /dev/null +++ b/Filtro_Rust/src/model/filter/registry.rs @@ -0,0 +1,386 @@ +//! Déclaration, configuration et instanciation des filtres. +//! +//! Un filtre se demande en une chaîne : `jaune`, `jaune:niveau=200`, +//! `noir-blanc:niveau=128,clair=ffcc00`. +//! +//! * [`FilterRequest`] découpe cette expression ; +//! * [`FilterParams`] convertit les valeurs (toujours du texte au départ) ; +//! * [`FilterFactory`] valide et construit le filtre ; +//! * [`FilterRegistry`] fait l'annuaire des fabriques connues. +//! +//! Les paramètres sont volontairement textuels : une fabrique décide seule du +//! type attendu, ce qui évite au cœur de connaître la nature des filtres. + +use std::collections::BTreeMap; +use std::fmt; +use std::str::FromStr; + +use crate::model::error::{FiltroError, Result}; +use crate::model::filter::contract::{Filter, FilterChain}; +use crate::model::pixel::Rgba8; + +// --------------------------------------------------------------------------- +// Paramètres +// --------------------------------------------------------------------------- + +/// Paramètres nommés destinés à une fabrique. +/// +/// Les accesseurs prennent la valeur par défaut en argument : un filtre +/// s'écrit donc sans code de configuration répétitif. +/// +/// ``` +/// # use filtro::{FilterRequest, Rgba8}; +/// let request = FilterRequest::parse("jaune:niveau=200")?; +/// assert_eq!(request.params.u8("niveau", 180)?, 200); +/// assert_eq!(request.params.color("couleur", Rgba8::WHITE)?, Rgba8::WHITE); +/// # Ok::<(), filtro::FiltroError>(()) +/// ``` +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct FilterParams { + filter: String, + values: BTreeMap, +} + +impl FilterParams { + /// Crée un jeu de paramètres vide destiné au filtre `filter`. + pub fn new(filter: impl Into) -> Self { + Self { + filter: filter.into(), + values: BTreeMap::new(), + } + } + + pub fn set(&mut self, name: impl Into, value: impl Into) -> &mut Self { + self.values.insert(name.into(), value.into()); + self + } + + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } + + /// Valeur brute, sans conversion. + pub fn raw(&self, name: &str) -> Option<&str> { + self.values.get(name).map(String::as_str) + } + + /// Texte, ou la valeur par défaut. + pub fn text<'a>(&'a self, name: &str, default: &'a str) -> &'a str { + self.raw(name).unwrap_or(default) + } + + /// Entier 0–255 (seuils, composantes). + pub fn u8(&self, name: &str, default: u8) -> Result { + self.parse(name, default, "un entier entre 0 et 255") + } + + /// Entier positif. + pub fn u32(&self, name: &str, default: u32) -> Result { + self.parse(name, default, "un entier positif") + } + + /// Nombre réel. + pub fn f64(&self, name: &str, default: f64) -> Result { + self.parse(name, default, "un nombre") + } + + /// Nombre réel contraint à un intervalle. + pub fn f64_in( + &self, + name: &str, + default: f64, + range: std::ops::RangeInclusive, + ) -> Result { + let value = self.f64(name, default)?; + if !range.contains(&value) { + return Err(self.invalid( + name, + format!( + "{value} est hors de l'intervalle [{}, {}]", + range.start(), + range.end() + ), + )); + } + Ok(value) + } + + /// Booléen : `true`/`vrai`/`oui`/`1` ou `false`/`faux`/`non`/`0`. + pub fn bool(&self, name: &str, default: bool) -> Result { + match self.raw(name) { + None => Ok(default), + Some(raw) => match raw.trim().to_ascii_lowercase().as_str() { + "true" | "vrai" | "oui" | "1" => Ok(true), + "false" | "faux" | "non" | "0" => Ok(false), + other => Err(self.invalid(name, format!("« {other} » n'est pas un booléen"))), + }, + } + } + + /// Couleur hexadécimale : `fefe01`, `#fefe01` ou `fefe01ff` (avec alpha). + /// + /// La virgule séparant les paramètres, la notation `r,v,b` n'est pas + /// utilisable ici. + pub fn color(&self, name: &str, default: Rgba8) -> Result { + match self.raw(name) { + None => Ok(default), + Some(raw) => Rgba8::from_hex(raw).ok_or_else(|| { + self.invalid( + name, + format!("« {raw} » n'est pas une couleur hexadécimale (ex. fefe01)"), + ) + }), + } + } + + /// Rejette les paramètres absents de la déclaration de la fabrique. + /// Rend les fautes de frappe visibles immédiatement. + pub fn check(&self, specs: &[ParamSpec]) -> Result<()> { + let unknown = self + .values + .keys() + .find(|name| !specs.iter().any(|spec| spec.name == name.as_str())); + match unknown { + None => Ok(()), + Some(name) => { + let known: Vec<&str> = specs.iter().map(|spec| spec.name).collect(); + Err(self.invalid( + name, + if known.is_empty() { + "ce filtre n'accepte aucun paramètre".to_owned() + } else { + format!("paramètres acceptés : {}", known.join(", ")) + }, + )) + } + } + } + + fn parse(&self, name: &str, default: T, expected: &str) -> Result { + match self.raw(name) { + None => Ok(default), + Some(raw) => raw + .trim() + .parse() + .map_err(|_| self.invalid(name, format!("« {raw} » n'est pas {expected}"))), + } + } + + fn invalid(&self, name: &str, reason: impl Into) -> FiltroError { + FiltroError::InvalidParameter { + filter: self.filter.clone(), + name: name.to_owned(), + reason: reason.into(), + } + } +} + +/// Description d'un paramètre, pour l'aide en ligne et la détection des fautes. +/// +/// `default` est purement documentaire : la valeur réellement appliquée est +/// celle passée aux accesseurs de [`FilterParams`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ParamSpec { + pub name: &'static str, + pub default: &'static str, + pub help: &'static str, +} + +// --------------------------------------------------------------------------- +// Fabriques et registre +// --------------------------------------------------------------------------- + +/// Sait décrire un filtre et l'instancier à partir de paramètres. +pub trait FilterFactory: Send + Sync { + /// Identifiant unique, en minuscules, sans espace ni deux-points. + fn id(&self) -> &'static str; + + /// Résumé d'une ligne affiché par `filtro --list-filters`. + fn description(&self) -> &'static str; + + /// Paramètres acceptés. + fn parameters(&self) -> &'static [ParamSpec] { + &[] + } + + /// Valide les paramètres et construit le filtre. + fn build(&self, params: &FilterParams) -> Result>; +} + +/// Filtre demandé par l'utilisateur, avant résolution. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FilterRequest { + pub id: String, + pub params: FilterParams, +} + +impl FilterRequest { + /// Analyse une expression `id` ou `id:clé=valeur,clé=valeur`. + pub fn parse(expression: &str) -> Result { + let expression = expression.trim(); + let (id, rest) = match expression.split_once(':') { + Some((id, rest)) => (id.trim(), rest), + None => (expression, ""), + }; + if id.is_empty() { + return Err(FiltroError::Config(format!( + "expression de filtre invalide : « {expression} »" + ))); + } + + let mut params = FilterParams::new(id); + for pair in rest.split(',').filter(|p| !p.trim().is_empty()) { + let (key, value) = pair.split_once('=').ok_or_else(|| { + FiltroError::Config(format!( + "paramètre « {} » mal formé (attendu clé=valeur)", + pair.trim() + )) + })?; + params.set(key.trim(), value.trim()); + } + + Ok(Self { + id: id.to_owned(), + params, + }) + } +} + +/// Annuaire des filtres disponibles à l'exécution. +/// +/// Le cœur crée un registre vide ; les filtres s'y ajoutent au démarrage. +#[derive(Default)] +pub struct FilterRegistry { + factories: BTreeMap<&'static str, Box>, +} + +impl FilterRegistry { + pub fn new() -> Self { + Self::default() + } + + /// Ajoute une fabrique. + /// + /// # Erreurs + /// Identifiant mal formé ou déjà pris. + pub fn register(&mut self, factory: impl FilterFactory + 'static) -> Result<()> { + let id = factory.id(); + if id.is_empty() || id.contains(char::is_whitespace) || id.contains(':') { + return Err(FiltroError::Config(format!( + "identifiant de filtre invalide : « {id} »" + ))); + } + if self.factories.contains_key(id) { + return Err(FiltroError::Config(format!( + "deux filtres portent l'identifiant « {id} »" + ))); + } + self.factories.insert(id, Box::new(factory)); + Ok(()) + } + + pub fn is_empty(&self) -> bool { + self.factories.is_empty() + } + + pub fn len(&self) -> usize { + self.factories.len() + } + + /// Fabriques disponibles, triées par identifiant. + pub fn factories(&self) -> impl Iterator + '_ { + self.factories.values().map(AsRef::as_ref) + } + + pub fn get(&self, id: &str) -> Option<&dyn FilterFactory> { + self.factories.get(id).map(AsRef::as_ref) + } + + /// Construit un filtre unique. + pub fn build(&self, request: &FilterRequest) -> Result> { + let factory = self + .get(&request.id) + .ok_or_else(|| FiltroError::UnknownFilter { + name: request.id.clone(), + available: self + .factories + .keys() + .copied() + .collect::>() + .join(", "), + })?; + request.params.check(factory.parameters())?; + factory.build(&request.params) + } + + /// Construit une chaîne complète, dans l'ordre fourni. + pub fn build_chain(&self, requests: &[FilterRequest]) -> Result { + let mut filters = Vec::with_capacity(requests.len()); + for request in requests { + filters.push(self.build(request)?); + } + Ok(FilterChain::new(filters)) + } +} + +impl fmt::Debug for FilterRegistry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterRegistry") + .field("filtres", &self.factories.keys().collect::>()) + .finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_identifiant_seul() { + let request = FilterRequest::parse("jaune").unwrap(); + assert_eq!(request.id, "jaune"); + assert!(request.params.is_empty()); + } + + #[test] + fn parse_parametres() { + let request = FilterRequest::parse("jaune:niveau=200,couleur=fefe01").unwrap(); + assert_eq!(request.params.u8("niveau", 180).unwrap(), 200); + assert_eq!( + request.params.color("couleur", Rgba8::WHITE).unwrap(), + Rgba8::new(254, 254, 1, 255) + ); + } + + #[test] + fn valeurs_par_defaut() { + let params = FilterParams::new("test"); + assert_eq!(params.u8("niveau", 180).unwrap(), 180); + assert!(!params.bool("inverse", false).unwrap()); + assert_eq!(params.text("mode", "canaux"), "canaux"); + } + + #[test] + fn conversions_invalides() { + let request = FilterRequest::parse("test:niveau=300,actif=peut-etre").unwrap(); + assert!(request.params.u8("niveau", 0).is_err()); + assert!(request.params.bool("actif", false).is_err()); + } + + #[test] + fn parse_refuse_les_expressions_malformees() { + assert!(FilterRequest::parse("").is_err()); + assert!(FilterRequest::parse("jaune:niveau").is_err()); + } + + #[test] + fn parametre_inconnu_signale() { + let request = FilterRequest::parse("jaune:nivo=200").unwrap(); + let specs = [ParamSpec { + name: "niveau", + default: "180", + help: "seuil", + }]; + assert!(request.params.check(&specs).is_err()); + } +} diff --git a/Filtro_Rust/src/model/mod.rs b/Filtro_Rust/src/model/mod.rs new file mode 100644 index 0000000..fe39772 --- /dev/null +++ b/Filtro_Rust/src/model/mod.rs @@ -0,0 +1,21 @@ +//! Le cœur métier de Filtro — la couche **Model** du MVC. +//! +//! Ce module ne connaît ni la ligne de commande ni le terminal : les couches +//! `controller` et `view` du binaire le consomment uniquement à travers la +//! façade publique réexportée par [`crate`]. +//! +//! * [`error`] — le type d'erreur unique ; +//! * [`pixel`] — les types d'image partagés, indépendants de toute bibliothèque ; +//! * [`pipeline`] — les quatre étapes stables et leur orchestration ; +//! * [`filter`] — le *contrat* des filtres et leur annuaire d'instanciation. +//! +//! `codec` (pont vers le crate `image`) et `resolution` (lecture des ppp) restent +//! internes au crate. + +pub mod error; +pub mod filter; +pub mod pipeline; +pub mod pixel; + +pub(crate) mod codec; +pub(crate) mod resolution; diff --git a/Filtro_Rust/src/model/pipeline/analyzer.rs b/Filtro_Rust/src/model/pipeline/analyzer.rs new file mode 100644 index 0000000..6f5f7e3 --- /dev/null +++ b/Filtro_Rust/src/model/pipeline/analyzer.rs @@ -0,0 +1,298 @@ +//! Étape 1 — **Analyzer**. +//! +//! Lit l'image d'entrée, la décode en RGBA8 et produit une description +//! exploitable par le reste de la chaîne : dimensions, résolution, format +//! source, caractéristiques colorimétriques. +//! +//! L'Analyzer ne connaît aucun filtre. + +use std::path::{Path, PathBuf}; + +use crate::model::codec; +use crate::model::error::{FiltroError, Result}; +use crate::model::pixel::{Dimensions, PixelBuffer}; +use crate::model::resolution::{self, Resolution}; + +/// Format du fichier source tel que détecté à la lecture. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SourceFormat { + Png, + Jpeg, + Gif, + WebP, + Bmp, + Tiff, + /// Format reconnu par le décodeur mais hors de la liste ci-dessus. + Other(String), + /// Format indéterminé (flux brut, en-tête tronqué…). + Unknown, +} + +impl SourceFormat { + fn from_image_format(format: Option) -> Self { + match format { + Some(image::ImageFormat::Png) => Self::Png, + Some(image::ImageFormat::Jpeg) => Self::Jpeg, + Some(image::ImageFormat::Gif) => Self::Gif, + Some(image::ImageFormat::WebP) => Self::WebP, + Some(image::ImageFormat::Bmp) => Self::Bmp, + Some(image::ImageFormat::Tiff) => Self::Tiff, + Some(other) => Self::Other(format!("{other:?}").to_lowercase()), + None => Self::Unknown, + } + } +} + +impl std::fmt::Display for SourceFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Png => f.write_str("png"), + Self::Jpeg => f.write_str("jpeg"), + Self::Gif => f.write_str("gif"), + Self::WebP => f.write_str("webp"), + Self::Bmp => f.write_str("bmp"), + Self::Tiff => f.write_str("tiff"), + Self::Other(name) => f.write_str(name), + Self::Unknown => f.write_str("inconnu"), + } + } +} + +/// Provenance de l'image analysée. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Origin { + /// Fichier sur disque. + Path(PathBuf), + /// Flux mémoire (entrée standard, test, appel bibliothèque). + Memory { label: String }, +} + +impl Origin { + /// Radical utilisable pour nommer les fichiers de sortie. + pub fn stem(&self) -> String { + match self { + Self::Path(path) => path + .file_stem() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_else(|| "image".to_owned()), + Self::Memory { label } => label.clone(), + } + } +} + +impl std::fmt::Display for Origin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Path(path) => write!(f, "{}", path.display()), + Self::Memory { label } => write!(f, "<{label}>"), + } + } +} + +/// Statistiques calculées en un seul passage sur les pixels. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ColorStats { + /// Vrai si au moins un pixel n'est pas opaque. + pub has_transparency: bool, + /// Vrai si tous les pixels vérifient r == g == b. + pub is_grayscale: bool, + /// Luminance moyenne (0.0 – 255.0). + pub mean_luminance: f32, + /// Luminance minimale et maximale observées. + pub luminance_range: (u8, u8), +} + +/// Description de l'image, sans les pixels. +/// +/// C'est cette structure — légère et clonable — qui circule dans les contextes +/// de filtre et dans les journaux. +#[derive(Debug, Clone, PartialEq)] +pub struct ImageMetadata { + pub origin: Origin, + pub source_format: SourceFormat, + pub dimensions: Dimensions, + pub resolution: Option, + pub byte_size: usize, + pub stats: ColorStats, +} + +/// Résultat complet de l'analyse : description + pixels décodés. +#[derive(Debug, Clone, PartialEq)] +pub struct ImageAnalysis { + pub metadata: ImageMetadata, + pub buffer: PixelBuffer, +} + +impl ImageAnalysis { + /// Sépare la description des pixels ; consommé par le Constructor. + pub fn into_parts(self) -> (ImageMetadata, PixelBuffer) { + (self.metadata, self.buffer) + } +} + +/// Étape d'analyse. +#[derive(Debug, Clone)] +pub struct Analyzer { + /// Garde-fou contre les images décompressées démesurées (bombes de décompression). + max_pixels: Option, +} + +impl Default for Analyzer { + fn default() -> Self { + Self { + // 100 Mpx ≈ 400 Mio en RGBA8. + max_pixels: Some(100_000_000), + } + } +} + +impl Analyzer { + pub fn new() -> Self { + Self::default() + } + + /// Modifie la limite de pixels acceptés (`None` = aucune limite). + pub fn with_max_pixels(mut self, max_pixels: Option) -> Self { + self.max_pixels = max_pixels; + self + } + + /// Analyse un fichier. + pub fn analyze_path(&self, path: impl AsRef) -> Result { + let path = path.as_ref(); + let bytes = std::fs::read(path).map_err(|source| FiltroError::io(path, source))?; + self.analyze_bytes(&bytes, Origin::Path(path.to_path_buf())) + } + + /// Analyse un flux déjà en mémoire. + pub fn analyze_bytes(&self, bytes: &[u8], origin: Origin) -> Result { + if bytes.is_empty() { + return Err(FiltroError::InvalidImage(format!("flux vide : {origin}"))); + } + + let (buffer, detected) = codec::decode(bytes, &origin.to_string())?; + let dimensions = buffer.dimensions(); + + if let Some(limit) = self.max_pixels + && dimensions.pixel_count() > limit + { + return Err(FiltroError::InvalidImage(format!( + "image de {dimensions} ({} pixels) au-delà de la limite de {limit} pixels", + dimensions.pixel_count() + ))); + } + + let source_format = SourceFormat::from_image_format(detected); + let resolution = resolution::read(bytes, &source_format); + let stats = compute_stats(&buffer); + + Ok(ImageAnalysis { + metadata: ImageMetadata { + origin, + source_format, + dimensions, + resolution, + byte_size: bytes.len(), + stats, + }, + buffer, + }) + } +} + +fn compute_stats(buffer: &PixelBuffer) -> ColorStats { + let mut has_transparency = false; + let mut is_grayscale = true; + let mut sum = 0.0f64; + let mut min = u8::MAX; + let mut max = u8::MIN; + + for pixel in buffer.pixels() { + if pixel.a != 255 { + has_transparency = true; + } + if pixel.r != pixel.g || pixel.g != pixel.b { + is_grayscale = false; + } + let luma = pixel.luminance(); + sum += f64::from(luma); + min = min.min(luma); + max = max.max(luma); + } + + let count = buffer.dimensions().pixel_count().max(1) as f64; + ColorStats { + has_transparency, + is_grayscale, + mean_luminance: (sum / count) as f32, + luminance_range: (min, max), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::model::pixel::Rgba8; + + fn png_bytes(width: u32, height: u32, pixel: Rgba8) -> Vec { + let dims = Dimensions::new(width, height).unwrap(); + let mut buffer = PixelBuffer::new(dims).unwrap(); + buffer.map_pixels(|_, _, _| pixel); + crate::model::codec::encode( + &buffer, + crate::model::pipeline::formatter::OutputFormat::Png, + &crate::model::pipeline::formatter::FormatOptions::default(), + ) + .expect("encodage png") + } + + #[test] + fn analyse_une_image_png() { + let bytes = png_bytes(4, 2, Rgba8::new(200, 200, 200, 255)); + let analyzer = Analyzer::new(); + let analysis = analyzer + .analyze_bytes( + &bytes, + Origin::Memory { + label: "test".into(), + }, + ) + .expect("analyse"); + + assert_eq!(analysis.metadata.source_format, SourceFormat::Png); + assert_eq!(analysis.metadata.dimensions.width, 4); + assert_eq!(analysis.metadata.dimensions.height, 2); + assert!(analysis.metadata.stats.is_grayscale); + assert!(!analysis.metadata.stats.has_transparency); + } + + #[test] + fn refuse_un_flux_vide() { + let analyzer = Analyzer::new(); + let err = analyzer + .analyze_bytes( + &[], + Origin::Memory { + label: "vide".into(), + }, + ) + .unwrap_err(); + assert!(matches!(err, FiltroError::InvalidImage(_))); + } + + #[test] + fn respecte_la_limite_de_pixels() { + let bytes = png_bytes(8, 8, Rgba8::BLACK); + let analyzer = Analyzer::new().with_max_pixels(Some(16)); + assert!( + analyzer + .analyze_bytes( + &bytes, + Origin::Memory { + label: "gros".into() + } + ) + .is_err() + ); + } +} diff --git a/Filtro_Rust/src/model/pipeline/constructor.rs b/Filtro_Rust/src/model/pipeline/constructor.rs new file mode 100644 index 0000000..39b558b --- /dev/null +++ b/Filtro_Rust/src/model/pipeline/constructor.rs @@ -0,0 +1,154 @@ +//! Étape 2 — **Constructor**. +//! +//! Reçoit l'analyse (Analyzer) et les exigences de la chaîne de filtres, et en +//! tire une image de travail cohérente : c'est le seul endroit où l'image +//! d'origine est normalisée avant traitement. +//! +//! Le Constructor ne connaît **aucun filtre concret** : il ne lit que +//! [`FilterRequirements`], donnée déclarative fournie par le contrat. + +use crate::model::codec; +use crate::model::error::Result; +use crate::model::filter::contract::{FilterChain, FilterRequirements}; +use crate::model::pipeline::analyzer::{ImageAnalysis, ImageMetadata}; +use crate::model::pixel::{Dimensions, PixelBuffer, Rgba8}; + +/// Politique de préparation, indépendante des filtres. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ConstructionPolicy { + /// Plus grand côté de l'image de travail (`None` = taille d'origine). + pub max_dimension: Option, + /// Couleur de fond utilisée lorsqu'il faut supprimer la transparence. + pub background: Rgba8, +} + +impl Default for ConstructionPolicy { + fn default() -> Self { + Self { + max_dimension: None, + background: Rgba8::WHITE, + } + } +} + +/// Image prête à être déclinée par le Formatter puis rendue par le Renderer. +#[derive(Debug, Clone, PartialEq)] +pub struct PreparedImage { + /// Description de la source (dimensions d'origine incluses). + pub metadata: ImageMetadata, + /// Image de travail en RGBA8. + pub canvas: PixelBuffer, + /// Exigences cumulées de la chaîne, transmises aux étapes suivantes. + pub requirements: FilterRequirements, + /// Couleur de fond retenue pour les formats sans alpha. + pub background: Rgba8, + /// Dimensions d'origine si l'image a été réduite, `None` sinon. + pub resized_from: Option, +} + +impl PreparedImage { + pub fn dimensions(&self) -> Dimensions { + self.canvas.dimensions() + } +} + +/// Étape de préparation. +#[derive(Debug, Clone, Default)] +pub struct Constructor { + policy: ConstructionPolicy, +} + +impl Constructor { + pub fn new() -> Self { + Self::default() + } + + pub fn with_policy(policy: ConstructionPolicy) -> Self { + Self { policy } + } + + pub fn policy(&self) -> ConstructionPolicy { + self.policy + } + + /// Prépare l'image de travail. + /// + /// La borne dimensionnelle retenue est la plus stricte entre la politique + /// de l'application et les exigences de la chaîne de filtres. + pub fn prepare(&self, analysis: ImageAnalysis, chain: &FilterChain) -> Result { + let requirements = chain.requirements(); + let (metadata, mut canvas) = analysis.into_parts(); + + // Borne la plus stricte entre la politique de l'application et les + // exigences déclarées par les filtres. + let bound = match (self.policy.max_dimension, requirements.max_dimension) { + (Some(a), Some(b)) => Some(a.min(b)), + (Some(v), None) | (None, Some(v)) => Some(v), + (None, None) => None, + }; + + let mut resized_from = None; + if let Some(max_side) = bound + && let Some(target) = canvas.dimensions().scaled_to_fit(max_side) + { + resized_from = Some(canvas.dimensions()); + canvas = codec::resize(&canvas, target)?; + } + + // Le tampon reste en RGBA8 à alpha non prémultiplié : c'est la forme + // canonique du cœur. Une future exigence de conversion (espace + // linéaire, etc.) se traiterait ici, à partir de `requirements` seul. + + Ok(PreparedImage { + metadata, + canvas, + requirements, + background: self.policy.background, + resized_from, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_support::{canvas, metadata}; + + fn analyse(width: u32, height: u32) -> ImageAnalysis { + let buffer = canvas(width, height, Rgba8::BLACK); + ImageAnalysis { + metadata: metadata(buffer.dimensions()), + buffer, + } + } + + #[test] + fn conserve_les_dimensions_sans_contrainte() { + let prepared = Constructor::new() + .prepare(analyse(120, 80), &FilterChain::empty()) + .unwrap(); + assert_eq!(prepared.dimensions().width, 120); + assert!(prepared.resized_from.is_none()); + } + + #[test] + fn applique_la_borne_de_la_politique() { + let constructor = Constructor::with_policy(ConstructionPolicy { + max_dimension: Some(60), + ..ConstructionPolicy::default() + }); + + let prepared = constructor + .prepare(analyse(120, 80), &FilterChain::empty()) + .unwrap(); + + assert_eq!(prepared.dimensions().width, 60); + assert_eq!(prepared.dimensions().height, 40); + assert_eq!( + prepared.resized_from, + Some(Dimensions::new(120, 80).unwrap()) + ); + // Les dimensions d'origine restent lisibles dans la description. + assert_eq!(prepared.metadata.dimensions.width, 120); + } +} diff --git a/Filtro_Rust/src/model/pipeline/formatter.rs b/Filtro_Rust/src/model/pipeline/formatter.rs new file mode 100644 index 0000000..17cfe49 --- /dev/null +++ b/Filtro_Rust/src/model/pipeline/formatter.rs @@ -0,0 +1,362 @@ +//! Étape 3 — **Formatter**. +//! +//! Décline l'image préparée en variantes : combinaisons (format de sortie × +//! déclinaison dimensionnelle). Chaque variante possède son propre tampon, +//! déjà adapté aux contraintes du format (aplatissement de l'alpha pour JPEG +//! et BMP, par exemple). +//! +//! Le Formatter ne connaît **aucun filtre** : il prépare des supports, le +//! Renderer y appliquera la chaîne. + +use crate::model::codec; +use crate::model::error::{FiltroError, Result}; +use crate::model::pipeline::constructor::PreparedImage; +use crate::model::pixel::{Dimensions, PixelBuffer, Rgba8}; + +/// Format de fichier proposé en sortie. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum OutputFormat { + Png, + Jpeg, + WebP, + Bmp, + Tiff, +} + +impl OutputFormat { + /// Tous les formats gérés, dans l'ordre d'affichage. + pub const ALL: [OutputFormat; 5] = [ + OutputFormat::Png, + OutputFormat::Jpeg, + OutputFormat::WebP, + OutputFormat::Bmp, + OutputFormat::Tiff, + ]; + + /// Extension de fichier, sans point. + pub fn extension(self) -> &'static str { + match self { + Self::Png => "png", + Self::Jpeg => "jpg", + Self::WebP => "webp", + Self::Bmp => "bmp", + Self::Tiff => "tiff", + } + } + + /// Vrai si le format transporte un canal alpha. + pub fn supports_alpha(self) -> bool { + matches!(self, Self::Png | Self::WebP | Self::Tiff) + } + + /// Vrai si le format est compressé avec perte (le réglage qualité s'applique). + pub fn is_lossy(self) -> bool { + matches!(self, Self::Jpeg) + } + + /// Analyse un nom de format (`png`, `jpg`, `jpeg`, `webp`, `bmp`, `tiff`…). + pub fn parse(raw: &str) -> Result { + match raw.trim().to_ascii_lowercase().as_str() { + "png" => Ok(Self::Png), + "jpg" | "jpeg" => Ok(Self::Jpeg), + "webp" => Ok(Self::WebP), + "bmp" => Ok(Self::Bmp), + "tif" | "tiff" => Ok(Self::Tiff), + other => Err(FiltroError::UnknownFormat(other.to_owned())), + } + } +} + +impl std::fmt::Display for OutputFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Png => "png", + Self::Jpeg => "jpeg", + Self::WebP => "webp", + Self::Bmp => "bmp", + Self::Tiff => "tiff", + }) + } +} + +/// Réglages d'encodage communs à une variante. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct FormatOptions { + /// Qualité 1–100 pour les formats avec perte. + pub quality: u8, + /// Fond appliqué lorsque le format ne gère pas la transparence. + pub background: Rgba8, +} + +impl Default for FormatOptions { + fn default() -> Self { + Self { + quality: 85, + background: Rgba8::WHITE, + } + } +} + +/// Déclinaison dimensionnelle demandée. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SizeVariant { + /// Dimensions de l'image de travail. + Original, + /// Réduction pour tenir dans une boîte carrée de `max_side` pixels. + Bounded { label: String, max_side: u32 }, +} + +impl SizeVariant { + pub fn label(&self) -> &str { + match self { + Self::Original => "original", + Self::Bounded { label, .. } => label, + } + } + + /// Analyse une expression `étiquette:côté`, par exemple `vignette:256`. + pub fn parse(expression: &str) -> Result { + let (label, side) = expression.split_once(':').ok_or_else(|| { + FiltroError::Config(format!( + "déclinaison « {expression} » mal formée (attendu étiquette:pixels)" + )) + })?; + let label = label.trim(); + if label.is_empty() { + return Err(FiltroError::Config( + "étiquette de déclinaison vide".to_owned(), + )); + } + let max_side: u32 = side.trim().parse().map_err(|_| { + FiltroError::Config(format!("« {} » n'est pas un nombre de pixels", side.trim())) + })?; + if max_side == 0 { + return Err(FiltroError::Config( + "la taille d'une déclinaison doit être supérieure à zéro".to_owned(), + )); + } + Ok(Self::Bounded { + label: label.to_owned(), + max_side, + }) + } +} + +/// Identité d'une variante de sortie, transmise aux filtres via le contexte. +#[derive(Debug, Clone, PartialEq)] +pub struct OutputTarget { + pub format: OutputFormat, + pub size: SizeVariant, + pub dimensions: Dimensions, + pub options: FormatOptions, +} + +impl OutputTarget { + /// Nom de fichier construit à partir d'un radical. + pub fn file_name(&self, stem: &str) -> String { + match &self.size { + SizeVariant::Original => format!("{stem}.{}", self.format.extension()), + SizeVariant::Bounded { label, .. } => { + format!("{stem}-{label}.{}", self.format.extension()) + } + } + } +} + +/// Une variante prête à être filtrée puis encodée. +#[derive(Debug, Clone, PartialEq)] +pub struct FormattedImage { + pub target: OutputTarget, + pub canvas: PixelBuffer, +} + +/// Ensemble des variantes issues d'une même image préparée. +#[derive(Debug, Clone, PartialEq)] +pub struct FormatSet { + pub variants: Vec, +} + +impl FormatSet { + pub fn len(&self) -> usize { + self.variants.len() + } + + pub fn is_empty(&self) -> bool { + self.variants.is_empty() + } +} + +/// Étape de déclinaison. +#[derive(Debug, Clone)] +pub struct Formatter { + formats: Vec, + sizes: Vec, + options: FormatOptions, +} + +impl Default for Formatter { + fn default() -> Self { + Self { + formats: vec![OutputFormat::Png], + sizes: vec![SizeVariant::Original], + options: FormatOptions::default(), + } + } +} + +impl Formatter { + pub fn new() -> Self { + Self::default() + } + + /// Définit les formats produits (au moins un). + pub fn with_formats(mut self, formats: Vec) -> Result { + if formats.is_empty() { + return Err(FiltroError::Config( + "au moins un format de sortie est requis".to_owned(), + )); + } + self.formats = formats; + self.formats.dedup(); + Ok(self) + } + + /// Définit les déclinaisons dimensionnelles (`Original` est ajouté si la liste est vide). + pub fn with_sizes(mut self, sizes: Vec) -> Self { + self.sizes = if sizes.is_empty() { + vec![SizeVariant::Original] + } else { + sizes + }; + self + } + + /// Définit la qualité d'encodage des formats avec perte. + pub fn with_quality(mut self, quality: u8) -> Result { + if !(1..=100).contains(&quality) { + return Err(FiltroError::Config(format!( + "qualité {quality} hors de l'intervalle 1–100" + ))); + } + self.options.quality = quality; + Ok(self) + } + + pub fn formats(&self) -> &[OutputFormat] { + &self.formats + } + + pub fn sizes(&self) -> &[SizeVariant] { + &self.sizes + } + + /// Produit toutes les variantes (déclinaison × format). + /// + /// Le ré-échantillonnage n'est calculé qu'une fois par déclinaison, puis + /// partagé entre les formats. + pub fn format(&self, prepared: &PreparedImage) -> Result { + let mut variants = Vec::with_capacity(self.sizes.len() * self.formats.len()); + let mut options = self.options; + options.background = prepared.background; + + for size in &self.sizes { + let canvas = match size { + SizeVariant::Original => prepared.canvas.clone(), + SizeVariant::Bounded { max_side, .. } => { + match prepared.canvas.dimensions().scaled_to_fit(*max_side) { + Some(target) => codec::resize(&prepared.canvas, target)?, + None => prepared.canvas.clone(), + } + } + }; + let dimensions = canvas.dimensions(); + + for format in &self.formats { + let mut variant_canvas = canvas.clone(); + if !format.supports_alpha() { + variant_canvas.flatten_onto(options.background); + } + variants.push(FormattedImage { + target: OutputTarget { + format: *format, + size: size.clone(), + dimensions, + options, + }, + canvas: variant_canvas, + }); + } + } + + Ok(FormatSet { variants }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_support::prepared; + + #[test] + fn produit_le_produit_cartesien() { + let formatter = Formatter::new() + .with_formats(vec![OutputFormat::Png, OutputFormat::Jpeg]) + .unwrap() + .with_sizes(vec![ + SizeVariant::Original, + SizeVariant::Bounded { + label: "vignette".into(), + max_side: 50, + }, + ]); + + let set = formatter + .format(&prepared(200, 100, Rgba8::new(10, 20, 30, 255))) + .unwrap(); + + assert_eq!(set.len(), 4); + let vignette = set + .variants + .iter() + .find(|v| v.target.size.label() == "vignette") + .unwrap(); + assert_eq!(vignette.target.dimensions.width, 50); + assert_eq!(vignette.canvas.width(), 50); + } + + #[test] + fn aplatit_l_alpha_pour_les_formats_sans_transparence() { + let formatter = Formatter::new() + .with_formats(vec![OutputFormat::Png, OutputFormat::Jpeg]) + .unwrap(); + + let set = formatter + .format(&prepared(4, 4, Rgba8::new(10, 20, 30, 0))) + .unwrap(); + + for variant in &set.variants { + assert_eq!( + variant.canvas.has_transparency(), + variant.target.format.supports_alpha() + ); + } + } + + #[test] + fn nomme_les_fichiers() { + let formatter = Formatter::new().with_sizes(vec![SizeVariant::Bounded { + label: "web".into(), + max_side: 800, + }]); + let set = formatter + .format(&prepared(1600, 900, Rgba8::BLACK)) + .unwrap(); + assert_eq!(set.variants[0].target.file_name("photo"), "photo-web.png"); + } + + #[test] + fn refuse_une_qualite_invalide() { + assert!(Formatter::new().with_quality(0).is_err()); + assert!(Formatter::new().with_quality(101).is_err()); + } +} diff --git a/Filtro_Rust/src/model/pipeline/mod.rs b/Filtro_Rust/src/model/pipeline/mod.rs new file mode 100644 index 0000000..6f875c8 --- /dev/null +++ b/Filtro_Rust/src/model/pipeline/mod.rs @@ -0,0 +1,17 @@ +//! Le pipeline : quatre étapes stables, plus leur orchestration. +//! +//! | Étape | Module | Entrée | Sortie | +//! |-------|--------|--------|--------| +//! | [`Analyzer`](analyzer::Analyzer) | [`analyzer`] | octets | [`ImageAnalysis`](analyzer::ImageAnalysis) | +//! | [`Constructor`](constructor::Constructor) | [`constructor`] | analyse + exigences | [`PreparedImage`](constructor::PreparedImage) | +//! | [`Formatter`](formatter::Formatter) | [`formatter`] | image préparée | [`FormatSet`](formatter::FormatSet) | +//! | [`Renderer`](renderer::Renderer) | [`renderer`] | variantes + chaîne | [`RenderOutput`](renderer::RenderOutput) | +//! +//! [`orchestrator`] assemble ces étapes en un [`Pipeline`](orchestrator::Pipeline) +//! immuable et réutilisable. + +pub mod analyzer; +pub mod constructor; +pub mod formatter; +pub mod orchestrator; +pub mod renderer; diff --git a/Filtro_Rust/src/model/pipeline/orchestrator.rs b/Filtro_Rust/src/model/pipeline/orchestrator.rs new file mode 100644 index 0000000..4270836 --- /dev/null +++ b/Filtro_Rust/src/model/pipeline/orchestrator.rs @@ -0,0 +1,145 @@ +//! Orchestration des quatre étapes du cœur. +//! +//! ```text +//! Utilisateur ─ image ─▶ Analyzer ─┐ +//! ├─▶ Constructor ─▶ Formatter ─▶ Renderer ─▶ sorties +//! Utilisateur ─ filtres ─▶ Registre ┘ (applique les filtres) +//! ``` +//! +//! Le [`Pipeline`] est immuable et réutilisable pour plusieurs images. + +use std::path::Path; + +use crate::model::error::Result; +use crate::model::filter::contract::FilterChain; +use crate::model::pipeline::analyzer::{Analyzer, ImageAnalysis, Origin}; +use crate::model::pipeline::constructor::{ConstructionPolicy, Constructor}; +use crate::model::pipeline::formatter::{Formatter, OutputFormat, SizeVariant}; +use crate::model::pipeline::renderer::{RenderOutput, Renderer}; + +/// Chaîne de traitement complète. +#[derive(Debug, Clone, Default)] +pub struct Pipeline { + pub analyzer: Analyzer, + pub constructor: Constructor, + pub formatter: Formatter, + pub renderer: Renderer, +} + +impl Pipeline { + /// Pipeline par défaut : PNG, dimensions d'origine, aucune limite. + pub fn new() -> Self { + Self::default() + } + + pub fn builder() -> PipelineBuilder { + PipelineBuilder::default() + } + + /// Traite un fichier de bout en bout. + pub fn run_path(&self, input: impl AsRef, chain: &FilterChain) -> Result { + let analysis = self.analyzer.analyze_path(input)?; + self.run_analysis(analysis, chain) + } + + /// Traite un flux déjà en mémoire. + pub fn run_bytes( + &self, + bytes: &[u8], + origin: Origin, + chain: &FilterChain, + ) -> Result { + let analysis = self.analyzer.analyze_bytes(bytes, origin)?; + self.run_analysis(analysis, chain) + } + + /// Reprend le pipeline après l'analyse (utile pour les tests et les caches). + pub fn run_analysis( + &self, + analysis: ImageAnalysis, + chain: &FilterChain, + ) -> Result { + let prepared = self.constructor.prepare(analysis, chain)?; + let set = self.formatter.format(&prepared)?; + self.renderer.render(set, chain, &prepared.metadata) + } +} + +/// Construction pas à pas d'un [`Pipeline`], avec validation des réglages. +#[derive(Debug, Clone, Default)] +pub struct PipelineBuilder { + formats: Vec, + sizes: Vec, + quality: Option, + max_dimension: Option, + max_pixels: Option>, + stem: Option, +} + +impl PipelineBuilder { + pub fn formats(mut self, formats: Vec) -> Self { + self.formats = formats; + self + } + + pub fn sizes(mut self, sizes: Vec) -> Self { + self.sizes = sizes; + self + } + + pub fn quality(mut self, quality: u8) -> Self { + self.quality = Some(quality); + self + } + + /// Borne le plus grand côté de l'image de travail. + pub fn max_dimension(mut self, max_dimension: Option) -> Self { + self.max_dimension = max_dimension; + self + } + + /// Borne la taille décompressée acceptée par l'Analyzer. + pub fn max_pixels(mut self, max_pixels: Option) -> Self { + self.max_pixels = Some(max_pixels); + self + } + + pub fn stem(mut self, stem: impl Into) -> Self { + self.stem = Some(stem.into()); + self + } + + /// Valide les réglages et assemble le pipeline. + pub fn build(self) -> Result { + let mut analyzer = Analyzer::new(); + if let Some(max_pixels) = self.max_pixels { + analyzer = analyzer.with_max_pixels(max_pixels); + } + + let constructor = Constructor::with_policy(ConstructionPolicy { + max_dimension: self.max_dimension, + ..ConstructionPolicy::default() + }); + + let mut formatter = Formatter::new(); + if !self.formats.is_empty() { + formatter = formatter.with_formats(self.formats)?; + } + formatter = formatter.with_sizes(self.sizes); + if let Some(quality) = self.quality { + formatter = formatter.with_quality(quality)?; + } + + let renderer = match self.stem { + Some(stem) => Renderer::new().with_stem(stem), + None => Renderer::new(), + }; + + Ok(Pipeline { + analyzer, + constructor, + formatter, + renderer, + }) + } +} diff --git a/Filtro_Rust/src/model/pipeline/renderer.rs b/Filtro_Rust/src/model/pipeline/renderer.rs new file mode 100644 index 0000000..3d628a6 --- /dev/null +++ b/Filtro_Rust/src/model/pipeline/renderer.rs @@ -0,0 +1,193 @@ +//! Étape 4 — **Drawer / Renderer**. +//! +//! Applique la chaîne de filtres à chaque variante produite par le Formatter, +//! puis encode le résultat. Le Renderer est le seul composant du cœur qui +//! *exécute* du code de filtre — et il ne le fait qu'à travers le trait +//! [`Filter`](crate::model::filter::contract::Filter) : il ignore totalement ce que ces filtres font. + +use std::path::{Path, PathBuf}; + +use crate::model::codec; +use crate::model::error::{FiltroError, Result}; +use crate::model::filter::contract::FilterChain; +use crate::model::pipeline::analyzer::ImageMetadata; +use crate::model::pipeline::formatter::{FormatSet, OutputTarget}; + +/// Une image finale encodée, prête à être écrite ou servie. +#[derive(Debug, Clone, PartialEq)] +pub struct RenderedArtifact { + /// Variante à l'origine de ce rendu. + pub target: OutputTarget, + /// Nom de fichier suggéré. + pub file_name: String, + /// Octets encodés. + pub bytes: Vec, +} + +impl RenderedArtifact { + pub fn byte_size(&self) -> usize { + self.bytes.len() + } +} + +/// Résultat complet du pipeline, proposé à l'utilisateur. +#[derive(Debug, Clone, PartialEq)] +pub struct RenderOutput { + /// Description de l'image source. + pub metadata: ImageMetadata, + /// Identifiants des filtres appliqués, dans l'ordre. + pub applied_filters: Vec, + /// Une entrée par variante. + pub artifacts: Vec, +} + +impl RenderOutput { + pub fn is_empty(&self) -> bool { + self.artifacts.is_empty() + } + + pub fn len(&self) -> usize { + self.artifacts.len() + } + + /// Écrit toutes les variantes dans un répertoire, créé si nécessaire. + pub fn write_to_dir(&self, dir: impl AsRef) -> Result> { + let dir = dir.as_ref(); + std::fs::create_dir_all(dir).map_err(|source| FiltroError::io(dir, source))?; + let mut written = Vec::with_capacity(self.artifacts.len()); + for artifact in &self.artifacts { + let path = dir.join(&artifact.file_name); + std::fs::write(&path, &artifact.bytes) + .map_err(|source| FiltroError::io(&path, source))?; + written.push(path); + } + Ok(written) + } +} + +/// Étape de rendu. +#[derive(Debug, Clone, Default)] +pub struct Renderer { + stem: Option, +} + +impl Renderer { + pub fn new() -> Self { + Self::default() + } + + /// Force le radical des noms de fichiers (par défaut : celui de la source). + pub fn with_stem(mut self, stem: impl Into) -> Self { + self.stem = Some(stem.into()); + self + } + + /// Applique la chaîne à chaque variante et encode le résultat. + /// + /// Les filtres sont appliqués **par variante** : un filtre peut donc + /// s'adapter au format ou aux dimensions cibles via + /// [`FilterContext`](crate::model::filter::contract::FilterContext). + pub fn render( + &self, + set: FormatSet, + chain: &FilterChain, + metadata: &ImageMetadata, + ) -> Result { + let stem = self.stem.clone().unwrap_or_else(|| metadata.origin.stem()); + + let mut artifacts = Vec::with_capacity(set.variants.len()); + for mut variant in set.variants { + chain.apply(&mut variant.canvas, metadata, &variant.target)?; + + // Un filtre a pu modifier les dimensions : la cible est alignée + // sur le résultat réel avant encodage. + variant.target.dimensions = variant.canvas.dimensions(); + + // Second passage de sécurité : si un filtre a réintroduit de la + // transparence sur un format qui ne la gère pas. + if !variant.target.format.supports_alpha() { + variant + .canvas + .flatten_onto(variant.target.options.background); + } + + let bytes = codec::encode( + &variant.canvas, + variant.target.format, + &variant.target.options, + )?; + + artifacts.push(RenderedArtifact { + file_name: variant.target.file_name(&stem), + target: variant.target, + bytes, + }); + } + + Ok(RenderOutput { + metadata: metadata.clone(), + applied_filters: chain.ids().into_iter().map(str::to_owned).collect(), + artifacts, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::model::filter::contract::{Filter, FilterContext}; + use crate::model::pipeline::formatter::{Formatter, OutputFormat}; + use crate::model::pixel::{PixelBuffer, Rgba8}; + use crate::test_support::prepared; + + /// Filtre factice, défini ici pour vérifier que le trait suffit. + struct Inverseur; + + impl Filter for Inverseur { + fn id(&self) -> &str { + "inverseur-de-test" + } + + fn apply(&self, canvas: &mut PixelBuffer, _ctx: &FilterContext<'_>) -> Result<()> { + canvas.map_pixels(|_, _, p| Rgba8::new(255 - p.r, 255 - p.g, 255 - p.b, p.a)); + Ok(()) + } + } + + #[test] + fn rend_toutes_les_variantes() { + let image = prepared(8, 8, Rgba8::BLACK); + let set = Formatter::new() + .with_formats(vec![OutputFormat::Png, OutputFormat::Jpeg]) + .unwrap() + .format(&image) + .unwrap(); + + let output = Renderer::new() + .with_stem("photo") + .render(set, &FilterChain::empty(), &image.metadata) + .unwrap(); + + assert_eq!(output.len(), 2); + assert!(output.applied_filters.is_empty()); + assert_eq!(output.artifacts[0].file_name, "photo.png"); + assert!(output.artifacts.iter().all(|a| a.byte_size() > 0)); + } + + #[test] + fn applique_la_chaine_a_chaque_variante() { + let image = prepared(8, 8, Rgba8::BLACK); + let set = Formatter::new().format(&image).unwrap(); + let chain = FilterChain::new(vec![Box::new(Inverseur)]); + + let output = Renderer::new() + .render(set, &chain, &image.metadata) + .unwrap(); + + assert_eq!(output.applied_filters, vec!["inverseur-de-test"]); + + // L'image noire est devenue blanche : on relit le PNG produit. + let (buffer, _) = codec::decode(&output.artifacts[0].bytes, "test").unwrap(); + assert_eq!(buffer.pixel(0, 0), Some(Rgba8::WHITE)); + } +} diff --git a/Filtro_Rust/src/model/pixel.rs b/Filtro_Rust/src/model/pixel.rs new file mode 100644 index 0000000..9c03907 --- /dev/null +++ b/Filtro_Rust/src/model/pixel.rs @@ -0,0 +1,342 @@ +//! Représentation mémoire des pixels, indépendante de toute bibliothèque tierce. +//! +//! C'est le seul type de données que les filtres manipulent. Il ne dépend +//! volontairement pas du crate `image` : la bibliothèque de décodage peut être +//! remplacée sans casser le contrat public offert aux filtres. + +use crate::model::error::{FiltroError, Result}; + +/// Nombre d'octets par pixel (RGBA 8 bits, alpha non prémultiplié). +pub const CHANNELS: usize = 4; + +/// Un pixel RGBA 8 bits, alpha non prémultiplié. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rgba8 { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +impl Rgba8 { + /// Pixel entièrement transparent. + pub const TRANSPARENT: Self = Self::new(0, 0, 0, 0); + /// Blanc opaque (fond par défaut lors de l'aplatissement de l'alpha). + pub const WHITE: Self = Self::new(255, 255, 255, 255); + /// Noir opaque. + pub const BLACK: Self = Self::new(0, 0, 0, 255); + + pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self { + Self { r, g, b, a } + } + + /// Analyse une couleur hexadécimale : `fefe01`, `#fefe01`, `fefe01ff`. + /// + /// Renvoie `None` si la chaîne n'est pas une couleur valide. + pub fn from_hex(raw: &str) -> Option { + let hex = raw.trim().trim_start_matches('#'); + if !hex.chars().all(|c| c.is_ascii_hexdigit()) { + return None; + } + let byte = |i: usize| u8::from_str_radix(&hex[i..i + 2], 16).ok(); + match hex.len() { + 6 => Some(Self::new(byte(0)?, byte(2)?, byte(4)?, 255)), + 8 => Some(Self::new(byte(0)?, byte(2)?, byte(4)?, byte(6)?)), + _ => None, + } + } + + /// Luminance perçue (Rec. 601), utile aux filtres de seuillage. + pub fn luminance(self) -> u8 { + let y = 0.299 * f32::from(self.r) + 0.587 * f32::from(self.g) + 0.114 * f32::from(self.b); + y.round().clamp(0.0, 255.0) as u8 + } + + /// Composite ce pixel sur un fond opaque et renvoie le résultat opaque. + pub fn over(self, background: Rgba8) -> Rgba8 { + if self.a == 255 { + return self; + } + let alpha = f32::from(self.a) / 255.0; + let mix = |fg: u8, bg: u8| -> u8 { + let value = f32::from(fg) * alpha + f32::from(bg) * (1.0 - alpha); + value.round().clamp(0.0, 255.0) as u8 + }; + Rgba8::new( + mix(self.r, background.r), + mix(self.g, background.g), + mix(self.b, background.b), + 255, + ) + } +} + +/// Dimensions d'une image, toujours strictement positives. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Dimensions { + pub width: u32, + pub height: u32, +} + +impl Dimensions { + pub fn new(width: u32, height: u32) -> Result { + if width == 0 || height == 0 { + return Err(FiltroError::InvalidImage(format!( + "dimensions nulles ({width}x{height})" + ))); + } + Ok(Self { width, height }) + } + + /// Nombre total de pixels, calculé en `u64` pour éviter tout débordement. + pub fn pixel_count(self) -> u64 { + u64::from(self.width) * u64::from(self.height) + } + + /// Plus grand côté, base des variantes dimensionnelles du Formatter. + pub fn longest_side(self) -> u32 { + self.width.max(self.height) + } + + /// Dimensions réduites pour tenir dans une boîte de `max_side` pixels. + /// Renvoie `None` si l'image tient déjà dans la boîte. + pub fn scaled_to_fit(self, max_side: u32) -> Option { + if max_side == 0 || self.longest_side() <= max_side { + return None; + } + let ratio = f64::from(max_side) / f64::from(self.longest_side()); + let width = ((f64::from(self.width) * ratio).round() as u32).max(1); + let height = ((f64::from(self.height) * ratio).round() as u32).max(1); + Some(Dimensions { width, height }) + } +} + +impl std::fmt::Display for Dimensions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}x{}", self.width, self.height) + } +} + +/// Tampon de pixels RGBA8 contigu, ligne par ligne. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PixelBuffer { + dimensions: Dimensions, + data: Vec, +} + +impl PixelBuffer { + /// Crée un tampon entièrement transparent. + pub fn new(dimensions: Dimensions) -> Result { + let len = usize::try_from(dimensions.pixel_count()) + .ok() + .and_then(|count| count.checked_mul(CHANNELS)) + .ok_or_else(|| { + FiltroError::InvalidImage(format!("image trop grande : {dimensions}")) + })?; + Ok(Self { + dimensions, + data: vec![0; len], + }) + } + + /// Construit un tampon à partir d'octets bruts RGBA8. + /// + /// # Erreurs + /// Renvoie [`FiltroError::InvalidImage`] si la taille du tampon ne + /// correspond pas aux dimensions annoncées. + pub fn from_raw(dimensions: Dimensions, data: Vec) -> Result { + let expected = usize::try_from(dimensions.pixel_count()) + .ok() + .and_then(|count| count.checked_mul(CHANNELS)) + .ok_or_else(|| { + FiltroError::InvalidImage(format!("image trop grande : {dimensions}")) + })?; + if data.len() != expected { + return Err(FiltroError::InvalidImage(format!( + "tampon de {} octets pour {dimensions} (attendu : {expected})", + data.len() + ))); + } + Ok(Self { dimensions, data }) + } + + pub fn dimensions(&self) -> Dimensions { + self.dimensions + } + + pub fn width(&self) -> u32 { + self.dimensions.width + } + + pub fn height(&self) -> u32 { + self.dimensions.height + } + + /// Accès en lecture aux octets bruts (RGBA entrelacés). + pub fn as_bytes(&self) -> &[u8] { + &self.data + } + + /// Accès en écriture aux octets bruts, pour les filtres vectorisés. + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self.data + } + + /// Consomme le tampon et rend les octets bruts. + pub fn into_raw(self) -> Vec { + self.data + } + + fn offset(&self, x: u32, y: u32) -> Option { + if x >= self.dimensions.width || y >= self.dimensions.height { + return None; + } + let index = u64::from(y) * u64::from(self.dimensions.width) + u64::from(x); + usize::try_from(index).ok()?.checked_mul(CHANNELS) + } + + /// Lit un pixel, ou `None` hors limites. + pub fn pixel(&self, x: u32, y: u32) -> Option { + let offset = self.offset(x, y)?; + Some(Rgba8::new( + self.data[offset], + self.data[offset + 1], + self.data[offset + 2], + self.data[offset + 3], + )) + } + + /// Lit un pixel en repliant les coordonnées sur le bord le plus proche. + /// Pratique pour les noyaux de convolution. + pub fn pixel_clamped(&self, x: i64, y: i64) -> Rgba8 { + let cx = x.clamp(0, i64::from(self.dimensions.width - 1)) as u32; + let cy = y.clamp(0, i64::from(self.dimensions.height - 1)) as u32; + self.pixel(cx, cy).unwrap_or(Rgba8::TRANSPARENT) + } + + /// Écrit un pixel ; sans effet si les coordonnées sortent de l'image. + pub fn set_pixel(&mut self, x: u32, y: u32, pixel: Rgba8) { + if let Some(offset) = self.offset(x, y) { + self.data[offset] = pixel.r; + self.data[offset + 1] = pixel.g; + self.data[offset + 2] = pixel.b; + self.data[offset + 3] = pixel.a; + } + } + + /// Itère sur les pixels dans l'ordre de lecture. + pub fn pixels(&self) -> impl Iterator + '_ { + self.data + .as_chunks::() + .0 + .iter() + .map(|c| Rgba8::new(c[0], c[1], c[2], c[3])) + } + + /// Applique une transformation pixel à pixel, en place. + pub fn map_pixels(&mut self, mut f: F) + where + F: FnMut(u32, u32, Rgba8) -> Rgba8, + { + let width = self.dimensions.width; + for (index, chunk) in self + .data + .as_chunks_mut::() + .0 + .iter_mut() + .enumerate() + { + let index = index as u32; + let x = index % width; + let y = index / width; + let out = f(x, y, Rgba8::new(chunk[0], chunk[1], chunk[2], chunk[3])); + chunk[0] = out.r; + chunk[1] = out.g; + chunk[2] = out.b; + chunk[3] = out.a; + } + } + + /// Vrai si au moins un pixel n'est pas totalement opaque. + pub fn has_transparency(&self) -> bool { + self.data + .as_chunks::() + .0 + .iter() + .any(|c| c[3] != 255) + } + + /// Aplatit la transparence sur un fond opaque (formats sans canal alpha). + pub fn flatten_onto(&mut self, background: Rgba8) { + if !self.has_transparency() { + return; + } + self.map_pixels(|_, _, pixel| pixel.over(background)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn buffer(width: u32, height: u32) -> PixelBuffer { + PixelBuffer::new(Dimensions::new(width, height).expect("dimensions valides")) + .expect("allocation") + } + + #[test] + fn dimensions_refuse_zero() { + assert!(Dimensions::new(0, 10).is_err()); + assert!(Dimensions::new(10, 0).is_err()); + } + + #[test] + fn from_raw_verifie_la_taille() { + let dims = Dimensions::new(2, 2).unwrap(); + assert!(PixelBuffer::from_raw(dims, vec![0; 16]).is_ok()); + assert!(PixelBuffer::from_raw(dims, vec![0; 15]).is_err()); + } + + #[test] + fn lecture_ecriture_pixel() { + let mut buf = buffer(3, 2); + buf.set_pixel(2, 1, Rgba8::new(10, 20, 30, 40)); + assert_eq!(buf.pixel(2, 1), Some(Rgba8::new(10, 20, 30, 40))); + assert_eq!(buf.pixel(3, 1), None); + } + + #[test] + fn map_pixels_fournit_les_coordonnees() { + let mut buf = buffer(2, 2); + buf.map_pixels(|x, y, _| Rgba8::new(x as u8, y as u8, 0, 255)); + assert_eq!(buf.pixel(1, 0), Some(Rgba8::new(1, 0, 0, 255))); + assert_eq!(buf.pixel(0, 1), Some(Rgba8::new(0, 1, 0, 255))); + } + + #[test] + fn aplatissement_alpha() { + let mut buf = buffer(1, 1); + buf.set_pixel(0, 0, Rgba8::new(0, 0, 0, 0)); + buf.flatten_onto(Rgba8::WHITE); + assert_eq!(buf.pixel(0, 0), Some(Rgba8::WHITE)); + } + + #[test] + fn lecture_couleur_hexadecimale() { + assert_eq!( + Rgba8::from_hex("#fefe01"), + Some(Rgba8::new(254, 254, 1, 255)) + ); + assert_eq!(Rgba8::from_hex("000000ff"), Some(Rgba8::BLACK)); + assert_eq!(Rgba8::from_hex("fff"), None); + assert_eq!(Rgba8::from_hex("zzzzzz"), None); + } + + #[test] + fn reduction_proportionnelle() { + let dims = Dimensions::new(1000, 500).unwrap(); + assert_eq!(dims.scaled_to_fit(2000), None); + let reduced = dims.scaled_to_fit(100).unwrap(); + assert_eq!((reduced.width, reduced.height), (100, 50)); + } +} diff --git a/Filtro_Rust/src/model/resolution.rs b/Filtro_Rust/src/model/resolution.rs new file mode 100644 index 0000000..39a0ebc --- /dev/null +++ b/Filtro_Rust/src/model/resolution.rs @@ -0,0 +1,120 @@ +//! Lecture de la résolution (points par pouce) dans les métadonnées. +//! +//! Le crate `image` n'expose pas la densité : on lit donc directement le chunk +//! `pHYs` d'un PNG ou le segment `JFIF` d'un JPEG. La lecture est défensive : +//! toute anomalie renvoie `None` plutôt qu'une erreur, car l'absence de +//! résolution n'empêche pas le traitement. + +use crate::model::pipeline::analyzer::SourceFormat; + +/// Résolution déclarée par le fichier source. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Resolution { + pub dpi_x: f32, + pub dpi_y: f32, +} + +const METERS_PER_INCH: f32 = 0.0254; + +/// Extrait la résolution si le format la porte et qu'elle est exploitable. +pub(crate) fn read(bytes: &[u8], format: &SourceFormat) -> Option { + match format { + SourceFormat::Png => png_phys(bytes), + SourceFormat::Jpeg => jpeg_jfif(bytes), + _ => None, + } +} + +fn be_u32(slice: &[u8]) -> Option { + Some(u32::from_be_bytes(slice.get(..4)?.try_into().ok()?)) +} + +fn be_u16(slice: &[u8]) -> Option { + Some(u16::from_be_bytes(slice.get(..2)?.try_into().ok()?)) +} + +/// Parcourt les chunks PNG jusqu'à `pHYs` (toujours placé avant `IDAT`). +fn png_phys(bytes: &[u8]) -> Option { + const SIGNATURE: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A]; + if bytes.get(..8)? != SIGNATURE { + return None; + } + + let mut cursor = 8usize; + while cursor + 8 <= bytes.len() { + let length = be_u32(&bytes[cursor..])? as usize; + let kind = bytes.get(cursor + 4..cursor + 8)?; + let data_start = cursor + 8; + let data_end = data_start.checked_add(length)?; + if data_end > bytes.len() { + return None; + } + + if kind == b"pHYs" && length >= 9 { + let data = &bytes[data_start..data_end]; + // Unité 1 = pixels par mètre ; 0 = simple rapport d'aspect. + if data[8] != 1 { + return None; + } + return Some(Resolution { + dpi_x: be_u32(data)? as f32 * METERS_PER_INCH, + dpi_y: be_u32(&data[4..])? as f32 * METERS_PER_INCH, + }); + } + if kind == b"IDAT" || kind == b"IEND" { + return None; + } + + cursor = data_end.checked_add(4)?; // + CRC + } + None +} + +/// Parcourt les segments JPEG jusqu'à l'en-tête APP0/JFIF. +fn jpeg_jfif(bytes: &[u8]) -> Option { + if bytes.get(..2)? != [0xFF, 0xD8] { + return None; + } + + let mut cursor = 2usize; + while cursor + 4 <= bytes.len() { + if bytes[cursor] != 0xFF { + return None; + } + let marker = bytes[cursor + 1]; + // Marqueurs sans charge utile. + if marker == 0xD8 || marker == 0x01 || (0xD0..=0xD7).contains(&marker) { + cursor += 2; + continue; + } + // Début des données compressées : plus rien à lire. + if marker == 0xDA { + return None; + } + + let length = be_u16(&bytes[cursor + 2..])? as usize; + if length < 2 { + return None; + } + let segment = bytes.get(cursor + 4..cursor + 2 + length)?; + + if marker == 0xE0 && segment.len() >= 12 && &segment[..5] == b"JFIF\0" { + let x = be_u16(&segment[8..])? as f32; + let y = be_u16(&segment[10..])? as f32; + if x == 0.0 || y == 0.0 { + return None; + } + return match segment[7] { + 1 => Some(Resolution { dpi_x: x, dpi_y: y }), // points par pouce + 2 => Some(Resolution { + dpi_x: x * 2.54, + dpi_y: y * 2.54, + }), // points par centimètre + _ => None, + }; + } + + cursor += 2 + length; + } + None +} diff --git a/Filtro_Rust/src/test_support.rs b/Filtro_Rust/src/test_support.rs new file mode 100644 index 0000000..a01dee0 --- /dev/null +++ b/Filtro_Rust/src/test_support.rs @@ -0,0 +1,74 @@ +//! Fabriques d'objets pour les tests — compilé uniquement avec `cargo test`. +//! +//! Évite de répéter la construction d'une `ImageMetadata` ou d'une +//! `OutputTarget` complète dans chaque test. + +use crate::model::filter::contract::{Filter, FilterContext, FilterRequirements}; +use crate::model::pipeline::analyzer::{ColorStats, ImageMetadata, Origin, SourceFormat}; +use crate::model::pipeline::constructor::PreparedImage; +use crate::model::pipeline::formatter::{FormatOptions, OutputFormat, OutputTarget, SizeVariant}; +use crate::model::pixel::{Dimensions, PixelBuffer, Rgba8}; + +/// Description d'image factice, cohérente avec les dimensions données. +pub(crate) fn metadata(dimensions: Dimensions) -> ImageMetadata { + ImageMetadata { + origin: Origin::Memory { + label: "test".into(), + }, + source_format: SourceFormat::Png, + dimensions, + resolution: None, + byte_size: 0, + stats: ColorStats { + has_transparency: false, + is_grayscale: false, + mean_luminance: 0.0, + luminance_range: (0, 255), + }, + } +} + +/// Image uniforme de la taille demandée. +pub(crate) fn canvas(width: u32, height: u32, pixel: Rgba8) -> PixelBuffer { + let dimensions = Dimensions::new(width, height).expect("dimensions valides"); + let mut canvas = PixelBuffer::new(dimensions).expect("allocation"); + canvas.map_pixels(|_, _, _| pixel); + canvas +} + +/// Image préparée uniforme, prête pour le Formatter ou le Renderer. +pub(crate) fn prepared(width: u32, height: u32, pixel: Rgba8) -> PreparedImage { + let canvas = canvas(width, height, pixel); + PreparedImage { + metadata: metadata(canvas.dimensions()), + canvas, + requirements: FilterRequirements::default(), + background: Rgba8::WHITE, + resized_from: None, + } +} + +/// Variante de sortie factice (PNG, taille d'origine). +pub(crate) fn target(dimensions: Dimensions) -> OutputTarget { + OutputTarget { + format: OutputFormat::Png, + size: SizeVariant::Original, + dimensions, + options: FormatOptions::default(), + } +} + +/// Applique un filtre à une image de 1×1 pixel et renvoie le résultat. +pub(crate) fn apply_to_pixel(filter: &dyn Filter, pixel: Rgba8) -> Rgba8 { + let mut buffer = canvas(1, 1, pixel); + let dimensions = buffer.dimensions(); + let metadata = metadata(dimensions); + let target = target(dimensions); + let ctx = FilterContext { + metadata: &metadata, + target: &target, + position: 0, + }; + filter.apply(&mut buffer, &ctx).expect("filtre appliqué"); + buffer.pixel(0, 0).expect("pixel présent") +} diff --git a/Filtro_Rust/src/view/catalog.rs b/Filtro_Rust/src/view/catalog.rs new file mode 100644 index 0000000..7499e77 --- /dev/null +++ b/Filtro_Rust/src/view/catalog.rs @@ -0,0 +1,29 @@ +//! Affichage du catalogue de filtres (`filtro --list-filters`). + +use filtro::FilterRegistry; + +/// Liste les filtres enregistrés, leurs paramètres et quelques exemples. +pub fn print_filters(registry: &FilterRegistry) { + if registry.is_empty() { + println!("Aucun filtre enregistré."); + println!( + "Les filtres s'ajoutent en implémentant `Filter` + `FilterFactory`, \ + puis en les enregistrant dans `register_filters` — sans modifier le cœur." + ); + return; + } + println!("Filtres disponibles ({}) :", registry.len()); + for factory in registry.factories() { + println!("\n {:<12} {}", factory.id(), factory.description()); + for param in factory.parameters() { + println!( + " {:<12} défaut {:<8} {}", + param.name, param.default, param.help + ); + } + } + println!("\nExemples :"); + println!(" filtro photo.jpg -f jaune"); + println!(" filtro photo.jpg -f noir-blanc:niveau=128,mode=luminance"); + println!(" filtro photo.jpg -f rouge -f noir-blanc (chaînage)"); +} diff --git a/Filtro_Rust/src/view/mod.rs b/Filtro_Rust/src/view/mod.rs new file mode 100644 index 0000000..1652fb3 --- /dev/null +++ b/Filtro_Rust/src/view/mod.rs @@ -0,0 +1,33 @@ +//! Couche **View** — tout ce qui s'écrit sur le terminal. +//! +//! Aucune logique métier ici : les fonctions reçoivent des types du cœur +//! (`filtro::…`) déjà calculés et se contentent de les mettre en forme. + +mod catalog; +mod report; + +pub use catalog::print_filters; +pub use report::print_summary; + +use filtro::FiltroError; + +/// Écrit une erreur sur la sortie d'erreur, en déroulant la chaîne de causes. +pub fn report_error(error: &FiltroError) { + eprintln!("erreur : {error}"); + let mut source = std::error::Error::source(error); + while let Some(cause) = source { + eprintln!(" ↳ {cause}"); + source = cause.source(); + } +} + +/// Taille lisible par un humain (o, Kio, Mio). +pub fn human_size(bytes: usize) -> String { + const KIO: usize = 1024; + const MIO: usize = KIO * 1024; + match bytes { + 0..=1023 => format!("{bytes} o"), + 1024..=1_048_575 => format!("{:.1} Kio", bytes as f64 / KIO as f64), + _ => format!("{:.1} Mio", bytes as f64 / MIO as f64), + } +} diff --git a/Filtro_Rust/src/view/report.rs b/Filtro_Rust/src/view/report.rs new file mode 100644 index 0000000..9d60004 --- /dev/null +++ b/Filtro_Rust/src/view/report.rs @@ -0,0 +1,40 @@ +//! Résumé d'un traitement terminé : source, filtres, variantes écrites. + +use std::path::PathBuf; + +use filtro::RenderOutput; + +use super::human_size; + +/// Écrit sur la sortie standard le compte rendu d'un [`RenderOutput`]. +/// +/// `written` est la liste des chemins réellement créés, dans le même ordre que +/// `output.artifacts`. +pub fn print_summary(output: &RenderOutput, written: &[PathBuf]) { + let source = &output.metadata; + println!( + "source : {} — {} {} ({} octets)", + source.origin, source.source_format, source.dimensions, source.byte_size + ); + if let Some(resolution) = source.resolution { + println!( + "résolution : {:.0}×{:.0} ppp", + resolution.dpi_x, resolution.dpi_y + ); + } + if output.applied_filters.is_empty() { + println!("filtres : aucun"); + } else { + println!("filtres : {}", output.applied_filters.join(" → ")); + } + println!("{} variante(s) écrite(s) :", written.len()); + for (path, artifact) in written.iter().zip(&output.artifacts) { + println!( + " {} — {} {} ({})", + path.display(), + artifact.target.format, + artifact.target.dimensions, + human_size(artifact.byte_size()) + ); + } +} diff --git a/Filtro_Rust/tests/pipeline.rs b/Filtro_Rust/tests/pipeline.rs new file mode 100644 index 0000000..e197f19 --- /dev/null +++ b/Filtro_Rust/tests/pipeline.rs @@ -0,0 +1,204 @@ +//! Test d'intégration bout-en-bout. +//! +//! Il n'utilise **que l'API publique** `filtro::…` — y compris pour définir un +//! filtre `luminosite` hors du cœur : la preuve que le contrat exposé suffit à +//! écrire un filtre sans toucher au pipeline. + +use std::io::Cursor; + +use filtro::{ + Analyzer, Filter, FilterChain, FilterContext, Origin, OutputFormat, Pipeline, PixelBuffer, + Result, Rgba8, SizeVariant, +}; + +// --- Un filtre défini entièrement hors du crate --------------------------- + +struct Luminosite { + delta: i16, +} + +impl Filter for Luminosite { + fn id(&self) -> &str { + "luminosite" + } + + fn apply(&self, canvas: &mut PixelBuffer, _ctx: &FilterContext<'_>) -> Result<()> { + canvas.map_pixels(|_, _, pixel| { + let adjust = |channel: u8| (i16::from(channel) + self.delta).clamp(0, 255) as u8; + Rgba8::new(adjust(pixel.r), adjust(pixel.g), adjust(pixel.b), pixel.a) + }); + Ok(()) + } +} + +// --- Fabrique d'une image source en mémoire ------------------------------ + +/// Encode un PNG uniforme de `width`×`height` de la couleur donnée. +fn source_png(width: u32, height: u32, color: [u8; 4]) -> Vec { + let image = image::RgbaImage::from_pixel(width, height, image::Rgba(color)); + let mut bytes = Vec::new(); + image::DynamicImage::ImageRgba8(image) + .write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Png) + .expect("encodage du PNG de test"); + bytes +} + +fn analyze(bytes: &[u8]) -> filtro::ImageAnalysis { + Analyzer::new() + .analyze_bytes( + bytes, + Origin::Memory { + label: "relecture".into(), + }, + ) + .expect("relecture d'une variante produite") +} + +// --- Les tests ---------------------------------------------------------- + +#[test] +fn bout_en_bout_avec_un_filtre_externe() { + let png = source_png(64, 48, [100, 100, 100, 255]); + + let pipeline = Pipeline::builder() + .formats(vec![OutputFormat::Png, OutputFormat::Jpeg]) + .sizes(vec![ + SizeVariant::Original, + SizeVariant::parse("vignette:32").unwrap(), + ]) + .quality(90) + .build() + .expect("pipeline valide"); + + let chain = FilterChain::new(vec![Box::new(Luminosite { delta: 40 })]); + let output = pipeline + .run_bytes( + &png, + Origin::Memory { + label: "source".into(), + }, + &chain, + ) + .expect("traitement"); + + // 2 formats × 2 tailles. + assert_eq!(output.artifacts.len(), 4); + assert_eq!(output.applied_filters, vec!["luminosite"]); + + for artifact in &output.artifacts { + assert!(!artifact.bytes.is_empty(), "variante vide"); + assert!( + artifact.file_name.starts_with("relecture") || artifact.file_name.starts_with("source"), + "nom inattendu : {}", + artifact.file_name + ); + } + + // La vignette PNG : plus grand côté ramené à 32, effet du filtre visible. + let vignette = output + .artifacts + .iter() + .find(|a| a.target.format == OutputFormat::Png && a.target.size.label() == "vignette") + .expect("vignette png présente"); + assert_eq!(vignette.target.dimensions.longest_side(), 32); + + // Le PNG pleine taille : 100 + 40 = 140 sur chaque canal. + let plein = output + .artifacts + .iter() + .find(|a| a.target.format == OutputFormat::Png && a.target.size.label() == "original") + .expect("png original présent"); + let relu = analyze(&plein.bytes); + assert_eq!( + relu.buffer.pixel(0, 0), + Some(Rgba8::new(140, 140, 140, 255)) + ); +} + +#[test] +fn sans_filtre_le_pipeline_convertit_les_formats() { + let png = source_png(20, 10, [10, 120, 200, 255]); + + let pipeline = Pipeline::builder() + .formats(vec![OutputFormat::Png, OutputFormat::WebP]) + .build() + .unwrap(); + + let output = pipeline + .run_bytes( + &png, + Origin::Memory { + label: "src".into(), + }, + &FilterChain::empty(), + ) + .unwrap(); + + assert!(output.applied_filters.is_empty()); + assert_eq!(output.artifacts.len(), 2); + for artifact in &output.artifacts { + assert_eq!(artifact.target.dimensions.width, 20); + assert_eq!(artifact.target.dimensions.height, 10); + } +} + +#[test] +fn jpeg_ne_conserve_pas_la_transparence() { + let png = source_png(16, 16, [200, 50, 50, 0]); // entièrement transparent + + let pipeline = Pipeline::builder() + .formats(vec![OutputFormat::Jpeg]) + .build() + .unwrap(); + + let output = pipeline + .run_bytes( + &png, + Origin::Memory { + label: "src".into(), + }, + &FilterChain::empty(), + ) + .unwrap(); + + let jpeg = &output.artifacts[0]; + assert!(!analyze(&jpeg.bytes).metadata.stats.has_transparency); +} + +#[test] +fn un_flux_vide_est_rejete() { + let pipeline = Pipeline::new(); + let result = pipeline.run_bytes( + &[], + Origin::Memory { + label: "vide".into(), + }, + &FilterChain::empty(), + ); + assert!(result.is_err()); +} + +#[test] +fn la_borne_dimensionnelle_reduit_l_image_de_travail() { + let png = source_png(200, 120, [255, 255, 255, 255]); + + let pipeline = Pipeline::builder() + .formats(vec![OutputFormat::Png]) + .max_dimension(Some(50)) + .build() + .unwrap(); + + let output = pipeline + .run_bytes( + &png, + Origin::Memory { + label: "src".into(), + }, + &FilterChain::empty(), + ) + .unwrap(); + + assert_eq!(output.artifacts[0].target.dimensions.longest_side(), 50); + // La description conserve les dimensions d'origine. + assert_eq!(output.metadata.dimensions.width, 200); +}