001package cnslab.image;
002import java.awt.*;
003import java.io.*;
004import java.awt.image.*;
005
006public class BMPFile extends Component {
007
008    //--- Private constants
009    private final static int BITMAPFILEHEADER_SIZE = 14;
010    private final static int BITMAPINFOHEADER_SIZE = 40;
011
012    //--- Private variable declaration
013
014    //--- Bitmap file header
015    private byte bitmapFileHeader [] = new byte [14];
016    private byte bfType [] = {(byte)'B', (byte)'M'};
017    private int bfSize = 0;
018    private int bfReserved1 = 0;
019    private int bfReserved2 = 0;
020    private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;
021
022    //--- Bitmap info header
023    private byte bitmapInfoHeader [] = new byte [40];
024    private int biSize = BITMAPINFOHEADER_SIZE;
025    private int biWidth = 0;
026    private int biHeight = 0;
027    private int biPlanes = 1;
028    private int biBitCount = 24;
029    private int biCompression = 0;
030    private int biSizeImage = 0x030000;
031    private int biXPelsPerMeter = 0x0;
032    private int biYPelsPerMeter = 0x0;
033    private int biClrUsed = 0;
034    private int biClrImportant = 0;
035
036    //--- Bitmap raw data
037    private int bitmap [];
038
039    //--- File section
040    private FileOutputStream fo;
041
042    //--- Default constructor
043    public BMPFile() {
044
045    }
046
047
048    public void saveBitmap (String parFilename, int[] imagePix, int
049                            parWidth, int parHeight) {
050
051        try {
052            fo = new FileOutputStream (parFilename);
053            save (imagePix, parWidth, parHeight);
054            fo.close ();
055        }
056        catch (Exception saveEx) {
057            saveEx.printStackTrace ();
058        }
059
060    }
061
062
063    /*
064     * The saveMethod is the main method of the process. This method
065     * will call the convertImage method to convert the memory image to
066     * a byte array; method writeBitmapFileHeader creates and writes
067     * the bitmap file header; writeBitmapInfoHeader creates the
068     * information header; and writeBitmap writes the image.
069     *
070     */
071    private void save (int[] imagePix, int parWidth, int parHeight) {
072
073        try {
074            convertImage (imagePix, parWidth, parHeight);
075            writeBitmapFileHeader ();
076            writeBitmapInfoHeader ();
077            writeBitmap ();
078        }
079        catch (Exception saveEx) {
080            saveEx.printStackTrace ();
081        }
082    }
083
084
085    /*
086     * convertImage converts the memory image to the bitmap format (BRG).
087     * It also computes some information for the bitmap info header.
088     *
089     */
090    private boolean convertImage (int[] imagePix, int parWidth, int parHeight) {
091
092        int pad;
093        bitmap = imagePix;
094
095        //          //PixelGrabber pg = new PixelGrabber (parImage, 0, 0, parWidth, parHeight,
096        //          //                            bitmap, 0, parWidth);
097
098        //          try {
099        //              pg.grabPixels ();
100        //          }
101        //          catch (InterruptedException e) {
102        //              e.printStackTrace ();
103        //              return (false);
104        //          }
105
106        pad = (4 - ((parWidth * 3) % 4)) * parHeight;
107
108        if (4 - ((parWidth * 3) % 4) == 4) pad = 0 ;
109
110        biSizeImage = ((parWidth * parHeight) * 3) + pad;
111        bfSize = biSizeImage + BITMAPFILEHEADER_SIZE +
112            BITMAPINFOHEADER_SIZE;
113        biWidth = parWidth;
114        biHeight = parHeight;
115
116        return (true);
117    }
118
119    /*
120     * writeBitmap converts the image returned from the pixel grabber to
121     * the format required. Remember: scan lines are inverted in
122     * a bitmap file!
123     *
124     * Each scan line must be padded to an even 4-byte boundary.
125     */
126    private void writeBitmap () {
127
128        int size;
129        int value;
130        int j;
131        int i;
132        int rowCount;
133        int rowIndex;
134        int lastRowIndex;
135        int pad;
136        int padCount;
137        byte rgb [] = new byte [3];
138
139
140        size = (biWidth * biHeight) - 1;
141        pad = 4 - ((biWidth * 3) % 4);
142
143        //The following bug correction will cause the bitmap to be unreadable by
144        //GIMP.  It must be there for the bitmap to be readable by most other
145        //graphics packages.
146        if (pad == 4){ // <==== Bug correction
147            pad = 0;
148        }// <==== Bug correction
149
150
151        rowCount = 1;
152        padCount = 0;
153        rowIndex = size - biWidth;
154        lastRowIndex = rowIndex;
155
156        try {
157            // The following three lines of code are a correction supplied
158            // by Alin Arsu, Feb 2003.  The original code set the top-right
159            // pixel in the image to black, and also shifted the bottom row
160            // of the image by one pixel.
161            // The original code was the following two lines:
162            // for (j = 0; j < size; j++) {
163            //     value = bitmap [rowIndex];
164            // This is replaced by the three lines that appear next.
165            for (j = 0; j < size+1; j++) {
166                if (j<biWidth) { value = bitmap [rowIndex+1]; }
167                else { value = bitmap [rowIndex]; }
168
169                rgb [0] = (byte) (value & 0xFF);
170                rgb [1] = (byte) ((value >> 8) & 0xFF);
171                rgb [2] = (byte) ((value >> 16) & 0xFF);
172                fo.write (rgb);
173                if (rowCount == biWidth) {
174                    padCount += pad;
175                    for (i = 1; i <= pad; i++) {
176                        fo.write (0x00);
177                    }
178                    rowCount = 1;
179                    rowIndex = lastRowIndex - biWidth;
180                    lastRowIndex = rowIndex;
181                }
182                else
183                    rowCount++;
184                rowIndex++;
185            }
186
187            //--- Update the size of the file
188            bfSize += padCount - pad;
189            biSizeImage += padCount - pad;
190        }
191        catch (Exception wb) {
192            wb.printStackTrace ();
193        }
194
195    }
196
197    /*
198     * writeBitmapFileHeader writes the bitmap file header to the file.
199     *
200     */
201    private void writeBitmapFileHeader () {
202
203        try {
204            fo.write (bfType);
205            fo.write (intToDWord (bfSize));
206            fo.write (intToWord (bfReserved1));
207            fo.write (intToWord (bfReserved2));
208            fo.write (intToDWord (bfOffBits));
209
210        }
211        catch (Exception wbfh) {
212            wbfh.printStackTrace ();
213        }
214
215    }
216
217    /*
218     *
219     * writeBitmapInfoHeader writes the bitmap information header
220     * to the file.
221     *
222     */
223
224    private void writeBitmapInfoHeader () {
225
226        try {
227            fo.write (intToDWord (biSize));
228            fo.write (intToDWord (biWidth));
229            fo.write (intToDWord (biHeight));
230            fo.write (intToWord (biPlanes));
231            fo.write (intToWord (biBitCount));
232            fo.write (intToDWord (biCompression));
233            fo.write (intToDWord (biSizeImage));
234            fo.write (intToDWord (biXPelsPerMeter));
235            fo.write (intToDWord (biYPelsPerMeter));
236            fo.write (intToDWord (biClrUsed));
237            fo.write (intToDWord (biClrImportant));
238        }
239        catch (Exception wbih) {
240            wbih.printStackTrace ();
241        }
242
243    }
244
245
246    /*
247     *
248     * intToWord converts an int to a word, where the return
249     * value is stored in a 2-byte array.
250     *
251     */
252    private byte [] intToWord (int parValue) {
253
254        byte retValue [] = new byte [2];
255
256        retValue [0] = (byte) (parValue & 0x00FF);
257        retValue [1] = (byte) ((parValue >> 8) & 0x00FF);
258
259        return (retValue);
260
261    }
262
263    /*
264     *
265     * intToDWord converts an int to a double word, where the return
266     * value is stored in a 4-byte array.
267     *
268     */
269    private byte [] intToDWord (int parValue) {
270
271        byte retValue [] = new byte [4];
272        retValue [0] = (byte) (parValue & 0x00FF);
273        retValue [1] = (byte) ((parValue >> 8) & 0x000000FF);
274        retValue [2] = (byte) ((parValue >> 16) & 0x000000FF);
275        retValue [3] = (byte) ((parValue >> 24) & 0x000000FF);
276
277        return (retValue);
278
279    }
280
281}