Προγραμματισμός

Συμβουλή Java 60: Αποθήκευση αρχείων bitmap στην Java

Αυτή η συμβουλή συμπληρώνει το Java Tip 43, το οποίο παρουσίασε τη διαδικασία φόρτωσης αρχείων bitmap σε εφαρμογές Java. Αυτό το μήνα, παρακολουθώ ένα σεμινάριο για το πώς να αποθηκεύσετε εικόνες σε αρχεία bitmap 24 bit και ένα απόσπασμα κώδικα που μπορείτε να χρησιμοποιήσετε για να γράψετε ένα αρχείο bitmap από ένα αντικείμενο εικόνας.

Η δυνατότητα δημιουργίας ενός αρχείου bitmap ανοίγει πολλές πόρτες εάν εργάζεστε σε περιβάλλον Microsoft Windows. Στο τελευταίο μου έργο, για παράδειγμα, έπρεπε να διασυνδέσω το Java με τη Microsoft Access. Το πρόγραμμα Java επέτρεψε στο χρήστη να σχεδιάσει έναν χάρτη στην οθόνη. Ο χάρτης στη συνέχεια εκτυπώθηκε σε μια αναφορά της Microsoft Access. Επειδή η Java δεν υποστηρίζει το OLE, η μόνη μου λύση ήταν να δημιουργήσω ένα αρχείο bitmap του χάρτη και να πω στην αναφορά Microsoft Access πού να το παραλάβω. Εάν είχατε ποτέ να γράψετε μια εφαρμογή για να στείλετε μια εικόνα στο πρόχειρο, αυτή η συμβουλή μπορεί να σας χρησιμεύσει - ειδικά εάν αυτές οι πληροφορίες διαβιβάζονται σε άλλη εφαρμογή των Windows.

Η μορφή ενός αρχείου bitmap

Η μορφή αρχείου bitmap υποστηρίζει RLE 4-bit (κωδικοποίηση μήκους εκτέλεσης), καθώς και κωδικοποίηση 8-bit και 24-bit. Επειδή ασχολούμαστε μόνο με τη μορφή 24-bit, ας ρίξουμε μια ματιά στη δομή του αρχείου.

Το αρχείο bitmap χωρίζεται σε τρεις ενότητες. Τα έβαλα για εσάς παρακάτω.

Ενότητα 1: Κεφαλίδα αρχείου Bitmap

Αυτή η κεφαλίδα περιέχει πληροφορίες σχετικά με το μέγεθος και τη διάταξη του αρχείου bitmap. Η δομή έχει ως εξής (προέρχεται από έναν ορισμό δομής γλώσσας C):

typedef struct tagBITMAPFILEHEADER {UINT bfType; DWORD bfSize; UINT bfReserve1; UINT bfReserve2; DWORD bfOffBits; } BITMAPFILEHEADER; 

Ακολουθεί μια περιγραφή των στοιχείων κώδικα από την παραπάνω λίστα:

  • bf Τύπος: Υποδεικνύει τον τύπο του αρχείου και ορίζεται πάντα σε BM.
  • bf Μέγεθος: Καθορίζει το μέγεθος ολόκληρου του αρχείου σε byte.
  • bfΔιατηρημένος1: Δεσμευμένο - πρέπει να οριστεί σε 0.
  • bfΕπιφύλαξη2: Δεσμευμένο - πρέπει να οριστεί σε 0.
  • bfOffBits: Καθορίζει τη μετατόπιση byte από το BitmapFileHeader στην αρχή της εικόνας.

Εδώ έχετε δει ότι ο σκοπός της κεφαλίδας bitmap είναι να προσδιορίσει το αρχείο bitmap. Κάθε πρόγραμμα που διαβάζει αρχεία bitmap χρησιμοποιεί την κεφαλίδα bitmap για επικύρωση αρχείων.

Ενότητα 2: Κεφαλίδα πληροφοριών Bitmap

Η επόμενη κεφαλίδα, που ονομάζεται κεφαλίδα πληροφοριών, περιέχει όλες τις ιδιότητες της ίδιας της εικόνας.

Δείτε πώς καθορίζετε πληροφορίες σχετικά με τη διάσταση και τη μορφή χρώματος ενός bitmap (DIB) ανεξάρτητης συσκευής Windows 3.0 (ή νεότερης):

typedef struct tagBITMAPINFOHEADER {DWORD biSize; LONG biWidth; LONG biHeight; Διπλάσια WORD; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrΣημαντικό; } BITMAPINFOHEADER; 

