Programmering

Java Tips 60: Lagre bitmapfiler i Java

Dette tipset utfyller Java Tip 43, som demonstrerte prosessen med å laste inn bitmap-filer i Java-applikasjoner. Denne måneden følger jeg opp med en veiledning om hvordan du lagrer bilder i 24-biters bitmapfiler og et kodebit du kan bruke til å skrive en bitmapfil fra et bildeobjekt.

Evnen til å lage en bitmappefil åpner mange dører hvis du jobber i et Microsoft Windows-miljø. På mitt siste prosjekt måtte jeg for eksempel grensesnitt Java med Microsoft Access. Java-programmet tillot brukeren å tegne et kart på skjermen. Kartet ble deretter skrevet ut i en Microsoft Access-rapport. Fordi Java ikke støtter OLE, var den eneste løsningen min å lage en bitmappefil av kartet og fortelle Microsoft Access-rapporten hvor den skulle hentes. Hvis du noen gang har måttet skrive et program for å sende et bilde til utklippstavlen, kan dette tipset være til nytte for deg - spesielt hvis denne informasjonen blir sendt til et annet Windows-program.

Formatet på en bitmappefil

Bitmap-filformatet støtter 4-biters RLE (kjørelengdekoding), så vel som 8-biters og 24-biters koding. Fordi vi bare har å gjøre med 24-biters format, la oss ta en titt på filstrukturen.

Bitmap-filen er delt inn i tre seksjoner. Jeg har lagt dem ut for deg nedenfor.

Seksjon 1: Bitmap-filoverskrift

Denne overskriften inneholder informasjon om typen størrelse og utforming av bitmappefilen. Strukturen er som følger (hentet fra en definisjon av C-språkstruktur):

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

Her er en beskrivelse av kodeelementene fra listen ovenfor:

  • bfType: Indikerer filtypen og er alltid satt til BM.
  • bfSize: Spesifiserer størrelsen på hele filen i byte.
  • bfReservert1: Reservert - må være satt til 0.
  • bfReserverte2: Reservert - må være satt til 0.
  • bfOffBits: Spesifiserer byteforskyvning fra BitmapFileHeader til starten av bildet.

Her har du sett at formålet med bitmap-overskriften er å identifisere bitmap-filen. Hvert program som leser bitmap-filer bruker bitmap-overskriften for filvalidering.

Seksjon 2: Bitmapinformasjonsoverskrift

Neste topptekst, kalt informasjonsoverskrift, inneholder alle egenskapene til selve bildet.

Slik spesifiserer du informasjon om dimensjonen og fargeformatet til en enhetsuavhengig bitmap (DIB) for Windows 3.0 (eller høyere):

typedef struct tagBITMAPINFOHEADER {DWORD biSize; LANG bredvidde; LANG biHøyde; WORD biPlanes; WORD biBitCount; DWORD bi-kompresjon; DWORD biSizeImage; LANGT biXPelsPerMeter; LANGT biYPelsPerMeter; DWORD biClrUsed; DWORD biClrViktig; } BITMAPINFOHEADER; 

Hvert element i kodelisten ovenfor er beskrevet nedenfor:

  • biSize: Spesifiserer antall byte som kreves av BITMAPINFOHEADER struktur.
  • biBredde: Spesifiserer bredden på bitmappen i piksler.
  • biHøyde: Spesifiserer høyden på bitmappen i piksler.
  • biPlanes: Spesifiserer antall fly for målenheten. Dette medlemmet må være satt til 1.
  • biBitCount: Spesifiserer antall bits per piksel. Denne verdien må være 1, 4, 8 eller 24.
  • biKompresjon: Angir typen komprimering for en komprimert bitmap. I et 24-biters format er variabelen satt til 0.
  • biSizeImage: Spesifiserer størrelsen i byte på bildet. Det er gyldig å sette dette medlemmet til 0 hvis bitmappen er i BI_RGB format.
  • biXPelsPerMeter: Spesifiserer den horisontale oppløsningen, i piksler per meter, til målenheten for bitmappen. En applikasjon kan bruke denne verdien til å velge en bitmap fra en ressursgruppe som best samsvarer med egenskapene til gjeldende enhet.
  • biYPelsPerMeter: Spesifiserer den vertikale oppløsningen, i piksler per meter, til målenheten for bitmappen.
  • biClrUsed: Spesifiserer antall fargeindekser i fargetabellen som faktisk brukes av bitmappen. Hvis biBitCount er satt til 24, biClrUsed angir størrelsen på referansefargetabellen som brukes for å optimalisere ytelsen til Windows-fargepaletter.
  • biClrImportant: Spesifiserer antall fargeindekser som anses som viktige for visning av bitmappen. Hvis denne verdien er 0, er alle farger viktige.

Nå er all informasjon som er nødvendig for å lage bildet definert.

Seksjon 3: Bilde

