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}