Κάθε στοιχείο της παραπάνω λίστας κώδικα περιγράφεται παρακάτω:

  • biSize: Καθορίζει τον αριθμό των byte που απαιτούνται από το BITMAPINFOHEADER δομή.
  • biWidth: Καθορίζει το πλάτος του bitmap σε pixel.
  • biHeight: Καθορίζει το ύψος του bitmap σε pixel.
  • διπλάνα: Καθορίζει τον αριθμό των επιπέδων για τη συσκευή προορισμού. Αυτό το μέλος πρέπει να οριστεί σε 1.
  • biBitCount: Καθορίζει τον αριθμό των bit ανά pixel. Αυτή η τιμή πρέπει να είναι 1, 4, 8 ή 24.
  • διπλή συμπίεση: Καθορίζει τον τύπο συμπίεσης για ένα συμπιεσμένο bitmap. Σε μορφή 24-bit, η μεταβλητή ορίζεται σε 0.
  • biSizeImage: Καθορίζει το μέγεθος σε byte της εικόνας. Είναι έγκυρο να ορίσετε αυτό το μέλος σε 0 εάν το bitmap βρίσκεται στο BI_RGB μορφή.
  • biXPelsPerMeter: Καθορίζει την οριζόντια ανάλυση, σε pixel ανά μέτρο, της συσκευής στόχου για το bitmap. Μια εφαρμογή μπορεί να χρησιμοποιήσει αυτήν την τιμή για να επιλέξει ένα bitmap από μια ομάδα πόρων που ταιριάζει καλύτερα με τα χαρακτηριστικά της τρέχουσας συσκευής.
  • biYPelsPerMeter: Καθορίζει την κατακόρυφη ανάλυση, σε pixel ανά μέτρο, της συσκευής στόχου για το bitmap.
  • biClrUsed: Καθορίζει τον αριθμό των ευρετηρίων χρωμάτων στον πίνακα χρωμάτων που χρησιμοποιείται πραγματικά από το bitmap. Αν biBitCount έχει οριστεί σε 24, biClrUsed καθορίζει το μέγεθος του πίνακα χρωμάτων αναφοράς που χρησιμοποιείται για τη βελτιστοποίηση της απόδοσης των παλετών χρωμάτων των Windows.
  • biClrImportant: Καθορίζει τον αριθμό των χρωμάτων που θεωρούνται σημαντικοί για την εμφάνιση του bitmap. Εάν αυτή η τιμή είναι 0, όλα τα χρώματα είναι σημαντικά.

Τώρα έχουν καθοριστεί όλες οι πληροφορίες που απαιτούνται για τη δημιουργία της εικόνας.

Ενότητα 3: Εικόνα

Στη μορφή 24-bit, κάθε εικονοστοιχείο στην εικόνα αντιπροσωπεύεται από μια σειρά τριών bytes RGB αποθηκευμένων ως BRG. Κάθε γραμμή σάρωσης είναι γεμάτη σε ένα ομοιόμορφο όριο 4 byte. Για να περιπλέξει τη διαδικασία λίγο περισσότερο, η εικόνα αποθηκεύεται από κάτω προς τα πάνω, πράγμα που σημαίνει ότι η πρώτη γραμμή σάρωσης είναι η τελευταία γραμμή σάρωσης στην εικόνα. Το παρακάτω σχήμα δείχνει και τις δύο κεφαλίδες (BITMAPHEADER) και (BITMAPINFOHEADER) και μέρος της εικόνας. Κάθε ενότητα οριοθετείται από μια κάθετη γραμμή:

 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 * 

Τώρα, στον κώδικα

Τώρα που γνωρίζουμε τα πάντα για τη δομή ενός αρχείου bitmap 24-bit, εδώ είναι αυτό που περιμένατε: ο κώδικας για τη σύνταξη ενός αρχείου bitmap από ένα αντικείμενο εικόνας.

