Java savjet 60: Spremanje bitmap datoteka na Javi

Ovaj savjet nadopunjuje Java Tip 43, koji je pokazao postupak učitavanja bitmap datoteka u Java programima. Ovog mjeseca slijedim upute za spremanje slika u 24-bitne bitmap datoteke i isječak koda koji možete koristiti za pisanje bitmap datoteke iz slikovnog objekta.

Sposobnost stvaranja bitmap datoteke otvara mnoga vrata ako radite u okruženju Microsoft Windows. Na mom posljednjem projektu, na primjer, morao sam povezati Javu s Microsoft Accessom. Java program omogućio je korisniku da crta kartu na ekranu. Karta je potom ispisana u izvješću Microsoft Accessa. Budući da Java ne podržava OLE, moje jedino rješenje bilo je stvoriti bitmap datoteku karte i reći izvješću Microsoft Accessa gdje je treba preuzeti. Ako ste ikada morali napisati aplikaciju za slanje slike u međuspremnik, ovaj će vam savjet možda biti od koristi - pogotovo ako se ti podaci prenose u drugu Windowsovu aplikaciju.

Format bitmap datoteke

Format bitmap datoteke podržava 4-bitno RLE (kodiranje duljine izvođenja), kao i 8-bitno i 24-bitno kodiranje. Budući da imamo posla samo s 24-bitnim formatom, pogledajmo strukturu datoteke.

Bitmap datoteka podijeljena je u tri odjeljka. Izložio sam ih za vas u nastavku.

Odjeljak 1: Zaglavlje bitmap datoteke

Ovo zaglavlje sadrži informacije o veličini vrste i izgledu bitmap datoteke. Struktura je sljedeća (preuzeto iz definicije strukture jezika C):

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

Evo opisa elemenata koda iz gornjeg popisa:

  • bfType: Označava vrstu datoteke i uvijek je postavljena na BM.
  • bfSize: Određuje veličinu cijele datoteke u bajtovima.
  • bfReserved1: Rezervirano - mora biti postavljeno na 0.
  • bfReserved2: Rezervirano - mora biti postavljeno na 0.
  • bfOffBits: Određuje pomak bajta od BitmapFileHeaderdo početka slike.

Ovdje ste vidjeli da je svrha bitmap zaglavlja identificirati bitmap datoteku. Svaki program koji čita bitmap datoteke koristi bitmap zaglavlje za provjeru valjanosti datoteka.

Odjeljak 2: Zaglavlje informacija o bitmapi

Sljedeće zaglavlje, nazvano zaglavljem informacija, sadrži sva svojstva same slike.

Evo kako odredite informacije o dimenziji i formatu boja bitne slike (DIB) neovisne o uređaju sa sustavom Windows 3.0 (ili novijom):

