sae fini les gars

This commit is contained in:
AlgaLaptop
2026-01-07 19:27:03 +01:00
parent c8556d469a
commit bf03d0cf8d
137 changed files with 13871 additions and 1525 deletions
@@ -0,0 +1,115 @@
package fr.iutfbleau.sae.mpif;
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();
}
}
@@ -0,0 +1,151 @@
package fr.iutfbleau.sae.mpif;
import java.io.IOException;
import java.io.OutputStream;
/**
* Décorateur de flux permettant l'écriture binaire à granularité du bit.
* <p>
* Cette classe encapsule un {@link OutputStream} existant et permet
* l'écriture de bits individuellement ou par groupes.
* Les bits sont accumulés afin de former des octets avant écriture.
* </p>
* <p>
* Utilisée notamment pour l'encodage 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 BitOutputStream {
/** Flux de sortie sous-jacent */
private final OutputStream fluxSortie;
/** Octet en cours de construction */
private int octetEnConstruction;
/** Position du prochain bit à écrire (de 7 à 0) */
private int positionBit;
/** Indique si le flux est fermé */
private boolean fluxFerme;
/**
* Construit un écrivain binaire à partir d'un flux existant.
*
* @param fluxSortie flux de sortie à décorer
* @throws IllegalArgumentException si le flux est nul
*/
public BitOutputStream(OutputStream fluxSortie) {
if (fluxSortie == null) {
throw new IllegalArgumentException("Le flux de sortie ne peut pas être nul");
}
this.fluxSortie = fluxSortie;
this.octetEnConstruction = 0;
this.positionBit = 7;
this.fluxFerme = false;
}
/**
* Écrit un bit dans le flux binaire.
*
* @param bit bit à écrire (0 ou 1)
* @throws IOException si une erreur d'écriture survient
* @throws IllegalArgumentException si le bit n'est ni 0 ni 1
*/
public void writeBit(int bit) throws IOException {
if (bit != 0 && bit != 1) {
throw new IllegalArgumentException("Le bit doit être 0 ou 1");
}
if (fluxFerme) {
throw new IOException("Le flux de sortie est fermé");
}
if (bit == 1) {
this.octetEnConstruction = this.octetEnConstruction | (1 << this.positionBit);
}
this.positionBit--;
// si on atteint la fin de l'octet, on le grave dans le flux et rebolotte
if(this.positionBit < 0){
this.fluxSortie.write(this.octetEnConstruction);
this.octetEnConstruction = 0;
this.positionBit = 7;
}
}
/**
* Écrit une séquence de bits correspondant à une valeur entière.
*
* @param valeur valeur contenant les bits à écrire
* @param nombreBits nombre de bits à écrire (strictement positif)
* @throws IOException si une erreur d'écriture survient
*/
public void writeBits(int valeur, int nombreBits) throws IOException {
for (int i = nombreBits - 1; i >= 0; i--) {
int bit = (valeur >> i) & 1;
writeBit(bit);
}
}
/**
* Force l'écriture immédiate des données accumulées dans le flux sous-jacent.
*
* @throws IOException si une erreur survient lors du flush
*/
public void flush() throws IOException {
if (fluxFerme) {
throw new IOException("Le flux de sortie est fermé");
}
// Si l'octet nes pas vide on le complete avec des 0
if(this.positionBit < 7){
this.fluxSortie.write(this.octetEnConstruction);
this.octetEnConstruction = 0;
this.positionBit = 7;
}
this.fluxSortie.flush(); // Force l'écriture dans le flux sous-jacent dans le but de vider le buffer
}
/**
* Écrit une séquence de bits à partir d'une chaîne de '0' et '1'.
*
* @param codeBinaire chaîne contenant uniquement '0' et '1'
* @throws IOException si une erreur d'écriture survient
* @throws IllegalArgumentException si la chaîne contient autre chose que '0' ou '1'
*/
public void writeBitString(String codeBinaire) throws IOException {
if (codeBinaire == null) {
throw new IllegalArgumentException("Le code binaire ne peut pas être null");
}
for (int i = 0; i < codeBinaire.length(); i++) {
char c = codeBinaire.charAt(i);
if (c == '0') {
writeBit(0);
} else if (c == '1') {
writeBit(1);
} else {
throw new IllegalArgumentException("Le code binaire ne doit contenir que '0' et '1'");
}
}
}
/**
* Vide les buffers internes et ferme le flux de sortie.
*
* @throws IOException si une erreur survient lors de la fermeture
*/
public void fermerFlux() throws IOException {
// si le flux n'est pas déjà fermé
if (!fluxFerme) {
this.flush(); // compléter l'octet et forcer l'écriture
this.fluxSortie.close(); // fermer le flux sous-jacent
this.fluxFerme = true; // marquer le flux comme fermé
}
}
}
@@ -1,35 +1,41 @@
package fr.iutfbleau.sae.mpif;
import java.util.Comparator;
import java.util.Comparator;
import java.util.Map;
/**
* Comparateur pour trier les entrées (symbole, longueur) lors de la reconstruction
* des codes canoniques.
* Le tri s'effectue d'abord par longueur croissante, puis par symbole croissant
* en cas d'égalité de longueur.
* </p>
*
* Comparateur utilisé pendant la reconstruction des codes canoniques.
* Il permet de trier les paires (symbole, longueur) dans le bon ordre
* avant de générer les codes binaires.
*
* Le tri se fait en deux étapes :
* - d'abord par la longueur du code (ordre croissant)
* - si deux longueurs sont identiques, on trie par le symbole lui-même
*
* Ce comparateur est utilisé dans la méthode rebuildCanonical du PIFReader.
*/
public class ComparateurEntreeCanonique implements Comparator<Map.Entry<Integer, Integer>> {
/**
* Compare deux entrées (symbole, longueur).
*
* @param entree1 première entrée
* @param entree2 deuxième entrée
* @return un entier négatif, zéro, ou positif selon que la première entrée
* est inférieure, égale ou supérieure à la seconde
* Compare deux entrées contenant chacune un symbole et sa longueur de code.
* Le but est de les classer dans l'ordre nécessaire pour produire
* les codes canoniques dans l'ordre standard.
*
* @param entree1 première paire (symbole, longueur)
* @param entree2 deuxième paire (symbole, longueur)
* @return un entier indiquant si la première entrée doit être placée
* avant, au même niveau ou après la seconde
*/
@Override
public int compare(Map.Entry<Integer, Integer> entree1, Map.Entry<Integer, Integer> entree2) {
// Comparer d'abord par longueur (valeur)
int comparaisonLongueur = entree1.getValue().compareTo(entree2.getValue());
// Si les longueurs sont différentes, cest le critère principal
if (comparaisonLongueur != 0) {
return comparaisonLongueur;
}
// Si les longueurs sont égales, comparer par symbole (clé)
// Sinon, on départage avec le symbole (la clé)
return entree1.getKey().compareTo(entree2.getKey());
}
}
}
+61
View File
@@ -0,0 +1,61 @@
package fr.iutfbleau.sae.mpif;
/**
*
* Représente un nœud utilisé pour décoder les données dans un arbre binaire.
*
* Un nœud peut être :
* - un nœud interne : il possède un fils gauche et/ou un fils droit. Il ne contient pas de valeur utile.
* - une feuille : les deux fils sont null et le nœud contient une valeur (symbole entre 0 et 255).
*
* Cette structure est utilisée par PIFReader pour reconstruire l'arbre de décodage
* des codes canoniques et retrouver les valeurs des pixels.
*/
public class DecodeNode {
/** Fils gauche du nœud. Null si le nœud est une feuille. */
public DecodeNode left;
/** Fils droit du nœud. Null si le nœud est une feuille. */
public DecodeNode right;
/**
* Valeur associée à la feuille.
* Vaut -1 pour les nœuds internes qui ne représentent aucun symbole.
*/
public Integer value;
/**
* Constructeur d'un nœud interne vide.
*
* Le nœud ne contient pas de valeur et possède des pointeurs
* initialement à null pour ses enfants.
*/
public DecodeNode() {
this.left = null;
this.right = null;
this.value = -1;
}
/**
* Constructeur d'un nœud complet, utilisé pour créer une feuille ou un nœud interne.
*
* @param left fils gauche
* @param right fils droit
* @param value valeur associée si c'est une feuille, ou -1 pour un nœud interne
*/
public DecodeNode(DecodeNode left, DecodeNode right, Integer value) {
this.left = left;
this.right = right;
this.value = value;
}
/**
* Indique si le nœud est une feuille (aucun enfant).
*
* @return true si le nœud est une feuille, false sinon
*/
public boolean isLeaf() {
return this.left == null && this.right == null;
}
}
-6
View File
@@ -1,7 +1,5 @@
package fr.iutfbleau.sae.mpif;
import fr.iutfbleau.sae.util.BitInputStream;
import fr.iutfbleau.sae.util.DecodeNode;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -60,7 +58,6 @@ public class PIFReader {
RGBImage img = decodePixels(lecteur, trieR, trieG, trieB);
lecteur.closeFlux();
System.out.println("Fichier PIF lu avec succès : " + width + "x" + height);
return img;
}
@@ -75,7 +72,6 @@ public class PIFReader {
public void readHeader(BitInputStream in) throws IOException {
this.width = in.readBits(16);
this.height = in.readBits(16);
System.out.println("Dimensions lues : " + this.width + "x" + this.height);
}
@@ -105,8 +101,6 @@ public class PIFReader {
for (int i = 0; i < 256; i++){
this.lenB[i] = in.readBits(8);
}
System.out.println("Tables de longueurs lues");
}
+52 -3
View File
@@ -1,13 +1,34 @@
package fr.iutfbleau.sae.mpif;
import fr.iutfbleau.sae.util.BitOutputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
/**
* Cette classe permet d'écrire une image au format PIF.
* Le fichier PIF contient une en-tête avec la taille de l'image,
* les longueurs des codes canoniques, puis l'encodage des pixels
* en utilisant ces codes.
*
* Les méthodes de cette classe suivent l'ordre d'écriture du fichier :
* 1) écrire l'en-tête
* 2) écrire les tables de longueurs
* 3) encoder les pixels un par un
*/
public class PIFWriter {
/**
* Sauvegarde une image au format PIF dans un fichier.
* Le fichier est créé ou écrasé si il existe déjà.
*
* @param filepath chemin du fichier à écrire
* @param image l'image RGB à encoder
* @param canonR codes canoniques de la composante rouge
* @param canonG codes canoniques de la composante verte
* @param canonB codes canoniques de la composante bleue
* @throws Exception si une erreur survient dans l'écriture
*/
public void writeTOFile(String filepath, RGBImage image,
Map<Integer,String> canonR,
Map<Integer,String> canonG,
@@ -31,7 +52,15 @@ public class PIFWriter {
ecriveur.fermerFlux();
}
// Ecriture de l'en-tête du fichier PIF (largeur et hauteur)
/**
* Écrit l'en-tête du fichier PIF.
* L'en-tête contient uniquement la largeur et la hauteur,
* chacune codée sur 16 bits.
*
* @param out flux de sortie binaire
* @param width largeur de l'image
* @param height hauteur de l'image
*/
public void writeHeader(BitOutputStream out, int width, int height) {
try {
out.writeBits(width, 16);
@@ -41,6 +70,16 @@ public class PIFWriter {
}
}
/**
* Écrit dans le fichier les longueurs des codes canoniques.
* Pour chaque composante (R, G, B), on parcourt les 256 valeurs possibles.
* Si un symbole n'existe pas dans les codes, on écrit une longueur 0.
*
* @param out flux binaire vers le fichier
* @param canonR longueurs pour la composante rouge
* @param canonG longueurs pour la composante verte
* @param canonB longueurs pour la composante bleue
*/
public void writeTables(BitOutputStream out, Map<Integer, String> canonR,
Map<Integer, String> canonG, Map<Integer, String> canonB) {
@@ -80,7 +119,17 @@ public class PIFWriter {
}
}
// Méthode pour encoder les pixels de l'image en utilisant les codes canoniques
/**
* Encode chaque pixel de l'image en utilisant les codes canoniques.
* Pour chaque pixel, les composants R, G et B sont écrits
* dans cet ordre en utilisant writeBitString().
*
* @param out flux d'écriture binaire
* @param image l'image source
* @param canonRED codes canoniques pour le rouge
* @param canonGREEN codes canoniques pour le vert
* @param canonBLUE codes canoniques pour le bleu
*/
public void encodePixels(BitOutputStream out, RGBImage image,
Map<Integer, String> canonRED,
Map<Integer, String> canonGREEN,
+34 -5
View File
@@ -1,38 +1,67 @@
package fr.iutfbleau.sae.mpif;
/**
* Cette classe représente un pixel composé de trois valeurs :
* rouge, vert et bleu. Chaque valeur est comprise entre 0 et 255.
* La classe sert surtout pour manipuler les pixels dans une image RGB.
*/
public class Pixel{
private int r;
private int g;
private int b;
//à completer
/**
* Crée un pixel avec ses trois composantes.
*
* @param red valeur de la composante rouge
* @param green valeur de la composante verte
* @param blue valeur de la composante bleue
*/
public Pixel(int red, int green, int blue){
this.r=red;
this.g=green;
this.b=blue;
this.r = red;
this.g = green;
this.b = blue;
}
/**
* Retourne la composante bleue du pixel.
*/
public int getB() {
return b;
}
/**
* Retourne la composante verte du pixel.
*/
public int getG() {
return g;
}
/**
* Retourne la composante rouge du pixel.
*/
public int getR() {
return r;
}
/**
* Modifie la composante rouge du pixel.
*/
public void setR(int r) {
this.r = r;
}
/**
* Modifie la composante bleue du pixel.
*/
public void setB(int b) {
this.b = b;
}
/**
* Modifie la composante verte du pixel.
*/
public void setG(int g) {
this.g = g;
}
}
}
+35 -4
View File
@@ -1,31 +1,62 @@
package fr.iutfbleau.sae.mpif;
/**
* Cette classe représente une image composée de pixels RGB.
* L'image est stockée sous forme d'un tableau 2D contenant des objets Pixel.
* Chaque pixel possède trois valeurs : rouge, vert et bleu.
*/
public class RGBImage {
private int width;
private int height;
private Pixel [][] pixels;
/**
* Crée une image vide avec une largeur et une hauteur données.
* Tous les pixels doivent être ajoutés ensuite avec setPixel().
*
* @param lar largeur de l'image
* @param haut hauteur de l'image
*/
public RGBImage (int lar, int haut){
this.width=lar;
this.height=haut;
this.width = lar;
this.height = haut;
this.pixels = new Pixel[this.width][this.height];
}
/**
* Retourne la largeur de l'image.
*/
public int getWidth() {
return width;
}
/**
* Retourne la hauteur de l'image.
*/
public int getHeight() {
return height;
}
/**
* Place un pixel aux coordonnées indiquées.
*
* @param x position horizontale
* @param y position verticale
* @param p pixel à insérer
*/
public void setPixel(int x, int y, Pixel p) {
this.pixels[x][y] = p;
}
/**
* Retourne le pixel situé aux coordonnées indiquées.
*
* @param x position horizontale
* @param y position verticale
* @return le pixel présent à cette position
*/
public Pixel getPixel(int x, int y) {
return this.pixels[x][y];
}
}
}