Merge branches 'dev' and 'dev' of https://grond.iut-fbleau.fr/dialloa/SAE32_2025 into dev
This commit is contained in:
+196
-1
@@ -1,3 +1,197 @@
|
||||
<<<<<<< HEAD
|
||||
# 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
|
||||
@@ -122,7 +316,7 @@ Objectif : Lecture du fichier `.pif` + visualisateur opérationnel
|
||||
|----------|---------|--------|-----|-------------|
|
||||
| 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-U1 | YB | WP | 🟨 | 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 |
|
||||
@@ -190,3 +384,4 @@ Objectif : Finalisation complète + livrable final
|
||||
- Diagrammes UML terminés
|
||||
- Makefile opérationnel
|
||||
- Projet soumis proprement sur Gitea
|
||||
>>>>>>> 40f71dddd52fc07edc1d47d48d56fd65a2a79fe3
|
||||
|
||||
+90
-90
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Cette classe gère le chargement des fichiers image et les opérations
|
||||
* de conversion associées. tel que
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
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<Integer, String> abrHuffmanR;
|
||||
private Map<Integer, String> abrHuffmanG;
|
||||
private Map<Integer, String> abrHuffmanB;
|
||||
|
||||
// Codes canoniques pour chaque composante
|
||||
private Map<Integer, String> canonRED;
|
||||
private Map<Integer, String> canonGREEN;
|
||||
private Map<Integer, String> 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.
|
||||
* <p>
|
||||
* Cette classe gère le chargement des fichiers image et les opérations
|
||||
* de conversion associées. tel que
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
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<Integer, String> abrHuffmanR;
|
||||
private Map<Integer, String> abrHuffmanG;
|
||||
private Map<Integer, String> abrHuffmanB;
|
||||
|
||||
// Codes canoniques pour chaque composante
|
||||
private Map<Integer, String> canonRED;
|
||||
private Map<Integer, String> canonGREEN;
|
||||
private Map<Integer, String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,36 @@
|
||||
<<<<<<< HEAD
|
||||
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");
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
//teste avec ia
|
||||
private void chargerFichier() {
|
||||
JTextArea zoneTexte = new JTextArea();
|
||||
zoneTexte.setEditable(false);
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(fichierPif))) {
|
||||
String ligne;
|
||||
while ((ligne = br.readLine()) != null) {
|
||||
zoneTexte.append(ligne + "\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Erreur lors de l'ouverture du fichier",
|
||||
"Erreur",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
add(new JScrollPane(zoneTexte), BorderLayout.CENTER);
|
||||
setVisible(true);
|
||||
}
|
||||
>>>>>>> 40f71dddd52fc07edc1d47d48d56fd65a2a79fe3
|
||||
|
||||
@@ -1,72 +1,61 @@
|
||||
package fr.iutfbleau.sae.mhuffman;
|
||||
import java.util.*;
|
||||
|
||||
public class CanonicalCode{
|
||||
//private Map<Integer, String> codeLengths = new HashMap<>();
|
||||
//private Map<Integer, String> canonicalCodes = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
public Map<Integer,String> generateCodes(Map<Integer,String> 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<Map.Entry<Integer, String>> 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<Map.Entry<Integer, String>>() {
|
||||
@Override
|
||||
public int compare(Map.Entry<Integer, String> arg1 ,Map.Entry<Integer, String> arg2) {
|
||||
|
||||
int length1 = arg1.getValue().length();
|
||||
int length2 = arg2.getValue().length();
|
||||
|
||||
if (length1 != length2) {
|
||||
return length1 - length2;
|
||||
}
|
||||
return arg1.getKey() - arg2.getKey();
|
||||
}
|
||||
});
|
||||
|
||||
Map<Integer,String> 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<Integer, String> 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<Integer,String> canonicalCodes,int value){
|
||||
return canonicalCodes.get(value);
|
||||
}
|
||||
|
||||
public int getLength(Map<Integer,String> codesH,int value){
|
||||
return codesH.get(value).length();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
package fr.iutfbleau.sae.mhuffman;
|
||||
import java.util.*;
|
||||
|
||||
public class CanonicalCode{
|
||||
//private Map<Integer, String> codeLengths = new HashMap<>();
|
||||
//private Map<Integer, String> canonicalCodes = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
public Map<Integer,String> generateCodes(Map<Integer,String> 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<Map.Entry<Integer, String>> liste = new ArrayList<>(codesHuffman.entrySet());
|
||||
|
||||
// ici on comparer par longueur de la valeur ou sinon par la clé
|
||||
|
||||
|
||||
|
||||
Collections.sort(liste, new ComparateurCanonique());
|
||||
|
||||
|
||||
|
||||
Map<Integer,String> 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<Integer, String> 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<Integer,String> canonicalCodes,int value){
|
||||
return canonicalCodes.get(value);
|
||||
}
|
||||
|
||||
public int getLength(Map<Integer,String> codesH,int value){
|
||||
return codesH.get(value).length();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package fr.iutfbleau.sae.mhuffman;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
|
||||
public class ComparateurCanonique implements Comparator<Map.Entry<Integer, String>> {
|
||||
|
||||
@Override
|
||||
public int compare(Map.Entry<Integer, String> entree1,Map.Entry<Integer, String> entree2) {
|
||||
|
||||
int longueur1 = entree1.getValue().length();
|
||||
int longueur2 = entree2.getValue().length();
|
||||
|
||||
if (longueur1 != longueur2) {
|
||||
return longueur1 - longueur2;
|
||||
}
|
||||
|
||||
return entree1.getKey() - entree2.getKey();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,11 +44,27 @@ public class PIFReader {
|
||||
|
||||
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) {
|
||||
// TODO: Implement canonical table reading
|
||||
}
|
||||
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<String,Integer> rebuildCanonical(int[] lengths) {
|
||||
// TODO: Implement canonical table reconstruction
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Utilisée notamment pour le décodage des fichiers compressés
|
||||
* (ex : format PIF utilisant des codes de Huffman).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Utilisée notamment pour le décodage des fichiers compressés
|
||||
* (ex : format PIF utilisant des codes de Huffman).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
package fr.iutfbleau.sae.util;
|
||||
|
||||
/**
|
||||
* Classe utilitaire regroupant des opérations de conversion entre
|
||||
* entiers et octets.
|
||||
* <p>
|
||||
* Elle est utilisée pour encoder et décoder les champs binaires
|
||||
* du format PIF (largeur, hauteur, tailles, etc.).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Cette classe :
|
||||
* <ul>
|
||||
* <li>ne lit aucun fichier</li>
|
||||
* <li>n'écrit aucun fichier</li>
|
||||
* <li>ne manipule pas les bits individuellement</li>
|
||||
* </ul>
|
||||
* Elle fournit uniquement des conversions octets ↔ entiers.
|
||||
* </p>
|
||||
*/
|
||||
public final class ByteUtils {
|
||||
|
||||
/**
|
||||
* Constructeur privé empêchant l'instanciation.
|
||||
* <p>
|
||||
* Cette classe est purement utilitaire et ne doit pas être instanciée.
|
||||
* </p>
|
||||
*/
|
||||
private ByteUtils() {
|
||||
// j'empêche l'instanciation
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit un entier non négatif en deux octets (ordre big-endian).
|
||||
* <p>
|
||||
* L'octet de poids fort est placé en première position,
|
||||
* suivi de l'octet de poids faible.
|
||||
* </p>
|
||||
*
|
||||
* @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).
|
||||
* <p>
|
||||
* L'octet de poids fort est replacé dans les bits 15 à 8,
|
||||
* puis combiné avec l'octet de poids faible.
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* Elle est utilisée pour encoder et décoder les champs binaires
|
||||
* du format PIF (largeur, hauteur, tailles, etc.).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Cette classe :
|
||||
* <ul>
|
||||
* <li>ne lit aucun fichier</li>
|
||||
* <li>n'écrit aucun fichier</li>
|
||||
* <li>ne manipule pas les bits individuellement</li>
|
||||
* </ul>
|
||||
* Elle fournit uniquement des conversions octets ↔ entiers.
|
||||
* </p>
|
||||
*/
|
||||
public final class ByteUtils {
|
||||
|
||||
/**
|
||||
* Constructeur privé empêchant l'instanciation.
|
||||
* <p>
|
||||
* Cette classe est purement utilitaire et ne doit pas être instanciée.
|
||||
* </p>
|
||||
*/
|
||||
private ByteUtils() {
|
||||
// j'empêche l'instanciation
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit un entier non négatif en deux octets (ordre big-endian).
|
||||
* <p>
|
||||
* L'octet de poids fort est placé en première position,
|
||||
* suivi de l'octet de poids faible.
|
||||
* </p>
|
||||
*
|
||||
* @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).
|
||||
* <p>
|
||||
* L'octet de poids fort est replacé dans les bits 15 à 8,
|
||||
* puis combiné avec l'octet de poids faible.
|
||||
* </p>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<Integer, String> rouge,
|
||||
Map<Integer, String> vert,
|
||||
Map<Integer, String> 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<Integer, String> rouge,
|
||||
Map<Integer, String> vert,
|
||||
Map<Integer, String> 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<Integer, String> codes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<Integer, String> 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<Integer, String> rouge,
|
||||
Map<Integer, String> vert,
|
||||
Map<Integer, String> 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<Integer, String> rouge,
|
||||
Map<Integer, String> vert,
|
||||
Map<Integer, String> 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<Integer, String> codes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<Integer, String> entry : codes.entrySet()) {
|
||||
sb.append(String.format("%3d : %s%n", entry.getKey(), entry.getValue()));
|
||||
}
|
||||
zone.setText(sb.toString());
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Elle sert de point d’entrée unique pour la partie graphique
|
||||
* </p>
|
||||
*/
|
||||
public class ConverterWindow extends JFrame {
|
||||
|
||||
private ImagePreviewPanel imagePreviewPanel;
|
||||
private FrequencyTablePanel frequencyTablePanel;
|
||||
private CodeTablePanel codeTablePanel;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Crée la fenêtre principale du convertisseur.
|
||||
*
|
||||
* <p>
|
||||
* Le constructeur initialise la fenêtre et met en place
|
||||
* les différents panneaux graphiques utilisés pour l’affichage.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* Elle permet uniquement d’afficher les codes
|
||||
* qui ont été produits par la partie traitement.
|
||||
* </p>
|
||||
*/
|
||||
public void setHuffmanTable(Map<Integer, String> codesRouge,
|
||||
Map<Integer, String> codesVert,
|
||||
Map<Integer, String> codesBleu) {
|
||||
codeTablePanel.updateCodes(codesRouge, codesVert, codesBleu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour l’affichage des codes canoniques.
|
||||
*
|
||||
* <p>
|
||||
* Les codes canoniques sont transmis au panneau
|
||||
* chargé de leur affichage.
|
||||
* </p>
|
||||
*/
|
||||
public void setCanonicalTable(Map<Integer, String> codesRouge,
|
||||
Map<Integer, String> codesVert,
|
||||
Map<Integer, String> 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.
|
||||
*
|
||||
* <p>
|
||||
* 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).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Elle sert de point d’entrée unique pour la partie graphique
|
||||
* </p>
|
||||
*/
|
||||
public class ConverterWindow extends JFrame {
|
||||
|
||||
private ImagePreviewPanel imagePreviewPanel;
|
||||
private FrequencyTablePanel frequencyTablePanel;
|
||||
private CodeTablePanel codeTablePanel;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Crée la fenêtre principale du convertisseur.
|
||||
*
|
||||
* <p>
|
||||
* Le constructeur initialise la fenêtre et met en place
|
||||
* les différents panneaux graphiques utilisés pour l’affichage.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* Elle permet uniquement d’afficher les codes
|
||||
* qui ont été produits par la partie traitement.
|
||||
* </p>
|
||||
*/
|
||||
public void setHuffmanTable(Map<Integer, String> codesRouge,
|
||||
Map<Integer, String> codesVert,
|
||||
Map<Integer, String> codesBleu) {
|
||||
codeTablePanel.updateCodes(codesRouge, codesVert, codesBleu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour l’affichage des codes canoniques.
|
||||
*
|
||||
* <p>
|
||||
* Les codes canoniques sont transmis au panneau
|
||||
* chargé de leur affichage.
|
||||
* </p>
|
||||
*/
|
||||
public void setCanonicalTable(Map<Integer, String> codesRouge,
|
||||
Map<Integer, String> codesVert,
|
||||
Map<Integer, String> 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>
|
||||
* Ce panneau affiche un aperçu de l’image en cours de conversion.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* Ce panneau affiche un aperçu de l’image en cours de conversion.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package fr.iutfbleau.sae.vviewer;
|
||||
|
||||
public class ImagePanel extends JPanel{
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package fr.iutfbleau.sae.vviewer;
|
||||
|
||||
public class ViewerWindow extends JFrame{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+49
-49
@@ -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
|
||||
Binary file not shown.
Reference in New Issue
Block a user