From 5cd5f4c0444d557a948c8f1e3a23607e5f7abbdd Mon Sep 17 00:00:00 2001
From: ANHDIRE
Date: Sat, 3 Jan 2026 13:08:16 +0100
Subject: [PATCH] =?UTF-8?q?Modifications=20apport=C3=A9s?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
PlaningDeTavail.md | 384 +++++++--------
ProductBacklog.MD | 180 +++----
README.md | 348 +++++++-------
.../sae/mhuffman/CanonicalCode$1.class | Bin 1308 -> 0 bytes
.../sae/mhuffman/CanonicalCode.class | Bin 2503 -> 2423 bytes
.../sae/mhuffman/ComparateurCanonique.class | Bin 0 -> 1106 bytes
makefile | 341 ++++++-------
src/fr/iutfbleau/sae/ConverterController.java | 450 +++++++++---------
src/fr/iutfbleau/sae/Convertisseur.java | 48 +-
.../iutfbleau/sae/ExportButtonListener.java | 34 +-
src/fr/iutfbleau/sae/PIFSaveTask.java | 26 +-
src/fr/iutfbleau/sae/Viewer.java | 14 +-
.../iutfbleau/sae/mhuffman/CanonicalCode.java | 133 +++---
.../sae/mhuffman/ComparateurCanonique.java | 19 +
.../sae/mhuffman/FrequencyTable.java | 230 ++++-----
.../iutfbleau/sae/mhuffman/HuffmanNode.java | 238 ++++-----
.../iutfbleau/sae/mhuffman/HuffmanTree.java | 436 ++++++++---------
src/fr/iutfbleau/sae/mimage/Pixel.java | 74 +--
src/fr/iutfbleau/sae/mimage/RGBImage.java | 58 +--
src/fr/iutfbleau/sae/mpif/DecodeNode.java | 46 +-
src/fr/iutfbleau/sae/mpif/PIFReader.java | 152 +++---
src/fr/iutfbleau/sae/mpif/PIFWriter.java | 252 +++++-----
.../iutfbleau/sae/util/BitOutputStream.java | 254 +++++-----
src/fr/iutfbleau/sae/util/BitinputStream.java | 230 ++++-----
src/fr/iutfbleau/sae/util/ByteUtils.java | 178 +++----
src/fr/iutfbleau/sae/util/GestErreur.java | 14 +-
.../sae/vconverter/CodeTablePanel.java | 218 ++++-----
.../sae/vconverter/ConverterWindow.java | 322 ++++++-------
.../sae/vconverter/FrequencyTablePanel.java | 140 +++---
.../sae/vconverter/ImagePreviewPanel.java | 150 +++---
src/fr/iutfbleau/sae/vviewer/ImagePanel.java | 7 +
.../iutfbleau/sae/vviewer/ViewerWindow.java | 11 +
temp/learning-Log.txt | 98 ++--
temp/test.pif | Bin 0 -> 10965990 bytes
34 files changed, 2566 insertions(+), 2519 deletions(-)
delete mode 100644 build/fr/iutfbleau/sae/mhuffman/CanonicalCode$1.class
create mode 100644 build/fr/iutfbleau/sae/mhuffman/ComparateurCanonique.class
create mode 100644 src/fr/iutfbleau/sae/mhuffman/ComparateurCanonique.java
create mode 100644 src/fr/iutfbleau/sae/vviewer/ImagePanel.java
create mode 100644 src/fr/iutfbleau/sae/vviewer/ViewerWindow.java
create mode 100644 temp/test.pif
diff --git a/PlaningDeTavail.md b/PlaningDeTavail.md
index de5c589..a1d9925 100644
--- a/PlaningDeTavail.md
+++ b/PlaningDeTavail.md
@@ -1,192 +1,192 @@
-# Sprint Planning – Projet PIF
-
-## Légende
-AD → Algassimou
-AA → Ayoub
-YB → Youness
-
-🟥 TODO = À faire
-🟨 DOING = En cours
-🟩 DONE = Terminé
-
-
-# ---------------------------------------
-# SPRINT 1 (13–20 décembre 2025)
-# ---------------------------------------
-Objectif : Mise en place des fondations techniques
-(image, binaire, Huffman, canoniques)
-
-| US | Assigné | Statut | | Description |
-|-------|---------|--------|----|----------------------------------------------------|
-| US-D1 | AD | DONE | 🟩 | Implémenter BitInputStream (lecture bit par bit) |
-| US-D2 | AD | DONE | 🟩 | Implémenter BitOutputStream (écriture bit par bit) |
-| US-D3 | AD | DONE | 🟩 | Générer les tables de fréquences RGB |
-| US-D4 | AD | DONE | 🟩 | Construire l’arbre Huffman |
-| US-D5 | AA | DONE | 🟩 | Générer les codes Huffman |
-| US-D6 | AA | DONE | 🟩 | Générer les codes canoniques |
-| US-U5 | YB | DONE | 🟩 | Chargement d’image via ImageIO |
-| US-D8 | YB | DONE | 🟩 | Structure RGBImage + Pixel |
-| US-P1 | AA | DONE | 🟩 | Interface simple d’affichage des fréquences |
-| US-P2 | AD | DONE | 🟩 | Interface simple d’affichage codes Huffman |
-| US-P3 | AA | DONE | 🟩 | Interface simple d’affichage codes canoniques |
-
-## Fichiers à créer – Sprint 1
-
-### `src/mimage/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `Pixel.java` | Représente un pixel (r, g, b) | US-D8 |
-| `RGBImage.java` | Matrice de pixels + utilitaires | US-D8, US-U5 |
-
-### `src/mhuffman/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `FrequencyTable.java` | Stocke les fréquences R/G/B | US-D3 |
-| `HuffmanNode.java` | Nœud d’arbre Huffman | US-D4 |
-| `HuffmanTree.java` | Construction arbre + génération des codes | US-D4, US-D5 |
-| `CanonicalCode.java` | Génération des codes canoniques | US-D6 |
-
-### `src/util/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `BitInputStream.java` | Lecture bit par bit | US-D1 |
-| `BitOutputStream.java` | Écriture bit par bit | US-D2 |
-| `ByteUtils.java` | Conversion int octets | US-D3 et plus |
-| `FileUtils.java` | Méthodes utilitaires fichiers | US-U5 (indirect) |
-
-### `src/vconverter/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `ConverterWindow.java` | Fenêtre du convertisseur | US-P1, US-P2, US-P3 |
-| `PreviewPanel.java` | Aperçu de l’image chargée | US-U5 |
-| `FrequencyTablePanel.java` | Affichage fréquences RGB | US-P1 |
-| `CodeTablePanel.java` | Affichage Huffman + canoniques | US-P2, US-P3 |
-
-### `src/` (racine)
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `ConverterController.java` | Contrôleur du convertisseur | US-U5, US-D3..D6, US-P1..P3 |
-| `Convertisseur.java` | Lancement du convertisseur | — |
----
-
-### Résultat attendu Sprint 1
-- Compression entièrement fonctionnelle
-- Import d’image opérationnel
-- GUI minimaliste affichant fréquences / Huffman / canoniques
-- Aucun fichier `.pif` encore écrit
-
-
-# ---------------------------------------
-# SPRINT 2 (20–27 décembre 2025)
-# ---------------------------------------
-Objectif : Écriture du format `.pif` + finalisation convertisseur
-
-| US | Assigné | Statut | | Description |
-|------------|---------|--------|-----|-------------|
-| US-D2 | AA | DONE | 🟩 | Vérifier BitOutputStream avec flux réel |
-| US-C5 | AD | DONE | 🟩 | Implémenter PIFWriter (header + tables + pixels compressés) |
-| US-U6 | AD | DONE | 🟩 | Exporter une image en `.pif` |
-| US-P1 | AA | DONE | 🟩 | Finaliser affichage des fréquences |
-| US-P2 | AA | DONE | 🟩 | Finaliser affichage codes Huffman |
-| US-P3 | AA | DONE | 🟩 | Finaliser affichage codes canoniques |
-| US-U7 | AD | DONE | 🟩 | Implémenter l’aperçu (PreviewPanel) |
-
-## Fichiers à créer – Sprint 2
-
-### `src/mpif/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `PIFWriter.java` | Écriture du fichier `.pif` | US-C5, US-U6 |
-
-### `src/vconverter/` (complément)
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `SavePanel.java` en option a voir | Interface de sauvegarde `.pif` | US-U6 |
-
-
----
-
-### Résultat attendu Sprint 2
-- `.pif` généré correctement
-- Convertisseur fonctionnel à 80 %
-- UI complète pour la consultation des tables
-- Aperçu image fonctionnel
-
-
-# ---------------------------------------
-# SPRINT 3 (27 décembre 2025 – 3 janvier 2026)
-# ---------------------------------------
-Objectif : Lecture du fichier `.pif` + visualisateur opérationnel
-
-| US | Assigné | Statut | | Description |
-|----------|---------|--------|-----|-------------|
-| US-D7 | AD | DONE | 🟩 | Reconstruire les codes canoniques depuis fichier |
-| US-D8 | AD | DONE | 🟩 | Décoder pixels (implémenter PIFReader) |
-| US-U1 | YB | TODO | 🟥 | Ouvrir `.pif` via argument ou JFileChooser |
-| US-U2 | AA | TODO | 🟥 | Afficher l’image dans une fenêtre |
-| US-U3 | AA | TODO | 🟥 | Centrer l’image si elle est petite |
-| US-U4 | AD | TODO | 🟥 | Déplacement de l’image à la souris |
-| US-P6 | AA | TODO | 🟥 | Préparer diagrammes UML |
-
-## Fichiers à créer – Sprint 3
-
-### `src/mpif/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `PIFReader.java` | Lecture et décodage du `.pif` | US-D7, US-D8 |
-
-### `src/vviewer/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `ViewerWindow.java` | Fenêtre visualisation | US-U2 |
-| `ImagePanel.java` | Affichage + déplacement image | US-U2, US-U3, US-U4 |
-
-### `src/`
-| Nom du fichier | Rôle | US |
-|----------------|-------|----|
-| `ViewerController.java` | Contrôle du visualisateur | US-U1..U4 |
-| `Viewer.java` | Programme principal du visualisateur | US-U1 |
-
----
-
-### Résultat attendu Sprint 3
-- Visualisateur 100 % fonctionnel
-- Lecture complète du format `.pif`
-- Image affichée, centrée, déplaçable
-- UML structurel prêt pour rapport
-
-
-# ---------------------------------------
-# SPRINT 4 (3–10 janvier 2026)
-# ---------------------------------------
-Objectif : Finalisation complète + livrable final
-
-| US | Assigné | Statut | | Description |
-|------------|---------|--------|-----|-------------|
-| US-P5 | AD | TODO | 🟥 | Javadoc complète |
-| US-P7 | AD | TODO | 🟥 | Makefile (compilation + jar + exécution) |
-| US-P6 | AA | TODO | 🟥 | Finalisation des diagrammes UML |
-| Tests | YB | TODO | 🟥 | Tests convertisseur + visualisateur |
-| Rapport | AD/AA/YB| TODO | 🟥 | Rédaction rapport complet |
-| Livraison | AD/AA/YB| TODO | 🟥 | Vérification dépôt Gitea + JAR exécutables |
-
-## Fichiers à créer – Sprint 4
-
-### Racine creee tout au long
-| Nom du fichier | Rôle |
-|----------------|-------|
-| `Makefile` | Compilation + génération `.jar` |
-
-### `/doc`
-| Nom du fichier | Rôle |
-|----------------|-------|
-| `rapport.pdf` | Livrable final |
-| `UML/___.plantuml` | Sources de diagrammes |
-
----
-
-### Résultat attendu Sprint 4
-- Rapport PDF validé
-- Diagrammes UML terminés
-- Makefile opérationnel
-- Projet soumis proprement sur Gitea
+# Sprint Planning – Projet PIF
+
+## Légende
+AD → Algassimou
+AA → Ayoub
+YB → Youness
+
+🟥 TODO = À faire
+🟨 DOING = En cours
+🟩 DONE = Terminé
+
+
+# ---------------------------------------
+# SPRINT 1 (13–20 décembre 2025)
+# ---------------------------------------
+Objectif : Mise en place des fondations techniques
+(image, binaire, Huffman, canoniques)
+
+| US | Assigné | Statut | | Description |
+|-------|---------|--------|----|----------------------------------------------------|
+| US-D1 | AD | DONE | 🟩 | Implémenter BitInputStream (lecture bit par bit) |
+| US-D2 | AD | DONE | 🟩 | Implémenter BitOutputStream (écriture bit par bit) |
+| US-D3 | AD | DONE | 🟩 | Générer les tables de fréquences RGB |
+| US-D4 | AD | DONE | 🟩 | Construire l’arbre Huffman |
+| US-D5 | AA | DONE | 🟩 | Générer les codes Huffman |
+| US-D6 | AA | DONE | 🟩 | Générer les codes canoniques |
+| US-U5 | YB | DONE | 🟩 | Chargement d’image via ImageIO |
+| US-D8 | YB | DONE | 🟩 | Structure RGBImage + Pixel |
+| US-P1 | AA | DONE | 🟩 | Interface simple d’affichage des fréquences |
+| US-P2 | AD | DONE | 🟩 | Interface simple d’affichage codes Huffman |
+| US-P3 | AA | DONE | 🟩 | Interface simple d’affichage codes canoniques |
+
+## Fichiers à créer – Sprint 1
+
+### `src/mimage/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `Pixel.java` | Représente un pixel (r, g, b) | US-D8 |
+| `RGBImage.java` | Matrice de pixels + utilitaires | US-D8, US-U5 |
+
+### `src/mhuffman/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `FrequencyTable.java` | Stocke les fréquences R/G/B | US-D3 |
+| `HuffmanNode.java` | Nœud d’arbre Huffman | US-D4 |
+| `HuffmanTree.java` | Construction arbre + génération des codes | US-D4, US-D5 |
+| `CanonicalCode.java` | Génération des codes canoniques | US-D6 |
+
+### `src/util/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `BitInputStream.java` | Lecture bit par bit | US-D1 |
+| `BitOutputStream.java` | Écriture bit par bit | US-D2 |
+| `ByteUtils.java` | Conversion int octets | US-D3 et plus |
+| `FileUtils.java` | Méthodes utilitaires fichiers | US-U5 (indirect) |
+
+### `src/vconverter/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `ConverterWindow.java` | Fenêtre du convertisseur | US-P1, US-P2, US-P3 |
+| `PreviewPanel.java` | Aperçu de l’image chargée | US-U5 |
+| `FrequencyTablePanel.java` | Affichage fréquences RGB | US-P1 |
+| `CodeTablePanel.java` | Affichage Huffman + canoniques | US-P2, US-P3 |
+
+### `src/` (racine)
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `ConverterController.java` | Contrôleur du convertisseur | US-U5, US-D3..D6, US-P1..P3 |
+| `Convertisseur.java` | Lancement du convertisseur | — |
+---
+
+### Résultat attendu Sprint 1
+- Compression entièrement fonctionnelle
+- Import d’image opérationnel
+- GUI minimaliste affichant fréquences / Huffman / canoniques
+- Aucun fichier `.pif` encore écrit
+
+
+# ---------------------------------------
+# SPRINT 2 (20–27 décembre 2025)
+# ---------------------------------------
+Objectif : Écriture du format `.pif` + finalisation convertisseur
+
+| US | Assigné | Statut | | Description |
+|------------|---------|--------|-----|-------------|
+| US-D2 | AA | DONE | 🟩 | Vérifier BitOutputStream avec flux réel |
+| US-C5 | AD | DONE | 🟩 | Implémenter PIFWriter (header + tables + pixels compressés) |
+| US-U6 | AD | DONE | 🟩 | Exporter une image en `.pif` |
+| US-P1 | AA | DONE | 🟩 | Finaliser affichage des fréquences |
+| US-P2 | AA | DONE | 🟩 | Finaliser affichage codes Huffman |
+| US-P3 | AA | DONE | 🟩 | Finaliser affichage codes canoniques |
+| US-U7 | AD | DONE | 🟩 | Implémenter l’aperçu (PreviewPanel) |
+
+## Fichiers à créer – Sprint 2
+
+### `src/mpif/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `PIFWriter.java` | Écriture du fichier `.pif` | US-C5, US-U6 |
+
+### `src/vconverter/` (complément)
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `SavePanel.java` en option a voir | Interface de sauvegarde `.pif` | US-U6 |
+
+
+---
+
+### Résultat attendu Sprint 2
+- `.pif` généré correctement
+- Convertisseur fonctionnel à 80 %
+- UI complète pour la consultation des tables
+- Aperçu image fonctionnel
+
+
+# ---------------------------------------
+# SPRINT 3 (27 décembre 2025 – 3 janvier 2026)
+# ---------------------------------------
+Objectif : Lecture du fichier `.pif` + visualisateur opérationnel
+
+| US | Assigné | Statut | | Description |
+|----------|---------|--------|-----|-------------|
+| US-D7 | AD | DONE | 🟩 | Reconstruire les codes canoniques depuis fichier |
+| US-D8 | AD | DONE | 🟩 | Décoder pixels (implémenter PIFReader) |
+| US-U1 | YB | TODO | 🟥 | Ouvrir `.pif` via argument ou JFileChooser |
+| US-U2 | AA | TODO | 🟥 | Afficher l’image dans une fenêtre |
+| US-U3 | AA | TODO | 🟥 | Centrer l’image si elle est petite |
+| US-U4 | AD | TODO | 🟥 | Déplacement de l’image à la souris |
+| US-P6 | AA | TODO | 🟥 | Préparer diagrammes UML |
+
+## Fichiers à créer – Sprint 3
+
+### `src/mpif/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `PIFReader.java` | Lecture et décodage du `.pif` | US-D7, US-D8 |
+
+### `src/vviewer/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `ViewerWindow.java` | Fenêtre visualisation | US-U2 |
+| `ImagePanel.java` | Affichage + déplacement image | US-U2, US-U3, US-U4 |
+
+### `src/`
+| Nom du fichier | Rôle | US |
+|----------------|-------|----|
+| `ViewerController.java` | Contrôle du visualisateur | US-U1..U4 |
+| `Viewer.java` | Programme principal du visualisateur | US-U1 |
+
+---
+
+### Résultat attendu Sprint 3
+- Visualisateur 100 % fonctionnel
+- Lecture complète du format `.pif`
+- Image affichée, centrée, déplaçable
+- UML structurel prêt pour rapport
+
+
+# ---------------------------------------
+# SPRINT 4 (3–10 janvier 2026)
+# ---------------------------------------
+Objectif : Finalisation complète + livrable final
+
+| US | Assigné | Statut | | Description |
+|------------|---------|--------|-----|-------------|
+| US-P5 | AD | TODO | 🟥 | Javadoc complète |
+| US-P7 | AD | TODO | 🟥 | Makefile (compilation + jar + exécution) |
+| US-P6 | AA | TODO | 🟥 | Finalisation des diagrammes UML |
+| Tests | YB | TODO | 🟥 | Tests convertisseur + visualisateur |
+| Rapport | AD/AA/YB| TODO | 🟥 | Rédaction rapport complet |
+| Livraison | AD/AA/YB| TODO | 🟥 | Vérification dépôt Gitea + JAR exécutables |
+
+## Fichiers à créer – Sprint 4
+
+### Racine creee tout au long
+| Nom du fichier | Rôle |
+|----------------|-------|
+| `Makefile` | Compilation + génération `.jar` |
+
+### `/doc`
+| Nom du fichier | Rôle |
+|----------------|-------|
+| `rapport.pdf` | Livrable final |
+| `UML/___.plantuml` | Sources de diagrammes |
+
+---
+
+### Résultat attendu Sprint 4
+- Rapport PDF validé
+- Diagrammes UML terminés
+- Makefile opérationnel
+- Projet soumis proprement sur Gitea
diff --git a/ProductBacklog.MD b/ProductBacklog.MD
index 7e3e48e..7e78c61 100644
--- a/ProductBacklog.MD
+++ b/ProductBacklog.MD
@@ -1,90 +1,90 @@
-# PRODUCT BACKLOG — Projet PIF
-
-
-# 1. US UTILISATEUR
-Ce sont les besoins réels d’un utilisateur final qui voudrait simplement visualiser ou convertir une image.
-
----
-
-### **US-U1 — Ouvrir un fichier PIF**
-En tant qu’utilisateur, je veux pouvoir ouvrir un fichier `.pif` via un argument ou un sélecteur de fichiers, afin d’afficher l’image.
-
-### **US-U2 — Afficher l’image dans une fenêtre**
-En tant qu’utilisateur, je veux voir l’image affichée dans une fenêtre redimensionnable.
-
-### **US-U3 — Centrage automatique**
-En tant qu’utilisateur, je veux que l’image soit centrée si elle est plus petite que la fenêtre, pour une meilleure visibilité.
-
-### **US-U4 — Déplacement de l’image**
-En tant qu’utilisateur, je veux pouvoir déplacer l’image à la souris si elle dépasse la taille de la fenêtre.
-
-### **US-U5 — Charger une image RGB (PNG/JPEG)**
-En tant qu’utilisateur, je veux charger une image standard afin de la convertir en `.pif`.
-
-### **US-U6 — Exporter une image au format PIF**
-En tant qu’utilisateur, je veux enregistrer l’image sous format `.pif`.
-
-### **US-U7 — Aperçu de l’image avant conversion**
-En tant qu’utilisateur, je veux voir une miniature de l’image chargée.
-
----
-
-# 2. US DÉVELOPPEUR
-Ce sont les besoins techniques indispensables au fonctionnement interne du format PIF.
-
----
-
-### **US-D1 — Lire des bits depuis un flux**
-Le système doit permettre la lecture bit par bit depuis un fichier PIF.
-
-### **US-D2 — Écrire des bits dans un fichier**
-Le système doit permettre l’écriture de bits pour générer un fichier PIF.
-
-### **US-D3 — Construire les tables de fréquences RGB**
-Le système doit analyser l’image pour obtenir les fréquences des valeurs R, G, B.
-
-### **US-D4 — Construire un arbre de Huffman**
-Le système doit créer un arbre à partir des fréquences d’une composante.
-
-### **US-D5 — Générer les codes Huffman**
-Le système doit produire les codes initiaux à partir de l’arbre.
-
-### **US-D6 — Générer les codes canoniques**
-Le système doit transformer les codes Huffman en codes canoniques.
-
-### **US-D7 — Reconstruire les codes canoniques en lecture**
-Le système doit pouvoir reconstruire les codes à partir des longueurs contenues dans le fichier .pif.
-
-### **US-D8 — Décoder un fichier PIF**
-Le système doit pouvoir reconstituer l’image RGB à partir des données compressées.
-
----
-
-# 3. US PROFESSEUR (PEDAGOGIQUE)
-Ces fonctionnalités n’ont **aucune utilité pour un utilisateur réel**, mais sont demandées par le professeur pour vérifier le bon fonctionnement de notre projet.
-
----
-
-### **US-P1 — Affichage des tables de fréquences**
-En tant que professeur, je veux consulter la table de fréquences R, G et B pour vérifier que le calcul est correct.
-
-### **US-P2 — Affichage des codes Huffman**
-En tant que professeur, je veux voir les codes Huffman générés afin de valider votre algorithme.
-
-### **US-P3 — Affichage des codes canoniques**
-En tant que professeur, je veux visualiser les codes canoniques afin d’évaluer votre compréhension de leur construction.
-
-### **US-P4 — Affichage de l’arbre Huffman (optionnel)**
-En tant que professeur, je veux pouvoir inspecter la structure de l’arbre pour vérifier votre implémentation.
-
-### **US-P5 — Documentation Javadoc pour chaque classe**
-En tant que professeur, je veux avoir une documentation claire auto-générable.
-
-### **US-P6 — Diagrammes UML dans le rapport**
-En tant que professeur, je veux retrouver un diagramme de classes et un diagramme d’objets dans le rapport.
-
-### **US-P7 — Makefile complet**
-En tant que professeur, je veux pouvoir compiler les deux programmes en .jar exécutables avec un Makefile clair.
-
-
-
+# PRODUCT BACKLOG — Projet PIF
+
+
+# 1. US UTILISATEUR
+Ce sont les besoins réels d’un utilisateur final qui voudrait simplement visualiser ou convertir une image.
+
+---
+
+### **US-U1 — Ouvrir un fichier PIF**
+En tant qu’utilisateur, je veux pouvoir ouvrir un fichier `.pif` via un argument ou un sélecteur de fichiers, afin d’afficher l’image.
+
+### **US-U2 — Afficher l’image dans une fenêtre**
+En tant qu’utilisateur, je veux voir l’image affichée dans une fenêtre redimensionnable.
+
+### **US-U3 — Centrage automatique**
+En tant qu’utilisateur, je veux que l’image soit centrée si elle est plus petite que la fenêtre, pour une meilleure visibilité.
+
+### **US-U4 — Déplacement de l’image**
+En tant qu’utilisateur, je veux pouvoir déplacer l’image à la souris si elle dépasse la taille de la fenêtre.
+
+### **US-U5 — Charger une image RGB (PNG/JPEG)**
+En tant qu’utilisateur, je veux charger une image standard afin de la convertir en `.pif`.
+
+### **US-U6 — Exporter une image au format PIF**
+En tant qu’utilisateur, je veux enregistrer l’image sous format `.pif`.
+
+### **US-U7 — Aperçu de l’image avant conversion**
+En tant qu’utilisateur, je veux voir une miniature de l’image chargée.
+
+---
+
+# 2. US DÉVELOPPEUR
+Ce sont les besoins techniques indispensables au fonctionnement interne du format PIF.
+
+---
+
+### **US-D1 — Lire des bits depuis un flux**
+Le système doit permettre la lecture bit par bit depuis un fichier PIF.
+
+### **US-D2 — Écrire des bits dans un fichier**
+Le système doit permettre l’écriture de bits pour générer un fichier PIF.
+
+### **US-D3 — Construire les tables de fréquences RGB**
+Le système doit analyser l’image pour obtenir les fréquences des valeurs R, G, B.
+
+### **US-D4 — Construire un arbre de Huffman**
+Le système doit créer un arbre à partir des fréquences d’une composante.
+
+### **US-D5 — Générer les codes Huffman**
+Le système doit produire les codes initiaux à partir de l’arbre.
+
+### **US-D6 — Générer les codes canoniques**
+Le système doit transformer les codes Huffman en codes canoniques.
+
+### **US-D7 — Reconstruire les codes canoniques en lecture**
+Le système doit pouvoir reconstruire les codes à partir des longueurs contenues dans le fichier .pif.
+
+### **US-D8 — Décoder un fichier PIF**
+Le système doit pouvoir reconstituer l’image RGB à partir des données compressées.
+
+---
+
+# 3. US PROFESSEUR (PEDAGOGIQUE)
+Ces fonctionnalités n’ont **aucune utilité pour un utilisateur réel**, mais sont demandées par le professeur pour vérifier le bon fonctionnement de notre projet.
+
+---
+
+### **US-P1 — Affichage des tables de fréquences**
+En tant que professeur, je veux consulter la table de fréquences R, G et B pour vérifier que le calcul est correct.
+
+### **US-P2 — Affichage des codes Huffman**
+En tant que professeur, je veux voir les codes Huffman générés afin de valider votre algorithme.
+
+### **US-P3 — Affichage des codes canoniques**
+En tant que professeur, je veux visualiser les codes canoniques afin d’évaluer votre compréhension de leur construction.
+
+### **US-P4 — Affichage de l’arbre Huffman (optionnel)**
+En tant que professeur, je veux pouvoir inspecter la structure de l’arbre pour vérifier votre implémentation.
+
+### **US-P5 — Documentation Javadoc pour chaque classe**
+En tant que professeur, je veux avoir une documentation claire auto-générable.
+
+### **US-P6 — Diagrammes UML dans le rapport**
+En tant que professeur, je veux retrouver un diagramme de classes et un diagramme d’objets dans le rapport.
+
+### **US-P7 — Makefile complet**
+En tant que professeur, je veux pouvoir compiler les deux programmes en .jar exécutables avec un Makefile clair.
+
+
+
diff --git a/README.md b/README.md
index 655dda9..49f7856 100644
--- a/README.md
+++ b/README.md
@@ -1,174 +1,174 @@
-# Projet : Primitive Image Format (PIF)
-
-## Description générale
-
-Ce projet consiste à implémenter un nouveau format d’image compressé sans perte, appelé **PIF (Primitive Image Format)**, inspiré du format JFIF.
-Il s'appuie sur la création de tables de fréquences, de codes de Huffman, de codes canoniques et sur la manipulation binaire afin de réduire la taille des images.
-
-Deux programmes Java doivent être développés :
-
-1. **Visualisateur PIF**
- Programme capable d’ouvrir un fichier `.pif` et d’afficher l’image dans une interface graphique.
-
-2. **Convertisseur vers PIF**
- Programme permettant de charger une image classique (ImageIO), de générer ses tables de fréquences et codes associés, puis de produire un fichier `.pif`.
-
-Ce travail doit être réalisé en binôme ou trinôme.
-
----
-
-## Deadline
-
-**Date limite de rendu : dimanche 11 janvier 2025 à 23h59.**
-Toutes les sources doivent être présentes sur le serveur Gitea du département dans un dépôt privé nommé **SAE32_2025**.
-
----
-
-## Fonctionnalités attendues
-
-### 1. Visualisateur `.pif`
-
-- Lecture du fichier `.pif` via argument de ligne de commande ou `JFileChooser`.
-- Décodage :
- - de l’en-tête (largeur, hauteur),
- - des trois tables canoniques (R, G, B),
- - des données binaires des pixels.
-- Affichage graphique sous Swing :
- - fenêtre redimensionnable,
- - image centrée si petite,
- - image déplaçable à la souris si trop grande.
-
----
-
-### 2. Convertisseur vers format `.pif`
-
-- Chargement d'une image via `ImageIO.read()`.
-- Extraction :
- - des tables de fréquences,
- - des codes Huffman initiaux,
- - des codes canoniques triés.
-- Affichage des tables pour inspection.
-- Génération du fichier `.pif` :
- - en-tête,
- - tables canoniques compactes,
- - données binaires des pixels encodés.
-- Le deuxième argument de ligne de commande peut définir le nom du fichier `.pif`.
-
----
-
-## Structure du format PIF
-
-Un fichier `.pif` contient trois sections :
-
-1. **En-tête (4 octets)**
- - largeur (2 octets)
- - hauteur (2 octets)
-
-2. **Tables canoniques (768 octets)**
- Trois tables successives de 256 octets : rouge, vert, bleu.
- Chaque octet indique la longueur du code canonique d’une valeur entre 0 et 255.
-
-3. **Section pixels (à partir du 773e octet)**
- Les trois composantes (R, G, B) sont encodées via leurs codes canoniques respectifs.
- Les bits sont packés de manière contiguë.
-
----
-
-## Processus de compression
-
-### 1. Création des tables de fréquences
-
-Pour R, G et B : compter combien de fois chaque valeur apparaît dans l’image.
-
-### 2. Construction des codes Huffman
-
-- Un arbre est construit par composante.
-- Les valeurs les plus fréquentes reçoivent les codes les plus courts.
-- Les codes initiaux peuvent varier en longueur.
-
-### 3. Génération des codes canoniques
-
-- Tri des valeurs par longueur de code puis par valeur.
-- Premier code : rempli de zéros.
-- Les suivants sont obtenus par incrément binaire.
-- Permet une reconstruction simple côté visualisateur.
-
----
-
-## Architecture logicielle
-
-Le projet doit inclure :
-
-- un package Java unique,
-- toutes les classes nécessaires au traitement :
- - gestion du fichier PIF,
- - lecture/écriture binaire,
- - génération des fréquences,
- - Huffman,
- - codes canoniques,
- - interface graphique,
- - programme principal du visualisateur,
- - programme principal du convertisseur.
-- un `Makefile` générant deux exécutables `.jar`.
-
----
-
-## Classes Java
-
-Les classes Java utilisées dans le projet :
-
-- [BitinputStream](src/fr/iutfbleau/sae/util/BitinputStream.java)
-- [BitOutputStream](src/fr/iutfbleau/sae/util/BitOutputStream.java)
-- [ByteUtils](src/fr/iutfbleau/sae/util/ByteUtils.java)
-- [CanonicalCode](src/fr/iutfbleau/sae/mhuffman/CanonicalCode.java)
-- [FrequencyTable](src/fr/iutfbleau/sae/mhuffman/FrequencyTable.java)
-- [HuffmanNode](src/fr/iutfbleau/sae/mhuffman/HuffmanNode.java)
-- [HuffmanTree](src/fr/iutfbleau/sae/mhuffman/HuffmanTree.java)
-
----
-
-## Rapport à produire
-
-Un rapport PDF doit contenir :
-
-- noms des membres du groupe,
-- introduction résumant le sujet,
-- description des fonctionnalités du programme,
-- captures d’écran,
-- diagrammes UML simplifiés,
-- explication du compresseur (Huffman, canoniques, structure du fichier),
-- explication du décompresseur,
-- conclusion personnelle de chaque membre.
-
-Le rapport ne doit pas contenir de code source.
-
----
-
-## Compilation et exécution
-
-### Visualisateur
- java -jar pif-viewer.jar chemin/image.pif
-
-
-### Convertisseur
- java -jar pif-converter.jar image.png sortie.pif
-
-
-Si aucun argument n’est fourni, un `JFileChooser` est ouvert.
-
----
-
-
-- Les commits, leur fréquence et la collaboration seront pris en compte dans la note.
-
----
-
-## Auteurs
-
-- Algassimou Pellel DIALLO
-- Ayoub ANHDIRE
-- Youness BOULALAME
-
-## Enseignant
-- Luc HERNANDEZ
-
+# Projet : Primitive Image Format (PIF)
+
+## Description générale
+
+Ce projet consiste à implémenter un nouveau format d’image compressé sans perte, appelé **PIF (Primitive Image Format)**, inspiré du format JFIF.
+Il s'appuie sur la création de tables de fréquences, de codes de Huffman, de codes canoniques et sur la manipulation binaire afin de réduire la taille des images.
+
+Deux programmes Java doivent être développés :
+
+1. **Visualisateur PIF**
+ Programme capable d’ouvrir un fichier `.pif` et d’afficher l’image dans une interface graphique.
+
+2. **Convertisseur vers PIF**
+ Programme permettant de charger une image classique (ImageIO), de générer ses tables de fréquences et codes associés, puis de produire un fichier `.pif`.
+
+Ce travail doit être réalisé en binôme ou trinôme.
+
+---
+
+## Deadline
+
+**Date limite de rendu : dimanche 11 janvier 2025 à 23h59.**
+Toutes les sources doivent être présentes sur le serveur Gitea du département dans un dépôt privé nommé **SAE32_2025**.
+
+---
+
+## Fonctionnalités attendues
+
+### 1. Visualisateur `.pif`
+
+- Lecture du fichier `.pif` via argument de ligne de commande ou `JFileChooser`.
+- Décodage :
+ - de l’en-tête (largeur, hauteur),
+ - des trois tables canoniques (R, G, B),
+ - des données binaires des pixels.
+- Affichage graphique sous Swing :
+ - fenêtre redimensionnable,
+ - image centrée si petite,
+ - image déplaçable à la souris si trop grande.
+
+---
+
+### 2. Convertisseur vers format `.pif`
+
+- Chargement d'une image via `ImageIO.read()`.
+- Extraction :
+ - des tables de fréquences,
+ - des codes Huffman initiaux,
+ - des codes canoniques triés.
+- Affichage des tables pour inspection.
+- Génération du fichier `.pif` :
+ - en-tête,
+ - tables canoniques compactes,
+ - données binaires des pixels encodés.
+- Le deuxième argument de ligne de commande peut définir le nom du fichier `.pif`.
+
+---
+
+## Structure du format PIF
+
+Un fichier `.pif` contient trois sections :
+
+1. **En-tête (4 octets)**
+ - largeur (2 octets)
+ - hauteur (2 octets)
+
+2. **Tables canoniques (768 octets)**
+ Trois tables successives de 256 octets : rouge, vert, bleu.
+ Chaque octet indique la longueur du code canonique d’une valeur entre 0 et 255.
+
+3. **Section pixels (à partir du 773e octet)**
+ Les trois composantes (R, G, B) sont encodées via leurs codes canoniques respectifs.
+ Les bits sont packés de manière contiguë.
+
+---
+
+## Processus de compression
+
+### 1. Création des tables de fréquences
+
+Pour R, G et B : compter combien de fois chaque valeur apparaît dans l’image.
+
+### 2. Construction des codes Huffman
+
+- Un arbre est construit par composante.
+- Les valeurs les plus fréquentes reçoivent les codes les plus courts.
+- Les codes initiaux peuvent varier en longueur.
+
+### 3. Génération des codes canoniques
+
+- Tri des valeurs par longueur de code puis par valeur.
+- Premier code : rempli de zéros.
+- Les suivants sont obtenus par incrément binaire.
+- Permet une reconstruction simple côté visualisateur.
+
+---
+
+## Architecture logicielle
+
+Le projet doit inclure :
+
+- un package Java unique,
+- toutes les classes nécessaires au traitement :
+ - gestion du fichier PIF,
+ - lecture/écriture binaire,
+ - génération des fréquences,
+ - Huffman,
+ - codes canoniques,
+ - interface graphique,
+ - programme principal du visualisateur,
+ - programme principal du convertisseur.
+- un `Makefile` générant deux exécutables `.jar`.
+
+---
+
+## Classes Java
+
+Les classes Java utilisées dans le projet :
+
+- [BitinputStream](src/fr/iutfbleau/sae/util/BitinputStream.java)
+- [BitOutputStream](src/fr/iutfbleau/sae/util/BitOutputStream.java)
+- [ByteUtils](src/fr/iutfbleau/sae/util/ByteUtils.java)
+- [CanonicalCode](src/fr/iutfbleau/sae/mhuffman/CanonicalCode.java)
+- [FrequencyTable](src/fr/iutfbleau/sae/mhuffman/FrequencyTable.java)
+- [HuffmanNode](src/fr/iutfbleau/sae/mhuffman/HuffmanNode.java)
+- [HuffmanTree](src/fr/iutfbleau/sae/mhuffman/HuffmanTree.java)
+
+---
+
+## Rapport à produire
+
+Un rapport PDF doit contenir :
+
+- noms des membres du groupe,
+- introduction résumant le sujet,
+- description des fonctionnalités du programme,
+- captures d’écran,
+- diagrammes UML simplifiés,
+- explication du compresseur (Huffman, canoniques, structure du fichier),
+- explication du décompresseur,
+- conclusion personnelle de chaque membre.
+
+Le rapport ne doit pas contenir de code source.
+
+---
+
+## Compilation et exécution
+
+### Visualisateur
+ java -jar pif-viewer.jar chemin/image.pif
+
+
+### Convertisseur
+ java -jar pif-converter.jar image.png sortie.pif
+
+
+Si aucun argument n’est fourni, un `JFileChooser` est ouvert.
+
+---
+
+
+- Les commits, leur fréquence et la collaboration seront pris en compte dans la note.
+
+---
+
+## Auteurs
+
+- Algassimou Pellel DIALLO
+- Ayoub ANHDIRE
+- Youness BOULALAME
+
+## Enseignant
+- Luc HERNANDEZ
+
diff --git a/build/fr/iutfbleau/sae/mhuffman/CanonicalCode$1.class b/build/fr/iutfbleau/sae/mhuffman/CanonicalCode$1.class
deleted file mode 100644
index c337e61889e52ea8e6ebdd76c3e09da2ed5d1e0e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1308
zcmX^0Z`VEs1_l=fOD+Z`24;2!79Ivx1~x_pfvm)`ME#t^ymWp4q^#8B5=I6#o6Nk-
z5<5l)W)00SZUznpPId+^9tLg(9!3UXu=3K9%p85+!~zx9ypp0yMh1@b)RM5ooYGWA
z1`!QSAFvuEBdysP_)u&LE-A{)OXp(XWf0(D5M&TyWMIol%}X!I0Nd#avQvbIK@?;m
zTY74VcWNa&gE%9DAi`kJypq)P)FLhhF$PH<1}Tsc9GQ6#M{qI7Fvzkq$nh}9Gbk`J
zXr>kEXO@
zGBW6DK)iwKRcow*nx50$MHWiYvpk^YAfK;X-
zxd=re*m6WbLTv|&lI$cqYeojH;QZ2}%zBC@{z{FflMPC^9fGFf%YPureq!
zFfphwFfhn6FfuTKM6|XuFm7aEU|?iWWnf@nV_;-pU=U|e14%J5Ff<4-FoN<2R97hj
z69X#)1A~M1P6l>Hh64<|+Zg!mbdWe^EV803^BFi~MOjkTXzyeY2B{L;#vq}C#7D)GTcV9k7|Y40;R<3@i-#42BFQ
cU=e)=Qw9bGAqFM}4hAy@3pP#$83r%_09NTljsO4v
diff --git a/build/fr/iutfbleau/sae/mhuffman/CanonicalCode.class b/build/fr/iutfbleau/sae/mhuffman/CanonicalCode.class
index 28611d91e041e60980889292fc70a7e7fc2d76c3..d49fd34a11fcff286d6906017dcebeb99be7fcfd 100644
GIT binary patch
delta 980
zcmX>u{9TCa)W2Q(7#J8_7|JGct!LDm{GVA`H9xl?u_&=5wY11NF)u$av#>Oki$REi
znTtV;L7bgIf`>tpL29xlqp!LQBZFX8Vp*boX-Q^|zDHtlhHqj47lSkdGdF`gg91B)
zA`gQSgYx9LjON^`3~KBQ>O2e@44RYgGpci_GHCNK=rHI`=3`P|)Ss-+BvG%&V93K@
z#GuQ_z?Pm`;+O9P!HkE&oWX*Tfg>}oBrGwfG?kHo
zSwqv4n?aAkiig1(WC%xkDnyE%!IqIh0PIk(F~KE8nR)443^ok*JPZyXQ`mA+^U`@r
zGC(Ff@h~_uxJ7FDdu3vU}Ru2VB}|G$l_wiX2@Y@
z$ORdkH`$WyHcJsBgX-jYY~qvk*##I&COfd}3WqWh
z1_m&XfdM2F$q>cB#1PKFz!=29%)rgSz_4UD1ACyi0ohx
z-OV5!xt&4QkJ*fc*^E_ZCxaX#gB6=3+fD`*MuwRTIUtr6Bf|j(Ju7xe_MHp{Ahz)~
z22(Q*5Z4mKwW;66U}wfD$|A}s%9>JP#v;nHgTc{?OOk6FgPkbnA_jg*u3ZeS3=CFW
z%m2UC73J8$;3>(qlfjpnA-si!QIyk+b@~4Zx*Hf67?>D9UU6d(WME(rVBljA1EnYi
zMFwdG6$Tjwbp|;GR|a{85C#Q?WCkUMTn1%^5(YJf`Z@-6hAsvzh8Yan46_+@80ItR
zGAv@yXNYECU3B8!2+1gbfKfsrARAqk|Jfq_92s#g$2?=}XzH4F?4$l5KS+LIZW8B)NmNM%T4
pU{GS8r$k%1#KuOuunr!+Mdura|UMVWc&Tnu&$jyw!bAXC_KQuESzN-{tu
zyYMi$GPq4%%%sca!NcIm;5GRglPiZQgAWgbFN5D?WoC7@03L=whM>s-%&u%9JPe@>
zVUuSw8!|?0KFiF?#KY~ET3q6rnwykbR6O|ttEhN3BLkBGBR?BME*C={Lq0o00m#_G
z$$waHvy?J2s7_8~6Q69)#>d9aP|nC82j#NYGcvIGgflQPfB*{vBLgb~
z8v`RlBm)D3ECVA0BLf2itJZb~#*GXN42%p>3=Cjt1_qGQXoeUDCXiCbU*ChF*3|#$N{l*7#R*Q7+A4OvhQRt00|SEq10RDpgA{`*gA#)pgDQhMg9d{pgBybuLnwnbLkfc~
zLmq=3Ln(tHLp_5LLpOsd!%PMOka50YSKFaRmAV_*>d&mhjghN1!#^l=Of3``9142%p3
z42h7iW>8~bVqj)qVBpi1*}>qwlOdRi;Q)gj$WO@eW6uE5o5aA#kj#(*)vF2BE2y1?
zta%%Q!y2^sb%yFsWnf`Q1G^%fA%g)D??Mcj3=9k$42%pp42%p}3`GnK3{nit3__qZ
X#K6E{z`(^&%*M%3!cfLg!5|3$951cM
diff --git a/build/fr/iutfbleau/sae/mhuffman/ComparateurCanonique.class b/build/fr/iutfbleau/sae/mhuffman/ComparateurCanonique.class
new file mode 100644
index 0000000000000000000000000000000000000000..abe71acbaf5e06aaaed65cbdf8143c891ceaba04
GIT binary patch
literal 1106
zcmX^0Z`VEs1_l=fLoNm;24;2!79Ivx1~x_pfvm)`ME#t^ymWp4q^#8B5=I6#o6Nk-
z5<5l)W)00SZUznpPId+^9tLg(9!3UXu=3K9%p85+!~zx9ypp0yMh1@b)RM5ooYGWA
z1`!QSAFvuEBdysP_)u&LE-A{)OXp(XWf0(D5M&TyWMIol%}X!I0Nd#avQvbIK@?;m
zTY74VcWNa&gE%9DAi`kJypq)P)FLhhF$PH<1}Tsc9GQ6#M{qI7Fvzkq$nh}9Gbk`J
z=%p3uXO@SX^n%@k2pOn@
zz@j9(($1QZfh#z_v?w{%EfW-&k}!jjBUBF*Ba93@o_TqxMb0^i#l@+`>>xFt9-c85lw7je!ZQw3LB~ft7)Q!9jZ`13M$b0S4Y}4E%OFNE|a3Sy7hx3>>ne
zEGcWWcQOcrREce4kkCP5r>tRMU|<5^!GN^%-s|z?X$TMg_oubOX%)rdRz`(1eeSm>O=Kur8HU?>sxu~w!WME+6WMW_t
gU;txgs99PJ+F&zv7<3sJ7`PZ188{g97!24r0V@0sAOHXW
literal 0
HcmV?d00001
diff --git a/makefile b/makefile
index 9a018f1..d0e1e42 100644
--- a/makefile
+++ b/makefile
@@ -1,166 +1,175 @@
-# Outils
-JAVAC = javac
-JAVA = java
-JAVADOC = javadoc
-ARGS =
-
-# Dossiers
-SRC = src
-BIN = build
-DOC = docjava
-LIB = lib/
-
-# Package
-PKG_PATH = fr/iutfbleau/sae
-
-# Points d’entrée
-MAIN_CONVERTER = fr.iutfbleau.sae.Convertisseur
-MAIN_VIEWER = fr.iutfbleau.sae.Viewer
-
-# Séparateur classpath
-ifeq ($(OS),Windows_NT)
- SEP = ;
-else
- SEP = :
-endif
-
-# Règle par défaut
-all: \
- $(BIN)/$(PKG_PATH)/Convertisseur.class \
- $(BIN)/$(PKG_PATH)/Viewer.class
-
-# Compilation des classes main
-$(BIN)/$(PKG_PATH)/Convertisseur.class: $(BIN) \
- $(BIN)/$(PKG_PATH)/ConverterController.class \
- $(BIN)/$(PKG_PATH)/vconverter/ConverterWindow.class \
- $(BIN)/$(PKG_PATH)/ExportButtonListener.class \
- $(SRC)/$(PKG_PATH)/Convertisseur.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/Convertisseur.java
-
-$(BIN)/$(PKG_PATH)/Viewer.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/Viewer.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/Viewer.java
-
-# Dossiers
-$(BIN):
- mkdir -p $(BIN)
-
-$(DOC):
- mkdir -p $(DOC)
-
-
-# Compilation des classes util
-$(BIN)/$(PKG_PATH)/util/ByteUtils.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/util/ByteUtils.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/util/ByteUtils.java
-
-$(BIN)/$(PKG_PATH)/util/BitInputStream.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/util/BitInputStream.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/util/BitInputStream.java
-
-$(BIN)/$(PKG_PATH)/util/BitOutputStream.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/util/BitOutputStream.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/util/BitOutputStream.java
-
-
-# Compilation des classes mhuffman
-$(BIN)/$(PKG_PATH)/mhuffman/CanonicalCode.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/mhuffman/CanonicalCode.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/CanonicalCode.java
-
-$(BIN)/$(PKG_PATH)/mhuffman/FrequencyTable.class: $(BIN) \
- $(BIN)/$(PKG_PATH)/mimage/RGBImage.class \
- $(SRC)/$(PKG_PATH)/mhuffman/FrequencyTable.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/FrequencyTable.java
-
-$(BIN)/$(PKG_PATH)/mhuffman/HuffmanNode.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/mhuffman/HuffmanNode.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/HuffmanNode.java
-
-$(BIN)/$(PKG_PATH)/mhuffman/HuffmanTree.class: $(BIN) \
- $(BIN)/$(PKG_PATH)/mhuffman/HuffmanNode.class \
- $(SRC)/$(PKG_PATH)/mhuffman/HuffmanTree.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/HuffmanTree.java
-
-
-# Compilation des classes mimage
-$(BIN)/$(PKG_PATH)/mimage/Pixel.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/mimage/Pixel.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mimage/Pixel.java
-
-$(BIN)/$(PKG_PATH)/mimage/RGBImage.class: $(BIN) \
- $(BIN)/$(PKG_PATH)/mimage/Pixel.class \
- $(SRC)/$(PKG_PATH)/mimage/RGBImage.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mimage/RGBImage.java
-
-
-# Interface graphique
-$(BIN)/$(PKG_PATH)/vconverter/ImagePreviewPanel.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/vconverter/ImagePreviewPanel.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/ImagePreviewPanel.java
-
-$(BIN)/$(PKG_PATH)/vconverter/FrequencyTablePanel.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/vconverter/FrequencyTablePanel.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/FrequencyTablePanel.java
-
-$(BIN)/$(PKG_PATH)/vconverter/CodeTablePanel.class: $(BIN) \
- $(SRC)/$(PKG_PATH)/vconverter/CodeTablePanel.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/CodeTablePanel.java
-
-# $(BIN)/$(PKG_PATH)/vconverter/ConverterWindow.class: $(BIN) \
-# $(BIN)/$(PKG_PATH)/ConverterController.class \
-# $(BIN)/$(PKG_PATH)/vconverter/ImagePreviewPanel.class \
-# $(BIN)/$(PKG_PATH)/vconverter/FrequencyTablePanel.class \
-# $(BIN)/$(PKG_PATH)/vconverter/CodeTablePanel.class \
-# $(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java
-# $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java
-
-
-
-# Compilation PIFWriter
-$(BIN)/$(PKG_PATH)/mpif/PIFWriter.class: $(BIN) \
- $(BIN)/$(PKG_PATH)/mimage/RGBImage.class \
- $(BIN)/$(PKG_PATH)/util/BitOutputStream.class \
- $(SRC)/$(PKG_PATH)/mpif/PIFWriter.java
- $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mpif/PIFWriter.java
-
-
-# GROSSE compilation du listener + ConvertController + ConvertWindow + PIFSaverTask car il y a une dependance cirulaire
-$(BIN)/$(PKG_PATH)/ConverterController.class \
-$(BIN)/$(PKG_PATH)/ExportButtonListener.class \
-$(BIN)/$(PKG_PATH)/PIFSaveTask.class \
-$(BIN)/$(PKG_PATH)/vconverter/ConverterWindow.class: \
-$(SRC)/$(PKG_PATH)/ConverterController.java \
-$(SRC)/$(PKG_PATH)/ExportButtonListener.java \
-$(SRC)/$(PKG_PATH)/PIFSaveTask.java \
-$(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java \
-$(BIN)/$(PKG_PATH)/mimage/Pixel.class \
-$(BIN)/$(PKG_PATH)/mimage/RGBImage.class \
-$(BIN)/$(PKG_PATH)/mhuffman/FrequencyTable.class \
-$(BIN)/$(PKG_PATH)/mhuffman/HuffmanTree.class \
-$(BIN)/$(PKG_PATH)/mhuffman/CanonicalCode.class \
-$(BIN)/$(PKG_PATH)/vconverter/ImagePreviewPanel.class \
-$(BIN)/$(PKG_PATH)/vconverter/FrequencyTablePanel.class \
-$(BIN)/$(PKG_PATH)/vconverter/CodeTablePanel.class \
-$(BIN)/$(PKG_PATH)/mpif/PIFWriter.class | $(BIN)
- @$(JAVAC) -cp $(BIN) -d $(BIN) \
- $(SRC)/$(PKG_PATH)/ConverterController.java \
- $(SRC)/$(PKG_PATH)/ExportButtonListener.java \
- $(SRC)/$(PKG_PATH)/PIFSaveTask.java \
- $(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java
-
-
-# Exécution
-run-conv: all
- $(JAVA) -cp $(BIN) $(MAIN_CONVERTER) $(ARGS)
-
-run-view: all
- $(JAVA) -cp $(BIN) $(MAIN_VIEWER)
-
-# Documentation
-doc: $(DOC)
- $(JAVADOC) -d $(DOC) $(SRC)/fr/iutfbleau/sae/**/*.java
-
-# Nettoyage
-clean:
- rm -rf $(BIN) $(DOC)
+# Outils
+JAVAC = javac
+JAVA = java
+JAVADOC = javadoc
+ARGS =
+
+# Dossiers
+SRC = src
+BIN = build
+DOC = docjava
+LIB = lib/
+
+# Package
+PKG_PATH = fr/iutfbleau/sae
+
+# Points d’entrée
+MAIN_CONVERTER = fr.iutfbleau.sae.Convertisseur
+MAIN_VIEWER = fr.iutfbleau.sae.Viewer
+
+# Séparateur classpath
+ifeq ($(OS),Windows_NT)
+ SEP = ;
+else
+ SEP = :
+endif
+
+# Règle par défaut
+all: \
+ $(BIN)/$(PKG_PATH)/Convertisseur.class \
+ $(BIN)/$(PKG_PATH)/Viewer.class
+
+# Compilation des classes main
+$(BIN)/$(PKG_PATH)/Convertisseur.class: $(BIN) \
+ $(BIN)/$(PKG_PATH)/ConverterController.class \
+ $(BIN)/$(PKG_PATH)/vconverter/ConverterWindow.class \
+ $(BIN)/$(PKG_PATH)/ExportButtonListener.class \
+ $(SRC)/$(PKG_PATH)/Convertisseur.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/Convertisseur.java
+
+$(BIN)/$(PKG_PATH)/Viewer.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/Viewer.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/Viewer.java
+
+# Dossiers
+$(BIN):
+ mkdir -p $(BIN)
+
+$(DOC):
+ mkdir -p $(DOC)
+
+
+# Compilation des classes util
+$(BIN)/$(PKG_PATH)/util/ByteUtils.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/util/ByteUtils.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/util/ByteUtils.java
+
+$(BIN)/$(PKG_PATH)/util/BitInputStream.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/util/BitInputStream.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/util/BitInputStream.java
+
+$(BIN)/$(PKG_PATH)/util/BitOutputStream.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/util/BitOutputStream.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/util/BitOutputStream.java
+
+
+# Compilation des classes mhuffman
+
+# Ajout de la classe ComparateurCanonique :
+$(BIN)/$(PKG_PATH)/mhuffman/ComparateurCanonique.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/mhuffman/ComparateurCanonique.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/ComparateurCanonique.java
+#
+
+
+$(BIN)/$(PKG_PATH)/mhuffman/CanonicalCode.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/mhuffman/CanonicalCode.java \
+ $(BIN)/$(PKG_PATH)/mhuffman/ComparateurCanonique.class
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/CanonicalCode.java
+
+$(BIN)/$(PKG_PATH)/mhuffman/FrequencyTable.class: $(BIN) \
+ $(BIN)/$(PKG_PATH)/mimage/RGBImage.class \
+ $(SRC)/$(PKG_PATH)/mhuffman/FrequencyTable.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/FrequencyTable.java
+
+$(BIN)/$(PKG_PATH)/mhuffman/HuffmanNode.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/mhuffman/HuffmanNode.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/HuffmanNode.java
+
+$(BIN)/$(PKG_PATH)/mhuffman/HuffmanTree.class: $(BIN) \
+ $(BIN)/$(PKG_PATH)/mhuffman/HuffmanNode.class \
+ $(SRC)/$(PKG_PATH)/mhuffman/HuffmanTree.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mhuffman/HuffmanTree.java
+
+
+# Compilation des classes mimage
+$(BIN)/$(PKG_PATH)/mimage/Pixel.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/mimage/Pixel.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mimage/Pixel.java
+
+$(BIN)/$(PKG_PATH)/mimage/RGBImage.class: $(BIN) \
+ $(BIN)/$(PKG_PATH)/mimage/Pixel.class \
+ $(SRC)/$(PKG_PATH)/mimage/RGBImage.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mimage/RGBImage.java
+
+
+# Interface graphique
+$(BIN)/$(PKG_PATH)/vconverter/ImagePreviewPanel.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/vconverter/ImagePreviewPanel.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/ImagePreviewPanel.java
+
+$(BIN)/$(PKG_PATH)/vconverter/FrequencyTablePanel.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/vconverter/FrequencyTablePanel.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/FrequencyTablePanel.java
+
+$(BIN)/$(PKG_PATH)/vconverter/CodeTablePanel.class: $(BIN) \
+ $(SRC)/$(PKG_PATH)/vconverter/CodeTablePanel.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/CodeTablePanel.java
+
+# $(BIN)/$(PKG_PATH)/vconverter/ConverterWindow.class: $(BIN) \
+# $(BIN)/$(PKG_PATH)/ConverterController.class \
+# $(BIN)/$(PKG_PATH)/vconverter/ImagePreviewPanel.class \
+# $(BIN)/$(PKG_PATH)/vconverter/FrequencyTablePanel.class \
+# $(BIN)/$(PKG_PATH)/vconverter/CodeTablePanel.class \
+# $(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java
+# $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java
+
+
+
+# Compilation PIFWriter
+$(BIN)/$(PKG_PATH)/mpif/PIFWriter.class: $(BIN) \
+ $(BIN)/$(PKG_PATH)/mimage/RGBImage.class \
+ $(BIN)/$(PKG_PATH)/util/BitOutputStream.class \
+ $(SRC)/$(PKG_PATH)/mpif/PIFWriter.java
+ $(JAVAC) -cp $(BIN) -d $(BIN) $(SRC)/$(PKG_PATH)/mpif/PIFWriter.java
+
+
+# GROSSE compilation du listener + ConvertController + ConvertWindow + PIFSaverTask car il y a une dependance cirulaire
+$(BIN)/$(PKG_PATH)/ConverterController.class \
+$(BIN)/$(PKG_PATH)/ExportButtonListener.class \
+$(BIN)/$(PKG_PATH)/PIFSaveTask.class \
+$(BIN)/$(PKG_PATH)/vconverter/ConverterWindow.class: \
+$(SRC)/$(PKG_PATH)/ConverterController.java \
+$(SRC)/$(PKG_PATH)/ExportButtonListener.java \
+$(SRC)/$(PKG_PATH)/PIFSaveTask.java \
+$(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java \
+$(BIN)/$(PKG_PATH)/mimage/Pixel.class \
+$(BIN)/$(PKG_PATH)/mimage/RGBImage.class \
+$(BIN)/$(PKG_PATH)/mhuffman/FrequencyTable.class \
+$(BIN)/$(PKG_PATH)/mhuffman/HuffmanTree.class \
+$(BIN)/$(PKG_PATH)/mhuffman/CanonicalCode.class \
+$(BIN)/$(PKG_PATH)/vconverter/ImagePreviewPanel.class \
+$(BIN)/$(PKG_PATH)/vconverter/FrequencyTablePanel.class \
+$(BIN)/$(PKG_PATH)/vconverter/CodeTablePanel.class \
+$(BIN)/$(PKG_PATH)/mpif/PIFWriter.class | $(BIN)
+ @$(JAVAC) -cp $(BIN) -d $(BIN) \
+ $(SRC)/$(PKG_PATH)/ConverterController.java \
+ $(SRC)/$(PKG_PATH)/ExportButtonListener.java \
+ $(SRC)/$(PKG_PATH)/PIFSaveTask.java \
+ $(SRC)/$(PKG_PATH)/vconverter/ConverterWindow.java
+
+
+# Exécution
+run-conv: all
+ $(JAVA) -cp $(BIN) $(MAIN_CONVERTER) $(ARGS)
+
+run-view: all
+ $(JAVA) -cp $(BIN) $(MAIN_VIEWER)
+
+# Documentation
+doc: $(DOC)
+ $(JAVADOC) -d $(DOC) $(SRC)/fr/iutfbleau/sae/**/*.java
+
+# Nettoyage
+clean:
+ rm -rf $(BIN) $(DOC)
diff --git a/src/fr/iutfbleau/sae/ConverterController.java b/src/fr/iutfbleau/sae/ConverterController.java
index 82d7ae8..82db243 100644
--- a/src/fr/iutfbleau/sae/ConverterController.java
+++ b/src/fr/iutfbleau/sae/ConverterController.java
@@ -1,225 +1,225 @@
-package fr.iutfbleau.sae;
-import fr.iutfbleau.sae.mhuffman.*;
-import fr.iutfbleau.sae.mimage.*;
-import fr.iutfbleau.sae.mpif.PIFWriter;
-import fr.iutfbleau.sae.vconverter.ConverterWindow;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.util.Map;
-import javax.imageio.ImageIO;
-import javax.swing.JFileChooser;
-import javax.swing.JOptionPane;
-
-/**
- * Contrôleur pour la conversion d'images.
- *
- * Cette classe gère le chargement des fichiers image et les opérations
- * de conversion associées. tel que
- *
- *
- */
-public class ConverterController {
- // Image convertie en RGBImage
- private RGBImage image;
-
- // Table de fréquences pour chaque composante
- private FrequencyTable frequencyTable;
-
- // Arbres de Huffman pour chaque composante
- private Map abrHuffmanR;
- private Map abrHuffmanG;
- private Map abrHuffmanB;
-
- // Codes canoniques pour chaque composante
- private Map canonRED;
- private Map canonGREEN;
- private Map canonBLUE;
-
- // La fenêtre du convertisseur
- private ConverterWindow fen;
-
- String outputPath;
- String inputPath;
-
- public ConverterController(ConverterWindow fen, String in, String out) {
- this.fen = fen;
- this.outputPath = out;
- this.inputPath = in;
-
- System.out.println(this.inputPath+" ==> "+this.outputPath);
- }
-
-
- // charger une image depuis un fichier avec bufferedImage et la convertir en RGBImage
- public void loadImage(File file) {
- try{
- // Lire l'image avec BufferedImage
- BufferedImage buffimage = ImageIO.read(file);
- if (buffimage == null) {
- throw new IllegalArgumentException("Le fichier spécifié n'est pas une image valide.");
- }
-
- int w = buffimage.getWidth();
- int h = buffimage.getHeight();
-
- // Créer une RGBImage de la même taille
- this.image = new RGBImage(w, h);
- // remplir la RGBImage avec les pixels de BufferedImage
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- int rgb = buffimage.getRGB(x, y); // obtenir la valeur RGB du pixel
- // Extraire les composantes R, G, B
- int r = (rgb >> 16) & 0xFF;
- int g = (rgb >> 8) & 0xFF;
- int b = rgb & 0xFF;
- // Créer un pixel et l'ajouter à la RGBImage
- this.image.setPixel(x, y, new Pixel(r, g, b));
- }
- }
-
- // Mettre à jour le GUI
- this.fen.setImagePreview(buffimage);
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-
- public void computeFrequencies() {
-
- if (this.image == null) {
- System.err.println("Aucune image chargée pour le calcul des fréquences.");
- return;
- }
-
- this.frequencyTable = new FrequencyTable();
- this.frequencyTable.computeFromImage(this.image);
-
- // Mettre à jour le GUI avec les fréquences
- int[] freqR = this.frequencyTable.getRed();
- int[] freqG = this.frequencyTable.getGreen();
- int[] freqB = this.frequencyTable.getBlue();
- this.fen.setFrequencyTable(freqR, freqG, freqB);
- }
-
-
- public void computeHuffman() {
-
- if (this.frequencyTable == null) {
- System.err.println("Les fréquences ne sont pas encore calculées.");
- return;
- }
-
- // Génération des arbres de Huffman pour chaque composante et stockage des codes
- HuffmanTree arbreR = new HuffmanTree(this.frequencyTable.getRed());
- this.abrHuffmanR = arbreR.generateCodes();
- HuffmanTree arbreG = new HuffmanTree(this.frequencyTable.getGreen());
- this.abrHuffmanG = arbreG.generateCodes();
- HuffmanTree arbreB = new HuffmanTree(this.frequencyTable.getBlue());
- this.abrHuffmanB = arbreB.generateCodes();
-
- // Mettre à jour le GUI avec les codes de Huffman
- this.fen.setHuffmanTable(this.abrHuffmanR, this.abrHuffmanG, this.abrHuffmanB);
- }
-
- public void computeCanonical() {
- if (this.abrHuffmanR == null || this.abrHuffmanG == null || this.abrHuffmanB == null) {
- System.err.println("Les codes de Huffman doivent être générés d'abord.");
- return;
- }
-
- CanonicalCode codeCanoniques = new CanonicalCode();
- this.canonRED = codeCanoniques.generateCodes(this.abrHuffmanR);
- this.canonGREEN = codeCanoniques.generateCodes(this.abrHuffmanG);
- this.canonBLUE = codeCanoniques.generateCodes(this.abrHuffmanB);
-
- // Mettre à jour le GUI avec les codes canoniques
- this.fen.setCanonicalTable(this.canonRED, this.canonGREEN, this.canonBLUE);
-
- }
-
- public void saveAsPIF(String pathfile) {
- // je Vérifie que l'image et les codes canoniques sont disponibles
- if(this.image == null || this.canonRED == null){
- System.err.println("Impossible d'ecrire le fichier PIF : données manquantes.");
- return;
- }
-
- try {
- PIFWriter ecriveur = new PIFWriter();
- ecriveur.writeTOFile(pathfile, this.image, this.canonRED, this.canonGREEN, this.canonBLUE);
- } catch (Exception e) {
- System.err.println("Erreur lors de l’écriture du fichier .pif : " + pathfile);
- }
- }
-
-
- public void saveViaBtn() {
- try {
- if (outputPath != null) {
- saveAsPIF(outputPath);
- System.out.println("Sauvegarde dans : " + outputPath);
- return;
- }
-
- // Sinon JFileChooser
- JFileChooser chooser = new JFileChooser();
- chooser.setDialogTitle("Enregistrer le fichier .pif");
-
- if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
- //saveAsPIF(chooser.getSelectedFile().getAbsolutePath());
- // On lance la sauvegarde lourde dans un thread séparé
- new Thread(() -> saveAsPIF(chooser.getSelectedFile().getAbsolutePath())).start();
- System.out.println("Fichier sauvegardé : " + chooser.getSelectedFile().getAbsolutePath());
- JOptionPane.showMessageDialog(null, "Fichier sauvegardé avec succès : " + chooser.getSelectedFile().getName());
- }
-
- System.out.println("Via BTN Sauvegarde terminée.");
-
- } catch (Exception ex) {
- System.out.println("Erreur lors de la sauvegarde : " + ex.getMessage());
- }
- }
-
- public void StartconvessionProcess(){
- // chragement
- if (this.inputPath != null) {
- // Chargement direct depuis les arguments
- File file = new File(inputPath);
- this.loadImage(file);
- }else{
- // Sinon JFileChooser pour choisir l'image
- JFileChooser choosser =new JFileChooser();
- choosser.setDialogTitle("Choisissez une image");
- if (choosser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
- this.loadImage(choosser.getSelectedFile());
- }else {
- System.err.println("Aucune image choisie. Arrêt du programme.");
- System.exit(1);
- return;
- }
- }
-
- // je place la logique de conversion dans lordre
- computeFrequencies();
- computeHuffman();
- computeCanonical();
-
- // Sauvegarder: un second argument est donné sauvegarde automatique
- if (this.outputPath != null) {
- this.saveAsPIF(this.outputPath);
- System.out.println("Fichier sauvegardé automatiquement : " + this.outputPath);
- }else{
- // pas de deuxième argument j'ajoute un boutton pour choisir avec un jfilechoser
- fen.addSaveButton(this);
- }
- }
-
-
-
-
- public RGBImage getImage(){
- return this.image;
- }
-}
+package fr.iutfbleau.sae;
+import fr.iutfbleau.sae.mhuffman.*;
+import fr.iutfbleau.sae.mimage.*;
+import fr.iutfbleau.sae.mpif.PIFWriter;
+import fr.iutfbleau.sae.vconverter.ConverterWindow;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.Map;
+import javax.imageio.ImageIO;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+/**
+ * Contrôleur pour la conversion d'images.
+ *
+ * Cette classe gère le chargement des fichiers image et les opérations
+ * de conversion associées. tel que
+ *
+ *
+ */
+public class ConverterController {
+ // Image convertie en RGBImage
+ private RGBImage image;
+
+ // Table de fréquences pour chaque composante
+ private FrequencyTable frequencyTable;
+
+ // Arbres de Huffman pour chaque composante
+ private Map abrHuffmanR;
+ private Map abrHuffmanG;
+ private Map abrHuffmanB;
+
+ // Codes canoniques pour chaque composante
+ private Map canonRED;
+ private Map canonGREEN;
+ private Map canonBLUE;
+
+ // La fenêtre du convertisseur
+ private ConverterWindow fen;
+
+ String outputPath;
+ String inputPath;
+
+ public ConverterController(ConverterWindow fen, String in, String out) {
+ this.fen = fen;
+ this.outputPath = out;
+ this.inputPath = in;
+
+ System.out.println(this.inputPath+" ==> "+this.outputPath);
+ }
+
+
+ // charger une image depuis un fichier avec bufferedImage et la convertir en RGBImage
+ public void loadImage(File file) {
+ try{
+ // Lire l'image avec BufferedImage
+ BufferedImage buffimage = ImageIO.read(file);
+ if (buffimage == null) {
+ throw new IllegalArgumentException("Le fichier spécifié n'est pas une image valide.");
+ }
+
+ int w = buffimage.getWidth();
+ int h = buffimage.getHeight();
+
+ // Créer une RGBImage de la même taille
+ this.image = new RGBImage(w, h);
+ // remplir la RGBImage avec les pixels de BufferedImage
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int rgb = buffimage.getRGB(x, y); // obtenir la valeur RGB du pixel
+ // Extraire les composantes R, G, B
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = rgb & 0xFF;
+ // Créer un pixel et l'ajouter à la RGBImage
+ this.image.setPixel(x, y, new Pixel(r, g, b));
+ }
+ }
+
+ // Mettre à jour le GUI
+ this.fen.setImagePreview(buffimage);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ public void computeFrequencies() {
+
+ if (this.image == null) {
+ System.err.println("Aucune image chargée pour le calcul des fréquences.");
+ return;
+ }
+
+ this.frequencyTable = new FrequencyTable();
+ this.frequencyTable.computeFromImage(this.image);
+
+ // Mettre à jour le GUI avec les fréquences
+ int[] freqR = this.frequencyTable.getRed();
+ int[] freqG = this.frequencyTable.getGreen();
+ int[] freqB = this.frequencyTable.getBlue();
+ this.fen.setFrequencyTable(freqR, freqG, freqB);
+ }
+
+
+ public void computeHuffman() {
+
+ if (this.frequencyTable == null) {
+ System.err.println("Les fréquences ne sont pas encore calculées.");
+ return;
+ }
+
+ // Génération des arbres de Huffman pour chaque composante et stockage des codes
+ HuffmanTree arbreR = new HuffmanTree(this.frequencyTable.getRed());
+ this.abrHuffmanR = arbreR.generateCodes();
+ HuffmanTree arbreG = new HuffmanTree(this.frequencyTable.getGreen());
+ this.abrHuffmanG = arbreG.generateCodes();
+ HuffmanTree arbreB = new HuffmanTree(this.frequencyTable.getBlue());
+ this.abrHuffmanB = arbreB.generateCodes();
+
+ // Mettre à jour le GUI avec les codes de Huffman
+ this.fen.setHuffmanTable(this.abrHuffmanR, this.abrHuffmanG, this.abrHuffmanB);
+ }
+
+ public void computeCanonical() {
+ if (this.abrHuffmanR == null || this.abrHuffmanG == null || this.abrHuffmanB == null) {
+ System.err.println("Les codes de Huffman doivent être générés d'abord.");
+ return;
+ }
+
+ CanonicalCode codeCanoniques = new CanonicalCode();
+ this.canonRED = codeCanoniques.generateCodes(this.abrHuffmanR);
+ this.canonGREEN = codeCanoniques.generateCodes(this.abrHuffmanG);
+ this.canonBLUE = codeCanoniques.generateCodes(this.abrHuffmanB);
+
+ // Mettre à jour le GUI avec les codes canoniques
+ this.fen.setCanonicalTable(this.canonRED, this.canonGREEN, this.canonBLUE);
+
+ }
+
+ public void saveAsPIF(String pathfile) {
+ // je Vérifie que l'image et les codes canoniques sont disponibles
+ if(this.image == null || this.canonRED == null){
+ System.err.println("Impossible d'ecrire le fichier PIF : données manquantes.");
+ return;
+ }
+
+ try {
+ PIFWriter ecriveur = new PIFWriter();
+ ecriveur.writeTOFile(pathfile, this.image, this.canonRED, this.canonGREEN, this.canonBLUE);
+ } catch (Exception e) {
+ System.err.println("Erreur lors de l’écriture du fichier .pif : " + pathfile);
+ }
+ }
+
+
+ public void saveViaBtn() {
+ try {
+ if (outputPath != null) {
+ saveAsPIF(outputPath);
+ System.out.println("Sauvegarde dans : " + outputPath);
+ return;
+ }
+
+ // Sinon JFileChooser
+ JFileChooser chooser = new JFileChooser();
+ chooser.setDialogTitle("Enregistrer le fichier .pif");
+
+ if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
+ //saveAsPIF(chooser.getSelectedFile().getAbsolutePath());
+ // On lance la sauvegarde lourde dans un thread séparé
+ new Thread(() -> saveAsPIF(chooser.getSelectedFile().getAbsolutePath())).start();
+ System.out.println("Fichier sauvegardé : " + chooser.getSelectedFile().getAbsolutePath());
+ JOptionPane.showMessageDialog(null, "Fichier sauvegardé avec succès : " + chooser.getSelectedFile().getName());
+ }
+
+ System.out.println("Via BTN Sauvegarde terminée.");
+
+ } catch (Exception ex) {
+ System.out.println("Erreur lors de la sauvegarde : " + ex.getMessage());
+ }
+ }
+
+ public void StartconvessionProcess(){
+ // chragement
+ if (this.inputPath != null) {
+ // Chargement direct depuis les arguments
+ File file = new File(inputPath);
+ this.loadImage(file);
+ }else{
+ // Sinon JFileChooser pour choisir l'image
+ JFileChooser choosser =new JFileChooser();
+ choosser.setDialogTitle("Choisissez une image");
+ if (choosser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
+ this.loadImage(choosser.getSelectedFile());
+ }else {
+ System.err.println("Aucune image choisie. Arrêt du programme.");
+ System.exit(1);
+ return;
+ }
+ }
+
+ // je place la logique de conversion dans lordre
+ computeFrequencies();
+ computeHuffman();
+ computeCanonical();
+
+ // Sauvegarder: un second argument est donné sauvegarde automatique
+ if (this.outputPath != null) {
+ this.saveAsPIF(this.outputPath);
+ System.out.println("Fichier sauvegardé automatiquement : " + this.outputPath);
+ }else{
+ // pas de deuxième argument j'ajoute un boutton pour choisir avec un jfilechoser
+ fen.addSaveButton(this);
+ }
+ }
+
+
+
+
+ public RGBImage getImage(){
+ return this.image;
+ }
+}
diff --git a/src/fr/iutfbleau/sae/Convertisseur.java b/src/fr/iutfbleau/sae/Convertisseur.java
index 8d7a19d..717df12 100644
--- a/src/fr/iutfbleau/sae/Convertisseur.java
+++ b/src/fr/iutfbleau/sae/Convertisseur.java
@@ -1,25 +1,25 @@
-package fr.iutfbleau.sae;
-
-import fr.iutfbleau.sae.vconverter.ConverterWindow;
-
-public class Convertisseur {
- public static void main(String[] args) {
-
- // chemins de l'image
- String inpuPath = null;
- String outputPath = null;
- if (args.length >= 1) {
- inpuPath = args[0];
- }
- if (args.length >= 2) {
- outputPath = args[1];
- }
-
- // Créer et stocker la référence à la fenêtre
- ConverterWindow window = new ConverterWindow();
- // je la passe au controleur
- ConverterController controller = new ConverterController(window, inpuPath, outputPath);
-
- controller.StartconvessionProcess();
- }
+package fr.iutfbleau.sae;
+
+import fr.iutfbleau.sae.vconverter.ConverterWindow;
+
+public class Convertisseur {
+ public static void main(String[] args) {
+
+ // chemins de l'image
+ String inpuPath = null;
+ String outputPath = null;
+ if (args.length >= 1) {
+ inpuPath = args[0];
+ }
+ if (args.length >= 2) {
+ outputPath = args[1];
+ }
+
+ // Créer et stocker la référence à la fenêtre
+ ConverterWindow window = new ConverterWindow();
+ // je la passe au controleur
+ ConverterController controller = new ConverterController(window, inpuPath, outputPath);
+
+ controller.StartconvessionProcess();
+ }
}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/ExportButtonListener.java b/src/fr/iutfbleau/sae/ExportButtonListener.java
index bffc4e5..ebb3223 100644
--- a/src/fr/iutfbleau/sae/ExportButtonListener.java
+++ b/src/fr/iutfbleau/sae/ExportButtonListener.java
@@ -1,17 +1,17 @@
-package fr.iutfbleau.sae;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import javax.swing.SwingUtilities;
-
-public class ExportButtonListener implements ActionListener {
- private PIFSaveTask saveTask;
-
- public ExportButtonListener(ConverterController controller){
- this.saveTask = new PIFSaveTask(controller);
- }
-
- @Override
- public void actionPerformed(ActionEvent e){
- SwingUtilities.invokeLater(saveTask);
- }
-}
+package fr.iutfbleau.sae;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.SwingUtilities;
+
+public class ExportButtonListener implements ActionListener {
+ private PIFSaveTask saveTask;
+
+ public ExportButtonListener(ConverterController controller){
+ this.saveTask = new PIFSaveTask(controller);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e){
+ SwingUtilities.invokeLater(saveTask);
+ }
+}
diff --git a/src/fr/iutfbleau/sae/PIFSaveTask.java b/src/fr/iutfbleau/sae/PIFSaveTask.java
index a3ed696..ad8cd2b 100644
--- a/src/fr/iutfbleau/sae/PIFSaveTask.java
+++ b/src/fr/iutfbleau/sae/PIFSaveTask.java
@@ -1,13 +1,13 @@
-package fr.iutfbleau.sae;
-public class PIFSaveTask implements Runnable{
- private ConverterController controller;
-
- public PIFSaveTask(ConverterController c) {
- this.controller = c;
- }
-
- @Override
- public void run() {
- controller.saveViaBtn();
- }
-}
+package fr.iutfbleau.sae;
+public class PIFSaveTask implements Runnable{
+ private ConverterController controller;
+
+ public PIFSaveTask(ConverterController c) {
+ this.controller = c;
+ }
+
+ @Override
+ public void run() {
+ controller.saveViaBtn();
+ }
+}
diff --git a/src/fr/iutfbleau/sae/Viewer.java b/src/fr/iutfbleau/sae/Viewer.java
index 8a8f41a..1e90e89 100644
--- a/src/fr/iutfbleau/sae/Viewer.java
+++ b/src/fr/iutfbleau/sae/Viewer.java
@@ -1,7 +1,7 @@
-package fr.iutfbleau.sae;
-
-public class Viewer {
- public static void main(String[] args) {
- System.out.println("dqkdjqkdjqkdjqkdjqkdj");
- }
-}
+package fr.iutfbleau.sae;
+
+public class Viewer {
+ public static void main(String[] args) {
+ System.out.println("dqkdjqkdjqkdjqkdjqkdj");
+ }
+}
diff --git a/src/fr/iutfbleau/sae/mhuffman/CanonicalCode.java b/src/fr/iutfbleau/sae/mhuffman/CanonicalCode.java
index 06a2497..af8c9f7 100644
--- a/src/fr/iutfbleau/sae/mhuffman/CanonicalCode.java
+++ b/src/fr/iutfbleau/sae/mhuffman/CanonicalCode.java
@@ -1,72 +1,61 @@
-package fr.iutfbleau.sae.mhuffman;
-import java.util.*;
-
-public class CanonicalCode{
- //private Map codeLengths = new HashMap<>();
- //private Map canonicalCodes = new HashMap<>();
-
-
-
- public Map generateCodes(Map codesHuffman){
- // 1 ere chose à faire : on regarde uniquement la longueur des codes initiaux(Huffman)
- // 2eme chose à faire : remettre dans l'ordre des longueurs : si meme taille ==> regarder valeur
- // 3eme chose à faire : ecriture des codes canoniques
-
- // on recupere les entrées des codes Huffman pour pouvoir les triés
- List> liste = new ArrayList<>(codesHuffman.entrySet());
-
- // ici on comparer par longueur de la valeur ou sinon par la clé
-
-
-
- // =============== ATTENTION CLASSE ANONYME !!!!!!! ================
- Collections.sort(liste, new Comparator>() {
- @Override
- public int compare(Map.Entry arg1 ,Map.Entry arg2) {
-
- int length1 = arg1.getValue().length();
- int length2 = arg2.getValue().length();
-
- if (length1 != length2) {
- return length1 - length2;
- }
- return arg1.getKey() - arg2.getKey();
- }
- });
-
- Map canonicalCodes = new HashMap<>();
- int code = 0; // code canonique à attribuer
- int temp = 0; //garde la longueur du code précedent , pour gérer le décalage
-
- for (Map.Entry entree : liste ) {
- int valeur = entree.getKey(); // symbole actuel
- int longueur = entree.getValue().length(); // longueur du code actuel
-
- code <<= (longueur - temp); // permet de décaler le code actuel si la longueur augmente
- String codeBinaire = Integer.toBinaryString(code);
-
- // permet d'ajouter des zeros si nécessaire !!!
- while (codeBinaire.length() < longueur) {
- codeBinaire = "0" + codeBinaire;
- }
-
- canonicalCodes.put(valeur,codeBinaire); // ajout dans le dictionnaire
- code++; // incrémentation pour la valeur qui suit
- temp = longueur; // mise à jour de la longueur précedente
- }
-
- return canonicalCodes;
- }
-
-
- public String getCode(Map canonicalCodes,int value){
- return canonicalCodes.get(value);
- }
-
- public int getLength(Map codesH,int value){
- return codesH.get(value).length();
- }
-
-}
-
-
+package fr.iutfbleau.sae.mhuffman;
+import java.util.*;
+
+public class CanonicalCode{
+ //private Map codeLengths = new HashMap<>();
+ //private Map canonicalCodes = new HashMap<>();
+
+
+
+ public Map generateCodes(Map codesHuffman){
+ // 1 ere chose à faire : on regarde uniquement la longueur des codes initiaux(Huffman)
+ // 2eme chose à faire : remettre dans l'ordre des longueurs : si meme taille ==> regarder valeur
+ // 3eme chose à faire : ecriture des codes canoniques
+
+ // on recupere les entrées des codes Huffman pour pouvoir les triés
+ List> liste = new ArrayList<>(codesHuffman.entrySet());
+
+ // ici on comparer par longueur de la valeur ou sinon par la clé
+
+
+
+ Collections.sort(liste, new ComparateurCanonique());
+
+
+
+ Map canonicalCodes = new HashMap<>();
+ int code = 0; // code canonique à attribuer
+ int temp = 0; //garde la longueur du code précedent , pour gérer le décalage
+
+ for (Map.Entry entree : liste ) {
+ int valeur = entree.getKey(); // symbole actuel
+ int longueur = entree.getValue().length(); // longueur du code actuel
+
+ code <<= (longueur - temp); // permet de décaler le code actuel si la longueur augmente
+ String codeBinaire = Integer.toBinaryString(code);
+
+ // permet d'ajouter des zeros si nécessaire !!!
+ while (codeBinaire.length() < longueur) {
+ codeBinaire = "0" + codeBinaire;
+ }
+
+ canonicalCodes.put(valeur,codeBinaire); // ajout dans le dictionnaire
+ code++; // incrémentation pour la valeur qui suit
+ temp = longueur; // mise à jour de la longueur précedente
+ }
+
+ return canonicalCodes;
+ }
+
+
+ public String getCode(Map canonicalCodes,int value){
+ return canonicalCodes.get(value);
+ }
+
+ public int getLength(Map codesH,int value){
+ return codesH.get(value).length();
+ }
+
+}
+
+
diff --git a/src/fr/iutfbleau/sae/mhuffman/ComparateurCanonique.java b/src/fr/iutfbleau/sae/mhuffman/ComparateurCanonique.java
new file mode 100644
index 0000000..a493726
--- /dev/null
+++ b/src/fr/iutfbleau/sae/mhuffman/ComparateurCanonique.java
@@ -0,0 +1,19 @@
+package fr.iutfbleau.sae.mhuffman;
+import java.util.Comparator;
+import java.util.Map;
+
+public class ComparateurCanonique implements Comparator> {
+
+ @Override
+ public int compare(Map.Entry entree1,Map.Entry entree2) {
+
+ int longueur1 = entree1.getValue().length();
+ int longueur2 = entree2.getValue().length();
+
+ if (longueur1 != longueur2) {
+ return longueur1 - longueur2;
+ }
+
+ return entree1.getKey() - entree2.getKey();
+ }
+}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/mhuffman/FrequencyTable.java b/src/fr/iutfbleau/sae/mhuffman/FrequencyTable.java
index e290162..efa9c1a 100644
--- a/src/fr/iutfbleau/sae/mhuffman/FrequencyTable.java
+++ b/src/fr/iutfbleau/sae/mhuffman/FrequencyTable.java
@@ -1,115 +1,115 @@
-package fr.iutfbleau.sae.mhuffman;
-import fr.iutfbleau.sae.mimage.RGBImage;
-
-/**
- * Représente une table de fréquences pour une image RGB.
- *
- * Cette classe est utilisée dans le cadre de la compression de Huffman.
- * Elle compte le nombre d'occurrences de chaque valeur possible (0 à 255)
- * pour chacune des composantes de couleur : rouge, vert et bleu.
- *
- *
- *
- * Chaque composante est stockée dans un tableau de taille 256 :
- *
- * - {@code freqR} pour le rouge
- * - {@code freqG} pour le vert
- * - {@code freqB} pour le bleu
- *
- * L'indice du tableau correspond à la valeur de la composante,
- * et la valeur stockée correspond à sa fréquence d'apparition.
- *
- *
- *
- * Cette table de fréquences sert ensuite à construire les arbres de Huffman
- * nécessaires à la compression des données de l'image.
- *
- *
- * @author Algassimou Pellel Diallo
- * @version 1.0
- * @since 2025-12-13
- */
-public class FrequencyTable {
-
- /** Tableau des fréquences pour la composante rouge (valeurs de 0 à 255). */
- private int[] freqR;
-
- /** Tableau des fréquences pour la composante verte (valeurs de 0 à 255). */
- private int[] freqG;
-
- /** Tableau des fréquences pour la composante bleue (valeurs de 0 à 255). */
- private int[] freqB;
-
- /**
- * Construit une table de fréquences vide.
- *
- * Les trois tableaux de fréquences sont initialisés
- * avec 256 cases mises à zéro.
- *
- */
- public FrequencyTable() {
- this.freqR = new int[256];
- this.freqG = new int[256];
- this.freqB = new int[256];
- }
-
- /**
- * Calcule les fréquences des composantes RGB à partir d'une image.
- *
- * Pour chaque pixel de l'image, la méthode récupère les valeurs
- * rouge, verte et bleue, puis incrémente la case correspondante
- * dans le tableau de fréquences associé.
- *
- *
- * @param img l'image RGB à analyser
- */
- public void computeFromImage(RGBImage img) {
- /*Nb: une composante de couleur est un entier entre 0 et 255 qui représente la part de rouge,vert ou bleu
- dans la couleur d'un pixel.
- */
- /* pour chaque composante de couleur de chaque pixel, on incrémente la fréquence correspondante,
- c'est-à-dire on compte le nombre de fois que la composante apparaît dans l'image.
- ex: si un pixel P a une composante rouge de 150, on incrémente freqR[150] de 1.
- puis on fait de même pour les composantes verte et bleue.
- on répète ce processus pour tous les pixels de l'image.
- */
- for (int ligne = 0; ligne < img.getWidth(); ligne++) {
- for (int colonne = 0; colonne < img.getHeight(); colonne++) {
- this.freqR[img.getPixel(ligne, colonne).getR()]++; // Incrémente la fréquence de la composante rouge
- this.freqG[img.getPixel(ligne, colonne).getG()]++; // Incrémente la fréquence de la composante verte
- this.freqB[img.getPixel(ligne, colonne).getB()]++; // Incrémente la fréquence de la composante bleue
- }
- }
- }
-
- /**
- * Retourne le tableau des fréquences de la composante rouge.
- *
- * @return un tableau de 256 entiers représentant les fréquences du rouge
- */
- public int[] getRed() {
- return this.freqR;
- }
-
- /**
- * Retourne le tableau des fréquences de la composante verte.
- *
- * @return un tableau de 256 entiers représentant les fréquences du vert
- */
- public int[] getGreen() {
- return this.freqG;
- }
-
- /**
- * Retourne le tableau des fréquences de la composante bleue.
- *
- * @return un tableau de 256 entiers représentant les fréquences du bleu
- */
- public int[] getBlue() {
- return this.freqB;
- }
-}
-
-
-
-
+package fr.iutfbleau.sae.mhuffman;
+import fr.iutfbleau.sae.mimage.RGBImage;
+
+/**
+ * Représente une table de fréquences pour une image RGB.
+ *
+ * Cette classe est utilisée dans le cadre de la compression de Huffman.
+ * Elle compte le nombre d'occurrences de chaque valeur possible (0 à 255)
+ * pour chacune des composantes de couleur : rouge, vert et bleu.
+ *
+ *
+ *
+ * Chaque composante est stockée dans un tableau de taille 256 :
+ *
+ * - {@code freqR} pour le rouge
+ * - {@code freqG} pour le vert
+ * - {@code freqB} pour le bleu
+ *
+ * L'indice du tableau correspond à la valeur de la composante,
+ * et la valeur stockée correspond à sa fréquence d'apparition.
+ *
+ *
+ *
+ * Cette table de fréquences sert ensuite à construire les arbres de Huffman
+ * nécessaires à la compression des données de l'image.
+ *
+ *
+ * @author Algassimou Pellel Diallo
+ * @version 1.0
+ * @since 2025-12-13
+ */
+public class FrequencyTable {
+
+ /** Tableau des fréquences pour la composante rouge (valeurs de 0 à 255). */
+ private int[] freqR;
+
+ /** Tableau des fréquences pour la composante verte (valeurs de 0 à 255). */
+ private int[] freqG;
+
+ /** Tableau des fréquences pour la composante bleue (valeurs de 0 à 255). */
+ private int[] freqB;
+
+ /**
+ * Construit une table de fréquences vide.
+ *
+ * Les trois tableaux de fréquences sont initialisés
+ * avec 256 cases mises à zéro.
+ *
+ */
+ public FrequencyTable() {
+ this.freqR = new int[256];
+ this.freqG = new int[256];
+ this.freqB = new int[256];
+ }
+
+ /**
+ * Calcule les fréquences des composantes RGB à partir d'une image.
+ *
+ * Pour chaque pixel de l'image, la méthode récupère les valeurs
+ * rouge, verte et bleue, puis incrémente la case correspondante
+ * dans le tableau de fréquences associé.
+ *
+ *
+ * @param img l'image RGB à analyser
+ */
+ public void computeFromImage(RGBImage img) {
+ /*Nb: une composante de couleur est un entier entre 0 et 255 qui représente la part de rouge,vert ou bleu
+ dans la couleur d'un pixel.
+ */
+ /* pour chaque composante de couleur de chaque pixel, on incrémente la fréquence correspondante,
+ c'est-à-dire on compte le nombre de fois que la composante apparaît dans l'image.
+ ex: si un pixel P a une composante rouge de 150, on incrémente freqR[150] de 1.
+ puis on fait de même pour les composantes verte et bleue.
+ on répète ce processus pour tous les pixels de l'image.
+ */
+ for (int ligne = 0; ligne < img.getWidth(); ligne++) {
+ for (int colonne = 0; colonne < img.getHeight(); colonne++) {
+ this.freqR[img.getPixel(ligne, colonne).getR()]++; // Incrémente la fréquence de la composante rouge
+ this.freqG[img.getPixel(ligne, colonne).getG()]++; // Incrémente la fréquence de la composante verte
+ this.freqB[img.getPixel(ligne, colonne).getB()]++; // Incrémente la fréquence de la composante bleue
+ }
+ }
+ }
+
+ /**
+ * Retourne le tableau des fréquences de la composante rouge.
+ *
+ * @return un tableau de 256 entiers représentant les fréquences du rouge
+ */
+ public int[] getRed() {
+ return this.freqR;
+ }
+
+ /**
+ * Retourne le tableau des fréquences de la composante verte.
+ *
+ * @return un tableau de 256 entiers représentant les fréquences du vert
+ */
+ public int[] getGreen() {
+ return this.freqG;
+ }
+
+ /**
+ * Retourne le tableau des fréquences de la composante bleue.
+ *
+ * @return un tableau de 256 entiers représentant les fréquences du bleu
+ */
+ public int[] getBlue() {
+ return this.freqB;
+ }
+}
+
+
+
+
diff --git a/src/fr/iutfbleau/sae/mhuffman/HuffmanNode.java b/src/fr/iutfbleau/sae/mhuffman/HuffmanNode.java
index 8c46124..652cce4 100644
--- a/src/fr/iutfbleau/sae/mhuffman/HuffmanNode.java
+++ b/src/fr/iutfbleau/sae/mhuffman/HuffmanNode.java
@@ -1,119 +1,119 @@
-package fr.iutfbleau.sae.mhuffman;
-
-/**
- * Représente un nœud de l'arbre de Huffman.
- *
- * Un {@code HuffmanNode} peut être :
- *
- * - une feuille, contenant une valeur (symbole) et une fréquence
- * - un nœud interne, contenant uniquement une fréquence et deux enfants
- *
- *
- *
- *
- * Cette classe est une structure de données utilisée par {@code HuffmanTree}
- * pour construire l'arbre de Huffman.
- *
- *
- * @author Algassimou Pellel Diallo
- * @version 1.1
- * @since 2025-12-17
- */
-public class HuffmanNode {
-
- /** Valeur de la composante (cette valeur est appelée symbole, voir l'histoire de huffman, tres interressant) représentée par ce nœud (si feuille). c'est la part de la composante (rouge, verte ou bleue) dans la couleur d'un pixel. */
- private int value;
-
- /** Fréquence du symbole (somme des fréquences des enfants). */
- private int frequence;
-
- /** Fils gauche du nœud (null si feuille). */
- private HuffmanNode left;
-
- /** Fils droit du nœud (null si feuille). */
- private HuffmanNode right;
-
- /**
- * Construit un nœud feuille de Huffman.
- *
- * Ce constructeur est utilisé pour représenter une valeur
- * issue de la table de fréquences.
- *
- *
- * @param value la valeur (symbole) représentée par ce nœud
- * @param frequence la fréquence d'apparition de la valeur
- */
- public HuffmanNode(int value, int frequence) {
- this.value = value;
- this.frequence = frequence;
- this.left = null;
- this.right = null;
- }
-
- /**
- * Construit un nœud interne de Huffman.
- *
- * Ce constructeur est utilisé lors de la fusion de deux nœuds
- * de plus faible fréquence lors de la construction de l'arbre.
- *
- *
- * @param left le fils gauche
- * @param right le fils droit
- */
- public HuffmanNode(HuffmanNode left, HuffmanNode right) {
- this.left = left;
- this.right = right;
- this.frequence = left.frequence + right.frequence;
- }
-
- /**
- * Indique si ce nœud est une feuille.
- *
- * @return {@code true} si le nœud est une feuille, {@code false} sinon
- */
- public boolean isLeaf() {
- return this.left == null && this.right == null;
- }
-
- /**
- * Retourne la fréquence du nœud.
- *
- * @return la fréquence
- */
- public int getFrequence() {
- return this.frequence;
- }
-
- /**
- * Retourne le fils gauche du nœud.
- *
- * @return le fils gauche
- */
- public HuffmanNode getLeft() {
- return this.left;
- }
-
- /**
- * Retourne le fils droit du nœud.
- *
- * @return le fils droit
- */
- public HuffmanNode getRight() {
- return this.right;
- }
-
- /**
- * Retourne la valeur représentée par ce nœud.
- *
- * Cette méthode n'a de sens que si le nœud est une feuille.
- *
- *
- * @return la valeur du symbole
- */
- public int getValue() {
- if (!this.isLeaf()) {
- throw new IllegalStateException("La valeur n'est définie que pour les feuilles.");
- }
- return this.value;
- }
-}
+package fr.iutfbleau.sae.mhuffman;
+
+/**
+ * Représente un nœud de l'arbre de Huffman.
+ *
+ * Un {@code HuffmanNode} peut être :
+ *
+ * - une feuille, contenant une valeur (symbole) et une fréquence
+ * - un nœud interne, contenant uniquement une fréquence et deux enfants
+ *
+ *
+ *
+ *
+ * Cette classe est une structure de données utilisée par {@code HuffmanTree}
+ * pour construire l'arbre de Huffman.
+ *
+ *
+ * @author Algassimou Pellel Diallo
+ * @version 1.1
+ * @since 2025-12-17
+ */
+public class HuffmanNode {
+
+ /** Valeur de la composante (cette valeur est appelée symbole, voir l'histoire de huffman, tres interressant) représentée par ce nœud (si feuille). c'est la part de la composante (rouge, verte ou bleue) dans la couleur d'un pixel. */
+ private int value;
+
+ /** Fréquence du symbole (somme des fréquences des enfants). */
+ private int frequence;
+
+ /** Fils gauche du nœud (null si feuille). */
+ private HuffmanNode left;
+
+ /** Fils droit du nœud (null si feuille). */
+ private HuffmanNode right;
+
+ /**
+ * Construit un nœud feuille de Huffman.
+ *
+ * Ce constructeur est utilisé pour représenter une valeur
+ * issue de la table de fréquences.
+ *
+ *
+ * @param value la valeur (symbole) représentée par ce nœud
+ * @param frequence la fréquence d'apparition de la valeur
+ */
+ public HuffmanNode(int value, int frequence) {
+ this.value = value;
+ this.frequence = frequence;
+ this.left = null;
+ this.right = null;
+ }
+
+ /**
+ * Construit un nœud interne de Huffman.
+ *
+ * Ce constructeur est utilisé lors de la fusion de deux nœuds
+ * de plus faible fréquence lors de la construction de l'arbre.
+ *
+ *
+ * @param left le fils gauche
+ * @param right le fils droit
+ */
+ public HuffmanNode(HuffmanNode left, HuffmanNode right) {
+ this.left = left;
+ this.right = right;
+ this.frequence = left.frequence + right.frequence;
+ }
+
+ /**
+ * Indique si ce nœud est une feuille.
+ *
+ * @return {@code true} si le nœud est une feuille, {@code false} sinon
+ */
+ public boolean isLeaf() {
+ return this.left == null && this.right == null;
+ }
+
+ /**
+ * Retourne la fréquence du nœud.
+ *
+ * @return la fréquence
+ */
+ public int getFrequence() {
+ return this.frequence;
+ }
+
+ /**
+ * Retourne le fils gauche du nœud.
+ *
+ * @return le fils gauche
+ */
+ public HuffmanNode getLeft() {
+ return this.left;
+ }
+
+ /**
+ * Retourne le fils droit du nœud.
+ *
+ * @return le fils droit
+ */
+ public HuffmanNode getRight() {
+ return this.right;
+ }
+
+ /**
+ * Retourne la valeur représentée par ce nœud.
+ *
+ * Cette méthode n'a de sens que si le nœud est une feuille.
+ *
+ *
+ * @return la valeur du symbole
+ */
+ public int getValue() {
+ if (!this.isLeaf()) {
+ throw new IllegalStateException("La valeur n'est définie que pour les feuilles.");
+ }
+ return this.value;
+ }
+}
diff --git a/src/fr/iutfbleau/sae/mhuffman/HuffmanTree.java b/src/fr/iutfbleau/sae/mhuffman/HuffmanTree.java
index d87c0b3..0ae0341 100644
--- a/src/fr/iutfbleau/sae/mhuffman/HuffmanTree.java
+++ b/src/fr/iutfbleau/sae/mhuffman/HuffmanTree.java
@@ -1,218 +1,218 @@
-package fr.iutfbleau.sae.mhuffman;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-// test
-
-/**
- * Implémente un arbre de Huffman utilisé pour la compression de données.
- *
- * La classe {@code HuffmanTree} est chargée de représenter la structure
- * de l'arbre de Huffman et de générer les codes binaires associés aux symboles.
- * Elle s'appuie sur la classe {@link HuffmanNode} pour représenter les nœuds
- * de l'arbre.
- *
- *
- *
- * L'arbre est construit à partir des fréquences des symboles calculées
- * en amont (par exemple à l'aide d'une {@code FrequencyTable}).
- * Chaque symbole est d'abord représenté par une feuille, puis les nœuds
- * sont combinés progressivement selon l'algorithme de Huffman afin
- * d'obtenir un arbre binaire optimal.
- *
- *
- *
- * Une fois l'arbre construit, celui-ci est parcouru afin de générer une
- * table de correspondance associant à chaque symbole un code binaire unique.
- * Les symboles les plus fréquents se retrouvent plus proches de la racine
- * et possèdent donc des codes plus courts, ce qui permet de réduire
- * la taille des données compressées.
- *
- *
- *
- * Cette classe ne s'occupe pas de la lecture ou de l'écriture des bits.
- * Elle fournit uniquement la structure et les informations nécessaires
- * à la compression, qui sont ensuite exploitées par des flux binaires
- * dédiés.
- *
- *
- * @author Algassimou Pellel Diallo,Ayoub Anhdire
- * @version 1.0
- * @since 2025-12-13
- */
-public class HuffmanTree {
-
- /**
- * Racine de l'arbre de Huffman.
- *
- * Ce nœud est le résultat final de la construction de l'arbre et constitue
- * le point de départ pour la génération des codes binaires ainsi que
- * pour le décodage des données compressées.
- *
- */
- private HuffmanNode root;
-
- /**
- * Dictionnaire pour enregistrer les codes Huffman
- */
- // j'ai retirer le static car chaque arbre a ses propres codes et j'utilise string plutot que int pour stocker les codes car on construit une chaine de 0 et de 1
- private Map codes;
-
-
- /**
- * Chaine de caracteres qui va nous permettre de sauvegader le code Huffman
- * Permet en d'autres termes de construire la chaine de 1 et de 0
- */
-
- private String chaineCarac;
-
- /**
- * Construit un arbre de Huffman.
- *
- * Le constructeur est responsable de l'initialisation de la structure
- * de l'arbre. En pratique, il combine les nœuds feuilles représentant
- * les symboles par ordre croissant de fréquence jusqu'à obtenir un
- * unique nœud racine.
- *
- *
- *
- * Les détails de la construction (structure de données utilisée,
- * ordre des fusions, etc.) sont volontairement séparés de la logique
- * de génération des codes.
- *
- */
- public HuffmanTree(int[] freq) {
- // J'initialise la racine à null.
- this.root = null;
-
- // je cree une collection de feuilles
- List feuilles = new ArrayList<>();
-
- // pour chaque valeur(symbole) dans la table de frequence
- for (int i = 0; i < freq.length; i++) {
- // si la frequence est superieure a 0 , on cree une feuille
- if (freq[i] > 0) {
- // pour la valeur (symbole) i avec frequence freq[i], on cree une feuille
- HuffmanNode feuille = new HuffmanNode(i, freq[i]);
- // on ajoute la feuille à la collection
- feuilles.add(feuille);
- }
- }
-
- // On tri les feuilles par frequence croissante j'utilise un comparator qui compare la valeur retournee par getFrequence de chaque feuille
- // Referencement de methode avec ::
- feuilles.sort(Comparator.comparingInt(HuffmanNode::getFrequence));
- // flemme de faire un algo de tri alors que java le fait tres bien a voir a la fin si je vais coder une liste chainee avec un tri par insertion personnalise
-
-
- // Fusion des nœuds jusqu'à obtenir la racine
- // Tant qu'il y a plus d'une feuille dans la collection
- while (feuilles.size() > 1) {
- // je prends les deux feuilles de plus faible fréquence
- HuffmanNode left = feuilles.remove(0);
- HuffmanNode right = feuilles.remove(0);
-
- // je crée un nœud interne en les combinant
- HuffmanNode parent = new HuffmanNode(left, right);
-
- // j'insère le nœud parent dans la collection à la bonne position pour maintenir l'ordre (plus performant qu'un tri complet à chaque itération)
- int index = 0;
- // tant que l'index est dans les limites et que la frequence du noeud à l'index est inférieure à celle du parent
- while (index < feuilles.size() && feuilles.get(index).getFrequence() < parent.getFrequence()) {
- index++;
- }
- feuilles.add(index, parent);
- }
-
- // a la fin il ne reste qu'un seul noeud : la racine de l'arbre
- this.root = feuilles.get(0);
- }
-
- // Méthode pour générer les codes Huffman à partir de l'arbre
- // pourquoi string et pas int ? car on va construire une chaine de 0 et de 1
- /**
- * @return Map on stockera les codes Huffman sous forme de dictionnaire
- */
- public Map generateCodes() {
- /**
- * Le but de cette méthode est de pouvoir generer les codes Huffman à partir de l'arbre :
- * Les branches prendront comme valeur 1 ou 0 selon differents cas :
- * 1 - si on saute vers un fils droit
- * 0 - si on saute vers un fils gauche.
- * On construit les codes qui partent de la racine jusqu'à notre objectif
- */
- this.codes = new HashMap<>();
- // je lance la methode recursive avec une chaine vide qui va se remplir au fur et à mesure
- generateCodesRec(this.root, "");
- return codes;
- }
-
-
-
- private void generateCodesRec(HuffmanNode node, String prefiixe) {
- // Cas de base: si le noeud est une feuille, on ajoute le code au dictionnaire
- if (node.isLeaf()) {
- if (prefiixe.length() > 0){
- this.codes.put(node.getValue(), prefiixe);
- }else{
- this.codes.put(node.getValue(), "0");
- }
- return;
- }
-
- //Case general : sinon on continue a parcourir l'arbre
- // On va a gauche en ajoutant "0" au code
- generateCodesRec(node.getLeft(), prefiixe + "0");
- // On va a droite en ajoutant "1" au code
- generateCodesRec(node.getRight(), prefiixe + "1");
-
-
- // this.codes = new HashMap<>();
- // this.chaineCarac = new String();
-
- // if(root.isLeaf()){
- // codes.put(root.getValue(),Integer.parseInt(chaineCarac));
- // return codes;
- // }
-
- // HuffmanNode temp = root;
-
- // if (root.getLeft() != null) {
- // root = root.getLeft();
- // chaineCarac = chaineCarac + "0";
- // generateCodes();
- // // on retire le dernier bit lorsqu'on remonte car sinon les codes seront faussés
- // chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1);
- // }
-
- // if (temp.getRight() != null) {
- // root = temp.getRight();
- // chaineCarac = chaineCarac + "1";
- // generateCodes();
- // chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1);
- // }
-
-
- // root = temp;
- // return codes;
- }
-
- /**
- * @return Dictionnaire des codes Huffman
- */
- public Map getCodes(){
- return codes;
- }
-
- /**
- * @return le nœud racine de l'arbre de Huffman
- */
- public HuffmanNode getRoot() {
- return root;
- }
-
-
-
-}
+package fr.iutfbleau.sae.mhuffman;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+// test
+
+/**
+ * Implémente un arbre de Huffman utilisé pour la compression de données.
+ *
+ * La classe {@code HuffmanTree} est chargée de représenter la structure
+ * de l'arbre de Huffman et de générer les codes binaires associés aux symboles.
+ * Elle s'appuie sur la classe {@link HuffmanNode} pour représenter les nœuds
+ * de l'arbre.
+ *
+ *
+ *
+ * L'arbre est construit à partir des fréquences des symboles calculées
+ * en amont (par exemple à l'aide d'une {@code FrequencyTable}).
+ * Chaque symbole est d'abord représenté par une feuille, puis les nœuds
+ * sont combinés progressivement selon l'algorithme de Huffman afin
+ * d'obtenir un arbre binaire optimal.
+ *
+ *
+ *
+ * Une fois l'arbre construit, celui-ci est parcouru afin de générer une
+ * table de correspondance associant à chaque symbole un code binaire unique.
+ * Les symboles les plus fréquents se retrouvent plus proches de la racine
+ * et possèdent donc des codes plus courts, ce qui permet de réduire
+ * la taille des données compressées.
+ *
+ *
+ *
+ * Cette classe ne s'occupe pas de la lecture ou de l'écriture des bits.
+ * Elle fournit uniquement la structure et les informations nécessaires
+ * à la compression, qui sont ensuite exploitées par des flux binaires
+ * dédiés.
+ *
+ *
+ * @author Algassimou Pellel Diallo,Ayoub Anhdire
+ * @version 1.0
+ * @since 2025-12-13
+ */
+public class HuffmanTree {
+
+ /**
+ * Racine de l'arbre de Huffman.
+ *
+ * Ce nœud est le résultat final de la construction de l'arbre et constitue
+ * le point de départ pour la génération des codes binaires ainsi que
+ * pour le décodage des données compressées.
+ *
+ */
+ private HuffmanNode root;
+
+ /**
+ * Dictionnaire pour enregistrer les codes Huffman
+ */
+ // j'ai retirer le static car chaque arbre a ses propres codes et j'utilise string plutot que int pour stocker les codes car on construit une chaine de 0 et de 1
+ private Map codes;
+
+
+ /**
+ * Chaine de caracteres qui va nous permettre de sauvegader le code Huffman
+ * Permet en d'autres termes de construire la chaine de 1 et de 0
+ */
+
+ private String chaineCarac;
+
+ /**
+ * Construit un arbre de Huffman.
+ *
+ * Le constructeur est responsable de l'initialisation de la structure
+ * de l'arbre. En pratique, il combine les nœuds feuilles représentant
+ * les symboles par ordre croissant de fréquence jusqu'à obtenir un
+ * unique nœud racine.
+ *
+ *
+ *
+ * Les détails de la construction (structure de données utilisée,
+ * ordre des fusions, etc.) sont volontairement séparés de la logique
+ * de génération des codes.
+ *
+ */
+ public HuffmanTree(int[] freq) {
+ // J'initialise la racine à null.
+ this.root = null;
+
+ // je cree une collection de feuilles
+ List feuilles = new ArrayList<>();
+
+ // pour chaque valeur(symbole) dans la table de frequence
+ for (int i = 0; i < freq.length; i++) {
+ // si la frequence est superieure a 0 , on cree une feuille
+ if (freq[i] > 0) {
+ // pour la valeur (symbole) i avec frequence freq[i], on cree une feuille
+ HuffmanNode feuille = new HuffmanNode(i, freq[i]);
+ // on ajoute la feuille à la collection
+ feuilles.add(feuille);
+ }
+ }
+
+ // On tri les feuilles par frequence croissante j'utilise un comparator qui compare la valeur retournee par getFrequence de chaque feuille
+ // Referencement de methode avec ::
+ feuilles.sort(Comparator.comparingInt(HuffmanNode::getFrequence));
+ // flemme de faire un algo de tri alors que java le fait tres bien a voir a la fin si je vais coder une liste chainee avec un tri par insertion personnalise
+
+
+ // Fusion des nœuds jusqu'à obtenir la racine
+ // Tant qu'il y a plus d'une feuille dans la collection
+ while (feuilles.size() > 1) {
+ // je prends les deux feuilles de plus faible fréquence
+ HuffmanNode left = feuilles.remove(0);
+ HuffmanNode right = feuilles.remove(0);
+
+ // je crée un nœud interne en les combinant
+ HuffmanNode parent = new HuffmanNode(left, right);
+
+ // j'insère le nœud parent dans la collection à la bonne position pour maintenir l'ordre (plus performant qu'un tri complet à chaque itération)
+ int index = 0;
+ // tant que l'index est dans les limites et que la frequence du noeud à l'index est inférieure à celle du parent
+ while (index < feuilles.size() && feuilles.get(index).getFrequence() < parent.getFrequence()) {
+ index++;
+ }
+ feuilles.add(index, parent);
+ }
+
+ // a la fin il ne reste qu'un seul noeud : la racine de l'arbre
+ this.root = feuilles.get(0);
+ }
+
+ // Méthode pour générer les codes Huffman à partir de l'arbre
+ // pourquoi string et pas int ? car on va construire une chaine de 0 et de 1
+ /**
+ * @return Map on stockera les codes Huffman sous forme de dictionnaire
+ */
+ public Map generateCodes() {
+ /**
+ * Le but de cette méthode est de pouvoir generer les codes Huffman à partir de l'arbre :
+ * Les branches prendront comme valeur 1 ou 0 selon differents cas :
+ * 1 - si on saute vers un fils droit
+ * 0 - si on saute vers un fils gauche.
+ * On construit les codes qui partent de la racine jusqu'à notre objectif
+ */
+ this.codes = new HashMap<>();
+ // je lance la methode recursive avec une chaine vide qui va se remplir au fur et à mesure
+ generateCodesRec(this.root, "");
+ return codes;
+ }
+
+
+
+ private void generateCodesRec(HuffmanNode node, String prefiixe) {
+ // Cas de base: si le noeud est une feuille, on ajoute le code au dictionnaire
+ if (node.isLeaf()) {
+ if (prefiixe.length() > 0){
+ this.codes.put(node.getValue(), prefiixe);
+ }else{
+ this.codes.put(node.getValue(), "0");
+ }
+ return;
+ }
+
+ //Case general : sinon on continue a parcourir l'arbre
+ // On va a gauche en ajoutant "0" au code
+ generateCodesRec(node.getLeft(), prefiixe + "0");
+ // On va a droite en ajoutant "1" au code
+ generateCodesRec(node.getRight(), prefiixe + "1");
+
+
+ // this.codes = new HashMap<>();
+ // this.chaineCarac = new String();
+
+ // if(root.isLeaf()){
+ // codes.put(root.getValue(),Integer.parseInt(chaineCarac));
+ // return codes;
+ // }
+
+ // HuffmanNode temp = root;
+
+ // if (root.getLeft() != null) {
+ // root = root.getLeft();
+ // chaineCarac = chaineCarac + "0";
+ // generateCodes();
+ // // on retire le dernier bit lorsqu'on remonte car sinon les codes seront faussés
+ // chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1);
+ // }
+
+ // if (temp.getRight() != null) {
+ // root = temp.getRight();
+ // chaineCarac = chaineCarac + "1";
+ // generateCodes();
+ // chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1);
+ // }
+
+
+ // root = temp;
+ // return codes;
+ }
+
+ /**
+ * @return Dictionnaire des codes Huffman
+ */
+ public Map getCodes(){
+ return codes;
+ }
+
+ /**
+ * @return le nœud racine de l'arbre de Huffman
+ */
+ public HuffmanNode getRoot() {
+ return root;
+ }
+
+
+
+}
diff --git a/src/fr/iutfbleau/sae/mimage/Pixel.java b/src/fr/iutfbleau/sae/mimage/Pixel.java
index 0619e06..eb8db1a 100644
--- a/src/fr/iutfbleau/sae/mimage/Pixel.java
+++ b/src/fr/iutfbleau/sae/mimage/Pixel.java
@@ -1,38 +1,38 @@
-package fr.iutfbleau.sae.mimage;
-
-public class Pixel{
- private int r;
- private int g;
- private int b;
-
- //à completer
- public Pixel(int red, int green, int blue){
- this.r=red;
- this.g=green;
- this.b=blue;
- }
-
- public int getB() {
- return b;
- }
-
- public int getG() {
- return g;
- }
-
- public int getR() {
- return r;
- }
-
- public void setR(int r) {
- this.r = r;
- }
-
- public void setB(int b) {
- this.b = b;
- }
-
- public void setG(int g) {
- this.g = g;
- }
+package fr.iutfbleau.sae.mimage;
+
+public class Pixel{
+ private int r;
+ private int g;
+ private int b;
+
+ //à completer
+ public Pixel(int red, int green, int blue){
+ this.r=red;
+ this.g=green;
+ this.b=blue;
+ }
+
+ public int getB() {
+ return b;
+ }
+
+ public int getG() {
+ return g;
+ }
+
+ public int getR() {
+ return r;
+ }
+
+ public void setR(int r) {
+ this.r = r;
+ }
+
+ public void setB(int b) {
+ this.b = b;
+ }
+
+ public void setG(int g) {
+ this.g = g;
+ }
}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/mimage/RGBImage.java b/src/fr/iutfbleau/sae/mimage/RGBImage.java
index 8808505..a92b2f0 100644
--- a/src/fr/iutfbleau/sae/mimage/RGBImage.java
+++ b/src/fr/iutfbleau/sae/mimage/RGBImage.java
@@ -1,30 +1,30 @@
-package fr.iutfbleau.sae.mimage;
-
-public class RGBImage {
-
- private int width;
- private int height;
- private Pixel [][] pixels;
-
- public RGBImage (int lar, int haut){
- this.width=lar;
- this.height=haut;
- this.pixels = new Pixel[this.width][this.height];
- }
-
- public int getWidth() {
- return width;
- }
-
- public int getHeight() {
- return height;
- }
-
- public void setPixel(int x, int y, Pixel p) {
- this.pixels[x][y] = p;
- }
-
- public Pixel getPixel(int x, int y) {
- return this.pixels[x][y];
- }
+package fr.iutfbleau.sae.mimage;
+
+public class RGBImage {
+
+ private int width;
+ private int height;
+ private Pixel [][] pixels;
+
+ public RGBImage (int lar, int haut){
+ this.width=lar;
+ this.height=haut;
+ this.pixels = new Pixel[this.width][this.height];
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setPixel(int x, int y, Pixel p) {
+ this.pixels[x][y] = p;
+ }
+
+ public Pixel getPixel(int x, int y) {
+ return this.pixels[x][y];
+ }
}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/mpif/DecodeNode.java b/src/fr/iutfbleau/sae/mpif/DecodeNode.java
index c995ed8..800852d 100644
--- a/src/fr/iutfbleau/sae/mpif/DecodeNode.java
+++ b/src/fr/iutfbleau/sae/mpif/DecodeNode.java
@@ -1,23 +1,23 @@
-package fr.iutfbleau.sae.mpif;
-
-public class DecodeNode {
- public DecodeNode left;
- public DecodeNode right;
- public Integer value; // null si pas une feuille
-
- public DecodeNode() {
- this.left = null;
- this.right = null;
- this.value = null;
- }
-
- public DecodeNode(DecodeNode left, DecodeNode right, Integer value) {
- this.left = left;
- this.right = right;
- this.value = value;
- }
-
- public boolean isLeaf() {
- return this.left == null && this.right == null;
- }
-}
+package fr.iutfbleau.sae.mpif;
+
+public class DecodeNode {
+ public DecodeNode left;
+ public DecodeNode right;
+ public Integer value; // null si pas une feuille
+
+ public DecodeNode() {
+ this.left = null;
+ this.right = null;
+ this.value = null;
+ }
+
+ public DecodeNode(DecodeNode left, DecodeNode right, Integer value) {
+ this.left = left;
+ this.right = right;
+ this.value = value;
+ }
+
+ public boolean isLeaf() {
+ return this.left == null && this.right == null;
+ }
+}
diff --git a/src/fr/iutfbleau/sae/mpif/PIFReader.java b/src/fr/iutfbleau/sae/mpif/PIFReader.java
index 80b3c2e..51602c0 100644
--- a/src/fr/iutfbleau/sae/mpif/PIFReader.java
+++ b/src/fr/iutfbleau/sae/mpif/PIFReader.java
@@ -1,70 +1,82 @@
-package fr.iutfbleau.sae.mpif;
-
-import fr.iutfbleau.sae.util.BitInputStream;
-import fr.iutfbleau.sae.util.BitOutputStream;
-
-import java.io.FileInputStream;
-import java.util.Map;
-
-import fr.iutfbleau.sae.mimage.RGBImage;
-
-
-public class PIFReader {
-
- private int width;
- private int height;
- private int[] lenR;
- private int[] lenG;
- private int[] lenB;
-
- public RGBImage read(String filepath)
- throws Exception {
- FileInputStream fis = new FileInputStream(filepath);
- BitInputStream lecteur = new BitInputStream(fis);
-
- // je lis l'entête et les tables canoniques
- this.readHeader(lecteur);
- this.readCanonicalTables(lecteur);
-
- // je reconstructe les tables canoniques car dans le fichier on a juste les longueurs en bits
- Map canonR = rebuildCanonical(lenR);
- Map canonG = rebuildCanonical(lenG);
- Map canonB = rebuildCanonical(lenB);
-
- DecodeNode trieR = buildDecodageTree(canonR);
- DecodeNode trieG = buildDecodageTree(canonG);
- DecodeNode trieB = buildDecodageTree(canonB);
-
- RGBImage img = decodePixels(lecteur, trieR, trieG, trieB);
-
- lecteur.closeFlux();
- return img;
- }
-
- public void readHeader(BitInputStream in) {
- // TODO: Implement header reading
- }
-
- public void readCanonicalTables(BitInputStream in) {
- // TODO: Implement canonical table reading
- }
-
- public Map rebuildCanonical(int[] lengths) {
- // TODO: Implement canonical table reconstruction
- return null;
- }
-
- public RGBImage decodePixels(BitInputStream in, DecodeNode red, DecodeNode green, DecodeNode blue) {
- // TODO: Implement pixel decoding
- return null;
- }
-
- public DecodeNode buildDecodageTree(Map codes) {
- // TODO: Implement trie building
- return null;
- }
-
-
-
-
-}
+package fr.iutfbleau.sae.mpif;
+import fr.iutfbleau.sae.util.BitInputStream;
+import fr.iutfbleau.sae.util.BitOutputStream;
+import java.io.FileInputStream;
+import java.util.Map;
+import fr.iutfbleau.sae.mimage.RGBImage;
+
+
+public class PIFReader {
+
+ private int width;
+ private int height;
+ private int[] lenR;
+ private int[] lenG;
+ private int[] lenB;
+
+ public RGBImage read(String filepath)
+ throws Exception {
+ FileInputStream fis = new FileInputStream(filepath);
+ BitInputStream lecteur = new BitInputStream(fis);
+
+ // je lis l'entête et les tables canoniques
+ this.readHeader(lecteur);
+ this.readCanonicalTables(lecteur);
+
+ // je reconstructe les tables canoniques car dans le fichier on a juste les longueurs en bits
+ Map canonR = rebuildCanonical(lenR);
+ Map canonG = rebuildCanonical(lenG);
+ Map canonB = rebuildCanonical(lenB);
+
+ DecodeNode trieR = buildDecodageTree(canonR);
+ DecodeNode trieG = buildDecodageTree(canonG);
+ DecodeNode trieB = buildDecodageTree(canonB);
+
+ RGBImage img = decodePixels(lecteur, trieR, trieG, trieB);
+
+ lecteur.closeFlux();
+ return img;
+ }
+
+ public void readHeader(BitInputStream in) {
+ // La largeur et l'hauteur de l'image occupe chaqun deux octets soit 16 bits :
+ this.width = in.readBits(16);
+ this.height = in.readBits(16);
+ }
+
+ public void readCanonicalTables(BitInputStream in) {
+ this.lenR = new int[256];
+ this.lenG = new int[256];
+ this.lenB = new int[256];
+
+ for (int i = 0; i < 256; i++){
+ lenR[i] = in.readBits(8);
+ }
+ for (int j = 0; j < 256; j++){
+ lenG[j] = in.readBits(8);
+ }
+
+ for (int k = 0; k < 256; k++){
+ lenB[k] = in.readBits(8);
+ }
+}
+
+ public Map rebuildCanonical(int[] lengths) {
+ // TODO: Implement canonical table reconstruction
+ return null;
+ }
+
+ public RGBImage decodePixels(BitInputStream in, DecodeNode red, DecodeNode green, DecodeNode blue) {
+ // TODO: Implement pixel decoding
+ return null;
+ }
+
+ public DecodeNode buildDecodageTree(Map codes) {
+ // TODO: Implement trie building
+ return null;
+ }
+
+
+
+
+}
diff --git a/src/fr/iutfbleau/sae/mpif/PIFWriter.java b/src/fr/iutfbleau/sae/mpif/PIFWriter.java
index a941a18..00a59f7 100644
--- a/src/fr/iutfbleau/sae/mpif/PIFWriter.java
+++ b/src/fr/iutfbleau/sae/mpif/PIFWriter.java
@@ -1,126 +1,126 @@
-package fr.iutfbleau.sae.mpif;
-
-import fr.iutfbleau.sae.mimage.RGBImage;
-import fr.iutfbleau.sae.util.BitOutputStream;
-import java.io.BufferedOutputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Map;
-
-public class PIFWriter {
-
- public void writeTOFile(String filepath, RGBImage image,
- Map canonR,
- Map canonG,
- Map canonB)
- throws Exception {
-
- // Création du flux de sortie binaire
- FileOutputStream fos =new FileOutputStream(filepath);
- BufferedOutputStream bos =new BufferedOutputStream(fos);
- BitOutputStream ecriveur =new BitOutputStream(bos);
-
- // Écriture de l'en-tête
- writeHeader(ecriveur, image.getWidth(), image.getHeight());
-
- // Écriture des tables de longueurs des codes canoniques
- writeTables(ecriveur, canonR, canonG, canonB);
-
- // Ecriture des pixels
- encodePixels(ecriveur, image, canonR, canonG, canonB);
-
- ecriveur.fermerFlux();
-
- System.err.println("SYSTEME");
- }
-
- // Ecriture de l'en-tête du fichier PIF (largeur et hauteur)
- public void writeHeader(BitOutputStream out,int width, int height){
- try {
- out.writeBits(width >> 8 & 0xFF, 8); // octet de poids fort
- out.writeBits(width & 0xFF, 8); // octet de poids faible
-
- out.writeBits(height >> 8 & 0xFF, 8); // octet de poids fort
- out.writeBits(height & 0xFF, 8); // octet de poids faible
- } catch (Exception e) {
- System.err.println("Erreur lors de l’écriture de l’en-tête du fichier PIF");
- }
-
- }
-
- public void writeTables(BitOutputStream out, Map canonR,
- Map canonG, Map canonB){
-
- try {
- // Écriture des longueurs des codes canoniques pour chaque composante
- for (int i = 0; i < 256; i++) {
- int len;
- if (canonR.containsKey(i)) { // petite securité (au cas où)
- len = canonR.get(i).length();
- }else {
- len = 0;
- }
- out.writeBits(len, 8);
-
- }
- for (int i = 0; i < 256; i++) {
- int len;
- if (canonG.containsKey(i)) {
- len = canonG.get(i).length();
- }else {
- len = 0;
- }
- out.writeBits(len, 8);
- }
- for (int i = 0; i < 256; i++) {
- int len;
- if (canonB.containsKey(i)) {
- len = canonB.get(i).length();
- }else {
- len = 0;
- }
- out.writeBits(len, 8);
- }
- } catch (IOException e) {
- System.err.println("Erreur lors de l’écriture des tables de fréquences dans le fichier PIF");
- }
- }
-
- private void writeBitFromString(BitOutputStream out, String code){
- try {
- for (int i = 0; i < code.length(); i++) {
- if (code.charAt(i) == '1') {
- out.writeBit(1);
- } else {
- out.writeBit(0);
- }
- }
- } catch (IOException e) {
- System.err.println("Erreur lors de l’écriture des bits dans le fichier PIF");
- }
- }
-
- // Méthode pour encoder les pixels de l'image en utilisant les codes canoniques
- public void encodePixels(BitOutputStream out, RGBImage image, Map canonRED, Map canonGREEN, Map canonBLUE){
- int width = image.getWidth();
- int height = image.getHeight();
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- // Récupérer les valeurs R, G, B du pixel
- int r = image.getPixel(x, y).getR();
- int g = image.getPixel(x, y).getG();
- int b = image.getPixel(x, y).getB();
-
- // Écrire les codes dans le flux binaire
- writeBitFromString(out, canonRED.get(r));
- writeBitFromString(out, canonGREEN.get(g));
- writeBitFromString(out, canonBLUE.get(b));
-
- }
- }
-
-
- }
-
-
-}
+package fr.iutfbleau.sae.mpif;
+
+import fr.iutfbleau.sae.mimage.RGBImage;
+import fr.iutfbleau.sae.util.BitOutputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+public class PIFWriter {
+
+ public void writeTOFile(String filepath, RGBImage image,
+ Map canonR,
+ Map canonG,
+ Map canonB)
+ throws Exception {
+
+ // Création du flux de sortie binaire
+ FileOutputStream fos =new FileOutputStream(filepath);
+ BufferedOutputStream bos =new BufferedOutputStream(fos);
+ BitOutputStream ecriveur =new BitOutputStream(bos);
+
+ // Écriture de l'en-tête
+ writeHeader(ecriveur, image.getWidth(), image.getHeight());
+
+ // Écriture des tables de longueurs des codes canoniques
+ writeTables(ecriveur, canonR, canonG, canonB);
+
+ // Ecriture des pixels
+ encodePixels(ecriveur, image, canonR, canonG, canonB);
+
+ ecriveur.fermerFlux();
+
+ System.err.println("SYSTEME");
+ }
+
+ // Ecriture de l'en-tête du fichier PIF (largeur et hauteur)
+ public void writeHeader(BitOutputStream out,int width, int height){
+ try {
+ out.writeBits(width >> 8 & 0xFF, 8); // octet de poids fort
+ out.writeBits(width & 0xFF, 8); // octet de poids faible
+
+ out.writeBits(height >> 8 & 0xFF, 8); // octet de poids fort
+ out.writeBits(height & 0xFF, 8); // octet de poids faible
+ } catch (Exception e) {
+ System.err.println("Erreur lors de l’écriture de l’en-tête du fichier PIF");
+ }
+
+ }
+
+ public void writeTables(BitOutputStream out, Map canonR,
+ Map canonG, Map canonB){
+
+ try {
+ // Écriture des longueurs des codes canoniques pour chaque composante
+ for (int i = 0; i < 256; i++) {
+ int len;
+ if (canonR.containsKey(i)) { // petite securité (au cas où)
+ len = canonR.get(i).length();
+ }else {
+ len = 0;
+ }
+ out.writeBits(len, 8);
+
+ }
+ for (int i = 0; i < 256; i++) {
+ int len;
+ if (canonG.containsKey(i)) {
+ len = canonG.get(i).length();
+ }else {
+ len = 0;
+ }
+ out.writeBits(len, 8);
+ }
+ for (int i = 0; i < 256; i++) {
+ int len;
+ if (canonB.containsKey(i)) {
+ len = canonB.get(i).length();
+ }else {
+ len = 0;
+ }
+ out.writeBits(len, 8);
+ }
+ } catch (IOException e) {
+ System.err.println("Erreur lors de l’écriture des tables de fréquences dans le fichier PIF");
+ }
+ }
+
+ private void writeBitFromString(BitOutputStream out, String code){
+ try {
+ for (int i = 0; i < code.length(); i++) {
+ if (code.charAt(i) == '1') {
+ out.writeBit(1);
+ } else {
+ out.writeBit(0);
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Erreur lors de l’écriture des bits dans le fichier PIF");
+ }
+ }
+
+ // Méthode pour encoder les pixels de l'image en utilisant les codes canoniques
+ public void encodePixels(BitOutputStream out, RGBImage image, Map canonRED, Map canonGREEN, Map canonBLUE){
+ int width = image.getWidth();
+ int height = image.getHeight();
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // Récupérer les valeurs R, G, B du pixel
+ int r = image.getPixel(x, y).getR();
+ int g = image.getPixel(x, y).getG();
+ int b = image.getPixel(x, y).getB();
+
+ // Écrire les codes dans le flux binaire
+ writeBitFromString(out, canonRED.get(r));
+ writeBitFromString(out, canonGREEN.get(g));
+ writeBitFromString(out, canonBLUE.get(b));
+
+ }
+ }
+
+
+ }
+
+
+}
diff --git a/src/fr/iutfbleau/sae/util/BitOutputStream.java b/src/fr/iutfbleau/sae/util/BitOutputStream.java
index 1ebc34d..71d0f55 100644
--- a/src/fr/iutfbleau/sae/util/BitOutputStream.java
+++ b/src/fr/iutfbleau/sae/util/BitOutputStream.java
@@ -1,127 +1,127 @@
-package fr.iutfbleau.sae.util;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Décorateur de flux permettant l'écriture binaire à granularité du bit.
- *
- * Cette classe encapsule un {@link OutputStream} existant et permet
- * l'écriture de bits individuellement ou par groupes.
- * Les bits sont accumulés afin de former des octets avant écriture.
- *
- *
- * Utilisée notamment pour l'encodage des fichiers compressés
- * (ex : format PIF utilisant des codes de Huffman).
- *
- * @author Algassimou Pellel Diallo
- * @version 1.0
- * @since 2025-12-13
- */
-public class BitOutputStream {
-
- /** Flux de sortie sous-jacent */
- private final OutputStream fluxSortie;
-
- /** Octet en cours de construction */
- private int octetEnConstruction;
-
- /** Position du prochain bit à écrire (de 7 à 0) */
- private int positionBit;
-
- /** Indique si le flux est fermé */
- private boolean fluxFerme;
-
- /**
- * Construit un écrivain binaire à partir d'un flux existant.
- *
- * @param fluxSortie flux de sortie à décorer
- * @throws IllegalArgumentException si le flux est nul
- */
- public BitOutputStream(OutputStream fluxSortie) {
- if (fluxSortie == null) {
- throw new IllegalArgumentException("Le flux de sortie ne peut pas être nul");
- }
- this.fluxSortie = fluxSortie;
- this.octetEnConstruction = 0;
- this.positionBit = 7;
- this.fluxFerme = false;
- }
-
- /**
- * Écrit un bit dans le flux binaire.
- *
- * @param bit bit à écrire (0 ou 1)
- * @throws IOException si une erreur d'écriture survient
- * @throws IllegalArgumentException si le bit n'est ni 0 ni 1
- */
- public void writeBit(int bit) throws IOException {
- if (bit != 0 && bit != 1) {
- throw new IllegalArgumentException("Le bit doit être 0 ou 1");
- }
- if (fluxFerme) {
- throw new IOException("Le flux de sortie est fermé");
- }
- if (bit == 1) {
- this.octetEnConstruction = this.octetEnConstruction | (1 << this.positionBit);
- }
- this.positionBit--;
-
- // si on atteint la fin de l'octet, on le grave dans le flux et rebolotte
- if(this.positionBit < 0){
- this.fluxSortie.write(this.octetEnConstruction);
- this.octetEnConstruction = 0;
- this.positionBit = 7;
- }
-
- }
-
- /**
- * Écrit une séquence de bits correspondant à une valeur entière.
- *
- * @param valeur valeur contenant les bits à écrire
- * @param nombreBits nombre de bits à écrire (strictement positif)
- * @throws IOException si une erreur d'écriture survient
- */
- public void writeBits(int valeur, int nombreBits) throws IOException {
- for (int i = nombreBits - 1; i >= 0; i--) {
- int bit = (valeur >> i) & 1;
- writeBit(bit);
- }
- }
-
- /**
- * Force l'écriture immédiate des données accumulées dans le flux sous-jacent.
- *
- * @throws IOException si une erreur survient lors du flush
- */
- public void flush() throws IOException {
- if (fluxFerme) {
- throw new IOException("Le flux de sortie est fermé");
- }
-
- // Si l'octet nes pas vide on le complete avec des 0
- if(this.positionBit < 7){
- this.fluxSortie.write(this.octetEnConstruction);
- this.octetEnConstruction = 0;
- this.positionBit = 7;
- }
-
- this.fluxSortie.flush(); // Force l'écriture dans le flux sous-jacent dans le but de vider le buffer
- }
-
- /**
- * Vide les buffers internes et ferme le flux de sortie.
- *
- * @throws IOException si une erreur survient lors de la fermeture
- */
- public void fermerFlux() throws IOException {
- // si le flux n'est pas déjà fermé
- if (!fluxFerme) {
- this.flush(); // compléter l'octet et forcer l'écriture
- this.fluxSortie.close(); // fermer le flux sous-jacent
- this.fluxFerme = true; // marquer le flux comme fermé
- }
- }
-
-
-}
+package fr.iutfbleau.sae.util;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Décorateur de flux permettant l'écriture binaire à granularité du bit.
+ *
+ * Cette classe encapsule un {@link OutputStream} existant et permet
+ * l'écriture de bits individuellement ou par groupes.
+ * Les bits sont accumulés afin de former des octets avant écriture.
+ *
+ *
+ * Utilisée notamment pour l'encodage des fichiers compressés
+ * (ex : format PIF utilisant des codes de Huffman).
+ *
+ * @author Algassimou Pellel Diallo
+ * @version 1.0
+ * @since 2025-12-13
+ */
+public class BitOutputStream {
+
+ /** Flux de sortie sous-jacent */
+ private final OutputStream fluxSortie;
+
+ /** Octet en cours de construction */
+ private int octetEnConstruction;
+
+ /** Position du prochain bit à écrire (de 7 à 0) */
+ private int positionBit;
+
+ /** Indique si le flux est fermé */
+ private boolean fluxFerme;
+
+ /**
+ * Construit un écrivain binaire à partir d'un flux existant.
+ *
+ * @param fluxSortie flux de sortie à décorer
+ * @throws IllegalArgumentException si le flux est nul
+ */
+ public BitOutputStream(OutputStream fluxSortie) {
+ if (fluxSortie == null) {
+ throw new IllegalArgumentException("Le flux de sortie ne peut pas être nul");
+ }
+ this.fluxSortie = fluxSortie;
+ this.octetEnConstruction = 0;
+ this.positionBit = 7;
+ this.fluxFerme = false;
+ }
+
+ /**
+ * Écrit un bit dans le flux binaire.
+ *
+ * @param bit bit à écrire (0 ou 1)
+ * @throws IOException si une erreur d'écriture survient
+ * @throws IllegalArgumentException si le bit n'est ni 0 ni 1
+ */
+ public void writeBit(int bit) throws IOException {
+ if (bit != 0 && bit != 1) {
+ throw new IllegalArgumentException("Le bit doit être 0 ou 1");
+ }
+ if (fluxFerme) {
+ throw new IOException("Le flux de sortie est fermé");
+ }
+ if (bit == 1) {
+ this.octetEnConstruction = this.octetEnConstruction | (1 << this.positionBit);
+ }
+ this.positionBit--;
+
+ // si on atteint la fin de l'octet, on le grave dans le flux et rebolotte
+ if(this.positionBit < 0){
+ this.fluxSortie.write(this.octetEnConstruction);
+ this.octetEnConstruction = 0;
+ this.positionBit = 7;
+ }
+
+ }
+
+ /**
+ * Écrit une séquence de bits correspondant à une valeur entière.
+ *
+ * @param valeur valeur contenant les bits à écrire
+ * @param nombreBits nombre de bits à écrire (strictement positif)
+ * @throws IOException si une erreur d'écriture survient
+ */
+ public void writeBits(int valeur, int nombreBits) throws IOException {
+ for (int i = nombreBits - 1; i >= 0; i--) {
+ int bit = (valeur >> i) & 1;
+ writeBit(bit);
+ }
+ }
+
+ /**
+ * Force l'écriture immédiate des données accumulées dans le flux sous-jacent.
+ *
+ * @throws IOException si une erreur survient lors du flush
+ */
+ public void flush() throws IOException {
+ if (fluxFerme) {
+ throw new IOException("Le flux de sortie est fermé");
+ }
+
+ // Si l'octet nes pas vide on le complete avec des 0
+ if(this.positionBit < 7){
+ this.fluxSortie.write(this.octetEnConstruction);
+ this.octetEnConstruction = 0;
+ this.positionBit = 7;
+ }
+
+ this.fluxSortie.flush(); // Force l'écriture dans le flux sous-jacent dans le but de vider le buffer
+ }
+
+ /**
+ * Vide les buffers internes et ferme le flux de sortie.
+ *
+ * @throws IOException si une erreur survient lors de la fermeture
+ */
+ public void fermerFlux() throws IOException {
+ // si le flux n'est pas déjà fermé
+ if (!fluxFerme) {
+ this.flush(); // compléter l'octet et forcer l'écriture
+ this.fluxSortie.close(); // fermer le flux sous-jacent
+ this.fluxFerme = true; // marquer le flux comme fermé
+ }
+ }
+
+
+}
diff --git a/src/fr/iutfbleau/sae/util/BitinputStream.java b/src/fr/iutfbleau/sae/util/BitinputStream.java
index 06ce9ca..0054036 100644
--- a/src/fr/iutfbleau/sae/util/BitinputStream.java
+++ b/src/fr/iutfbleau/sae/util/BitinputStream.java
@@ -1,115 +1,115 @@
-package fr.iutfbleau.sae.util;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Décorateur de flux permettant la lecture binaire à granularité du bit.
- *
- * Cette classe encapsule un {@link InputStream} existant et fournit
- * des opérations de lecture bit par bit ou par groupes de bits.
- * Elle ne gère ni l'ouverture ni la sélection du fichier source.
- *
- *
- *
- * Utilisée notamment pour le décodage des fichiers compressés
- * (ex : format PIF utilisant des codes de Huffman).
- *
- *
- *
- *
- * @author Algassimou Pellel Diallo
- * @version 1.0
- * @since 2025-12-13
- */
-public class BitInputStream {
-
- /** Flux d'entrée sous-jacent */
- private final InputStream fluxEntree;
-
- /** Octet actuellement chargé depuis le flux */
- private int octetCourant;
-
- /** Position du bit courant dans l'octet (du bit 7 au bit 0) */
- private int positionBit;
-
- /** Indique si la fin du flux a été atteinte */
- private boolean finDeFlux;
-
- /**
- * Construit un lecteur binaire à partir d'un flux existant.
- *
- * @param fluxEntree flux d'entrée à décorer
- * @throws IllegalArgumentException si le flux est nul
- */
- public BitInputStream(InputStream fluxEntree) {
- if (fluxEntree == null) {
- throw new IllegalArgumentException("Le flux d'entrée ne peut pas être nul");
- }
- this.fluxEntree = fluxEntree;
- this.octetCourant = 0;
- this.positionBit = -1; // force la lecture d'un nouvel octet
- this.finDeFlux = false;
- }
-
- /**
- * Lit un bit depuis le flux binaire.
- *
- * @return 0 ou 1 si un bit est lu, -1 si la fin du flux est atteinte
- * @throws IOException si une erreur de lecture survient
- */
- public int readBit() throws IOException {
- if (finDeFlux) {
- return -1;
- }
-
- if (this.positionBit < 0) {
- int octetLu = this.fluxEntree.read();
- if (octetLu == -1) {
- this.finDeFlux = true;
- } else {
- this.octetCourant = octetLu;
- this.positionBit = 7;
- }
- }
-
- if (finDeFlux) {
- return -1;
- }
-
- int bit = (this.octetCourant >> this.positionBit) & 1;
- this.positionBit--;
- return bit;
- }
-
- /**
- * Lit une séquence de bits consécutifs et les assemble dans un entier.
- *
- * @param nombreBits nombre de bits à lire (strictement positif)
- * @return valeur entière correspondant aux bits lus,
- * ou -1 si la fin du flux est atteinte prématurément
- * @throws IOException si une erreur de lecture survient
- */
- public int readBits(int nombreBits) throws IOException {
- int res=0;
- for (int i = 0; i < nombreBits; i++) {
- int bit = readBit();
- if (bit == -1) {
- return -1;
- }
- res = (res << 1) | bit;
- }
- return res;
- }
-
- /**
- * Ferme le flux d'entrée sous-jacent.
- *
- * @throws IOException si une erreur survient lors de la fermeture
- */
- public void closeFlux() throws IOException {
- this.fluxEntree.close();
- }
-
-
-
-}
+package fr.iutfbleau.sae.util;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Décorateur de flux permettant la lecture binaire à granularité du bit.
+ *
+ * Cette classe encapsule un {@link InputStream} existant et fournit
+ * des opérations de lecture bit par bit ou par groupes de bits.
+ * Elle ne gère ni l'ouverture ni la sélection du fichier source.
+ *
+ *
+ *
+ * Utilisée notamment pour le décodage des fichiers compressés
+ * (ex : format PIF utilisant des codes de Huffman).
+ *
+ *
+ *
+ *
+ * @author Algassimou Pellel Diallo
+ * @version 1.0
+ * @since 2025-12-13
+ */
+public class BitInputStream {
+
+ /** Flux d'entrée sous-jacent */
+ private final InputStream fluxEntree;
+
+ /** Octet actuellement chargé depuis le flux */
+ private int octetCourant;
+
+ /** Position du bit courant dans l'octet (du bit 7 au bit 0) */
+ private int positionBit;
+
+ /** Indique si la fin du flux a été atteinte */
+ private boolean finDeFlux;
+
+ /**
+ * Construit un lecteur binaire à partir d'un flux existant.
+ *
+ * @param fluxEntree flux d'entrée à décorer
+ * @throws IllegalArgumentException si le flux est nul
+ */
+ public BitInputStream(InputStream fluxEntree) {
+ if (fluxEntree == null) {
+ throw new IllegalArgumentException("Le flux d'entrée ne peut pas être nul");
+ }
+ this.fluxEntree = fluxEntree;
+ this.octetCourant = 0;
+ this.positionBit = -1; // force la lecture d'un nouvel octet
+ this.finDeFlux = false;
+ }
+
+ /**
+ * Lit un bit depuis le flux binaire.
+ *
+ * @return 0 ou 1 si un bit est lu, -1 si la fin du flux est atteinte
+ * @throws IOException si une erreur de lecture survient
+ */
+ public int readBit() throws IOException {
+ if (finDeFlux) {
+ return -1;
+ }
+
+ if (this.positionBit < 0) {
+ int octetLu = this.fluxEntree.read();
+ if (octetLu == -1) {
+ this.finDeFlux = true;
+ } else {
+ this.octetCourant = octetLu;
+ this.positionBit = 7;
+ }
+ }
+
+ if (finDeFlux) {
+ return -1;
+ }
+
+ int bit = (this.octetCourant >> this.positionBit) & 1;
+ this.positionBit--;
+ return bit;
+ }
+
+ /**
+ * Lit une séquence de bits consécutifs et les assemble dans un entier.
+ *
+ * @param nombreBits nombre de bits à lire (strictement positif)
+ * @return valeur entière correspondant aux bits lus,
+ * ou -1 si la fin du flux est atteinte prématurément
+ * @throws IOException si une erreur de lecture survient
+ */
+ public int readBits(int nombreBits) throws IOException {
+ int res=0;
+ for (int i = 0; i < nombreBits; i++) {
+ int bit = readBit();
+ if (bit == -1) {
+ return -1;
+ }
+ res = (res << 1) | bit;
+ }
+ return res;
+ }
+
+ /**
+ * Ferme le flux d'entrée sous-jacent.
+ *
+ * @throws IOException si une erreur survient lors de la fermeture
+ */
+ public void closeFlux() throws IOException {
+ this.fluxEntree.close();
+ }
+
+
+
+}
diff --git a/src/fr/iutfbleau/sae/util/ByteUtils.java b/src/fr/iutfbleau/sae/util/ByteUtils.java
index 8f0f8e4..864363b 100644
--- a/src/fr/iutfbleau/sae/util/ByteUtils.java
+++ b/src/fr/iutfbleau/sae/util/ByteUtils.java
@@ -1,89 +1,89 @@
-package fr.iutfbleau.sae.util;
-
-/**
- * Classe utilitaire regroupant des opérations de conversion entre
- * entiers et octets.
- *
- * Elle est utilisée pour encoder et décoder les champs binaires
- * du format PIF (largeur, hauteur, tailles, etc.).
- *
- *
- *
- * Cette classe :
- *
- * - ne lit aucun fichier
- * - n'écrit aucun fichier
- * - ne manipule pas les bits individuellement
- *
- * Elle fournit uniquement des conversions octets ↔ entiers.
- *
- */
-public final class ByteUtils {
-
- /**
- * Constructeur privé empêchant l'instanciation.
- *
- * Cette classe est purement utilitaire et ne doit pas être instanciée.
- *
- */
- private ByteUtils() {
- // j'empêche l'instanciation
- }
-
- /**
- * Convertit un entier non négatif en deux octets (ordre big-endian).
- *
- * L'octet de poids fort est placé en première position,
- * suivi de l'octet de poids faible.
- *
- *
- * @param value valeur entière à convertir (0 ≤ value ≤ 65535)
- * @return tableau de deux octets : [octetFort, octetFaible] M
- * @throws IllegalArgumentException si la valeur ne tient pas sur 2 octets
- */
- public static byte[] toBytes(int value) {
- if (value < 0 || value > 0xFFFF) {
- throw new IllegalArgumentException(
- "La valeur doit être comprise entre 0 et 65535"
- );
- }
-
- byte[] result = new byte[2];
-
- /*
- * Extraction de l'octet de poids fort :
- * - décalage de 8 bits vers la droite
- * - masquage pour ne conserver que les 8 bits utiles
- */
- result[0] = (byte) ((value >>> 8) & 0xFF);
-
- /*
- * Extraction de l'octet de poids faible :
- * - aucun décalage nécessaire
- * - masquage pour conserver les 8 bits de droite
- */
- result[1] = (byte) (value & 0xFF);
-
- return result;
- }
-
- /**
- * Reconstruit un entier à partir de deux octets (ordre big-endian).
- *
- * L'octet de poids fort est replacé dans les bits 15 à 8,
- * puis combiné avec l'octet de poids faible.
- *
- *
- * @param high octet de poids fort
- * @param low octet de poids faible
- * @return entier reconstruit à partir des deux octets
- */
- public static int toInt(byte high, byte low) {
- /*
- * - masquage pour supprimer le signe des octets Java
- * - décalage de l'octet fort vers la gauche
- * - combinaison des deux octets par un OU binaire
- */
- return ((high & 0xFF) << 8) | (low & 0xFF);
- }
-}
+package fr.iutfbleau.sae.util;
+
+/**
+ * Classe utilitaire regroupant des opérations de conversion entre
+ * entiers et octets.
+ *
+ * Elle est utilisée pour encoder et décoder les champs binaires
+ * du format PIF (largeur, hauteur, tailles, etc.).
+ *
+ *
+ *
+ * Cette classe :
+ *
+ * - ne lit aucun fichier
+ * - n'écrit aucun fichier
+ * - ne manipule pas les bits individuellement
+ *
+ * Elle fournit uniquement des conversions octets ↔ entiers.
+ *
+ */
+public final class ByteUtils {
+
+ /**
+ * Constructeur privé empêchant l'instanciation.
+ *
+ * Cette classe est purement utilitaire et ne doit pas être instanciée.
+ *
+ */
+ private ByteUtils() {
+ // j'empêche l'instanciation
+ }
+
+ /**
+ * Convertit un entier non négatif en deux octets (ordre big-endian).
+ *
+ * L'octet de poids fort est placé en première position,
+ * suivi de l'octet de poids faible.
+ *
+ *
+ * @param value valeur entière à convertir (0 ≤ value ≤ 65535)
+ * @return tableau de deux octets : [octetFort, octetFaible] M
+ * @throws IllegalArgumentException si la valeur ne tient pas sur 2 octets
+ */
+ public static byte[] toBytes(int value) {
+ if (value < 0 || value > 0xFFFF) {
+ throw new IllegalArgumentException(
+ "La valeur doit être comprise entre 0 et 65535"
+ );
+ }
+
+ byte[] result = new byte[2];
+
+ /*
+ * Extraction de l'octet de poids fort :
+ * - décalage de 8 bits vers la droite
+ * - masquage pour ne conserver que les 8 bits utiles
+ */
+ result[0] = (byte) ((value >>> 8) & 0xFF);
+
+ /*
+ * Extraction de l'octet de poids faible :
+ * - aucun décalage nécessaire
+ * - masquage pour conserver les 8 bits de droite
+ */
+ result[1] = (byte) (value & 0xFF);
+
+ return result;
+ }
+
+ /**
+ * Reconstruit un entier à partir de deux octets (ordre big-endian).
+ *
+ * L'octet de poids fort est replacé dans les bits 15 à 8,
+ * puis combiné avec l'octet de poids faible.
+ *
+ *
+ * @param high octet de poids fort
+ * @param low octet de poids faible
+ * @return entier reconstruit à partir des deux octets
+ */
+ public static int toInt(byte high, byte low) {
+ /*
+ * - masquage pour supprimer le signe des octets Java
+ * - décalage de l'octet fort vers la gauche
+ * - combinaison des deux octets par un OU binaire
+ */
+ return ((high & 0xFF) << 8) | (low & 0xFF);
+ }
+}
diff --git a/src/fr/iutfbleau/sae/util/GestErreur.java b/src/fr/iutfbleau/sae/util/GestErreur.java
index da4779a..be95dd0 100644
--- a/src/fr/iutfbleau/sae/util/GestErreur.java
+++ b/src/fr/iutfbleau/sae/util/GestErreur.java
@@ -1,8 +1,8 @@
-package fr.iutfbleau.sae.util;
-
-public class GestErreur {
- public static void erreur(String message) {
- System.err.println("Erreur : " + message);
- System.exit(1);
- }
+package fr.iutfbleau.sae.util;
+
+public class GestErreur {
+ public static void erreur(String message) {
+ System.err.println("Erreur : " + message);
+ System.exit(1);
+ }
}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/vconverter/CodeTablePanel.java b/src/fr/iutfbleau/sae/vconverter/CodeTablePanel.java
index 340a228..836d949 100644
--- a/src/fr/iutfbleau/sae/vconverter/CodeTablePanel.java
+++ b/src/fr/iutfbleau/sae/vconverter/CodeTablePanel.java
@@ -1,110 +1,110 @@
-package fr.iutfbleau.sae.vconverter;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.Map;
-
-/**
- * Panneau d'affichage des codes Huffman et canoniques.
- * Affiche les codes pour chaque composante de couleur (rouge, vert, bleu).
- * @author Algassimou
- */
-public class CodeTablePanel extends JPanel {
-
- // Zones de texte pour les codes Huffman
- private JTextArea textHuffRouge, textHuffVert, textHuffBleu;
-
- // Zones de texte pour les codes canoniques
- private JTextArea textCanonRouge, textCanonVert, textCanonBleu;
-
- /**
- * Constructeur qui initialise l'interface utilisateur.
- */
- public CodeTablePanel() {
- setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
- setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
-
- // Titre pour les codes Huffman
- JLabel titreHuff = new JLabel("Codes Huffman");
- titreHuff.setFont(new Font("SansSerif", Font.BOLD, 16));
- add(titreHuff);
- add(Box.createVerticalStrut(10));
-
- // Création des zones de texte pour les codes Huffman
- textHuffRouge = creerZoneTexte("Rouge");
- textHuffVert = creerZoneTexte("Vert");
- textHuffBleu = creerZoneTexte("Bleu");
-
- // Séparateur
- add(Box.createVerticalStrut(20));
-
- // Titre pour les codes canoniques
- JLabel titreCanon = new JLabel("Codes Canoniques");
- titreCanon.setFont(new Font("SansSerif", Font.BOLD, 16));
- add(titreCanon);
- add(Box.createVerticalStrut(10));
-
- // Création des zones de texte pour les codes canoniques
- textCanonRouge = creerZoneTexte("Rouge (Canonique)");
- textCanonVert = creerZoneTexte("Vert (Canonique)");
- textCanonBleu = creerZoneTexte("Bleu (Canonique)");
- }
-
- /**
- * Crée une zone de texte avec une étiquette.
- * @param titre Le titre à afficher au-dessus de la zone de texte
- * @return La zone de texte configurée
- */
- private JTextArea creerZoneTexte(String titre) {
- add(new JLabel(titre + ":"));
- JTextArea zone = new JTextArea(8, 30);
- zone.setEditable(false);
- zone.setFont(new Font("Monospaced", Font.PLAIN, 12));
- JScrollPane scroll = new JScrollPane(zone);
- scroll.setPreferredSize(new Dimension(300, 120));
- add(scroll);
- add(Box.createVerticalStrut(10));
- return zone;
- }
-
- /**
- * Met à jour l'affichage des codes Huffman.
- * @param rouge Les codes pour la composante rouge
- * @param vert Les codes pour la composante verte
- * @param bleu Les codes pour la composante bleue
- */
- public void updateCodes(Map rouge,
- Map vert,
- Map bleu) {
- mettreAJourZoneTexte(textHuffRouge, rouge);
- mettreAJourZoneTexte(textHuffVert, vert);
- mettreAJourZoneTexte(textHuffBleu, bleu);
- }
-
- /**
- * Met à jour l'affichage des codes canoniques.
- * @param rouge Les codes pour la composante rouge
- * @param vert Les codes pour la composante verte
- * @param bleu Les codes pour la composante bleue
- */
- public void updateCanonicalCodes(Map rouge,
- Map vert,
- Map bleu) {
- mettreAJourZoneTexte(textCanonRouge, rouge);
- mettreAJourZoneTexte(textCanonVert, vert);
- mettreAJourZoneTexte(textCanonBleu, bleu);
- }
-
- /**
- * Met à jour le contenu d'une zone de texte avec les codes fournis.
- * @param zone La zone de texte à mettre à jour
- * @param codes Les codes à afficher
- */
- private void mettreAJourZoneTexte(JTextArea zone, Map codes) {
- StringBuilder sb = new StringBuilder();
- for (Map.Entry entry : codes.entrySet()) {
- sb.append(String.format("%3d : %s%n", entry.getKey(), entry.getValue()));
- }
- zone.setText(sb.toString());
- }
+package fr.iutfbleau.sae.vconverter;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Map;
+
+/**
+ * Panneau d'affichage des codes Huffman et canoniques.
+ * Affiche les codes pour chaque composante de couleur (rouge, vert, bleu).
+ * @author Algassimou
+ */
+public class CodeTablePanel extends JPanel {
+
+ // Zones de texte pour les codes Huffman
+ private JTextArea textHuffRouge, textHuffVert, textHuffBleu;
+
+ // Zones de texte pour les codes canoniques
+ private JTextArea textCanonRouge, textCanonVert, textCanonBleu;
+
+ /**
+ * Constructeur qui initialise l'interface utilisateur.
+ */
+ public CodeTablePanel() {
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+
+ // Titre pour les codes Huffman
+ JLabel titreHuff = new JLabel("Codes Huffman");
+ titreHuff.setFont(new Font("SansSerif", Font.BOLD, 16));
+ add(titreHuff);
+ add(Box.createVerticalStrut(10));
+
+ // Création des zones de texte pour les codes Huffman
+ textHuffRouge = creerZoneTexte("Rouge");
+ textHuffVert = creerZoneTexte("Vert");
+ textHuffBleu = creerZoneTexte("Bleu");
+
+ // Séparateur
+ add(Box.createVerticalStrut(20));
+
+ // Titre pour les codes canoniques
+ JLabel titreCanon = new JLabel("Codes Canoniques");
+ titreCanon.setFont(new Font("SansSerif", Font.BOLD, 16));
+ add(titreCanon);
+ add(Box.createVerticalStrut(10));
+
+ // Création des zones de texte pour les codes canoniques
+ textCanonRouge = creerZoneTexte("Rouge (Canonique)");
+ textCanonVert = creerZoneTexte("Vert (Canonique)");
+ textCanonBleu = creerZoneTexte("Bleu (Canonique)");
+ }
+
+ /**
+ * Crée une zone de texte avec une étiquette.
+ * @param titre Le titre à afficher au-dessus de la zone de texte
+ * @return La zone de texte configurée
+ */
+ private JTextArea creerZoneTexte(String titre) {
+ add(new JLabel(titre + ":"));
+ JTextArea zone = new JTextArea(8, 30);
+ zone.setEditable(false);
+ zone.setFont(new Font("Monospaced", Font.PLAIN, 12));
+ JScrollPane scroll = new JScrollPane(zone);
+ scroll.setPreferredSize(new Dimension(300, 120));
+ add(scroll);
+ add(Box.createVerticalStrut(10));
+ return zone;
+ }
+
+ /**
+ * Met à jour l'affichage des codes Huffman.
+ * @param rouge Les codes pour la composante rouge
+ * @param vert Les codes pour la composante verte
+ * @param bleu Les codes pour la composante bleue
+ */
+ public void updateCodes(Map rouge,
+ Map vert,
+ Map bleu) {
+ mettreAJourZoneTexte(textHuffRouge, rouge);
+ mettreAJourZoneTexte(textHuffVert, vert);
+ mettreAJourZoneTexte(textHuffBleu, bleu);
+ }
+
+ /**
+ * Met à jour l'affichage des codes canoniques.
+ * @param rouge Les codes pour la composante rouge
+ * @param vert Les codes pour la composante verte
+ * @param bleu Les codes pour la composante bleue
+ */
+ public void updateCanonicalCodes(Map rouge,
+ Map vert,
+ Map bleu) {
+ mettreAJourZoneTexte(textCanonRouge, rouge);
+ mettreAJourZoneTexte(textCanonVert, vert);
+ mettreAJourZoneTexte(textCanonBleu, bleu);
+ }
+
+ /**
+ * Met à jour le contenu d'une zone de texte avec les codes fournis.
+ * @param zone La zone de texte à mettre à jour
+ * @param codes Les codes à afficher
+ */
+ private void mettreAJourZoneTexte(JTextArea zone, Map codes) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry entry : codes.entrySet()) {
+ sb.append(String.format("%3d : %s%n", entry.getKey(), entry.getValue()));
+ }
+ zone.setText(sb.toString());
+ }
}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/vconverter/ConverterWindow.java b/src/fr/iutfbleau/sae/vconverter/ConverterWindow.java
index 9fab33b..09df126 100644
--- a/src/fr/iutfbleau/sae/vconverter/ConverterWindow.java
+++ b/src/fr/iutfbleau/sae/vconverter/ConverterWindow.java
@@ -1,161 +1,161 @@
-package fr.iutfbleau.sae.vconverter;
-import java.awt.image.BufferedImage;
-import java.util.Map;
-import java.awt.*;
-import javax.swing.*;
-
-import fr.iutfbleau.sae.ConverterController;
-import fr.iutfbleau.sae.ExportButtonListener;
-
-/**
- * Fenêtre principale du convertisseur.
- *
- *
- * Cette classe correspond à la vue principale de l’application.
- * Elle centralise l’affichage des informations liées à la conversion
- * d’une image (aperçu, fréquences, codes).
- *
- *
- *
- *
- * Elle sert de point d’entrée unique pour la partie graphique
- *
- */
-public class ConverterWindow extends JFrame {
-
- private ImagePreviewPanel imagePreviewPanel;
- private FrequencyTablePanel frequencyTablePanel;
- private CodeTablePanel codeTablePanel;
-
-
-
- /**
- * Crée la fenêtre principale du convertisseur.
- *
- *
- * Le constructeur initialise la fenêtre et met en place
- * les différents panneaux graphiques utilisés pour l’affichage.
- *
- */
-
- public ConverterWindow() {
- // Configuration de la fenetre
- this.setTitle("Convertisseur PIF - Visualisation des données");
- this.setSize(900, 600);
- this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- this.setLocationRelativeTo(null); // Centre la fenêtre
- this.setResizable(true); // on autorise le
- this.setLayout(new BorderLayout());
-
-
-
- // Initialisation des panels
- this.imagePreviewPanel = new ImagePreviewPanel();
- this.frequencyTablePanel = new FrequencyTablePanel();
- this.codeTablePanel = new CodeTablePanel();
-
- // Je gere le panel principal
- JPanel contentPanel = new JPanel();
- contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
- //contentPanel.setBackground(new Color(255, 0, 0)); // rouge vif pour demo
- contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
-
- // Titre
- JLabel header = new JLabel(" Convertisseur PIF – Visualisation des données ");
- header.setFont(new Font("SansSerif", Font.BOLD, 18));
- header.setAlignmentX(Component.CENTER_ALIGNMENT);
- contentPanel.add(header);
-
- contentPanel.add(Box.createRigidArea(new Dimension(0, 5))); // espace
-
- // Ajout du panel d'aperçu
- contentPanel.add(imagePreviewPanel);
- contentPanel.add(Box.createRigidArea(new Dimension(0, 5)));
- // Ajout du panel des fréquences
- contentPanel.add(frequencyTablePanel);
- contentPanel.add(Box.createRigidArea(new Dimension(0, 5)));
- // Ajout panel des codes
- contentPanel.add(codeTablePanel);
- contentPanel.add(Box.createRigidArea(new Dimension(0, 5)));
- // la section du scrollpane
- JScrollPane scrollPane = new JScrollPane(contentPanel);
- scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
- scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
- scrollPane.getVerticalScrollBar().setUnitIncrement(16); // scroll plus adouci fluide
-
- this.add(scrollPane, BorderLayout.CENTER);
- this.setVisible(true);
- }
-
-
-
- /**
- * Met à jour l’image affichée dans la zone d’aperçu.
- *
- *
- * Cette méthode est appelée lorsque l’image à convertir
- * a été chargée. La fenêtre ne modifie pas l’image :
- * elle la transmet simplement au panneau d’aperçu.
- *
- *
- * @param img image à afficher
- */
- public void setImagePreview(BufferedImage img) {
- imagePreviewPanel.setImage(img);
- }
-
- /**
- * Met à jour l’affichage des tables de fréquences.
- */
- public void setFrequencyTable(int[] freqR,int[] freqG,int[] freqB) {
- frequencyTablePanel.updateFrequencies(freqR,freqG,freqB);
- }
-
- /**
- * Met à jour l’affichage des codes Huffman.
- *
- *
- * Elle permet uniquement d’afficher les codes
- * qui ont été produits par la partie traitement.
- *
- */
- public void setHuffmanTable(Map codesRouge,
- Map codesVert,
- Map codesBleu) {
- codeTablePanel.updateCodes(codesRouge, codesVert, codesBleu);
- }
-
- /**
- * Met à jour l’affichage des codes canoniques.
- *
- *
- * Les codes canoniques sont transmis au panneau
- * chargé de leur affichage.
- *
- */
- public void setCanonicalTable(Map codesRouge,
- Map codesVert,
- Map codesBleu) {
- codeTablePanel.updateCanonicalCodes(codesRouge, codesVert, codesBleu);
- }
-
- public void addSaveButton(ConverterController controller) {
- JButton saveBtn = new JButton("Exporter en .pif");
- ExportButtonListener ecouteur =new ExportButtonListener(controller);
- saveBtn.addActionListener(ecouteur);
-
- // panneau du bas
- JPanel bottomPanel = new JPanel();
- bottomPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
- bottomPanel.add(saveBtn);
-
- // ajoute au bas de la fenêtre
- this.add(bottomPanel, BorderLayout.SOUTH);
-
- // rafraîchir l'affichage
- this.revalidate();
- this.repaint();
-}
-
-
-}
+package fr.iutfbleau.sae.vconverter;
+import java.awt.image.BufferedImage;
+import java.util.Map;
+import java.awt.*;
+import javax.swing.*;
+
+import fr.iutfbleau.sae.ConverterController;
+import fr.iutfbleau.sae.ExportButtonListener;
+
+/**
+ * Fenêtre principale du convertisseur.
+ *
+ *
+ * Cette classe correspond à la vue principale de l’application.
+ * Elle centralise l’affichage des informations liées à la conversion
+ * d’une image (aperçu, fréquences, codes).
+ *
+ *
+ *
+ *
+ * Elle sert de point d’entrée unique pour la partie graphique
+ *
+ */
+public class ConverterWindow extends JFrame {
+
+ private ImagePreviewPanel imagePreviewPanel;
+ private FrequencyTablePanel frequencyTablePanel;
+ private CodeTablePanel codeTablePanel;
+
+
+
+ /**
+ * Crée la fenêtre principale du convertisseur.
+ *
+ *
+ * Le constructeur initialise la fenêtre et met en place
+ * les différents panneaux graphiques utilisés pour l’affichage.
+ *
+ */
+
+ public ConverterWindow() {
+ // Configuration de la fenetre
+ this.setTitle("Convertisseur PIF - Visualisation des données");
+ this.setSize(900, 600);
+ this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ this.setLocationRelativeTo(null); // Centre la fenêtre
+ this.setResizable(true); // on autorise le
+ this.setLayout(new BorderLayout());
+
+
+
+ // Initialisation des panels
+ this.imagePreviewPanel = new ImagePreviewPanel();
+ this.frequencyTablePanel = new FrequencyTablePanel();
+ this.codeTablePanel = new CodeTablePanel();
+
+ // Je gere le panel principal
+ JPanel contentPanel = new JPanel();
+ contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
+ //contentPanel.setBackground(new Color(255, 0, 0)); // rouge vif pour demo
+ contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ // Titre
+ JLabel header = new JLabel(" Convertisseur PIF – Visualisation des données ");
+ header.setFont(new Font("SansSerif", Font.BOLD, 18));
+ header.setAlignmentX(Component.CENTER_ALIGNMENT);
+ contentPanel.add(header);
+
+ contentPanel.add(Box.createRigidArea(new Dimension(0, 5))); // espace
+
+ // Ajout du panel d'aperçu
+ contentPanel.add(imagePreviewPanel);
+ contentPanel.add(Box.createRigidArea(new Dimension(0, 5)));
+ // Ajout du panel des fréquences
+ contentPanel.add(frequencyTablePanel);
+ contentPanel.add(Box.createRigidArea(new Dimension(0, 5)));
+ // Ajout panel des codes
+ contentPanel.add(codeTablePanel);
+ contentPanel.add(Box.createRigidArea(new Dimension(0, 5)));
+ // la section du scrollpane
+ JScrollPane scrollPane = new JScrollPane(contentPanel);
+ scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ scrollPane.getVerticalScrollBar().setUnitIncrement(16); // scroll plus adouci fluide
+
+ this.add(scrollPane, BorderLayout.CENTER);
+ this.setVisible(true);
+ }
+
+
+
+ /**
+ * Met à jour l’image affichée dans la zone d’aperçu.
+ *
+ *
+ * Cette méthode est appelée lorsque l’image à convertir
+ * a été chargée. La fenêtre ne modifie pas l’image :
+ * elle la transmet simplement au panneau d’aperçu.
+ *
+ *
+ * @param img image à afficher
+ */
+ public void setImagePreview(BufferedImage img) {
+ imagePreviewPanel.setImage(img);
+ }
+
+ /**
+ * Met à jour l’affichage des tables de fréquences.
+ */
+ public void setFrequencyTable(int[] freqR,int[] freqG,int[] freqB) {
+ frequencyTablePanel.updateFrequencies(freqR,freqG,freqB);
+ }
+
+ /**
+ * Met à jour l’affichage des codes Huffman.
+ *
+ *
+ * Elle permet uniquement d’afficher les codes
+ * qui ont été produits par la partie traitement.
+ *
+ */
+ public void setHuffmanTable(Map codesRouge,
+ Map codesVert,
+ Map codesBleu) {
+ codeTablePanel.updateCodes(codesRouge, codesVert, codesBleu);
+ }
+
+ /**
+ * Met à jour l’affichage des codes canoniques.
+ *
+ *
+ * Les codes canoniques sont transmis au panneau
+ * chargé de leur affichage.
+ *
+ */
+ public void setCanonicalTable(Map codesRouge,
+ Map codesVert,
+ Map codesBleu) {
+ codeTablePanel.updateCanonicalCodes(codesRouge, codesVert, codesBleu);
+ }
+
+ public void addSaveButton(ConverterController controller) {
+ JButton saveBtn = new JButton("Exporter en .pif");
+ ExportButtonListener ecouteur =new ExportButtonListener(controller);
+ saveBtn.addActionListener(ecouteur);
+
+ // panneau du bas
+ JPanel bottomPanel = new JPanel();
+ bottomPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+ bottomPanel.add(saveBtn);
+
+ // ajoute au bas de la fenêtre
+ this.add(bottomPanel, BorderLayout.SOUTH);
+
+ // rafraîchir l'affichage
+ this.revalidate();
+ this.repaint();
+}
+
+
+}
diff --git a/src/fr/iutfbleau/sae/vconverter/FrequencyTablePanel.java b/src/fr/iutfbleau/sae/vconverter/FrequencyTablePanel.java
index 0d0f1ca..dbe1f95 100644
--- a/src/fr/iutfbleau/sae/vconverter/FrequencyTablePanel.java
+++ b/src/fr/iutfbleau/sae/vconverter/FrequencyTablePanel.java
@@ -1,70 +1,70 @@
-package fr.iutfbleau.sae.vconverter;
-
-import javax.swing.*;
-import java.awt.*;
-
-
-public class FrequencyTablePanel extends JPanel {
- // 3 Zone de texte pour la fréquence du rouge , du vert et du bleu
- private JTextArea freqRouge , freqVert , freqBleu;
-
- public FrequencyTablePanel() {
- setLayout(new BoxLayout(this , BoxLayout.Y_AXIS));
- setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
-
- // Premiere étiquette pour les fréquences en géneral
- JLabel etiquette1 = new JLabel("Frequence");
- etiquette1.setFont(new Font("SansSerif", Font.BOLD, 16));
- super.add(etiquette1);
- super.add(Box.createVerticalStrut(10));
-
-
- // Puis création de zone de texte pour le rouge , le vert et le bleu
- this.freqRouge = creationZoneText("Rouge");
- this.freqVert = creationZoneText("Vert");
- this.freqBleu = creationZoneText("Bleu");
-
-
-
-
- }
-
- private JTextArea creationZoneText(String t) {
- super.add(new JLabel(t + ":"));
- GridLayout gestionnaire_mise_en_page = new GridLayout(5,5,10,10);
- JTextArea zone = new JTextArea(8, 30);
- zone.setLayout(gestionnaire_mise_en_page);
- zone.setEditable(false);
- zone.setFont(new Font("Monospaced", Font.PLAIN, 12));
- JScrollPane scroll = new JScrollPane(zone);
- scroll.setPreferredSize(new Dimension(300, 120));
- add(scroll);
- add(Box.createVerticalStrut(10));
- return zone;
- }
-
-
- public void updateFrequencies(int[] freqR,int[] freqG,int[] freqB) {
- mettreAJour(freqRouge,freqR);
- mettreAJour(freqVert,freqG);
- mettreAJour(freqBleu,freqB);
-
- }
-
-
- public void mettreAJour(JTextArea zone,int[] frequence){
- StringBuilder string = new StringBuilder();
-
- for(int i = 0 ; i < frequence.length ; i++){
- string.append(String.format("%3d : %s%n", i, frequence[i]));
-
- if(i%10 == 0 && i!=0){
- string.append("\n");
- }
-
- }
-
-
- zone.setText(string.toString());
- }
-}
+package fr.iutfbleau.sae.vconverter;
+
+import javax.swing.*;
+import java.awt.*;
+
+
+public class FrequencyTablePanel extends JPanel {
+ // 3 Zone de texte pour la fréquence du rouge , du vert et du bleu
+ private JTextArea freqRouge , freqVert , freqBleu;
+
+ public FrequencyTablePanel() {
+ setLayout(new BoxLayout(this , BoxLayout.Y_AXIS));
+ setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+
+ // Premiere étiquette pour les fréquences en géneral
+ JLabel etiquette1 = new JLabel("Frequence");
+ etiquette1.setFont(new Font("SansSerif", Font.BOLD, 16));
+ super.add(etiquette1);
+ super.add(Box.createVerticalStrut(10));
+
+
+ // Puis création de zone de texte pour le rouge , le vert et le bleu
+ this.freqRouge = creationZoneText("Rouge");
+ this.freqVert = creationZoneText("Vert");
+ this.freqBleu = creationZoneText("Bleu");
+
+
+
+
+ }
+
+ private JTextArea creationZoneText(String t) {
+ super.add(new JLabel(t + ":"));
+ GridLayout gestionnaire_mise_en_page = new GridLayout(5,5,10,10);
+ JTextArea zone = new JTextArea(8, 30);
+ zone.setLayout(gestionnaire_mise_en_page);
+ zone.setEditable(false);
+ zone.setFont(new Font("Monospaced", Font.PLAIN, 12));
+ JScrollPane scroll = new JScrollPane(zone);
+ scroll.setPreferredSize(new Dimension(300, 120));
+ add(scroll);
+ add(Box.createVerticalStrut(10));
+ return zone;
+ }
+
+
+ public void updateFrequencies(int[] freqR,int[] freqG,int[] freqB) {
+ mettreAJour(freqRouge,freqR);
+ mettreAJour(freqVert,freqG);
+ mettreAJour(freqBleu,freqB);
+
+ }
+
+
+ public void mettreAJour(JTextArea zone,int[] frequence){
+ StringBuilder string = new StringBuilder();
+
+ for(int i = 0 ; i < frequence.length ; i++){
+ string.append(String.format("%3d : %s%n", i, frequence[i]));
+
+ if(i%10 == 0 && i!=0){
+ string.append("\n");
+ }
+
+ }
+
+
+ zone.setText(string.toString());
+ }
+}
diff --git a/src/fr/iutfbleau/sae/vconverter/ImagePreviewPanel.java b/src/fr/iutfbleau/sae/vconverter/ImagePreviewPanel.java
index 803e11c..5123983 100644
--- a/src/fr/iutfbleau/sae/vconverter/ImagePreviewPanel.java
+++ b/src/fr/iutfbleau/sae/vconverter/ImagePreviewPanel.java
@@ -1,76 +1,76 @@
-package fr.iutfbleau.sae.vconverter;
-
-import java.awt.image.BufferedImage;
-import javax.swing.JPanel;
-import java.awt.*;
- /**
- * Le panneau d’aperçu de l’image.
- *
- *
- * Ce panneau affiche un aperçu de l’image en cours de conversion.
- *
- */
-
-
-public class ImagePreviewPanel extends JPanel {
-
- private BufferedImage image;
-
- // je donne une taille préférée au panel
- public ImagePreviewPanel() {
- this.setPreferredSize(new Dimension(600, 800));
- this.setMinimumSize(new Dimension(600, 800));
- }
-
-
- public void setImage(BufferedImage img) {
- this.image = img;
- repaint();
-
- }
-
- @Override
- protected void paintComponent(Graphics pinceau) {
- // Appel de la méthode parente pour effacer l'arrière-plan
- super.paintComponent(pinceau);
-
- if (image == null) {
- return;
- }
-
- // Recuperer les dimensions du panel pour centrer l'image
- int panelWidth = this.getWidth();
- int panelHeight = this.getHeight();
-
- // Recuperer les dimensions de l'image
- int imgWidth = image.getWidth();
- int imgHeight = image.getHeight();
-
- // Je calcule le facteur du reduction (si l'image est trop grande) en gros le dezoom
- double scale = Math.min(
- (double) panelWidth / imgWidth,
- (double) panelHeight / imgHeight
- );
-
- // Si l'image est plus petite que le panel, on ne la redimensionne pas donc scale = 1
- if (scale > 1.0) {
- scale = 1.0;
- }
-
- // je recalcule les dimensions de l'image à dessiner
- int drawWidth = (int) (imgWidth * scale);
- int drawHeight = (int) (imgHeight * scale);
-
- // Centrage de l'image dans le panel
- int x = (panelWidth - drawWidth) / 2;
- int y = (panelHeight - drawHeight) / 2;
-
- Graphics2D pinceau2D = (Graphics2D) pinceau;
- pinceau2D.setRenderingHint(
- RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR
- );
-
- pinceau2D.drawImage(image, x, y, drawWidth, drawHeight, this);
- }
+package fr.iutfbleau.sae.vconverter;
+
+import java.awt.image.BufferedImage;
+import javax.swing.JPanel;
+import java.awt.*;
+ /**
+ * Le panneau d’aperçu de l’image.
+ *
+ *
+ * Ce panneau affiche un aperçu de l’image en cours de conversion.
+ *
+ */
+
+
+public class ImagePreviewPanel extends JPanel {
+
+ private BufferedImage image;
+
+ // je donne une taille préférée au panel
+ public ImagePreviewPanel() {
+ this.setPreferredSize(new Dimension(600, 800));
+ this.setMinimumSize(new Dimension(600, 800));
+ }
+
+
+ public void setImage(BufferedImage img) {
+ this.image = img;
+ repaint();
+
+ }
+
+ @Override
+ protected void paintComponent(Graphics pinceau) {
+ // Appel de la méthode parente pour effacer l'arrière-plan
+ super.paintComponent(pinceau);
+
+ if (image == null) {
+ return;
+ }
+
+ // Recuperer les dimensions du panel pour centrer l'image
+ int panelWidth = this.getWidth();
+ int panelHeight = this.getHeight();
+
+ // Recuperer les dimensions de l'image
+ int imgWidth = image.getWidth();
+ int imgHeight = image.getHeight();
+
+ // Je calcule le facteur du reduction (si l'image est trop grande) en gros le dezoom
+ double scale = Math.min(
+ (double) panelWidth / imgWidth,
+ (double) panelHeight / imgHeight
+ );
+
+ // Si l'image est plus petite que le panel, on ne la redimensionne pas donc scale = 1
+ if (scale > 1.0) {
+ scale = 1.0;
+ }
+
+ // je recalcule les dimensions de l'image à dessiner
+ int drawWidth = (int) (imgWidth * scale);
+ int drawHeight = (int) (imgHeight * scale);
+
+ // Centrage de l'image dans le panel
+ int x = (panelWidth - drawWidth) / 2;
+ int y = (panelHeight - drawHeight) / 2;
+
+ Graphics2D pinceau2D = (Graphics2D) pinceau;
+ pinceau2D.setRenderingHint(
+ RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR
+ );
+
+ pinceau2D.drawImage(image, x, y, drawWidth, drawHeight, this);
+ }
}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/vviewer/ImagePanel.java b/src/fr/iutfbleau/sae/vviewer/ImagePanel.java
new file mode 100644
index 0000000..17f6cda
--- /dev/null
+++ b/src/fr/iutfbleau/sae/vviewer/ImagePanel.java
@@ -0,0 +1,7 @@
+package fr.iutfbleau.sae.vviewer;
+
+public class ImagePanel extends JPanel{
+
+
+
+}
\ No newline at end of file
diff --git a/src/fr/iutfbleau/sae/vviewer/ViewerWindow.java b/src/fr/iutfbleau/sae/vviewer/ViewerWindow.java
new file mode 100644
index 0000000..9df89f0
--- /dev/null
+++ b/src/fr/iutfbleau/sae/vviewer/ViewerWindow.java
@@ -0,0 +1,11 @@
+package fr.iutfbleau.sae.vviewer;
+
+public class ViewerWindow extends JFrame{
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/temp/learning-Log.txt b/temp/learning-Log.txt
index b85448d..ec1e78d 100644
--- a/temp/learning-Log.txt
+++ b/temp/learning-Log.txt
@@ -1,50 +1,50 @@
-Learning log – Problème de blocage de la fenêtre après la sauvegarde:
-
- Après avoir implémenté la fonctionnalité d’export au format PIF depuis l’interface du convertisseur,
- j’ai rencontré un problème important : une fois que je cliquais sur le bouton d’exportation,
- la fenêtre se figeait complètement. Elle restait visible, mais impossible à fermer ou à interagir avec.
- Le programme semblait bloqué.
-
- En analysant le comportement et en utilisant des impressions de debug, j’ai constaté que le blocage n’était pas lié à l’écriture du
- fichier ni à la logique du convertisseur, mais bien à un problème de gestion des threads dans Swing.
-
- Swing repose sur un fonctionnement particulier : toute l’interface graphique est gérée par un seul thread dédié,
- appelé l’Event Dispatch Thread (EDT). Ce thread est responsable de tout ce qui concerne l’interface utilisateur :
- la gestion des clics, le rafraîchissement de la fenêtre, la fermeture, le dessin et l’affichage en général.
- Tant que ce thread tourne correctement, l’application reste réactive.
-
- L’EDT ne démarre réellement qu’après l’appel à la méthode permettant d’afficher la fenêtre. À partir de ce moment,
- toutes les opérations qui modifient l’interface devraient strictement être exécutées sur ce thread.
- C’est une règle fondamentale pour éviter les blocages.
-
- En examinant mon programme, je me suis rendu compte que le problème venait de la manière dont j’avais structuré mon point d’entrée.
- Mon programme principal créait la fenêtre, le contrôleur, puis lançait immédiatement tout le processus de conversion,
- qui incluait le chargement du fichier image, le calcul des fréquences, la construction des arbres de Huffman,
- la génération des codes canoniques, et éventuellement l’écriture du fichier PIF.
- Ce sont des opérations potentiellement longues et qui se déroulaient sur le thread principal,
- avant même que l’EDT ne prenne le relais pour gérer l’interface.
-
- Cela avait pour conséquences que Swing se retrouvait dans un état instable, puisque certaines opérations graphiques avaient été réalisées
- hors du thread dédié.
- C’est exactement ce qui provoquait le gel de l’interface : une fois le fichier enregistré, la fenêtre ne répondait plus car
- l’EDT était bloqué ou interrompu, empêchant toute interaction, y compris la fermeture de la fenêtre.
-
- Une fois le problème identifié, les solutions étaient claires. Il fallait s’assurer que toutes les opérations qui touchent
- a l’interface graphique soient exécutées sur l’Event Dispatch Thread. Cela signifie que toute interaction, y compris l’ouverture
- d’un sélecteur de fichiers, doit obligatoirement être déclenchée dans ce contexte. De plus, il fallait veiller à ne pas exécuter
- de longues opérations synchrones avant le démarrage complet de l’EDT.
-
- La solution consiste donc à déléguer l’appel du processus de conversion au thread graphique, en utilisant le mécanisme fourni par
- Swing pour garantir que le code s’exécute sur l’EDT. Une autre possibilité serait d’exécuter les opérations lourdes dans un thread en
- arrière-plan pour éviter de bloquer l’interface, mais dans tous les cas le respect strict de la séparation entre traitements et interface
- est essentiel.
-
- Grâce à cette analyse, j’ai mieux compris la manière dont Swing gère les threads et j’ai pu corriger la structure de mon programme
- afin qu’il reste totalement réactif, même après l’export. Cette expérience m’a rappelé l’importance de maîtriser les principes fondamentaux
- des bibliothèques graphiques et leurs contraintes en matière de multithreading.
-
-
-
-
-
+Learning log – Problème de blocage de la fenêtre après la sauvegarde:
+
+ Après avoir implémenté la fonctionnalité d’export au format PIF depuis l’interface du convertisseur,
+ j’ai rencontré un problème important : une fois que je cliquais sur le bouton d’exportation,
+ la fenêtre se figeait complètement. Elle restait visible, mais impossible à fermer ou à interagir avec.
+ Le programme semblait bloqué.
+
+ En analysant le comportement et en utilisant des impressions de debug, j’ai constaté que le blocage n’était pas lié à l’écriture du
+ fichier ni à la logique du convertisseur, mais bien à un problème de gestion des threads dans Swing.
+
+ Swing repose sur un fonctionnement particulier : toute l’interface graphique est gérée par un seul thread dédié,
+ appelé l’Event Dispatch Thread (EDT). Ce thread est responsable de tout ce qui concerne l’interface utilisateur :
+ la gestion des clics, le rafraîchissement de la fenêtre, la fermeture, le dessin et l’affichage en général.
+ Tant que ce thread tourne correctement, l’application reste réactive.
+
+ L’EDT ne démarre réellement qu’après l’appel à la méthode permettant d’afficher la fenêtre. À partir de ce moment,
+ toutes les opérations qui modifient l’interface devraient strictement être exécutées sur ce thread.
+ C’est une règle fondamentale pour éviter les blocages.
+
+ En examinant mon programme, je me suis rendu compte que le problème venait de la manière dont j’avais structuré mon point d’entrée.
+ Mon programme principal créait la fenêtre, le contrôleur, puis lançait immédiatement tout le processus de conversion,
+ qui incluait le chargement du fichier image, le calcul des fréquences, la construction des arbres de Huffman,
+ la génération des codes canoniques, et éventuellement l’écriture du fichier PIF.
+ Ce sont des opérations potentiellement longues et qui se déroulaient sur le thread principal,
+ avant même que l’EDT ne prenne le relais pour gérer l’interface.
+
+ Cela avait pour conséquences que Swing se retrouvait dans un état instable, puisque certaines opérations graphiques avaient été réalisées
+ hors du thread dédié.
+ C’est exactement ce qui provoquait le gel de l’interface : une fois le fichier enregistré, la fenêtre ne répondait plus car
+ l’EDT était bloqué ou interrompu, empêchant toute interaction, y compris la fermeture de la fenêtre.
+
+ Une fois le problème identifié, les solutions étaient claires. Il fallait s’assurer que toutes les opérations qui touchent
+ a l’interface graphique soient exécutées sur l’Event Dispatch Thread. Cela signifie que toute interaction, y compris l’ouverture
+ d’un sélecteur de fichiers, doit obligatoirement être déclenchée dans ce contexte. De plus, il fallait veiller à ne pas exécuter
+ de longues opérations synchrones avant le démarrage complet de l’EDT.
+
+ La solution consiste donc à déléguer l’appel du processus de conversion au thread graphique, en utilisant le mécanisme fourni par
+ Swing pour garantir que le code s’exécute sur l’EDT. Une autre possibilité serait d’exécuter les opérations lourdes dans un thread en
+ arrière-plan pour éviter de bloquer l’interface, mais dans tous les cas le respect strict de la séparation entre traitements et interface
+ est essentiel.
+
+ Grâce à cette analyse, j’ai mieux compris la manière dont Swing gère les threads et j’ai pu corriger la structure de mon programme
+ afin qu’il reste totalement réactif, même après l’export. Cette expérience m’a rappelé l’importance de maîtriser les principes fondamentaux
+ des bibliothèques graphiques et leurs contraintes en matière de multithreading.
+
+
+
+
+
le proble aussi avec mon flush infini
\ No newline at end of file
diff --git a/temp/test.pif b/temp/test.pif
new file mode 100644
index 0000000000000000000000000000000000000000..49ed9a391dc3d595431de4604e608651b9b5b628
GIT binary patch
literal 10965990
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