Programmering

Java Tip 60: Gemme bitmapfiler i Java

Dette tip supplerer Java Tip 43, som demonstrerede processen med at indlæse bitmap-filer i Java-applikationer. Denne måned følger jeg op med en tutorial om, hvordan du gemmer billeder i 24-bit bitmap-filer og et kodestykke, du kan bruge til at skrive en bitmap-fil fra et billedobjekt.

Evnen til at oprette en bitmap-fil åbner mange døre, hvis du arbejder i et Microsoft Windows-miljø. På mit sidste projekt måtte jeg for eksempel interface Java med Microsoft Access. Java-programmet tillod brugeren at tegne et kort på skærmen. Kortet blev derefter udskrevet i en Microsoft Access-rapport. Fordi Java ikke understøtter OLE, var min eneste løsning at oprette en bitmapfil på kortet og fortælle Microsoft Access-rapporten, hvor den skulle hentes. Hvis du nogensinde har været nødt til at skrive en applikation for at sende et billede til udklipsholderen, kan dette tip være nyttigt for dig - især hvis disse oplysninger overføres til et andet Windows-program.

Formatet for en bitmapfil

Bitmap-filformatet understøtter 4-bit RLE (kørelængdekodning) samt 8-bit og 24-bit-kodning. Fordi vi kun har at gøre med 24-bit-formatet, lad os se på filens struktur.

Bitmap-filen er opdelt i tre sektioner. Jeg har lagt dem ud til dig nedenfor.

Afsnit 1: Bitmap-filoverskrift

Denne overskrift indeholder oplysninger om type størrelse og layout af bitmap-filen. Strukturen er som følger (taget fra en definition af C-sprogstruktur):

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

Her er en beskrivelse af kodeelementerne fra ovenstående liste:

  • bfType: Angiver filtypen og er altid indstillet til BM.
  • bfSize: Angiver størrelsen på hele filen i byte.
  • bfReserveret1: Reserveret - skal indstilles til 0.
  • bfReserved2: Reserveret - skal indstilles til 0.
  • bfOffBits: Angiver byteforskydningen fra BitmapFileHeader til starten af ​​billedet.

Her har du set, at formålet med bitmap-overskriften er at identificere bitmap-filen. Hvert program, der læser bitmap-filer, bruger bitmap-headeren til validering af filer.

Afsnit 2: Bitmap-informationsoverskrift

Den næste header, kaldet informationsoverskrift, indeholder alle egenskaberne for selve billedet.

Sådan angiver du oplysninger om dimensionen og farveformatet for en Windows 3.0 (eller højere) enhedsuafhængig bitmap (DIB):

typedef struct tagBITMAPINFOHEADER {DWORD biSize; LANG to-bredde; LANG biHøjde; WORD biPlanes; WORD biBitCount; DWORD bi-kompression; DWORD biSizeImage; LANG biXPelsPerMeter; LANGT biYPelsPerMeter; DWORD biClrUsed; DWORD biClrVigtigt; } BITMAPINFOHEADER; 

Hvert element i ovenstående kodeliste er beskrevet nedenfor:

  • biSize: Angiver antallet af byte, der kræves af BITMAPINFOHEADER struktur.
  • biBredde: Angiver bredden af ​​bitmap i pixels.
  • biHøjde: Angiver bitmapens højde i pixels.
  • biPlanes: Angiver antallet af fly til målenheden. Dette medlem skal indstilles til 1.
  • biBitCount: Angiver antallet af bits pr. Pixel. Denne værdi skal være 1, 4, 8 eller 24.
  • bi-kompression: Angiver typen af ​​komprimering for en komprimeret bitmap. I et 24-bit-format er variablen indstillet til 0.
  • biSizeImage: Angiver størrelsen i bytes på billedet. Det er gyldigt at indstille dette medlem til 0, hvis bitmap er i BI_RGB format.
  • biXPelsPerMeter: Angiver den vandrette opløsning i pixels pr. meter for målenheden til bitmap. Et program kan bruge denne værdi til at vælge en bitmap fra en ressourcegruppe, der bedst matcher egenskaberne for den aktuelle enhed.
  • biYPelsPerMeter: Angiver den lodrette opløsning i mål pr. meter for målenheden til bitmap.
  • biClrUsed: Angiver antallet af farveindekser i farvetabellen, der faktisk bruges af bitmap. Hvis biBitCount er indstillet til 24, biClrUsed angiver størrelsen på referencefarvetabellen, der bruges til at optimere ydeevnen for Windows-farvepaletter.
  • biClrImportant: Angiver antallet af farveindeks, der anses for vigtigt for visning af bitmap. Hvis denne værdi er 0, er alle farver vigtige.

Nu er alle de oplysninger, der er nødvendige for at oprette billedet, defineret.

Afsnit 3: Billede

I 24-bit-formatet er hver pixel i billedet repræsenteret af en serie på tre bytes RGB gemt som BRG. Hver scanningslinje er polstret til en jævn 4-byte grænse. For at komplicere processen lidt mere gemmes billedet fra bund til top, hvilket betyder at den første scanningslinje er den sidste scanningslinje i billedet. Følgende figur viser begge overskrifter (BITMAPHEADER) og (BITMAPINFOHEADER) og en del af billedet. Hver sektion afgrænses af en lodret bjælke:

 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 * 

