package ij.plugin;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import ij.*;
import ij.io.*;
public class BMP_Reader extends ImagePlus implements PlugIn {
public void run(String arg) {
OpenDialog od = new OpenDialog("Open BMP...", arg);
String directory = od.getDirectory();
String name = od.getFileName();
if (name==null)
return;
String path = directory + name;
BMPDecoder bmp = new BMPDecoder();
FileInputStream is = null;
try {
is = new FileInputStream(path);
bmp.read(is);
} catch (Exception e) {
String msg = e.getMessage();
if (msg==null || msg.equals(""))
msg = ""+e;
if (msg.equals("Compression not supported")) {
ImagePlus imp = Opener.openUsingImageIO(path);
if (imp!=null) {
setProcessor(name, imp.getProcessor());
if (arg.equals(""))
show();
}
} else
IJ.error("BMP Reader", msg);
return;
} finally {
if (is!=null) {
try {
is.close();
} catch (IOException e) {}
}
}
MemoryImageSource mis = bmp.makeImageSource();
if (mis==null) IJ.log("BMP_Reader: mis=null");
Image img = Toolkit.getDefaultToolkit().createImage(mis);
FileInfo fi = new FileInfo();
fi.fileFormat = FileInfo.BMP;
fi.fileName = name;
fi.directory = directory;
setImage(img);
setTitle(name);
setFileInfo(fi);
if (bmp.topDown)
getProcessor().flipVertical();
if (arg.equals(""))
show();
}
}
class BMPDecoder {
InputStream is;
int curPos = 0;
int bitmapOffset;
int width; int height; short bitsPerPixel; int compression; int actualSizeOfBitmap;
int scanLineSize;
int actualColorsUsed;
byte r[], g[], b[]; int noOfEntries;
byte[] byteData; int[] intData; boolean topDown;
private int readInt() throws IOException {
int b1 = is.read();
int b2 = is.read();
int b3 = is.read();
int b4 = is.read();
curPos += 4;
return ((b4 << 24) + (b3 << 16) + (b2 << 8) + (b1 << 0));
}
private short readShort() throws IOException {
int b1 = is.read();
int b2 = is.read();
curPos += 2;
return (short)((b2 << 8) + b1);
}
void getFileHeader() throws IOException, Exception {
short fileType = 0x4d42; int fileSize; short reserved1 = 0; short reserved2 = 0;
fileType = readShort();
if (fileType != 0x4d42)
throw new Exception("Not a BMP file"); fileSize = readInt();
reserved1 = readShort();
reserved2 = readShort();
bitmapOffset = readInt();
}
void getBitmapHeader() throws IOException {
int size; short planes; int sizeOfBitmap; int horzResolution; int vertResolution; int colorsUsed; int colorsImportant; int noOfPixels;
size = readInt();
width = readInt();
height = readInt();
planes = readShort();
bitsPerPixel = readShort();
compression = readInt();
sizeOfBitmap = readInt();
horzResolution = readInt();
vertResolution = readInt();
colorsUsed = readInt();
colorsImportant = readInt();
if (bitsPerPixel==24)
colorsUsed = colorsImportant = 0;
topDown = (height < 0);
if (topDown) height = -height;
noOfPixels = width * height;
scanLineSize = ((width * bitsPerPixel + 31) / 32) * 4;
actualSizeOfBitmap = scanLineSize * height;
if (colorsUsed != 0)
actualColorsUsed = colorsUsed;
else
if (bitsPerPixel < 16)
actualColorsUsed = 1 << bitsPerPixel;
else
actualColorsUsed = 0;
}
void getPalette() throws IOException {
noOfEntries = actualColorsUsed;
if (noOfEntries>0) {
r = new byte[noOfEntries];
g = new byte[noOfEntries];
b = new byte[noOfEntries];
int reserved;
for (int i = 0; i < noOfEntries; i++) {
b[i] = (byte)is.read();
g[i] = (byte)is.read();
r[i] = (byte)is.read();
reserved = is.read();
curPos += 4;
}
}
}
void unpack(byte[] rawData, int rawOffset, int bpp, byte[] byteData, int byteOffset, int w) throws Exception {
int j = byteOffset;
int k = rawOffset;
byte mask;
int pixPerByte;
switch (bpp) {
case 1: mask = (byte)0x01; pixPerByte = 8; break;
case 4: mask = (byte)0x0f; pixPerByte = 2; break;
case 8: mask = (byte)0xff; pixPerByte = 1; break;
default:
throw new Exception("Unsupported bits-per-pixel value: " + bpp);
}
for (int i = 0;;) {
int shift = 8 - bpp;
for (int ii = 0; ii < pixPerByte; ii++) {
byte br = rawData[k];
br >>= shift;
byteData[j] = (byte)(br & mask);
j++;
i++;
if (i == w) return;
shift -= bpp;
}
k++;
}
}
void unpack24(byte[] rawData, int rawOffset, int[] intData, int intOffset, int w) {
int j = intOffset;
int k = rawOffset;
int mask = 0xff;
for (int i = 0; i < w; i++) {
int b0 = (((int)(rawData[k++])) & mask);
int b1 = (((int)(rawData[k++])) & mask) << 8;
int b2 = (((int)(rawData[k++])) & mask) << 16;
intData[j] = 0xff000000 | b0 | b1 | b2;
j++;
}
}
void unpack32(byte[] rawData, int rawOffset, int[] intData, int intOffset, int w) {
int j = intOffset;
int k = rawOffset;
int mask = 0xff;
for (int i = 0; i < w; i++) {
int b0 = (((int)(rawData[k++])) & mask);
int b1 = (((int)(rawData[k++])) & mask) << 8;
int b2 = (((int)(rawData[k++])) & mask) << 16;
int b3 = (((int)(rawData[k++])) & mask) << 24; intData[j] = 0xff000000 | b0 | b1 | b2;
j++;
}
}
void getPixelData() throws IOException, Exception {
byte[] rawData;
long skip = bitmapOffset - curPos;
if (skip > 0) {
is.skip(skip);
curPos += skip;
}
int len = scanLineSize;
if (bitsPerPixel > 8)
intData = new int[width * height];
else
byteData = new byte[width * height];
rawData = new byte[actualSizeOfBitmap];
int rawOffset = 0;
int offset = (height - 1) * width;
for (int i = height - 1; i >= 0; i--) {
int n = is.read(rawData, rawOffset, len);
if (n < len) throw new Exception("Scan line ended prematurely after " + n + " bytes");
if (bitsPerPixel==24)
unpack24(rawData, rawOffset, intData, offset, width);
else if (bitsPerPixel==32)
unpack32( rawData, rawOffset, intData, offset, width);
else unpack(rawData, rawOffset, bitsPerPixel, byteData, offset, width);
rawOffset += len;
offset -= width;
}
}
public void read(InputStream is) throws IOException, Exception {
this.is = is;
getFileHeader();
getBitmapHeader();
if (compression!=0)
throw new Exception("Compression not supported");
getPalette();
getPixelData();
}
public MemoryImageSource makeImageSource() {
ColorModel cm;
MemoryImageSource mis;
if (noOfEntries>0 && bitsPerPixel!=24) {
cm = new IndexColorModel(bitsPerPixel, noOfEntries, r, g, b);
} else {
cm = ColorModel.getRGBdefault();
}
if (bitsPerPixel > 8) {
mis = new MemoryImageSource(width,
height, cm, intData, 0, width);
} else {
mis = new MemoryImageSource(width,
height, cm, byteData, 0, width);
}
return mis; }
}