Astuce Java 60: Enregistrement de fichiers bitmap en Java

Cette astuce complète l'astuce Java 43, qui illustre le processus de chargement de fichiers bitmap dans les applications Java. Ce mois-ci, je continue avec un tutoriel sur la façon d'enregistrer des images dans des fichiers bitmap 24 bits et un extrait de code que vous pouvez utiliser pour écrire un fichier bitmap à partir d'un objet image.

La possibilité de créer un fichier bitmap ouvre de nombreuses portes si vous travaillez dans un environnement Microsoft Windows. Sur mon dernier projet, par exemple, j'ai dû interfacer Java avec Microsoft Access. Le programme Java a permis à l'utilisateur de dessiner une carte sur l'écran. La carte a ensuite été imprimée dans un rapport Microsoft Access. Étant donné que Java ne prend pas en charge OLE, ma seule solution était de créer un fichier bitmap de la carte et d'indiquer au rapport Microsoft Access où le récupérer. Si vous avez déjà eu à écrire une application pour envoyer une image dans le presse-papiers, cette astuce peut vous être utile, surtout si ces informations sont transmises à une autre application Windows.

Le format d'un fichier bitmap

Le format de fichier bitmap prend en charge le RLE 4 bits (encodage de longueur d'exécution), ainsi que l'encodage 8 bits et 24 bits. Parce que nous ne traitons que du format 24 bits, examinons la structure du fichier.

Le fichier bitmap est divisé en trois sections. Je les ai présentés ci-dessous.

Section 1: En-tête de fichier Bitmap

Cet en-tête contient des informations sur la taille du type et la disposition du fichier bitmap. La structure est la suivante (tirée d'une définition de structure de langage C):

typedef struct tagBITMAPFILEHEADER {UINT bfType; DWORD bfSize; UINT bfReserved1; UINT bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;

Voici une description des éléments de code de la liste ci-dessus:

  • bfType: Indique le type du fichier et est toujours réglé sur BM.
  • bfSize: Spécifie la taille du fichier entier en octets.
  • bfReserved1: Réservé - doit être défini sur 0.
  • bfReserved2: Réservé - doit être défini sur 0.
  • bfOffBits: Spécifie le décalage d'octet entre le BitmapFileHeaderet le début de l'image.

Ici, vous avez vu que le but de l'en-tête bitmap est d'identifier le fichier bitmap. Chaque programme qui lit des fichiers bitmap utilise l'en-tête bitmap pour la validation de fichier.

Section 2: En-tête d'informations Bitmap

L'en-tête suivant, appelé l'en- tête d'information, contient toutes les propriétés de l'image elle-même.

Voici comment vous spécifiez des informations sur la dimension et le format de couleur d'un bitmap indépendant du périphérique Windows 3.0 (ou supérieur):

typedef struct tagBITMAPINFOHEADER {DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;

Chaque élément de la liste de codes ci-dessus est décrit ci-dessous:

  • biSize: Spécifie le nombre d'octets requis par la BITMAPINFOHEADERstructure.
  • biWidth: Spécifie la largeur du bitmap en pixels.
  • biHeight: Spécifie la hauteur du bitmap en pixels.
  • biPlanes: Spécifie le nombre de plans pour l'équipement cible. Ce membre doit être défini sur 1.
  • biBitCount: Spécifie le nombre de bits par pixel. Cette valeur doit être 1, 4, 8 ou 24.
  • biCompression: Spécifie le type de compression pour un bitmap compressé. Dans un format 24 bits, la variable est définie sur 0.
  • biSizeImage: spécifie la taille en octets de l'image. Il est valide de définir ce membre sur 0 si le bitmap est au BI_RGBformat.
  • biXPelsPerMeter: spécifie la résolution horizontale, en pixels par mètre, du périphérique cible pour le bitmap. Une application peut utiliser cette valeur pour sélectionner une image bitmap dans un groupe de ressources qui correspond le mieux aux caractéristiques du périphérique actuel.
  • biYPelsPerMeter: spécifie la résolution verticale, en pixels par mètre, du périphérique cible pour le bitmap.
  • biClrUsed: spécifie le nombre d'index de couleur dans la table de couleurs réellement utilisés par le bitmap. Si biBitCountest défini sur 24, biClrUsedspécifie la taille de la table des couleurs de référence utilisée pour optimiser les performances des palettes de couleurs Windows.
  • biClrImportant: spécifie le nombre d'index de couleur considérés comme importants pour l'affichage du bitmap. Si cette valeur est 0, toutes les couleurs sont importantes.

Maintenant, toutes les informations nécessaires pour créer l'image ont été définies.

Section 3: Image

Au format 24 bits, chaque pixel de l'image est représenté par une série de trois octets de RVB stockés en tant que BRG. Chaque ligne de balayage est complétée jusqu'à une limite paire de 4 octets. Pour compliquer un peu plus le processus, l'image est stockée de bas en haut, ce qui signifie que la première ligne de balayage est la dernière ligne de balayage de l'image. La figure suivante montre les en-têtes ( BITMAPHEADER) et ( BITMAPINFOHEADER) et une partie de l'image. Chaque section est délimitée par une barre verticale:

0000000000 4D42 B536 0002 0000 0000 0036 0000 | 0028 0000000020 0000 0107 0000 00E0 0000 0001 0018 0000 0000000040 0000 B500 0002 0EC4 0000 0EC4 0000 0000 0000000060 0000 0000 0000 | FFFF FFFF FFFF FFFF FFFF 0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF *

Maintenant, passons au code

Maintenant que nous savons tout sur la structure d'un fichier bitmap 24 bits, voici ce que vous attendiez: le code pour écrire un fichier bitmap à partir d'un objet image.

import java.awt. *; import java.io. *; import java.awt.image. *; classe publique BMPFile étend Component {// --- Constantes privées private final static int BITMAPFILEHEADER_SIZE = 14; int statique final privé BITMAPINFOHEADER_SIZE = 40; // --- Déclaration de variable privée // --- En-tête de fichier bitmap Octet privé bitmapFileHeader [] = nouvel octet [14]; octet privé bfType [] = {'B', 'M'}; private int bfSize = 0; private int bfReserved1 = 0; private int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- En-tête d'informations bitmap octet privé bitmapInfoHeader [] = nouvel octet [40]; private int biSize = BITMAPINFOHEADER_SIZE; private int biWidth = 0; privé int biHeight = 0; private int biPlanes = 1; private int biBitCount = 24; private int biCompression = 0; privé int biSizeImage = 0x030000;private int biXPelsPerMeter = 0x0; private int biYPelsPerMeter = 0x0; private int biClrUsed = 0; private int biClrImportant = 0; // --- Données brutes Bitmap private int bitmap []; // --- Section de fichier private FileOutputStream fo; // --- Constructeur par défaut public BMPFile () {} public void saveBitmap (String parFilename, Image parImage, int parWidth, int par parHeight) {try {fo = new FileOutputStream (parFilename); save (parImage, parWidth, parHeight); fo.close (); } catch (Exception saveEx) {saveEx.printStackTrace (); }} / * * La méthode saveMethod est la méthode principale du processus. Cette méthode * appellera la méthode convertImage pour convertir l'image mémoire en * un tableau d'octets; La méthode writeBitmapFileHeader crée et écrit * l'en-tête du fichier bitmap; writeBitmapInfoHeader crée l'en-tête d'information *; et writeBitmap écrit l'image.* * / private void save (Image parImage, int parWidth, int parHeight) {try {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } catch (Exception saveEx) {saveEx.printStackTrace (); }} / * * convertImage convertit l'image mémoire au format bitmap (BRG). * Il calcule également certaines informations pour l'en-tête d'informations bitmap. * * / private boolean convertImage (Image parImage, int parWidth, int parHeight) {int pad; bitmap = new int [parWidth * parHeight]; PixelGrabber pg = new PixelGrabber (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); essayez {pg.grabPixels (); } catch (InterruptedException e) {e.printStackTrace (); return (faux); } pad = (4 - ((parWidth * 3)% 4)) * parHeight; biSizeImage = ((parWidth * parHeight) * 3) + pad;bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight; return (vrai); } / * * writeBitmap convertit l'image renvoyée par le capteur de pixels * au format requis. Rappelez-vous: les lignes de scan sont inversées dans * un fichier bitmap! * * Chaque ligne de balayage doit être complétée jusqu'à une limite paire de 4 octets. * / private void writeBitmap () {taille int; valeur int; int j; int i; int rowCount; int rowIndex; int lastRowIndex; int pad; int padCount; byte rgb [] = nouvel octet [3]; taille = (biWidth * biHeight) - 1; pad = 4 - ((biWidth * 3)% 4); if (pad == 4) // <==== Bug correction pad = 0; // <==== Correction de bogue rowCount = 1; padCount = 0; rowIndex = taille - biWidth; lastRowIndex = rowIndex; essayez {pour (j = 0; j> 8) & 0xFF); rgb [2] = (octet) ((valeur >> 16) & 0xFF); fo.write (rgb);if (rowCount == biWidth) {padCount + = pad; pour (i = 1; i> 8) & 0x00FF); return (retValue); } / * * * intToDWord convertit un entier en un double mot, où la valeur de retour * est stockée dans un tableau de 4 octets. * * / private byte [] intToDWord (int parValue) {byte retValue [] = new byte [4]; retValue [0] = (octet) (parValue & 0x00FF); retValue [1] = (octet) ((parValue >> 8) & 0x000000FF); retValue [2] = (octet) ((parValue >> 16) & 0x000000FF); retValue [3] = (octet) ((parValue >> 24) & 0x000000FF); return (retValue); }}0x00FF); retValue [1] = (octet) ((parValue >> 8) & 0x000000FF); retValue [2] = (octet) ((parValue >> 16) & 0x000000FF); retValue [3] = (octet) ((parValue >> 24) & 0x000000FF); return (retValue); }}0x00FF); retValue [1] = (octet) ((parValue >> 8) & 0x000000FF); retValue [2] = (octet) ((parValue >> 16) & 0x000000FF); retValue [3] = (octet) ((parValue >> 24) & 0x000000FF); return (retValue); }}

Conclusion

C'est tout ce qu'on peut en dire. Je suis sûr que vous trouverez cette classe très utile, car à partir du JDK 1.1.6, Java ne prend pas en charge l'enregistrement d'images dans aucun des formats courants. JDK 1.2 offrira un support pour la création d'images JPEG, mais pas pour les bitmaps. Cette classe comblera donc toujours une lacune dans JDK 1.2.

Si vous jouez avec cette classe et trouvez des moyens de l'améliorer, faites-le moi savoir! Mon e-mail apparaît ci-dessous, avec ma bio.

Jean-Pierre Dubé est un consultant Java indépendant. Il a fondé Infocom, enregistré en 1988. Depuis, Infocom a développé plusieurs applications personnalisées allant de la fabrication, la gestion de documents et la gestion de lignes électriques à grande échelle. Il possède une vaste expérience de la programmation en C, Visual Basic et, plus récemment, Java, qui est désormais le principal langage utilisé par son entreprise. L'un des projets récents d'Infocom est une API de diagramme qui devrait bientôt être disponible en version bêta.

Cette histoire, "Astuce Java 60: Enregistrer des fichiers bitmap en Java" a été publiée à l'origine par JavaWorld.