Suggerimento Java 60: salvataggio di file bitmap in Java

Questo suggerimento completa Java Tip 43, che ha dimostrato il processo di caricamento dei file bitmap nelle applicazioni Java. Questo mese faccio seguito a un tutorial su come salvare le immagini in file bitmap a 24 bit e uno snip di codice che puoi utilizzare per scrivere un file bitmap da un oggetto immagine.

La possibilità di creare un file bitmap apre molte porte se lavori in un ambiente Microsoft Windows. Nel mio ultimo progetto, ad esempio, ho dovuto interfacciare Java con Microsoft Access. Il programma Java permetteva all'utente di disegnare una mappa sullo schermo. La mappa è stata quindi stampata in un report di Microsoft Access. Poiché Java non supporta OLE, la mia unica soluzione era creare un file bitmap della mappa e indicare al report di Microsoft Access dove prelevarlo. Se hai mai dovuto scrivere un'applicazione per inviare un'immagine negli appunti, questo suggerimento potrebbe esserti utile, specialmente se queste informazioni vengono passate a un'altra applicazione Windows.

Il formato di un file bitmap

Il formato file bitmap supporta RLE a 4 bit (codifica della lunghezza di esecuzione), nonché la codifica a 8 e 24 bit. Poiché abbiamo a che fare solo con il formato a 24 bit, diamo un'occhiata alla struttura del file.

Il file bitmap è diviso in tre sezioni. Li ho disposti per te di seguito.

Sezione 1: intestazione del file bitmap

Questa intestazione contiene informazioni sulla dimensione del tipo e sul layout del file bitmap. La struttura è la seguente (presa da una definizione della struttura del linguaggio C):

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

Ecco una descrizione degli elementi del codice dall'elenco sopra:

  • bfType: Indica il tipo di file ed è sempre impostato su BM.
  • bfSize: Specifica la dimensione dell'intero file in byte.
  • bfReserved1: Riservato - deve essere impostato su 0.
  • bfReserved2: Riservato - deve essere impostato su 0.
  • bfOffBits: Specifica l'offset di byte BitmapFileHeaderdall'inizio dell'immagine.

Qui hai visto che lo scopo dell'intestazione bitmap è identificare il file bitmap. Ogni programma che legge i file bitmap utilizza l'intestazione bitmap per la convalida dei file.

Sezione 2: intestazione delle informazioni bitmap

L'intestazione successiva, chiamata intestazione delle informazioni, contiene tutte le proprietà dell'immagine stessa.

Ecco come specificare le informazioni sulla dimensione e il formato del colore di una bitmap indipendente dal dispositivo (DIB) di Windows 3.0 (o versioni successive):

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;

Ogni elemento del listato di codice sopra è descritto di seguito:

  • biSize: Specifica il numero di byte richiesti dalla BITMAPINFOHEADERstruttura.
  • biWidth: Specifica la larghezza della bitmap in pixel.
  • biHeight: Specifica l'altezza della bitmap in pixel.
  • biPlanes: Specifica il numero di aerei per il dispositivo di destinazione. Questo membro deve essere impostato su 1.
  • biBitCount: Specifica il numero di bit per pixel. Questo valore deve essere 1, 4, 8 o 24.
  • biCompression: Specifica il tipo di compressione per una bitmap compressa. In un formato a 24 bit, la variabile è impostata su 0.
  • biSizeImage: specifica la dimensione in byte dell'immagine. È valido impostare questo membro su 0 se la bitmap è nel BI_RGBformato.
  • biXPelsPerMeter: specifica la risoluzione orizzontale, in pixel per metro, del dispositivo di destinazione per la bitmap. Un'applicazione può utilizzare questo valore per selezionare una bitmap da un gruppo di risorse che meglio corrisponde alle caratteristiche del dispositivo corrente.
  • biYPelsPerMeter: specifica la risoluzione verticale, in pixel per metro, del dispositivo di destinazione per la bitmap.
  • biClrUsed: specifica il numero di indici di colore nella tavola dei colori effettivamente utilizzati dalla bitmap. Se biBitCountè impostato su 24, biClrUsedspecifica la dimensione della tavola dei colori di riferimento utilizzata per ottimizzare le prestazioni delle tavolozze dei colori di Windows.
  • biClrImportant: specifica il numero di indici di colore considerati importanti per la visualizzazione della bitmap. Se questo valore è 0, tutti i colori sono importanti.

Ora sono state definite tutte le informazioni necessarie per creare l'immagine.

Sezione 3: Immagine

Nel formato a 24 bit, ogni pixel dell'immagine è rappresentato da una serie di tre byte di RGB memorizzati come BRG. Ogni riga di scansione viene riempita fino a un limite pari a 4 byte. Per complicare un po 'di più il processo, l'immagine viene memorizzata dal basso verso l'alto, il che significa che la prima linea di scansione è l'ultima linea di scansione nell'immagine. La figura seguente mostra sia le intestazioni ( BITMAPHEADER) che ( BITMAPINFOHEADER) e parte dell'immagine. Ogni sezione è delimitata da una barra 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 *

Ora, passiamo al codice

Ora che sappiamo tutto sulla struttura di un file bitmap a 24 bit, ecco cosa stavi aspettando: il codice per scrivere un file bitmap da un oggetto immagine.

