US-D1 et US-D2
This commit is contained in:
@@ -0,0 +1,173 @@
|
|||||||
|
@startuml
|
||||||
|
skinparam packageStyle rectangle
|
||||||
|
skinparam classAttributeIconSize 0
|
||||||
|
|
||||||
|
|
||||||
|
' ============================
|
||||||
|
' PACKAGE mimage
|
||||||
|
' ============================
|
||||||
|
package mimage {
|
||||||
|
|
||||||
|
class Pixel {
|
||||||
|
- int r
|
||||||
|
- int g
|
||||||
|
- int b
|
||||||
|
+ Pixel(int r, int g, int b)
|
||||||
|
+ int getR()
|
||||||
|
+ int getG()
|
||||||
|
+ int getB()
|
||||||
|
+ void setR(int r)
|
||||||
|
+ void setG(int g)
|
||||||
|
+ void setB(int b)
|
||||||
|
}
|
||||||
|
|
||||||
|
class RGBImage {
|
||||||
|
- int width
|
||||||
|
- int height
|
||||||
|
- Pixel[][] pixels
|
||||||
|
+ RGBImage(int width, int height)
|
||||||
|
+ int getWidth()
|
||||||
|
+ int getHeight()
|
||||||
|
+ Pixel getPixel(int x, int y)
|
||||||
|
+ void setPixel(int x, int y, Pixel p)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
' ============================
|
||||||
|
' PACKAGE mhuffman
|
||||||
|
' ============================
|
||||||
|
package mhuffman {
|
||||||
|
|
||||||
|
class FrequencyTable {
|
||||||
|
- int[] freqR
|
||||||
|
- int[] freqG
|
||||||
|
- int[] freqB
|
||||||
|
+ FrequencyTable()
|
||||||
|
+ void computeFromImage(RGBImage img)
|
||||||
|
+ int[] getRed()
|
||||||
|
+ int[] getGreen()
|
||||||
|
+ int[] getBlue()
|
||||||
|
}
|
||||||
|
|
||||||
|
class HuffmanNode {
|
||||||
|
+ int value
|
||||||
|
+ int frequency
|
||||||
|
+ HuffmanNode left
|
||||||
|
+ HuffmanNode right
|
||||||
|
+ boolean isLeaf()
|
||||||
|
}
|
||||||
|
|
||||||
|
class HuffmanTree {
|
||||||
|
- HuffmanNode root
|
||||||
|
+ HuffmanTree(FrequencyTable freq, char component)
|
||||||
|
+ Map<Integer,String> generateCodes()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CanonicalCode {
|
||||||
|
- Map<Integer,Integer> codeLengths
|
||||||
|
- Map<Integer,String> canonicalCodes
|
||||||
|
+ CanonicalCode(Map<Integer,String> huffmanCodes)
|
||||||
|
+ String getCode(int value)
|
||||||
|
+ int getLength(int value)
|
||||||
|
}
|
||||||
|
|
||||||
|
FrequencyTable --> RGBImage
|
||||||
|
HuffmanTree --> HuffmanNode
|
||||||
|
CanonicalCode --> HuffmanTree
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
' ============================
|
||||||
|
' PACKAGE util
|
||||||
|
' ============================
|
||||||
|
package util {
|
||||||
|
|
||||||
|
class BitInputStream {
|
||||||
|
- InputStream in
|
||||||
|
- int currentByte
|
||||||
|
- int bitPosition
|
||||||
|
+ BitInputStream(InputStream in)
|
||||||
|
+ int readBit()
|
||||||
|
+ int readBits(int n)
|
||||||
|
+ void close()
|
||||||
|
}
|
||||||
|
|
||||||
|
class BitOutputStream {
|
||||||
|
- OutputStream out
|
||||||
|
- int currentByte
|
||||||
|
- int bitCount
|
||||||
|
+ BitOutputStream(OutputStream out)
|
||||||
|
+ void writeBit(int bit)
|
||||||
|
+ void writeBits(int value, int n)
|
||||||
|
+ void flush()
|
||||||
|
+ void close()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByteUtils {
|
||||||
|
+ static int toInt(byte high, byte low)
|
||||||
|
+ static byte[] toBytes(int value)
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileUtils {
|
||||||
|
+ static boolean isPIFFile(File f)
|
||||||
|
+ static byte[] readBinaryFile(String path)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
' ============================
|
||||||
|
' PACKAGE vconverter
|
||||||
|
' ============================
|
||||||
|
package vconverter {
|
||||||
|
|
||||||
|
class ConverterWindow {
|
||||||
|
+ void setImagePreview(RGBImage img)
|
||||||
|
+ void setFrequencyTable(int[] r, int[] g, int[] b)
|
||||||
|
+ void setHuffmanTable(Map<Integer,String> red,
|
||||||
|
Map<Integer,String> green,
|
||||||
|
Map<Integer,String> blue)
|
||||||
|
+ void setCanonicalTable(CanonicalCode r, CanonicalCode g, CanonicalCode b)
|
||||||
|
}
|
||||||
|
|
||||||
|
class PreviewPanel {
|
||||||
|
- RGBImage image
|
||||||
|
+ void setImage(RGBImage img)
|
||||||
|
}
|
||||||
|
|
||||||
|
class FrequencyTablePanel {
|
||||||
|
+ void updateFrequencies(int[] r, int[] g, int[] b)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CodeTablePanel {
|
||||||
|
+ void updateCodes(Map<Integer,String> hR,
|
||||||
|
Map<Integer,String> hG,
|
||||||
|
Map<Integer,String> hB,
|
||||||
|
CanonicalCode cR,
|
||||||
|
CanonicalCode cG,
|
||||||
|
CanonicalCode cB)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConverterWindow --> PreviewPanel
|
||||||
|
ConverterWindow --> FrequencyTablePanel
|
||||||
|
ConverterWindow --> CodeTablePanel
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
' ============================
|
||||||
|
' CONTROLLER (Sprint 1)
|
||||||
|
' ============================
|
||||||
|
class ConverterController {
|
||||||
|
+ void loadImage(String path)
|
||||||
|
+ void computeFrequencies()
|
||||||
|
+ void computeHuffman()
|
||||||
|
+ void computeCanonical()
|
||||||
|
+ void saveAsPIF(String path) 'encore vide pour Sprint 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ConverterController --> RGBImage
|
||||||
|
ConverterController --> FrequencyTable
|
||||||
|
ConverterController --> HuffmanTree
|
||||||
|
ConverterController --> CanonicalCode
|
||||||
|
|
||||||
|
@enduml
|
||||||
+3
-3
@@ -18,8 +18,8 @@ Objectif : Mise en place des fondations techniques
|
|||||||
|
|
||||||
| US | Assigné | Statut | | Description |
|
| US | Assigné | Statut | | Description |
|
||||||
|----------|---------|--------|-----|-------------|
|
|----------|---------|--------|-----|-------------|
|
||||||
| US-D1 | AD | TODO | 🟥 | Implémenter BitInputStream (lecture bit par bit) |
|
| US-D1 | AD | TODO | 🟩 | Implémenter BitInputStream (lecture bit par bit) |
|
||||||
| US-D2 | AD | TODO | 🟥 | Implémenter BitOutputStream (écriture bit par bit) |
|
| US-D2 | AD | TODO | 🟩 | Implémenter BitOutputStream (écriture bit par bit) |
|
||||||
| US-D3 | AD | TODO | 🟥 | Générer les tables de fréquences RGB |
|
| US-D3 | AD | TODO | 🟥 | Générer les tables de fréquences RGB |
|
||||||
| US-D4 | AD | TODO | 🟥 | Construire l’arbre Huffman |
|
| US-D4 | AD | TODO | 🟥 | Construire l’arbre Huffman |
|
||||||
| US-D5 | AA | TODO | 🟥 | Générer les codes Huffman |
|
| US-D5 | AA | TODO | 🟥 | Générer les codes Huffman |
|
||||||
@@ -51,7 +51,7 @@ Objectif : Mise en place des fondations techniques
|
|||||||
|----------------|-------|----|
|
|----------------|-------|----|
|
||||||
| `BitInputStream.java` | Lecture bit par bit | US-D1 |
|
| `BitInputStream.java` | Lecture bit par bit | US-D1 |
|
||||||
| `BitOutputStream.java` | Écriture bit par bit | US-D2 |
|
| `BitOutputStream.java` | Écriture bit par bit | US-D2 |
|
||||||
| `ByteUtils.java` | Conversion int ↔ octets | — |
|
| `ByteUtils.java` | Conversion int octets | — |
|
||||||
| `FileUtils.java` | Méthodes utilitaires fichiers | US-U5 (indirect) |
|
| `FileUtils.java` | Méthodes utilitaires fichiers | US-U5 (indirect) |
|
||||||
|
|
||||||
### `src/vconverter/`
|
### `src/vconverter/`
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
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>
|
||||||
|
*/
|
||||||
|
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,119 @@
|
|||||||
|
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>
|
||||||
|
*/
|
||||||
|
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é");
|
||||||
|
}
|
||||||
|
while (this.positionBit >= 0) {
|
||||||
|
writeBit(0);
|
||||||
|
}
|
||||||
|
this.fluxSortie.flush(); // Force l'écriture dans le flux sous-jacent
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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é
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user