JAL-1807 Bob's first commit -- Applet loaded; needs image
[jalview.git] / src / javajs / img / BMPDecoder.java
1 /* $RCSfile$
2  * $Author: nicove $
3  * $Date: 2007-03-30 12:26:16 -0500 (Fri, 30 Mar 2007) $
4  * $Revision: 7275 $
5  *
6  * Copyright (C) 2002-2005  The Jmol Development Team
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package javajs.img;
25
26 import java.io.BufferedInputStream;
27 import java.io.IOException;
28
29 import javajs.util.Rdr;
30
31 /**
32  * src: http://www.javaworld.com/article/2077542/learn-java/java-tip-43--how-to-
33  * read-8--and-24-bit-microsoft-windows-bitmaps-in-java-applications.html
34  *
35  * see also: http://en.wikipedia.org/wiki/BMP_file_format
36  * 
37  * Modified by Bob Hanson hansonr@stolaf.edu
38  * 
39  * @author Bob Hanson (hansonr@stolaf.edu)
40  * 
41  */
42 public class BMPDecoder {
43
44   public BMPDecoder() {
45     // for reflection
46   }
47   
48   private BufferedInputStream bis;
49
50   /**
51    * original comment:
52    * 
53    * loadbitmap() method converted from Windows C code. Reads only uncompressed
54    * 24- and 8-bit images. Tested with images saved using Microsoft Paint in
55    * Windows 95. If the image is not a 24- or 8-bit image, the program refuses
56    * to even try. I guess one could include 4-bit images by masking the byte by
57    * first 1100 and then 0011. I am not really interested in such images. If a
58    * compressed image is attempted, the routine will probably fail by generating
59    * an IOException. Look for variable ncompression to be different from 0 to
60    * indicate compression is present.
61    * 
62    * @param bytes
63    * @return [image byte array, width, height]
64    */
65   public Object[] decodeWindowsBMP(byte[] bytes) {
66     try {
67       bis = Rdr.getBIS(bytes);
68       temp = new byte[4];
69       // read BITMAPFILEHEADER
70       if (readByte() != 'B' || readByte() != 'M')
71         return null;
72       readInt(); // file size; ignored
73       readShort(); // reserved
74       readShort(); // reserved
75       readInt(); // ptr to pixel array; ignored
76       int imageWidth, imageHeight, bitsPerPixel, nColors = 0, imageSize = 0;
77       // read BITMAP header
78       int headerSize = readInt();
79       switch (headerSize) {
80       case 12:
81         // BITMAPCOREHEADER
82         imageWidth = readShort();
83         imageHeight = readShort();
84         readShort(); // planes
85         bitsPerPixel = readShort();
86         break;
87       case 40:
88         // BITMAPINFOHEADER
89         imageWidth = readInt();
90         imageHeight = readInt();
91         readShort(); // planes
92         bitsPerPixel = readShort();
93         int ncompression = readInt();
94         if (ncompression != 0) {
95           System.out.println("BMP Compression is :" + ncompression
96               + " -- aborting");
97           return null;
98         }
99         imageSize = readInt();
100         readInt(); // hres
101         readInt(); // vres
102         nColors = readInt();
103         readInt(); // colors used
104         break;
105       default:
106         System.out.println("BMP Header unrecognized, length=" + headerSize
107             + " -- aborting");
108         return null;
109       }
110       boolean isYReversed = (imageHeight < 0);
111       if (isYReversed)
112         imageHeight = -imageHeight;
113       int nPixels = imageHeight * imageWidth;
114       int bytesPerPixel = bitsPerPixel / 8;
115       nColors = (nColors > 0 ? nColors : 1 << bitsPerPixel);
116       int npad = (bytesPerPixel == 4 ? 0
117           : imageSize == 0 ? 4 - (imageWidth % 4) : (imageSize / imageHeight)
118               - imageWidth * bytesPerPixel) % 4;
119       int[] palette;
120       int[] buf = new int[nPixels];
121       int dpt = (isYReversed ? imageWidth : -imageWidth);
122       int pt0 = (isYReversed ? 0 : nPixels + dpt);
123       int pt1 = (isYReversed ? nPixels : dpt);
124       switch (bitsPerPixel) {
125       case 32:
126       case 24:
127         for (int pt = pt0; pt != pt1; pt += dpt, pad(npad))
128           for (int i = 0; i < imageWidth; i++)
129             buf[pt + i] = readColor(bytesPerPixel);
130         break;
131       case 8:
132         palette = new int[nColors];
133         for (int i = 0; i < nColors; i++)
134           palette[i] = readColor(4);
135         for (int pt = pt0; pt != pt1; pt += dpt, pad(npad))
136           for (int i = 0; i < imageWidth; i++)
137             buf[pt + i] = palette[readByte()];
138         break;
139       case 4:
140         npad = (4 - (((imageWidth + 1) / 2) % 4)) % 4;
141         palette = new int[nColors];
142         for (int i = 0; i < nColors; i++)
143           palette[i] = readColor(4);
144         int b4 = 0;
145         for (int pt = pt0; pt != pt1; pt += dpt, pad(npad))
146           for (int i = 0, shift = 4; i < imageWidth; i++, shift = 4 - shift)
147             buf[pt + i] = palette[((shift == 4 ? (b4 = readByte()) : b4) >> shift) & 0xF];
148         break;
149       case 1:
150         int color1 = readColor(3);
151         int color2 = readColor(3);
152         npad = (4 - (((imageWidth + 7) / 8) % 4)) % 4;
153         int b = 0;
154         for (int pt = pt0; pt != pt1; pt += dpt, pad(npad))
155           for (int i = 0, bpt = -1; i < imageWidth; i++, bpt--) {
156             if (bpt < 0) {
157               b = readByte();
158               bpt = 7;
159             }
160             buf[pt + i] = ((b & (1 << bpt)) == 0 ? color1 : color2);
161           }
162         break;
163       case 64:
164       case 2:
165       default:
166         System.out
167             .println("Not a 32-, 24-, 8-, 4-, or 1-bit Windows Bitmap, aborting...");
168         return null;
169       }
170       return new Object[] { buf, Integer.valueOf(imageWidth),
171           Integer.valueOf(imageHeight) };
172     } catch (Exception e) {
173       System.out.println("Caught exception in loadbitmap!");
174     }
175     return null;
176   }
177
178   private boolean pad(int npad) throws IOException {
179     for (int i = 0; i < npad; i++)
180       readByte();
181     return true;
182   }
183
184   private byte[] temp;
185   
186   private int readColor(int n) throws IOException {
187     bis.read(temp, 0, n);
188     return 0xff << 24 | ((temp[2] & 0xff) << 16)
189         | ((temp[1] & 0xff) << 8) | temp[0] & 0xff;
190   }
191
192   private int readInt() throws IOException {
193     bis.read(temp, 0, 4);
194     return ((temp[3] & 0xff) << 24) | ((temp[2] & 0xff) << 16)
195         | ((temp[1] & 0xff) << 8) | temp[0] & 0xff;
196   }
197
198   private int readShort() throws IOException {
199     bis.read(temp, 0, 2);
200     return ((temp[1] & 0xff) << 8) | temp[0] & 0xff;
201   }
202
203   private int readByte() throws IOException {
204     bis.read(temp, 0, 1);
205     return temp[0] & 0xff;
206   }
207
208 }