package fr.iutfbleau.sae; import fr.iutfbleau.sae.mhuffman.*; import fr.iutfbleau.sae.mpif.PIFWriter; import fr.iutfbleau.sae.mpif.Pixel; import fr.iutfbleau.sae.mpif.RGBImage; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Map; import javax.imageio.ImageIO; import javax.swing.JFileChooser; /** * Controleur pour la conversion d'images au format PIF. * Gere le chargement des images, le calcul des frequences, * la generation des codes de Huffman et la sauvegarde au format PIF. */ public class ConverterController { /** Image convertie en RGBImage */ private RGBImage image; /** Table de frequences pour chaque composante */ private FrequencyTable frequencyTable; /** Codes de Huffman pour la composante rouge */ private Map abrHuffmanR; /** Codes de Huffman pour la composante verte */ private Map abrHuffmanG; /** Codes de Huffman pour la composante bleue */ private Map abrHuffmanB; /** Codes canoniques pour la composante rouge */ private Map canonRED; /** Codes canoniques pour la composante verte */ private Map canonGREEN; /** Codes canoniques pour la composante bleue */ private Map canonBLUE; /** La fenetre du convertisseur */ private ConverterWindow fen; /** Chemin du fichier de sortie */ String outputPath; /** Chemin du fichier d'entree */ String inputPath; /** * Construit un nouveau controleur de conversion. * * @param fen la fenetre de l'interface graphique * @param in le chemin du fichier d'entree (peut etre null) * @param out le chemin du fichier de sortie (peut etre null) */ public ConverterController(ConverterWindow fen, String in, String out) { this.fen = fen; this.outputPath = out; this.inputPath = in; } /** * Charge une image depuis un fichier et la convertit en RGBImage. * Extrait les composantes RGB de chaque pixel et met a jour l'interface. * * @param file le fichier image a charger */ public void loadImage(File file) { try { // Lire l'image avec BufferedImage BufferedImage buffimage = ImageIO.read(file); if (buffimage == null) { throw new IllegalArgumentException("Le fichier specifie n'est pas une image valide."); } int w = buffimage.getWidth(); int h = buffimage.getHeight(); // Creer une RGBImage de la meme 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); // Extraire les composantes R, G, B int r = (rgb >> 16) & 0xFF; int g = (rgb >> 8) & 0xFF; int b = rgb & 0xFF; // Creer un pixel et l'ajouter a la RGBImage this.image.setPixel(x, y, new Pixel(r, g, b)); } } // Mettre a jour le GUI this.fen.setImagePreview(buffimage); } catch (IOException e) { GestionErreur.afficherErreur("Impossible de charger l'image: \n \t Verifier que le Fichier transmit correspond a une image"); System.exit(2); } } /** * Calcule les frequences d'apparition de chaque valeur RGB dans l'image. * Met a jour l'interface avec les tables de frequences calculees. */ public void computeFrequencies() { this.frequencyTable = new FrequencyTable(); if (this.image == null) { return; } this.frequencyTable.computeFromImage(this.image); // Mettre a jour le GUI avec les frequences int[] freqR = this.frequencyTable.getRed(); int[] freqG = this.frequencyTable.getGreen(); int[] freqB = this.frequencyTable.getBlue(); this.fen.setFrequencyTable(freqR, freqG, freqB); } /** * Genere les arbres de Huffman pour chaque composante RGB. * Cree les codes binaires optimaux bases sur les frequences calculees. * Met a jour l'interface avec les codes de Huffman generes. */ public void computeHuffman() { // Generation des arbres de Huffman pour chaque composante 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 a jour le GUI avec les codes de Huffman this.fen.setHuffmanTable(this.abrHuffmanR, this.abrHuffmanG, this.abrHuffmanB); } /** * Genere les codes canoniques a partir des codes de Huffman. * Les codes canoniques sont une forme normalisee des codes de Huffman * qui facilite le stockage et le decodage. * Met a jour l'interface avec les codes canoniques generes. */ public void computeCanonical() { CanonicalCode codeCanoniques = new CanonicalCode(); this.canonRED = codeCanoniques.generateCodes(this.abrHuffmanR); this.canonGREEN = codeCanoniques.generateCodes(this.abrHuffmanG); this.canonBLUE = codeCanoniques.generateCodes(this.abrHuffmanB); // Mettre a jour le GUI avec les codes canoniques this.fen.setCanonicalTable(this.canonRED, this.canonGREEN, this.canonBLUE); } /** * Sauvegarde l'image au format PIF. * Verifie que l'image et les codes canoniques sont disponibles avant * d'effectuer la sauvegarde. * * @param pathfile le chemin du fichier de sortie */ public void saveAsPIF(String pathfile) { // Verifier que l'image et les codes canoniques sont disponibles if (this.image == null || this.canonRED == null) { GestionErreur.afficherErreur("Impossible de sauvegarder : image ou codes canoniques manquants."); return; } try { PIFWriter ecriveur = new PIFWriter(); ecriveur.writeTOFile(pathfile, this.image, this.canonRED, this.canonGREEN, this.canonBLUE); } catch (Exception e) { GestionErreur.afficherErreur("Erreur lors de l'ecriture du fichier .pif : "); } } /** * Gere la sauvegarde via le bouton de l'interface. * Si un chemin de sortie est defini, sauvegarde directement. * Sinon, ouvre un selecteur de fichier pour que l'utilisateur choisisse * l'emplacement de sauvegarde. * La sauvegarde est effectuee dans un thread separe pour ne pas bloquer * l'interface graphique. */ public void saveViaBtn() { if (outputPath != null) { saveAsPIF(outputPath); GestionErreur.afficherInfo("Fichier sauvegarde avec succes. Chemin : " + outputPath); return; } // Sinon JFileChooser JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Enregistrer le fichier .pif"); if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { File fichierSelectionne = chooser.getSelectedFile(); ThreadSauvegardePIF thread = new ThreadSauvegardePIF(this, fichierSelectionne); thread.start(); } } /** * Lance le processus complet de conversion d'une image au format PIF. * Etapes : * 1. Chargement de l'image (depuis un fichier ou via selecteur) * 2. Calcul des frequences * 3. Generation des codes de Huffman * 4. Generation des codes canoniques * 5. Sauvegarde (automatique ou via bouton selon les parametres) */ public void conversionProcess() { // Chargement if (this.inputPath != null) { // Chargement direct depuis les arguments File file = new File(inputPath); if (!file.exists()) { GestionErreur.afficherErreur("Le fichier n'existe pas : " + inputPath); System.exit(1); return; } // j'essaye de charge limage try { this.loadImage(file); } catch (IllegalArgumentException e) { GestionErreur.afficherErreur("Le fichier specifie n'est pas une image valide : " + inputPath); } } else { // Sinon JFileChooser pour choisir l'image JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Choisissez une image"); if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { this.loadImage(chooser.getSelectedFile()); } else { GestionErreur.afficherErreur("Aucune image choisie. Arret du programme."); System.exit(1); return; } } // Placer la logique de conversion dans l'ordre computeFrequencies(); computeHuffman(); computeCanonical(); // Sauvegarder : si un second argument est donne, sauvegarde automatique if (this.outputPath != null) { this.saveAsPIF(this.outputPath); GestionErreur.afficherInfo("Fichier sauvegarde automatiquement : " + this.outputPath); } else { // Pas de deuxieme argument : ajouter un bouton pour choisir avec un JFileChooser fen.addSaveButton(this); } } /** * Retourne l'image actuellement chargee. * * @return l'image RGB chargee, ou null si aucune image n'est chargee */ public RGBImage getImage() { return this.image; } }