typedef struct tagBITMAPINFOHEADER {DWORD biSize; DUGA ŠIRINA; DUGI biHeight; RIJEČ biPlanes; RIJEČ biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;

Svaki element gornjeg popisa kodova opisan je u nastavku:

  • biSize: Određuje broj bajtova potrebnih BITMAPINFOHEADERstrukturi.
  • biWidth: Određuje širinu bitmape u pikselima.
  • biHeight: Određuje visinu bitmape u pikselima.
  • biPlanes: Određuje broj ravnina za ciljni uređaj. Ovaj član mora biti postavljen na 1.
  • biBitCount: Određuje broj bitova po pikselu. Ova vrijednost mora biti 1, 4, 8 ili 24.
  • biCompression: Određuje vrstu kompresije za komprimiranu bitmapu. U 24-bitnom formatu varijabla je postavljena na 0.
  • biSizeImage: Određuje veličinu slike u bajtovima. Vrijedi postaviti ovog člana na 0 ako je bitmapa u BI_RGBformatu.
  • biXPelsPerMeter: Određuje vodoravnu razlučivost, u pikselima po metru, ciljnog uređaja za bitmapu. Aplikacija može koristiti ovu vrijednost za odabir bitne slike iz grupe resursa koja najbolje odgovara karakteristikama trenutnog uređaja.
  • biYPelsPerMeter: Određuje vertikalnu razlučivost, u pikselima po metru, ciljnog uređaja za bitmapu.
  • biClrUsed: Određuje broj indeksa boja u tablici boja koje stvarno koristi bitmapa. Ako biBitCountje postavljeno na 24, biClrUsedodređuje veličinu referentne tablice boja koja se koristi za optimizaciju izvedbe paleta boja Windows.
  • biClrImportant: Određuje broj indeksa boja koji se smatraju važnima za prikaz bitmape. Ako je ova vrijednost 0, bitne su sve boje.

Sada su definirane sve informacije potrebne za stvaranje slike.

Odjeljak 3: Slika

U 24-bitnom formatu svaki je piksel na slici predstavljen serijom od tri bajta RGB-a pohranjenih kao BRG. Svaka linija skeniranja dodana je na čak 4-bajtnu granicu. Da bi postupak malo zakomplicirao, slika se sprema odozdo prema gore, što znači da je prva linija skeniranja zadnja linija skeniranja na slici. Sljedeća slika prikazuje i zaglavlja ( BITMAPHEADER) i ( BITMAPINFOHEADER) i dio slike. Svaki je odjeljak omeđen okomitom trakom:

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 *

Sada, na kodu

Sad kad znamo sve o strukturi 24-bitne bitmap datoteke, evo što ste čekali: kôd za pisanje bitmap datoteke iz slikovnog objekta.

import java.awt. *; import java.io. *; import java.awt.image. *; javna klasa BMPFile proširuje Component {// --- Privatne konstante private final static int BITMAPFILEHEADER_SIZE = 14; privatni konačni statički int BITMAPINFOHEADER_SIZE = 40; // --- Deklaracija privatne varijable // --- Zaglavlje bitmap datoteke privatni bajt bitmapFileHeader [] = novi bajt [14]; privatni bajt bfType [] = {'B', 'M'}; privatno int bfSize = 0; privatno int bfReserved1 = 0; privatno int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- zaglavlje informacija o bitmapi privatni bajt bitmapInfoHeader [] = novi bajt [40]; private int biSize = BITMAPINFOHEADER_SIZE; private int biWidth = 0; privatni int biHeight = 0; privatni int biPlanes = 1; private int biBitCount = 24; private int biCompression = 0; private int biSizeImage = 0x030000;private int biXPelsPerMeter = 0x0; private int biYPelsPerMeter = 0x0; privatno int biClrUsed = 0; private int biClrImportant = 0; // --- Bitmap sirovi podaci private int bitmapa []; // --- odjeljak datoteke private FileOutputStream fo; // --- Zadani konstruktor public BMPFile () {} public void saveBitmap (String parFilename, Image parImage, int parWidth, int parHeight) {try {fo = new FileOutputStream (parFilename); spremi (parImage, parWidth, parHeight); fo.close (); } catch (Iznimka saveEx) {saveEx.printStackTrace (); }} / * * SaveMethod je glavna metoda postupka. Ova metoda * pozvat će metodu convertImage za pretvaranje slike memorije u * bajtni niz; metoda writeBitmapFileHeader kreira i zapisuje * zaglavlje bitmap datoteke; writeBitmapInfoHeader kreira zaglavlje * informacija; i writeBitmap piše sliku.* * / private void save (Slika parImage, int parWidth, int parHeight) {try {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } catch (Iznimka saveEx) {saveEx.printStackTrace (); }} / * * convertImage pretvara memorijsku sliku u bitmap format (BRG). * Također izračunava neke informacije za zaglavlje informacija o bitmapi. * * / private boolean convertImage (Slika parImage, int parWidth, int parHeight) {int pad; bitmapa = novo int [parWidth * parHeight]; PixelGrabber pg = novi PixelGrabber (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); isprobajte {pg.grabPixels (); } catch (InterruptedException e) {e.printStackTrace (); povratak (lažno); } pad = (4 - ((parWidth * 3)% 4)) * parHeight; biSizeImage = ((parWidth * parHeight) * 3) + pad;bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight; povratak (istina); } / * * writeBitmap pretvara sliku vraćenu iz hvatača piksela u * potreban format. Zapamtite: crte skeniranja obrnute su u * bitmap datoteku! * * Svaka linija za skeniranje mora biti postavljena na čak 4-bajtnu granicu. * / private void writeBitmap () {int veličina; int vrijednost; int j; int i; int rowCount; int rowIndex; int lastRowIndex; int jastučić; int padCount; bajt rgb [] = novi bajt [3]; veličina = (biWidth * biHeight) - 1; jastučić = 4 - ((biŠirina * 3)% 4); if (pad == 4) // <==== Pad za ispravljanje grešaka = 0; // <==== Ispravljanje grešaka rowCount = 1; padCount = 0; rowIndex = veličina - biŠirina; lastRowIndex = rowIndex; pokušajte {for (j = 0; j> 8) & 0xFF); rgb [2] = (bajt) ((vrijednost >> 16) & 0xFF); fo.write (rgb);if (rowCount == biWidth) {padCount + = pad; za (i = 1; i> 8) & 0x00FF); povratak (retValue); } / * * * intToDWord pretvara int u dvostruku riječ, gdje je povratna * vrijednost pohranjena u 4-bajtnom polju. * * / privatni bajt [] intToDWord (int parValue) {bajt retValue [] = novi bajt [4]; retValue [0] = (bajt) (parValue & 0x00FF); retValue [1] = (bajt) ((parValue >> 8) & 0x000000FF); retValue [2] = (bajt) ((parValue >> 16) & 0x000000FF); retValue [3] = (bajt) ((parValue >> 24) & 0x000000FF); povratak (retValue); }}0x00FF); retValue [1] = (bajt) ((parValue >> 8) & 0x000000FF); retValue [2] = (bajt) ((parValue >> 16) & 0x000000FF); retValue [3] = (bajt) ((parValue >> 24) & 0x000000FF); povratak (retValue); }}0x00FF); retValue [1] = (bajt) ((parValue >> 8) & 0x000000FF); retValue [2] = (bajt) ((parValue >> 16) & 0x000000FF); retValue [3] = (bajt) ((parValue >> 24) & 0x000000FF); povratak (retValue); }}

Zaključak

To je sve. Siguran sam da će vam ova klasa biti vrlo korisna, jer od JDK 1.1.6 Java ne podržava spremanje slika ni u jednom od popularnih formata. JDK 1.2 ponudit će podršku za stvaranje JPEG slika, ali ne i podršku za bitmape. Tako će ova klasa i dalje popuniti prazninu u JDK 1.2.

Ako se poigrate s ovom nastavom i pronađete načine da je poboljšate, javite mi! Moj e-mail pojavljuje se u nastavku, zajedno s mojom biografijom.

Jean-Pierre Dubé neovisni je Java savjetnik. Osnovao je Infocom, registriran 1988. Od tada je Infocom razvio nekoliko prilagođenih aplikacija, počevši od proizvodnje, upravljanja dokumentima i velikih elektroenergetskih vodova. Ima bogato iskustvo u programiranju na C, Visual Basicu, a nedavno i na Javi, koja je sada primarni jezik koji koristi njegova tvrtka. Jedan od nedavnih projekata Infocoma je API dijagrama koji bi uskoro trebao postati dostupan kao beta izdanje.

Ovu je priču "Java savjet 60: Spremanje bitmap datoteka na Javi" izvorno objavio JavaWorld.