1 /* BMPInfoHeader.java --
2    Copyright (C)  2005  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.javax.imageio.bmp;
39 
40 import java.awt.Dimension;
41 import java.awt.image.ColorModel;
42 import java.awt.image.RenderedImage;
43 import java.io.IOException;
44 import java.nio.ByteBuffer;
45 import java.nio.ByteOrder;
46 
47 import javax.imageio.IIOImage;
48 import javax.imageio.ImageWriteParam;
49 import javax.imageio.stream.ImageInputStream;
50 import javax.imageio.stream.ImageOutputStream;
51 
52 public class BMPInfoHeader
53 {
54     /** Size of the bitmap info header */
55   protected int biSize;
56 
57   /** Pixel width of the bitmap */
58   protected int biWidth;
59 
60   /** Pixel height of the bitmap */
61   protected int biHeight;
62 
63   /** Number of bitplanes = 1 */
64   protected short biPlanes;
65 
66   /** Number of bpp = 1,4,8,24 */
67   protected short biBitCount;
68 
69   /** Compression type, RGB8, RLE8, RLE4, BITFIELDS */
70   protected int biCompression;
71 
72   /** Byte size of the uncompressed bitmap, can be 0. */
73   protected int biSizeImage;
74 
75   /** X resolution, dots per meter */
76   protected int biXPelsPerMeter;
77 
78   /** Y resolution, dots per meter */
79   protected int biYPelsPerMeter;
80 
81   /** Number of colors used (palette only, can be 0 for all) */
82   protected int biClrUsed;
83 
84   /** Number of 'important' colors, 0 for all */
85   protected int biClrImportant;
86 
87   /** BITMAPINFOHEADER is 40 bytes */
88   public static final int SIZE = 40;
89 
90     /**
91      * Compression types
92      */
93     public static final int BI_RGB = 0;
94     public static final int BI_RLE8 = 1;
95     public static final int BI_RLE4 = 2;
96     public static final int BI_BITFIELDS = 3;
97 
98     /**
99      * Creates the header from an input stream, which is not closed.
100      *
101      * @param in - the image input stream
102      * @throws IOException if an I/O error occured.
103      * @throws BMPException if the header was invalid
104      */
BMPInfoHeader(ImageInputStream in)105   public BMPInfoHeader(ImageInputStream in) throws IOException, BMPException
106   {
107     byte[] data = new byte[SIZE];
108 
109     if (in.read(data) != SIZE)
110       throw new IOException("Couldn't read header.");
111     ByteBuffer buf = ByteBuffer.wrap(data);
112     buf.order(ByteOrder.LITTLE_ENDIAN);
113 
114     int n;
115     if ((n = buf.getInt()) != SIZE)
116       throw new BMPException("Invalid BITMAPINFOHEADER size: " + n);
117 
118     biWidth = buf.getInt();
119     biHeight = buf.getInt();
120     biPlanes = buf.getShort();
121     setBitCount(buf.getShort());
122     setCompression(buf.getInt());
123     biSizeImage = buf.getInt();
124     biXPelsPerMeter = buf.getInt();
125     biYPelsPerMeter = buf.getInt();
126     biClrUsed = buf.getInt();
127     biClrImportant = buf.getInt();
128   }
129 
130   /**
131    * Creates the info header from an output stream, which is not closed.
132    *
133    * @param out - the image output stream
134    * @param im - the image
135    * @param param - the image write param.
136    * @throws IOException if an I/O error occured.
137    */
BMPInfoHeader(ImageOutputStream out, IIOImage im, ImageWriteParam param)138   public BMPInfoHeader(ImageOutputStream out, IIOImage im, ImageWriteParam param) throws IOException
139   {
140     RenderedImage img = im.getRenderedImage();
141     ColorModel cMod = img.getColorModel();
142 
143     biSize = SIZE;
144     biWidth = img.getWidth();
145     biHeight = img.getHeight();
146     biPlanes = 1;
147 
148     if (param != null && param.canWriteCompressed())
149       {
150         String compType = param.getCompressionType();
151         if (compType.equals("BI_RLE8"))
152           {
153             biCompression = BI_RLE8;
154             biBitCount = 8;
155           }
156         else if (compType.equals("BI_RLE4"))
157           {
158             biCompression = BI_RLE4;
159             biBitCount = 4;
160           }
161         else
162           {
163             biCompression = BI_RGB;
164             biBitCount = (short) cMod.getPixelSize();
165           }
166       }
167     else
168       {
169         biBitCount = (short) cMod.getPixelSize();
170         biCompression = BI_RGB;
171       }
172 
173     biXPelsPerMeter = 0x0;
174     biYPelsPerMeter = 0x0;
175     biClrUsed = 0;
176     biClrImportant = 0;
177     biSizeImage = ((biWidth * biHeight) * 3)
178                   + ((4 - ((biWidth * 3) % 4)) * biHeight);
179     out.write(intToDWord(biSize));
180     out.write(intToDWord(biWidth));
181     out.write(intToDWord(biHeight));
182     out.write(intToWord(biPlanes));
183     out.write(intToWord(biBitCount));
184     out.write(intToDWord(biCompression));
185     out.write(intToDWord(biSizeImage));
186     out.write(intToDWord(biXPelsPerMeter));
187     out.write(intToDWord(biYPelsPerMeter));
188     out.write(intToDWord(biClrUsed));
189     out.write(intToDWord(biClrImportant));
190   }
191 
192   /**
193    * Converts an int to a word, where the return value is stored in a
194    * 2-byte array.
195    *
196    * @param val - the value to convert
197    * @return the array
198    */
intToWord(int val)199   private byte[] intToWord(int val)
200   {
201     byte b[] = new byte[2];
202     b[0] = (byte) (val & 0x00FF);
203     b[1] = (byte) ((val >> 8) & 0x00FF);
204     return b;
205   }
206 
207   /**
208    * Converts an int to a double word, where the return value is
209    * stored in a 4-byte array.
210    *
211    * @param val - the value to convert
212    * @return the array
213    */
intToDWord(int val)214   private byte[] intToDWord(int val)
215   {
216     byte b[] = new byte[4];
217     b[0] = (byte) (val & 0x00FF);
218     b[1] = (byte) ((val >> 8) & 0x000000FF);
219     b[2] = (byte) ((val >> 16) & 0x000000FF);
220     b[3] = (byte) ((val >> 24) & 0x000000FF);
221     return b;
222   }
223 
224 
setBitCount(short bitcount)225   public void setBitCount(short bitcount) throws BMPException
226   {
227     switch (bitcount)
228       {
229       case 1:
230       case 4:
231       case 8:
232       case 16:
233       case 24:
234       case 32:
235         biBitCount = bitcount;
236         break;
237 
238       default:
239         throw new BMPException("Invalid number of bits per pixel: " + bitcount);
240       }
241   }
242 
getBitCount()243   public short getBitCount()
244   {
245     return biBitCount;
246   }
247 
setCompression(int compression)248   public void setCompression(int compression) throws BMPException
249   {
250     switch (compression)
251       {
252       case BI_RLE8:
253         if (getBitCount() != 8)
254           throw new BMPException("Invalid number of bits per pixel.");
255         biCompression = compression;
256         break;
257       case BI_RLE4:
258         if (getBitCount() != 4)
259           throw new BMPException("Invalid number of bits per pixel.");
260         biCompression = compression;
261         break;
262 
263       case BI_RGB:
264       case BI_BITFIELDS:
265         biCompression = compression;
266         break;
267 
268       default:
269         throw new BMPException("Unknown bitmap compression type.");
270       }
271   }
272 
getNumberOfPaletteEntries()273   public int getNumberOfPaletteEntries()
274   {
275     if (biClrUsed == 0)
276       switch (biBitCount)
277         {
278         case 1:
279           return 2;
280         case 4:
281           return 16;
282         case 8:
283           return 256;
284 
285         default: // should not happen
286           return 0;
287         }
288 
289     return biClrUsed;
290   }
291 
getCompression()292   public int getCompression()
293   {
294     return biCompression;
295   }
296 
getSize()297   public Dimension getSize()
298   {
299     return new Dimension(biWidth, biHeight);
300   }
301 
getWidth()302   public int getWidth()
303   {
304     return biWidth;
305   }
306 
getHeight()307   public int getHeight()
308   {
309     return biHeight;
310   }
311 
setSize(Dimension d)312   public void setSize(Dimension d)
313   {
314     biWidth = (int) d.getWidth();
315     biHeight = (int) d.getHeight();
316   }
317 }
318