εισαγωγή java.awt. *; εισαγωγή java.io. *; εισαγωγή java.awt.image. *; δημόσια κλάση BMPFile επεκτείνει το στοιχείο {// --- Ιδιωτικές σταθερές ιδιωτικό τελικό στατικό int BITMAPFILEHEADER_SIZE = 14; ιδιωτικό τελικό στατικό int BITMAPINFOHEADER_SIZE = 40; // --- Δήλωση ιδιωτικής μεταβλητής // --- Κεφαλίδα αρχείου bitmap ιδιωτικό byte bitmapFileHeader [] = νέο byte [14]; ιδιωτικό byte bfType [] = {'B', 'M'}; ιδιωτικό int bfSize = 0; ιδιωτικό int bfReserve1 = 0; ιδιωτικό int bfReserve2 = 0; ιδιωτικό int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- Bitmap info header private byte bitmapInfoHeader [] = νέο byte [40]; ιδιωτικό int biSize = BITMAPINFOHEADER_SIZE; ιδιωτικό int biWidth = 0; ιδιωτικό int biHeight = 0; ιδιωτικό int biPlanes = 1; ιδιωτικό int biBitCount = 24; ιδιωτικό int biCompression = 0; ιδιωτικό int biSizeImage = 0x030000; ιδιωτικό int biXPelsPerMeter = 0x0; ιδιωτικό int biYPelsPerMeter = 0x0; ιδιωτικό int biClrUsed = 0; ιδιωτικό int biClrImportant = 0; // --- Bitmap raw data private int bitmap []; // --- Ιδιωτική ενότητα αρχείων FileOutputStream fo; // --- Προεπιλεγμένο δημόσιο BMPFile κατασκευαστή () {} public void saveBitmap (String parFilename, Image parImage, int parWidth, int parHeight) {try {fo = new FileOutputStream (parFilename); αποθήκευση (parImage, parWidth, parHeight); fo.close (); } catch (Εξαίρεση saveEx) {saveEx.printStackTrace (); }} / * * Η μέθοδος αποθήκευσης είναι η κύρια μέθοδος της διαδικασίας. Αυτή η μέθοδος * θα καλέσει τη μέθοδο convertImage για να μετατρέψει την εικόνα μνήμης σε * έναν πίνακα byte. η μέθοδος writeBitmapFileHeader δημιουργεί και γράφει * την κεφαλίδα αρχείου bitmap. Το writeBitmapInfoHeader δημιουργεί την κεφαλίδα πληροφοριών *. και το writeBitmap γράφει την εικόνα. * * / private void save (Image parImage, int parWidth, int parHeight) {δοκιμάστε {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } catch (Εξαίρεση saveEx) {saveEx.printStackTrace (); }} / * * convertImage μετατρέπει την εικόνα μνήμης σε μορφή bitmap (BRG). * Υπολογίζει επίσης ορισμένες πληροφορίες για την κεφαλίδα πληροφοριών bitmap. * * / ιδιωτικό boolean convertImage (Image parImage, int parWidth, int parHeight) {int pad; bitmap = νέο int [parWidth * parHeight]; PixelGrabber pg = νέο PixelGrabber (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); δοκιμάστε το {pg.grabPixels (); } catch (InterruptException e) {e.printStackTrace (); επιστροφή (false); } pad = (4 - ((parWidth * 3)% 4)) * parHeight; biSizeImage = ((parWidth * parHeight) * 3) + pad; bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight; επιστροφή (αληθινή); } / * * writeBitmap μετατρέπει την εικόνα που επιστρέφεται από το αρπακτικό pixel σε * την απαιτούμενη μορφή. Θυμηθείτε: οι γραμμές σάρωσης αντιστρέφονται σε * ένα αρχείο bitmap! * * Κάθε γραμμή σάρωσης πρέπει να είναι γεμισμένη σε ένα ομοιόμορφο όριο 4 byte. * / private void writeBitmap () {int μέγεθος; τιμή int; int j; int i; int rowCount; int rowIndex; int lastRowIndex; int pad; int padCount; byte rgb [] = νέο byte [3]; μέγεθος = (biWidth * biHeight) - 1; pad = 4 - ((biWidth * 3)% 4); if (pad == 4) // <==== Μαξιλάρι διόρθωσης σφαλμάτων = 0; // <==== Σειρά διόρθωσης σφαλμάτωνCount = 1; padCount = 0; rowIndex = μέγεθος - biWidth; lastRowIndex = rowIndex; δοκιμάστε το {for (j = 0; j> 8) & 0xFF); rgb [2] = (byte) ((τιμή >> 16) & 0xFF); fo.write (rgb); εάν (rowCount == biWidth) {padCount + = pad; για (i = 1; i> 8) & 0x00FF); επιστροφή (retValue); } / * * * intToDWord μετατρέπει ένα int σε διπλή λέξη, όπου η τιμή επιστροφής * αποθηκεύεται σε έναν πίνακα 4-byte. * * / ιδιωτικό byte [] intToDWord (int parValue) {byte retValue [] = νέο 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); επιστροφή (retValue); }} 

συμπέρασμα

Αυτό είναι το μόνο που υπάρχει. Είμαι σίγουρος ότι θα βρείτε αυτήν την τάξη πολύ χρήσιμη, καθώς, από το JDK 1.1.6, η Java δεν υποστηρίζει την αποθήκευση εικόνων σε καμία από τις δημοφιλείς μορφές. Το JDK 1.2 θα προσφέρει υποστήριξη για τη δημιουργία εικόνων JPEG, αλλά όχι υποστήριξη για bitmap. Έτσι, αυτή η τάξη θα καλύψει ακόμα ένα κενό στο JDK 1.2.

Εάν παίζετε με αυτήν την τάξη και βρείτε τρόπους για να τη βελτιώσετε, ενημερώστε με! Το e-mail μου εμφανίζεται παρακάτω, μαζί με το βιογραφικό μου.

Ο Jean-Pierre Dubé είναι ανεξάρτητος σύμβουλος της Java. Ίδρυσε την Infocom, η οποία καταχωρήθηκε το 1988. Έκτοτε, η Infocom έχει αναπτύξει διάφορες προσαρμοσμένες εφαρμογές που κυμαίνονται από την κατασκευή, τη διαχείριση εγγράφων και τη διαχείριση ηλεκτρικών γραμμών μεγάλης κλίμακας. Έχει εκτεταμένη εμπειρία προγραμματισμού σε C, Visual Basic και πιο πρόσφατα Java, η οποία είναι τώρα η κύρια γλώσσα που χρησιμοποιείται από την εταιρεία του. Ένα από τα πρόσφατα έργα της Infocom είναι ένα διάγραμμα API που θα πρέπει να είναι διαθέσιμο ως έκδοση beta σύντομα.

Αυτή η ιστορία, "Java Tip 60: Saving bitmap files in Java" δημοσιεύθηκε αρχικά από το JavaWorld.