import java.awt. *; import java.io. *; import java.awt.image. *; la classe pubblica BMPFile estende Component {// --- Costanti private private final static int BITMAPFILEHEADER_SIZE = 14; int statico finale privato BITMAPINFOHEADER_SIZE = 40; // --- Dichiarazione della variabile privata // --- Bitmap file header private byte bitmapFileHeader [] = nuovo byte [14]; byte privato bfType [] = {'B', 'M'}; private int bfSize = 0; private int bfReserved1 = 0; private int bfReserved2 = 0; int privato bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- Bitmap info header byte privato bitmapInfoHeader [] = nuovo byte [40]; private int biSize = BITMAPINFOHEADER_SIZE; int biWidth privato = 0; private int biHeight = 0; private int biPlanes = 1; private int biBitCount = 24; private int biCompression = 0; private int biSizeImage = 0x030000;private int biXPelsPerMeter = 0x0; biYPelsPerMeter int privato = 0x0; private int biClrUsed = 0; private int biClrImportant = 0; // --- Bitmap raw data private int bitmap []; // --- Sezione file private FileOutputStream fo; // --- Costruttore predefinito public BMPFile () {} public void saveBitmap (String parFilename, Image parImage, int parWidth, int parHeight) {try {fo = new FileOutputStream (parFilename); save (parImage, parWidth, parHeight); fo.close (); } catch (eccezione saveEx) {saveEx.printStackTrace (); }} / * * SaveMethod è il metodo principale del processo. Questo metodo * chiamerà il metodo convertImage per convertire l'immagine della memoria in * un array di byte; il metodo writeBitmapFileHeader crea e scrive * l'intestazione del file bitmap; writeBitmapInfoHeader crea * l'intestazione delle informazioni; e writeBitmap scrive l'immagine.* * / private void save (Image parImage, int parWidth, int parHeight) {try {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } catch (eccezione saveEx) {saveEx.printStackTrace (); }} / * * convertImage converte l'immagine in memoria nel formato bitmap (BRG). * Calcola anche alcune informazioni per l'intestazione delle informazioni bitmap. * * / private booleano convertImage (Image parImage, int parWidth, int parHeight) {int pad; bitmap = new int [parWidth * parHeight]; PixelGrabber pg = nuovo PixelGrabber (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); prova {pg.grabPixels (); } catch (InterrruptException e) {e.printStackTrace (); ritorno (falso); } pad = (4 - ((parWidth * 3)% 4)) * parHeight; biSizeImage = ((parWidth * parHeight) * 3) + pad;bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight; ritorno (vero); } / * * writeBitmap converte l'immagine restituita dal pixel grabber * nel formato richiesto. Ricorda: le linee di scansione sono invertite * in un file bitmap! * * Ogni riga di scansione deve essere riempita fino a un limite pari a 4 byte. * / private void writeBitmap () {int size; valore int; int j; int i; int rowCount; int rowIndex; int lastRowIndex; int pad; int padCount; byte rgb [] = nuovo byte [3]; size = (biWidth * biHeight) - 1; pad = 4 - ((biWidth * 3)% 4); if (pad == 4) // <==== Pad correzione bug = 0; // <==== Correzione bug rowCount = 1; padCount = 0; rowIndex = size - biWidth; lastRowIndex = rowIndex; prova {for (j = 0; j> 8) & 0xFF); rgb [2] = (byte) ((valore >> 16) & 0xFF); fo.write (rgb);if (rowCount == biWidth) {padCount + = pad; per (i = 1; i> 8) & 0x00FF); return (retValue); } / * * * intToDWord converte un int in una doppia parola, dove il valore restituito * è memorizzato in una matrice di 4 byte. * * / private byte [] intToDWord (int parValue) {byte retValue [] = nuovo byte [4]; retValue [0] = (byte) (parValue & 0x00FF); retValue [1] = (byte) ((parValue >> 8) & 0x000000FF); retValue [2] = (byte) ((parValue >> 16) & 0x000000FF); retValue [3] = (byte) ((parValue >> 24) & 0x000000FF); return (retValue); }}0x00FF); retValue [1] = (byte) ((parValue >> 8) & 0x000000FF); retValue [2] = (byte) ((parValue >> 16) & 0x000000FF); retValue [3] = (byte) ((parValue >> 24) & 0x000000FF); return (retValue); }}0x00FF); retValue [1] = (byte) ((parValue >> 8) & 0x000000FF); retValue [2] = (byte) ((parValue >> 16) & 0x000000FF); retValue [3] = (byte) ((parValue >> 24) & 0x000000FF); return (retValue); }}

Conclusione

È tutto quello che c'è da fare. Sono sicuro che troverai questa classe molto utile, poiché, a partire da JDK 1.1.6, Java non supporta il salvataggio di immagini in nessuno dei formati più diffusi. JDK 1.2 offrirà il supporto per la creazione di immagini JPEG, ma non il supporto per le bitmap. Quindi questa classe colmerà ancora una lacuna in JDK 1.2.

Se giochi con questa classe e trovi modi per migliorarla, fammelo sapere! La mia e-mail appare di seguito, insieme alla mia biografia.

Jean-Pierre Dubé è un consulente Java indipendente. Ha fondato Infocom, registrata nel 1988. Da allora, Infocom ha sviluppato diverse applicazioni personalizzate che vanno dalla produzione, alla gestione dei documenti e alla gestione delle linee elettriche su larga scala. Ha una vasta esperienza di programmazione in C, Visual Basic e, più recentemente, Java, che ora è il linguaggio principale utilizzato dalla sua azienda. Uno dei progetti recenti di Infocom è un'API per diagrammi che dovrebbe diventare presto disponibile come versione beta.

Questa storia, "Java Tip 60: Salvataggio di file bitmap in Java" è stata originariamente pubblicata da JavaWorld.