Although Java provides several mechanisms that open images, saving images is not its strength. This trick will tell how to save images in a 24-bit bitmap file. In addition, Jean-Pierre provides all code required to write image files to the bitmap file. This tip is the addition of Java Tips 43, that tip shows the process of loading bitmap files in the Java application. This month I will provide a tutorial that explains how to save images in a 24-bit bitmap file, which also contains a piece of code to write image objects into the bitgraph file. If you work in the Microsoft Windows environment, the function of creating bitmap files will provide you with many convenience. For example, in my last project, I have to connect Java with Microsoft Access. The Java program allows the user to draw on the screen. This picture is then printed into the Microsoft Access report. Since Java does not support OLE, my only choice is to create a bitmap file for this figure and inform the Microsoft Access report where to find this bitmap file. If you write an application that sends an image to the clipboard, this tip may be useful to you - especially when you pass this information to another application.
The format bitmap file format of the bitmap file supports 4-bit RLE (stroke length coding) and 8-bit and 24-bit encoding. Because we only deal with 24-bit format, let's take a look at the structure of the file.
The bitmap file is divided into three parts. I have listed them below.
Part 1: The header header of the bitmap file contains the type size information and layout information of the bitmap file. The structure is as follows (taken from the C language structure):
Typedef struct tagbitmapfileHeader {uint bftype; dword bfsize; uint bfreserved1; uint bfreserved2; dword bfofbits;} BitmapfileHeader;
Here is an instructions for code elements in this list:
BFTYPE: Specifies the file type whose value is always BM.
BFSIZE: Specifies the size of the entire file (in bytes).
BFRESERVED1: Reserved - must be 0.
BFRESERVED2: Reserved - must be 0.
BFOFFBITS: Specifies the byte offset from BitmapFileHeader to the image header. Now you have understood the use of bitmap headers to identify bitmap files. Each program of reading a bitmap file uses a bitmap header to perform file verification.
Part 2: The header of the bitmap information header is referred to as the information header, which contains the properties of the image itself.
Here's how to specify the size and color format of the Windows 3.0 (or higher) device stand-alone bitmap (DIB):
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;
Each element of the above code list is as follows:
Bisize: Specifies the number of bytes required for the BitmapInfoHeader structure.
BiWidth: Specify the width of the bit map (in pixel).
Biheight: Specifies the height of the bitmap (in pixel). BIPLANES: Specifies the number of parties for the target device. The value of this member variable must be 1.
BIBITCOUNT: Specifies the number of bits of each pixel. Its value must be 1,4, 8 or 24.
Bicompression: Specifies the compression type of the compressed bitmap. In the 24-bit format, the variable is set to 0.
BisizeImage: Specifies the size of the image (in bytes). If the format of the bitmap is BI_RGB, set this member variable to 0 is effective.
Bixpelspermeter: Specify the horizontal resolution of the target device (in "pixel / m"). The application can use this value to select a bitmap from a resource group that is most consistent with the current device feature.
Biypelspermeter: Specifies the vertical resolution of the target device for a bitmap (in "pixel / meter").
BICLRUSED: Indicates the color index number in the color table used in the location map. If BIBITCOUNT is set to 24, biclrused specifies a reference color table to optimize Windows palette performance.
BICLRIMPORTANT: Specifies the number of color indexes that have important impact on the display of bitmaps. If this value is 0, all colors is important. All information needed to create images now has now defined.
Part 3: Image in the 24-bit format, each pixel in the image is represented by a three-byte RGB sequence stored as a BRG. Each scan line is complemented to 4 digits. In order to make this process slightly more complicated, the image is stored on the bottom, that is, the first scan line is the last scanning line in the image. The following figure shows the header (BitmapHeader) and (BitMapInfoHead) and some images. Each part is separated by the vertical line:
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 *
Now, we begin to see the code now we already know the structure of the 24-bit bitmap file. Here is the long-awaited content: used to write the image object to the code of the bitmap file.
Import java.awt. *; import java.io. *; import java.awt.image. *;
Public class bmpfile extends component {
// - Private constant private final static int bitmapfileHeader_size = 14; private final static int bitmapinfoHeader_size = 40;
// - private variable declaration
// --- bitmap file header private bote bitmapfileHeader [] = new byte [14]; private bote bftype [] = {'b', 'm'}; private int bfSize = 0; private int bfReserved1 = 0; private int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE BITMAPINFOHEADER_SIZE; // --- bitmap information header private byte bitmapInfoHeader [] = new byte [40]; private int biSize = BITMAPINFOHEADER_SIZE; private int biWidth = 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; private int biYPelsPerMeter = 0x0; private int biClrUsed = 0; private int biClrImportant = 0;
// - Bitmap Original Data Private Int Bitmap [];
// --- Document part private fileoutputstream fo;
// - By default constructor public bmpfile () {
}
Public Void Savebitmap (String Parfilename, Image Parmage, Int Parwidth, Int Parheight) {
Try {fo = new fileoutputstream (Parfilename); Save (Parimage, Parwidth, Parheight); fo.close ();} catch (exception saveex) {saveex.printStackTrace ();
}
/ * * SaveMethod is the main method of the process. This method * will call the ConvertImage method to convert the memory image to * byte arrays; WriteBitMapFileHeader method creation and write * bitmap file header; WriteBitMapInfoHeader creation * Information header; WriteBitmap is written to the image. * * / Private void save (image parimage, int parwidth, int parheight) {
Try {ConvertImage (Parimage, Parwidth, Parheight); WriteBitmapFileHeader (); WriteBitMapInfoHeader (); WriteBitmap ();} catch (exception saveex) {saveex.printStackTrace ();}}
/ * * ConvertImage converts memory images to a bitmap format (BRG). * It also calculates some information used by the bitmap information header. * / Private Boolean ConvertImage (Image Parimage, Int Parwidth, INT Parheight) {INT PAD; Bitmap = New Int [Parwidth * Parheight];
Pixelgrabber pg = new PixelGrabber (Parimage, 0, 0, Parwidth, Parheight, Bitmap, 0, Parwidth);
Try {pg.grabpixels ();} catch (interruptedException e) {E.PrintStackTrace (); return (false);}
pad = (4 - ((parWidth * 3)% 4)) * parHeight; biSizeImage = ((parWidth * parHeight) * 3) pad; bfSize = biSizeImage BITMAPFILEHEADER_SIZE BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight;
Return (True);
/ * * WriteBitmap converts the image returned by the pixel capture to * required format. Keep in mind: Scanning line is in the bitmap file in reverse storage! * * Each scan must make up to 4 bytes. * / Private void writebitmap () {
INT Size; Int Value; Int J; Int i; int ROWCOUNT; INT Rowindex; int LastrowIndex; int pad; int padcount; byte rgb [] = new byte [3];
Size = (BiWidth * Biheight) - 1; PAD = 4 - ((BiWidth * 3)% 4); if (PAD == 4) // <==== Error correction PAD = 0; // <=== = Error correction rowcount = 1; padcount = 0; rowindex = size - biwidth; lastrowindex = rowindex;
Try {for (j = 0; j
}
/ * * WriteBitMapFileHeader writes the bitmap file header into the file. * * / private void writebitmapfileheader () {
Try {fo.write (bftype); fo.write (intertoword (bFReServed1)); fo.write (Intest (bFReServed2)); fo.write
} Catch (Exception WBFH) {wbfh.printStackTrace ();
}
/ * * WriteBitMapInfoHead write bitmap information header * to the file. * * /
Private void writebitmapinfoheader () {
Try {fo.write; fo.Write (INTTODWORD (Biheight)); fo.write (INTTOT (INTTOTCOT (Bibitcount) ); fo.write (intToDWord (biCompression)); fo.write (intToDWord (biSizeImage)); fo.write (intToDWord (biXPelsPerMeter)); fo.write (intToDWord (biYPelsPerMeter)); fo.write (intToDWord (biClrUsed) ); fo.stodword (biclrimportant);} catch (exception wbih) {wbih.printstackTrace ();}
}
/ * * * INTTOWORD converts an integer to a single word, return value * stored in a double-byage array. * * / private byte [] INTTOWORD (INT PARVALUE) {
BYTE RETVALUE [] = new byte [2];
RetValue [0] = (Byte) (PARVALUE & 0x00FF); RetValue [1] = (Byte) ((PARVALUE >> 8) & 0x00FF); Return (RetValue);
}
/ * * INTTODWORD converts the integer to double words, return value * Store in a 4-byte array. * * / Private byte [] INTTTODWORD (INT PARVALUE) {
Byte RetValue [] = new 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);
}
}
Summary This is all the work you have to do. I am sure that you will find that this class is useful, because to JDK 1.1.6, Java does not support saving images in any common format. JDK 1.2 will support creating a JPEG image, but does not support creation bitmaps. So this class will still fill the gap in JDK1.2.
Please inform me if you use this class and find it! There is my email address in the following personal resume.
How to read 8 bits and 24-bit Microsoft Windows bitmaps in the Java application
Currently, standard GetImage () methods only support GIF and JPEG images. Although there is a Java routine for reading a PNG (portable network graphic) format, we haven't heard of reading programs for reading Microsoft Windows bitmap images. This trick written by Jeff West provides a code that loads a Windows bitmap image. Java's current release does not officially support reading Microsoft Windows bitmap files in the Java application. But don't worry, we have a way to solve this problem! This tip will explain how to complete this task - we first explain the basic steps to read the Microsoft Windows file format. Windows Dib (Device Separate Bitmap) File format is relatively simple. Unlike the pure bit map format, the DIB format retains the explicit information for storing images in memory. The problem is that variants in the image format are so many (1 bits, 4 bits, 8 bits, and 16 bits, and other formats). The solutions provided in this Java trick use only two formats of 8 bits and 24-bit. These two formats represent the most common variants.
Regardless of the Windows Dib subtype, this file format is always consisting of 14 file headers and 40-bit information headers. These two headers exactly contain information about the storage content of the file and the order of storage. For the exact meaning of each of the headers, please refer to Microsoft Software Development Kit (SDK). The content of the file is different from the data in the information head.
Let's take a look at the two subtypes you want to process. The 24-bit format is very simple: RGB (red-green-blue) color value (3 bytes, and sorted by BGR) immediately after the information head. However, each scan line is complemented to 4 bytes. According to the description document (see Microsoft SDK), this "complement" is to optimize the Windows bitmap drawing API. At the same time, the scan line of the bottom is the first content in the file - thus relatively conventional graphics coordinate systems (the forward direction of the vector direction is down and right respectively), the image must be read forward. The 8-bit type is complicated due to the insertion palette information between the information head and pixel data. Therefore, each pixel entry is just an 8-bit index of the panel array of 24-bit RGB colors. In pixel information, each scanning is also complemented to 4 bytes.
Note that the bitmap image load method provided herein does not support the decompression of the compressed bitmap image. In fact, this routine doesn't even seek this possibility! If you encounter a compressed Windows Dib file, this routine will definitely produce an exception. A description of the compressed Windows DIB format is compressed in Windows SDK.
As for performance, on the 486-DX2-66 MHz system running Microsoft Windows 95, the routine reads 24-bit 640 x 480 The time required for the file (approximately 920 kilobytes) of no more than 10 seconds. Using BufferedInputStream instead of FileInputStream significantly improves performance.
The following routines read any of two file formats and generate an image image. The following code does not contain a comprehensive error and exception handling to avoid making this routine more complicated. You can always convert unsupported Windows DIB subtypes with Windows Paint programs.
/ ** loadBitmap () method is converted by Windows C code. Only uncompressed 24-bit and 8-bit images can be read. The image saved with Microsoft Paint with Microsoft Paint is tested on Windows 95. If the image is not a 24-bit or 8-bit image, the program refuses to make any attempts. I guess if the mask operation is performed with 1100, and then use 0011 to perform the mask operation, the 4-digit image can also be included. I actually not interested in these images. If you try to read a compressed image, the routine may fail and generate an oewception exception. If the variable NCompRESSION is not 0, it is indicated that it has been compressed.
Parameters: SDIR and SFile are the results of FileDialog's getDirectory () and getFile () methods.
Return Value: Image object, remember to check (image) null !!!!
* / Public Image loadbitmap (String sdir, String sfile) {Image image; System.out.println ( "loading:" sdir sfile); try {FileInputStream fs = new FileInputStream (sdir sfile); int bflen = 14; // 14 byte BitmapfileHeader Byte bf [] = new byte [bflen]; fs.read (bf, 0, bflen); int bilen = 40; // 40 byte BitmapInfoHeader Byte bi [] = new byte [bilen]; fs.read (Bi, 0, Bilen); // Interpret the data. INT nsize = ((int) bf [5] & 0xff) << 24) | ((int) BF [4] & 0xff) << 16) | (((int) bf [3] & 0xff) << 8) | (int) BF [2] & 0xff; System.out.Println ("File Type IS:" (Char) BF [0] (Char) BF [1]); System.out.Println ("Size of File IS: " nsize);
INT NBISIZE = ((int) BI [3] & 0xFF) << 24) | ((int) Bi [2] & 0xff) << 16) | (((int) BI [1] & 0xFF) << 8) | (int) BI [0] & 0xff; System.out.println ("Size of BitmapInfoHeader IS:" Nbisize);
INT NWIDTH = ((int) BI [7] & 0xFF) << 24) | ((int) Bi [6] & 0xff) << 16) | ((int) Bi [5] & 0xff) << 8) | (int) Bi [4] & 0xff; System.out.println ("Width IS: NWIDTH);
INT NHEIGHT = ((int) BI [11] & 0xff) << 24) | ((int) Bi [10] & 0xff) << 16) | ((int)) Bi [9] & 0xFF) << 8) | (int) BI [8] & 0xff; System.out.println ("Height is:" NHEight;
INT NPLANES = ((int) BI [13] & 0xFF) << 8) | (int) bi [12] & 0xff; System.out.Println ("Plaso");
INT nbitcount = ((int) BI [15] & 0xFF) << 8) | (int) bi [14] & 0xff; system.out.println ("Bitcount IS:" nbitcount;
/ / Find a non-zero value int ncompression = ((int) bi [19]) << 24) | ((int) BI [18]) << 16) | ((int)) ]) << 8) | (int) bi [16]; system.out.println ("compression is:" ncompression); int nsizeImage = ((int) Bi [23] & 0xff) << 24) | ((int) BI [22] & 0xFF) << 16) | ((int) BI [21] & 0xFF) << 8) | (int) bi [20] & 0xff; system.out.println ("SizeImage IS: " nsizeImage);
INT NXPM = ((int) BI [27] & 0xFF) << 24) | ((int) BI [26] & 0xFF) << 16) | ((int) Bi [25] & 0xff) << 8) | (int) BI [24] & 0xff; System.out.println ("X-Pixels Per meter is:" NXPM);
INT NYPM = ((int) BI [31] & 0xFF) << 24) | ((int) BI [30] & 0xFF) << 16) | ((int)) Bi [29] & 0xff) << 8) | (int) Bi [28] & 0xff; System.out.println ("Y-Pixels Per meter is:" NYPM);
INT nclrused = ((int) Bi [35] & 0xff) << 24) | ((int) Bi [34] & 0xff) << 16) | (((int)) Bi [33] & 0xFF) << 8) | (int) bi [32] & 0xff; System.out.println ("Colors Used Are: NClRused);
INT nclrimp = ((int) BI [39] & 0xFF) << 24) | ((int) Bi [38] & 0xff) << 16) | (((int) Bi [37] & 0xff) << 8) | (int) BI [36] & 0xff; System.out.println ("Colors Important Are: NClrim);
If (nbitcount == 24) {// 24-bit format does not include palette data, but the scanning is complement to // 4 bytes. INT NPAD = (nsizeImage / nHEight) - nwidth * 3; int nData [] = new int =NT [NHEIGHT * NWIDTH]; BYTE brGb [] = new byte [(nwidth nPAD) * 3 * nHEight]; fs.read (brgb) , 0, (NWIDTH NPAD); INT NINDEX = 0; for (int J = 0; j
// Some bitmaps do not calculate the SizeImage domain, find out // These situations and correct them. IF (nsizeImage = (((nwidth * nbitcount) 31) & ~ 31) >> 3); nsizeImage * = nHEight; system.out.println ("nsizeImage (backup) nsizeImage }
// Read the color of the palette. Int npalette [] = new int [nnumcolors]; byte bPALETTE [] = new byte [nnumcolors * 4]; fs.read (bPALETTE, 0, NNUMCOLORS * 4); int NINDEX8 = 0; for (int N = 0; N INT NDATA8 [] = new int [nwidth * nheight]; byte bdata [] = new byte [(nwidth nPad8) * nHEight]; fs.read (bdata, 0, (nwidth nPad8) * nHEight); NINDEX8 = 0 For (int J8 = 0; J8 Image = CreateImage (NWidth, NHEIGHT, NDATA8, 0, NWIDTH));} else {system.out.println ("NOT A 24-Bit or 8-bit Windows Bitmap, Aborting ..."); Image = (IMage) null;} fs.close (); return image;} catch (exception e) {system.out.println ("Caught Exception in LoadBitmap!");} return (image) null;}