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