001: // ymageBMPParser.java
002: // (C) 2007 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
003: // first published 15.07.2007 on http://yacy.net
004: //
005: // $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $
006: // $LastChangedRevision: 1986 $
007: // $LastChangedBy: orbiter $
008: //
009: // LICENSE
010: //
011: // This program is free software; you can redistribute it and/or modify
012: // it under the terms of the GNU General Public License as published by
013: // the Free Software Foundation; either version 2 of the License, or
014: // (at your option) any later version.
015: //
016: // This program is distributed in the hope that it will be useful,
017: // but WITHOUT ANY WARRANTY; without even the implied warranty of
018: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019: // GNU General Public License for more details.
020: //
021: // You should have received a copy of the GNU General Public License
022: // along with this program; if not, write to the Free Software
023: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: package de.anomic.ymage;
026:
027: import java.awt.image.BufferedImage;
028: import java.io.File;
029: import java.io.FileInputStream;
030: import java.io.FileNotFoundException;
031: import java.io.IOException;
032:
033: import javax.imageio.ImageIO;
034:
035: public class ymageBMPParser {
036:
037: // this is a implementation of http://de.wikipedia.org/wiki/Windows_Bitmap
038:
039: // file offsets
040: private static int FILEHEADER_offset = 0;
041: private static int INFOHEADER_offset = 14;
042: public static int INFOHEADER_size = 40;
043:
044: // compression tags
045: private static int BI_RGB = 0;
046: //private static int BI_RLE8 = 1;
047: //private static int BI_RLE4 = 2;
048: //private static int BI_BITFIELDS = 3;
049:
050: private IMAGEMAP imagemap;
051:
052: //boolean debugmode = false;
053:
054: public static final boolean isBMP(byte[] source) {
055: // check the file magic
056: return (source != null) && (source.length >= 2)
057: && (source[0] == 'B') && (source[1] == 'M');
058: }
059:
060: public ymageBMPParser(byte[] source) {
061:
062: // read info-header
063: int bfOffBits = DWORD(source, FILEHEADER_offset + 10);
064:
065: INFOHEADER infoheader = new INFOHEADER(source,
066: INFOHEADER_offset);
067: COLORTABLE colortable = new COLORTABLE(source,
068: INFOHEADER_offset + INFOHEADER_size, infoheader);
069:
070: // check consistency with bfOffBits
071: assert bfOffBits == INFOHEADER_offset + 40
072: + colortable.colorbytes : "bfOffBits = " + bfOffBits
073: + ", colorbytes = " + colortable.colorbytes;
074: assert infoheader.biSizeImage <= source.length - bfOffBits : "bfOffBits = "
075: + bfOffBits
076: + ", biSizeImage = "
077: + infoheader.biSizeImage
078: + ", source.length = "
079: + source.length;
080:
081: imagemap = new IMAGEMAP(source, bfOffBits, infoheader.biWidth,
082: infoheader.biHeight, infoheader.biCompression,
083: infoheader.biBitCount, colortable);
084: }
085:
086: public BufferedImage getImage() {
087: return imagemap.image;
088: }
089:
090: public static final int DWORD(byte[] b, int offset) {
091: if (offset + 3 >= b.length)
092: return 0;
093: int ret = (b[offset + 3] & 0xff);
094: ret = (ret << 8) | (b[offset + 2] & 0xff);
095: ret = (ret << 8) | (b[offset + 1] & 0xff);
096: ret = (ret << 8) | (b[offset] & 0xff);
097: return ret;
098: }
099:
100: public static final int WORD(byte[] b, int offset) {
101: int ret = ((b[offset + 1] & 0xff) << 8) | (b[offset] & 0xff);
102: return ret;
103: }
104:
105: public static final int BYTE(byte[] b, int offset) {
106: int ret = (b[offset] & 0xff);
107: return ret;
108: }
109:
110: public static class INFOHEADER {
111:
112: public int biWidth, biHeight, biBitCount, biCompression,
113: biSizeImage, biClrUsed;
114:
115: public INFOHEADER(byte[] s, int offset) {
116: // read info-header
117: biWidth = DWORD(s, offset + 4);
118: biHeight = DWORD(s, offset + 8);
119: biBitCount = WORD(s, offset + 14);
120: biCompression = WORD(s, offset + 16);
121: biSizeImage = DWORD(s, offset + 20);
122: biClrUsed = DWORD(s, offset + 32);
123: }
124: }
125:
126: public static class COLORTABLE {
127:
128: public int colorbytes;
129: public int[] colorindex;
130:
131: public COLORTABLE(byte[] s, int offset, INFOHEADER infoheader) {
132: // read colortable
133: colorbytes = 0; // for consistency check
134: if (infoheader.biClrUsed == 0) {
135: if ((infoheader.biBitCount == 1)
136: || (infoheader.biBitCount == 4)
137: || (infoheader.biBitCount == 8)) {
138: colorindex = new int[1 << infoheader.biBitCount];
139: colorbytes = 4 * colorindex.length;
140: int color;
141: for (int i = 0; i < colorindex.length; i++) {
142: // translate BGR into RGB color Scheme
143: color = 0xffffff & DWORD(s, offset + 4 * i);
144: colorindex[i] = color;
145: }
146: } else {
147: colorindex = null;
148: }
149: } else {
150: colorindex = new int[infoheader.biClrUsed];
151: colorbytes = 4 * colorindex.length;
152: int color;
153: for (int i = 0; i < colorindex.length; i++) {
154: // translate BGR into RGB color Scheme
155: color = 0xffffff & DWORD(s, offset + 4 * i);
156: colorindex[i] = color;
157: //if (debugmode) System.out.println("Color " + i + " = " + Integer.toHexString(colorindex[i]));
158: }
159: }
160: }
161: }
162:
163: public static class IMAGEMAP {
164:
165: public BufferedImage image;
166:
167: public IMAGEMAP(byte[] s, int offset, int width, int height,
168: int compression, int bitcount, COLORTABLE colortable) {
169: // parse picture content
170: if ((width != 0) && (height != 0)) {
171: image = new BufferedImage(width, height,
172: BufferedImage.TYPE_INT_RGB);
173:
174: if (compression == BI_RGB) {
175: if (bitcount == 1)
176: parseBMP1(s, offset, width, height, colortable);
177: else if (bitcount == 4)
178: parseBMP4(s, offset, width, height, colortable);
179: else if (bitcount == 8)
180: parseBMP8(s, offset, width, height, colortable);
181: else if (bitcount == 24)
182: parseBMP24(s, offset, width, height);
183: else if (bitcount == 32)
184: parseBMP32(s, offset, width, height);
185: else
186: System.out
187: .println("unsupported BMP format: biCompression = "
188: + compression
189: + ", biBitCount = " + bitcount);
190: } else {
191: System.out
192: .println("unsupported BMP format: biCompression = "
193: + compression
194: + ", biBitCount = "
195: + bitcount);
196: }
197: }
198: }
199:
200: private void parseBMP1(byte[] s, int offset, int width,
201: int height, COLORTABLE colortable) {
202: int n = 0;
203: int b;
204: for (int rows = 0; rows < height; rows++) {
205: for (int columns = 0; columns < width; columns = columns + 8) {
206: b = (s[offset + n] & 0xff);
207: n++;
208: image.setRGB(columns, (height - rows - 1),
209: colortable.colorindex[(b & 0x80) >> 7]);
210: image.setRGB(columns + 1, (height - rows - 1),
211: colortable.colorindex[(b & 0x40) >> 6]);
212: image.setRGB(columns + 2, (height - rows - 1),
213: colortable.colorindex[(b & 0x20) >> 5]);
214: image.setRGB(columns + 3, (height - rows - 1),
215: colortable.colorindex[(b & 0x10) >> 4]);
216: image.setRGB(columns + 4, (height - rows - 1),
217: colortable.colorindex[(b & 0x08) >> 3]);
218: image.setRGB(columns + 5, (height - rows - 1),
219: colortable.colorindex[(b & 0x04) >> 2]);
220: image.setRGB(columns + 6, (height - rows - 1),
221: colortable.colorindex[(b & 0x02) >> 1]);
222: image.setRGB(columns + 7, (height - rows - 1),
223: colortable.colorindex[b & 0x01]);
224: }
225: n += fill4(n);
226: }
227: }
228:
229: private void parseBMP4(byte[] s, int offset, int width,
230: int height, COLORTABLE colortable) {
231: int n = 0;
232: int b;
233: for (int rows = 0; rows < height; rows++) {
234: for (int columns = 0; columns < width; columns = columns + 2) {
235: b = (s[offset + n] & 0xff);
236: n++;
237: image.setRGB(columns, (height - rows - 1),
238: colortable.colorindex[(b & 0xf0) >> 4]);
239: image.setRGB(columns + 1, (height - rows - 1),
240: colortable.colorindex[b & 0xf]);
241: }
242: n += fill4(n);
243: }
244: }
245:
246: private void parseBMP8(byte[] s, int offset, int width,
247: int height, COLORTABLE colortable) {
248: int n = 0;
249: for (int rows = 0; rows < height; rows++) {
250: for (int columns = 0; columns < width; columns++) {
251: image
252: .setRGB(
253: columns,
254: (height - rows - 1),
255: colortable.colorindex[(s[offset + n] & 0xff)]);
256: n++;
257: }
258: n += fill4(n);
259: }
260: }
261:
262: private void parseBMP24(byte[] s, int offset, int width,
263: int height) {
264: int n = 0;
265: for (int rows = 0; rows < height; rows++) {
266: for (int columns = 0; columns < width; columns++) {
267: image.setRGB(columns, (height - rows - 1),
268: 0xffffff & DWORD(s, offset + n));
269: n += 3;
270: }
271: n += fill4(n);
272: }
273: }
274:
275: private void parseBMP32(byte[] s, int offset, int width,
276: int height) {
277: int n = 0;
278: for (int rows = 0; rows < height; rows++) {
279: for (int columns = 0; columns < width; columns++) {
280: image.setRGB(columns, (height - rows - 1),
281: 0xffffff & DWORD(s, offset + n));
282: n += 4;
283: }
284: }
285: }
286:
287: private final int fill4(int x) {
288: int r = x % 4;
289: if (r == 0)
290: return 0;
291: else
292: return 4 - r;
293: }
294: }
295:
296: public static void main(String[] args) {
297: // read a bmp and write it as png
298: System.setProperty("java.awt.headless", "true");
299: File in = new File(args[0]);
300: File out = new File(args[1]);
301:
302: byte[] file = new byte[(int) in.length()];
303: FileInputStream fis = null;
304: try {
305: fis = new FileInputStream(in);
306: } catch (FileNotFoundException e) {
307: e.printStackTrace();
308: }
309: try {
310: fis.read(file);
311: } catch (IOException e) {
312: e.printStackTrace();
313: }
314:
315: ymageBMPParser parser = new ymageBMPParser(file);
316:
317: try {
318: ImageIO.write(parser.getImage(), "PNG", out);
319: } catch (IOException e) {
320: e.printStackTrace();
321: }
322: }
323:
324: }
|