Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
BMP_Reader.java
1 package ij.plugin;
2 
3 import java.awt.*;
4 import java.awt.image.*;
5 import java.io.*;
6 import ij.*;
7 import ij.io.*;
8 
9 
16 public class BMP_Reader extends ImagePlus implements PlugIn {
17 
18  private static String defaultDirectory;
19 
20  public void run(String path) {
21 
22  //IJ.showStatus("Opening: " + path);
23  BMPDecoder bmp = new BMPDecoder();
24  FileInputStream is = null;
25  try {
26  is = new FileInputStream(path);
27  bmp.read(is);
28  }
29  catch (Exception e) {
30  String msg = e.getMessage();
31  if (msg==null || msg.equals(""))
32  msg = ""+e;
33  IJ.showMessage("BMP Decoder", msg);
34  return;
35  }
36 
37  MemoryImageSource mis = bmp.makeImageSource();
38  if (mis==null) IJ.write("mis=null");
39  Image img = Toolkit.getDefaultToolkit().createImage(mis);
40  FileInfo fi = new FileInfo();
41  fi.fileFormat = FileInfo.BMP;
42  File fp = new File(path);
43  fi.fileName = fp.getName();
44  fi.directory = fp.getParent();
45  setImage(img);
46  setTitle(fp.getName());
47  setFileInfo(fi);
48  if (path.equals(""))
49  show();
50  }
51 
52 }
53 
54 
56 class BMPDecoder {
57  InputStream is;
58  int curPos = 0;
59 
60  int bitmapOffset; // starting position of image data
61 
62  int width; // image width in pixels
63  int height; // image height in pixels
64  short bitsPerPixel; // 1, 4, 8, or 24 (no color map)
65  int compression; // 0 (none), 1 (8-bit RLE), or 2 (4-bit RLE)
66  int actualSizeOfBitmap;
67  int scanLineSize;
68  int actualColorsUsed;
69 
70  byte r[], g[], b[]; // color palette
71  int noOfEntries;
72 
73  byte[] byteData; // Unpacked data
74  int[] intData; // Unpacked data
75 
76 
77  private int readInt() throws IOException {
78  int b1 = is.read();
79  int b2 = is.read();
80  int b3 = is.read();
81  int b4 = is.read();
82  curPos += 4;
83  return ((b4 << 24) + (b3 << 16) + (b2 << 8) + (b1 << 0));
84  }
85 
86 
87  private short readShort() throws IOException {
88  int b1 = is.read();
89  int b2 = is.read();
90  curPos += 2;
91  return (short)((b2 << 8) + b1);
92  }
93 
94 
95  void getFileHeader() throws IOException, Exception {
96  // Actual contents (14 bytes):
97  short fileType = 0x4d42;// always "BM"
98  int fileSize; // size of file in bytes
99  short reserved1 = 0; // always 0
100  short reserved2 = 0; // always 0
101 
102  fileType = readShort();
103  if (fileType != 0x4d42)
104  throw new Exception("Not a BMP file"); // wrong file type
105  fileSize = readInt();
106  reserved1 = readShort();
107  reserved2 = readShort();
108  bitmapOffset = readInt();
109  }
110 
111  void getBitmapHeader() throws IOException {
112 
113  // Actual contents (40 bytes):
114  int size; // size of this header in bytes
115  short planes; // no. of color planes: always 1
116  int sizeOfBitmap; // size of bitmap in bytes (may be 0: if so, calculate)
117  int horzResolution; // horizontal resolution, pixels/meter (may be 0)
118  int vertResolution; // vertical resolution, pixels/meter (may be 0)
119  int colorsUsed; // no. of colors in palette (if 0, calculate)
120  int colorsImportant; // no. of important colors (appear first in palette) (0 means all are important)
121  boolean topDown;
122  int noOfPixels;
123 
124  size = readInt();
125  width = readInt();
126  height = readInt();
127  planes = readShort();
128  bitsPerPixel = readShort();
129  compression = readInt();
130  sizeOfBitmap = readInt();
131  horzResolution = readInt();
132  vertResolution = readInt();
133  colorsUsed = readInt();
134  colorsImportant = readInt();
135 
136  topDown = (height < 0);
137  noOfPixels = width * height;
138 
139  // Scan line is padded with zeroes to be a multiple of four bytes
140  scanLineSize = ((width * bitsPerPixel + 31) / 32) * 4;
141 
142  if (sizeOfBitmap != 0)
143  actualSizeOfBitmap = sizeOfBitmap;
144  else
145  // a value of 0 doesn't mean zero - it means we have to calculate it
146  actualSizeOfBitmap = scanLineSize * height;
147 
148  if (colorsUsed != 0)
149  actualColorsUsed = colorsUsed;
150  else
151  // a value of 0 means we determine this based on the bits per pixel
152  if (bitsPerPixel < 16)
153  actualColorsUsed = 1 << bitsPerPixel;
154  else
155  actualColorsUsed = 0; // no palette
156  }
157 
158  void getPalette() throws IOException {
159  noOfEntries = actualColorsUsed;
160  //IJ.write("noOfEntries: " + noOfEntries);
161  if (noOfEntries>0) {
162  r = new byte[noOfEntries];
163  g = new byte[noOfEntries];
164  b = new byte[noOfEntries];
165 
166  int reserved;
167  for (int i = 0; i < noOfEntries; i++) {
168  b[i] = (byte)is.read();
169  g[i] = (byte)is.read();
170  r[i] = (byte)is.read();
171  reserved = is.read();
172  curPos += 4;
173  }
174  }
175  }
176 
177  void unpack(byte[] rawData, int rawOffset, int bpp, byte[] byteData, int byteOffset, int w) throws Exception {
178  int j = byteOffset;
179  int k = rawOffset;
180  byte mask;
181  int pixPerByte;
182 
183  switch (bpp) {
184  case 1: mask = (byte)0x01; pixPerByte = 8; break;
185  case 4: mask = (byte)0x0f; pixPerByte = 2; break;
186  case 8: mask = (byte)0xff; pixPerByte = 1; break;
187  default:
188  throw new Exception("Unsupported bits-per-pixel value: " + bpp);
189  }
190 
191  for (int i = 0;;) {
192  int shift = 8 - bpp;
193  for (int ii = 0; ii < pixPerByte; ii++) {
194  byte br = rawData[k];
195  br >>= shift;
196  byteData[j] = (byte)(br & mask);
197  j++;
198  i++;
199  if (i == w) return;
200  shift -= bpp;
201  }
202  k++;
203  }
204  }
205 
206  void unpack24(byte[] rawData, int rawOffset, int[] intData, int intOffset, int w) {
207  int j = intOffset;
208  int k = rawOffset;
209  int mask = 0xff;
210  for (int i = 0; i < w; i++) {
211  int b0 = (((int)(rawData[k++])) & mask);
212  int b1 = (((int)(rawData[k++])) & mask) << 8;
213  int b2 = (((int)(rawData[k++])) & mask) << 16;
214  intData[j] = 0xff000000 | b0 | b1 | b2;
215  j++;
216  }
217  }
218 
219  void unpack32(byte[] rawData, int rawOffset, int[] intData, int intOffset, int w) {
220  int j = intOffset;
221  int k = rawOffset;
222  int mask = 0xff;
223  for (int i = 0; i < w; i++) {
224  int b0 = (((int)(rawData[k++])) & mask);
225  int b1 = (((int)(rawData[k++])) & mask) << 8;
226  int b2 = (((int)(rawData[k++])) & mask) << 16;
227  int b3 = (((int)(rawData[k++])) & mask) << 24; // this gets ignored!
228  intData[j] = 0xff000000 | b0 | b1 | b2;
229  j++;
230  }
231  }
232 
233  void getPixelData() throws IOException, Exception {
234  byte[] rawData; // the raw unpacked data
235 
236  // Skip to the start of the bitmap data (if we are not already there)
237  long skip = bitmapOffset - curPos;
238  if (skip > 0) {
239  is.skip(skip);
240  curPos += skip;
241  }
242 
243  int len = scanLineSize;
244  if (bitsPerPixel > 8)
245  intData = new int[width * height];
246  else
247  byteData = new byte[width * height];
248  rawData = new byte[actualSizeOfBitmap];
249  int rawOffset = 0;
250  int offset = (height - 1) * width;
251  for (int i = height - 1; i >= 0; i--) {
252  int n = is.read(rawData, rawOffset, len);
253  if (n < len) throw new Exception("Scan line ended prematurely after " + n + " bytes");
254  if (bitsPerPixel==24)
255  unpack24(rawData, rawOffset, intData, offset, width);
256  else if (bitsPerPixel==32)
257  unpack32( rawData, rawOffset, intData, offset, width);
258  else // 8-bits or less
259  unpack(rawData, rawOffset, bitsPerPixel, byteData, offset, width);
260  rawOffset += len;
261  offset -= width;
262  }
263  }
264 
265 
266  public void read(InputStream is) throws IOException, Exception {
267  this.is = is;
268  getFileHeader();
269  getBitmapHeader();
270  if (compression!=0)
271  throw new Exception("Compression not supported");
272  getPalette();
273  getPixelData();
274  }
275 
276 
277  public MemoryImageSource makeImageSource() {
278  ColorModel cm;
279  MemoryImageSource mis;
280 
281  if (noOfEntries > 0) {
282  // There is a color palette; create an IndexColorModel
283  cm = new IndexColorModel(bitsPerPixel,
284  noOfEntries, r, g, b);
285  } else {
286  // There is no palette; use the default RGB color model
287  cm = ColorModel.getRGBdefault();
288  }
289 
290  // Create MemoryImageSource
291 
292  if (bitsPerPixel > 8) {
293  // use one int per pixel
294  mis = new MemoryImageSource(width,
295  height, cm, intData, 0, width);
296  } else {
297  // use one byte per pixel
298  mis = new MemoryImageSource(width,
299  height, cm, byteData, 0, width);
300  }
301 
302  return mis; // this can be used by Component.createImage()
303  }
304 }