Nu videre til koden

Nu hvor vi ved alt om strukturen i en 24-bit bitmap-fil, her er hvad du har ventet på: koden til at skrive en bitmap-fil fra et billedobjekt.

import java.awt. *; import java.io. *; import java.awt.image. *; offentlig klasse BMPFile udvider komponent {// --- Private konstanter privat endelig statisk int BITMAPFILEHEADER_SIZE = 14; privat endelig statisk int BITMAPINFOHEADER_SIZE = 40; // --- Privat variabelerklæring // --- Bitmap-filoverskrift privat byte bitmapFileHeader [] = ny byte [14]; privat byte bfType [] = {'B', 'M'}; privat int bfSize = 0; privat int bfReserved1 = 0; privat int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- Bitmap info header privat byte bitmapInfoHeader [] = ny byte [40]; privat int biSize = BITMAPINFOHEADER_SIZE; privat int biWidth = 0; privat int biHøjde = 0; private int biPlanes = 1; privat int biBitCount = 24; privat int biCompression = 0; privat int biSizeImage = 0x030000; privat int biXPelsPerMeter = 0x0; privat int biYPelsPerMeter = 0x0; privat int biClrUsed = 0; privat int biClrImportant = 0; // --- Bitmap rådata private int bitmap []; // --- Filafsnit privat FileOutputStream fo; // --- Standardkonstruktør offentlig BMPFile () {} offentlig ugyldig saveBitmap (String parFilename, Image parImage, int parWidth, int parHeight) {try {fo = new FileOutputStream (parFilename); gem (parImage, parWidth, parHeight); fo.close (); } fange (Undtagelse saveEx) {saveEx.printStackTrace (); }} / * * SaveMethod er den vigtigste metode til processen. Denne metode * kalder convertImage-metoden for at konvertere hukommelsesbilledet til * et byte-array; metode writeBitmapFileHeader opretter og skriver * bitmap-filoverskriften; writeBitmapInfoHeader opretter * information header; og writeBitmap skriver billedet. * * / private void save (Image parImage, int parWidth, int parHeight) {try {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } fange (Undtagelse saveEx) {saveEx.printStackTrace (); }} / * * convertImage konverterer hukommelsesbilledet til bitmapformatet (BRG). * Det beregner også nogle oplysninger til bitmap info header. * * / privat boolsk convertImage (Image parImage, int parWidth, int parHeight) {int pad; bitmap = ny int [parWidth * parHøjde]; PixelGrabber pg = ny PixelGrabber (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); prøv {pg.grabPixels (); } fange (InterruptedException e) {e.printStackTrace (); returnere (falsk); } pad = (4 - ((parWidth * 3)% 4)) * parHøjde; biSizeImage = ((parWidth * parHøjde) * 3) + pad; bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHøjde = parHøjde; returnere (sandt); } / * * writeBitmap konverterer billedet, der returneres fra pixel grabber, til * det krævede format. Husk: Scanningslinjer er inverteret i * en bitmapfil! * * Hver scanningslinje skal polstres til en jævn 4-byte grænse. * / private ugyldige writeBitmap () {int størrelse; int-værdi; int j; int i; int rowCount; int rowIndex; int lastRowIndex; int pad; int padCount; byte rgb [] = ny byte [3]; størrelse = (biBredde * biHøjde) - 1; pad = 4 - ((biWidth * 3)% 4); hvis (pad == 4) // <==== Bug correction pad = 0; // <==== Fejlretning rækkeCount = 1; padCount = 0; rowIndex = størrelse - biWidth; lastRowIndex = rækkeIndex; prøv {for (j = 0; j> 8) & 0xFF); rgb [2] = (byte) ((værdi >> 16) & 0xFF); fo.write (rgb); hvis (rowCount == biWidth) {padCount + = pad; for (i = 1; i> 8) & 0x00FF); return (retValue); } / * * * intToDWord konverterer en int til et dobbelt ord, hvor retur * -værdien er gemt i et 4-byte-array. * * / privat byte [] intToDWord (int parValue) {byte retValue [] = ny 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); }} 

Konklusion

Det er alt der er ved det. Jeg er sikker på, at du finder denne klasse meget nyttig, da Java fra og med JDK 1.1.6 ikke understøtter lagring af billeder i nogen af ​​de populære formater. JDK 1.2 tilbyder support til oprettelse af JPEG-billeder, men ikke understøttelse af bitmaps. Så denne klasse vil stadig udfylde et hul i JDK 1.2.

Hvis du leger med denne klasse og finder måder at forbedre den på, så lad mig det vide! Min e-mail vises nedenfor sammen med min biografi.

Jean-Pierre Dubé er en uafhængig Java-konsulent. Han grundlagde Infocom, registreret i 1988. Siden da har Infocom udviklet adskillige brugerdefinerede applikationer lige fra fremstilling, dokumentstyring og storskala elektrisk ledning. Han har omfattende programmeringserfaring inden for C, Visual Basic og senest Java, som nu er det primære sprog, som hans firma bruger. Et af Infocoms seneste projekter er et diagram-API, der snart bliver tilgængeligt som en betaudgivelse.

Denne historie, "Java Tip 60: Gemme bitmapfiler i Java" blev oprindeligt udgivet af JavaWorld.