Merge branches 'dev' and 'dev' of https://grond.iut-fbleau.fr/dialloa/SAE32_2025 into dev

This commit is contained in:
AlgaLaptop
2026-01-04 18:13:25 +01:00
29 changed files with 1826 additions and 1551 deletions
+225 -225
View File
@@ -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;
}
}
+24 -24
View File
@@ -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();
}
}
+17 -17
View File
@@ -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);
}
}
+13 -13
View File
@@ -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();
}
}
+32 -3
View File
@@ -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();
}
}
+37 -37
View File
@@ -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;
}
}
+29 -29
View File
@@ -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];
}
}
+23 -23
View File
@@ -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;
}
}
+18 -2
View File
@@ -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
+115 -115
View File
@@ -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();
}
}
+89 -89
View File
@@ -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);
}
}
+7 -7
View File
@@ -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 lapplication.
* Elle centralise laffichage des informations liées à la conversion
* dune image (aperçu, fréquences, codes).
* </p>
*
*
* <p>
* Elle sert de point dentré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 laffichage.
* </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 limage affichée dans la zone daperçu.
*
* <p>
* Cette méthode est appelée lorsque limage à convertir
* a été chargée. La fenêtre ne modifie pas limage :
* elle la transmet simplement au panneau daperçu.
* </p>
*
* @param img image à afficher
*/
public void setImagePreview(BufferedImage img) {
imagePreviewPanel.setImage(img);
}
/**
* Met à jour laffichage des tables de fréquences.
*/
public void setFrequencyTable(int[] freqR,int[] freqG,int[] freqB) {
frequencyTablePanel.updateFrequencies(freqR,freqG,freqB);
}
/**
* Met à jour laffichage des codes Huffman.
*
* <p>
* Elle permet uniquement dafficher 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 laffichage 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 lapplication.
* Elle centralise laffichage des informations liées à la conversion
* dune image (aperçu, fréquences, codes).
* </p>
*
*
* <p>
* Elle sert de point dentré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 laffichage.
* </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 limage affichée dans la zone daperçu.
*
* <p>
* Cette méthode est appelée lorsque limage à convertir
* a été chargée. La fenêtre ne modifie pas limage :
* elle la transmet simplement au panneau daperçu.
* </p>
*
* @param img image à afficher
*/
public void setImagePreview(BufferedImage img) {
imagePreviewPanel.setImage(img);
}
/**
* Met à jour laffichage des tables de fréquences.
*/
public void setFrequencyTable(int[] freqR,int[] freqG,int[] freqB) {
frequencyTablePanel.updateFrequencies(freqR,freqG,freqB);
}
/**
* Met à jour laffichage des codes Huffman.
*
* <p>
* Elle permet uniquement dafficher 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 laffichage 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 daperçu de limage.
*
* <p>
* Ce panneau affiche un aperçu de limage 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 daperçu de limage.
*
* <p>
* Ce panneau affiche un aperçu de limage 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{
}