I 24-bitersformatet representeres hver piksel i bildet av en serie på tre byte RGB lagret som BRG. Hver skannelinje er polstret til en jevn 4-byte-grense. For å komplisere prosessen litt mer lagres bildet fra bunnen til toppen, noe som betyr at den første skannelinjen er den siste skannelinjen i bildet. Følgende figur viser begge topptekstene (BITMAPHEADER) og (BITMAPINFOHEADER) og en del av bildet. Hver seksjon avgrenses av en loddrett stolpe:

 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 * 

Nå videre til koden

Nå som vi vet alt om strukturen til en 24-bit bitmapfil, er det du har ventet på: koden for å skrive en bitmapfil fra et bildeobjekt.

importer java.awt. *; importer java.io. *; importer java.awt.image. *; offentlig klasse BMPFile utvider 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'}; private int bfSize = 0; privat int bfReserved1 = 0; privat int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- Bitmap info header private byte bitmapInfoHeader [] = ny byte [40]; private int biSize = BITMAPINFOHEADER_SIZE; privat int biWidth = 0; privat int biHeight = 0; private int biPlanes = 1; privat int biBitCount = 24; privat int biCompression = 0; private int biSizeImage = 0x030000; private int biXPelsPerMeter = 0x0; private int biYPelsPerMeter = 0x0; private int biClrUsed = 0; private int biClrImportant = 0; // --- Bitmap rådata private int bitmap []; // --- Fildel privat FileOutputStream fo; // --- Standard konstruktør offentlig BMPFile () {} offentlig ugyldig saveBitmap (String parFilename, Image parImage, int parWidth, int parHeight) {try {fo = new FileOutputStream (parFilename); lagre (parImage, parWidth, parHeight); fo.close (); } fange (Unntak saveEx) {saveEx.printStackTrace (); }} / * * SaveMethod er hovedmetoden for prosessen. Denne metoden * vil kalle convertImage-metoden for å konvertere minnebildet til * en byte-array; metode writeBitmapFileHeader oppretter og skriver * bitmap-filoverskriften; writeBitmapInfoHeader oppretter * informasjonshode; og writeBitmap skriver bildet. * * / private void save (Image parImage, int parWidth, int parHeight) {try {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } fange (Unntak saveEx) {saveEx.printStackTrace (); }} / * * convertImage konverterer minnebildet til bitmapformat (BRG). * Den beregner også litt informasjon for bitmap-infooverskriften. * * / privat boolsk convertImage (Image parImage, int parWidth, int parHeight) {int pad; bitmap = new int [parWidth * parHeight]; PixelGrabber pg = ny PixelGrabber (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); prøv {pg.grabPixels (); } fange (InterruptedException e) {e.printStackTrace (); retur (falsk); } pad = (4 - ((parBredde * 3)% 4)) * parHøyde; biSizeImage = ((parWidth * parHeight) * 3) + pad; bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHøyde = parHøyde; retur (sant); } / * * writeBitmap konverterer bildet som returneres fra pixel grabber til * ønsket format. Husk: skannelinjer er invertert i * en bitmappefil! * * Hver skannelinje må være polstret til en jevn 4-byte-grense. * / privat ugyldig writeBitmap () {int størrelse; int verdi; int j; int i; int rowCount; int rowIndex; int lastRowIndex; int pad; int padCount; byte rgb [] = ny byte [3]; størrelse = (biBredde * biHøyde) - 1; pad = 4 - ((biWidth * 3)% 4); hvis (pad == 4) // <==== Feilkorreksjonspute = 0; // <==== FeilrettingsradCount = 1; padCount = 0; rowIndex = størrelse - biWidth; lastRowIndex = rowIndex; prøv {for (j = 0; j> 8) & 0xFF); rgb [2] = (byte) ((verdi >> 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, der retur * -verdien er lagret i en 4-byte-matrise. * * / 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); }} 

Konklusjon

Det er alt det er med det. Jeg er sikker på at du vil finne denne klassen veldig nyttig, siden fra og med JDK 1.1.6 støtter Java ikke lagring av bilder i noen av de populære formatene. JDK 1.2 vil tilby støtte for å lage JPEG-bilder, men ikke støtte for bitmaps. Så denne klassen vil fortsatt fylle et hull i JDK 1.2.

Hvis du leker med denne klassen og finner måter å forbedre den på, så gi meg beskjed! Min e-post vises nedenfor, sammen med biografien min.

Jean-Pierre Dubé er en uavhengig Java-konsulent. Han grunnla Infocom, registrert i 1988. Siden den gang har Infocom utviklet flere tilpassede applikasjoner, alt fra produksjon, dokumenthåndtering og storskala elektrisk ledning. Han har omfattende programmeringserfaring innen C, Visual Basic og sist Java, som nå er det primære språket som brukes av firmaet hans. Et av Infocoms nylige prosjekter er et API-diagram som snart vil bli tilgjengelig som en betaversjon.

Denne historien, "Java Tips 60: Saving bitmap files in Java" ble opprinnelig utgitt av JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found