Source code: com/memoire/bu/BuBmpLoader.java
1
2
3 package com.memoire.bu;
4
5
6
7 /**
8
9 * This class implements a Bmp Loader.
10
11 *
12
13 *-----------------------------------------------------------------------
14
15 * This program is free software; you can redistribute it and/or modify
16
17 * it under the terms of the GNU Library General Public License as published
18
19 * by the Free Software Foundation; either version 2 of the License, or
20
21 * (at your option) any later version.
22
23 *
24
25 * This program is distributed in the hope that it will be useful,
26
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30
31 * GNU Library General Public License for more details.
32
33 *
34
35 * You should have received a copy of the GNU Library General Public
36
37 * License along with this program; if not, write to the Free Software
38
39 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
40
41 *----------------------------------------------------------------------
42
43 */
44
45
46
47 import java.awt.*;
48
49 import java.awt.image.*;
50
51 import java.io.*;
52
53
54
55 /**
56
57 * A decoder for Windows bitmap (.Bmp) files.
58
59 * Compression not supported.
60
61 * Source code provided by JavaZoom under the LGPL.
62
63 */
64
65 public class BuBmpLoader
66
67 {
68
69 private InputStream is;
70
71 private int curPos = 0;
72
73
74
75 private int bitmapOffset; // starting position of image data
76
77
78
79 private int width; // image width in pixels
80
81 private int height; // image height in pixels
82
83 private short bitsPerPixel; // 1, 4, 8, or 24 (no color map)
84
85 private int compression; // 0 (none), 1 (8-bit RLE), or 2 (4-bit RLE)
86
87 private int actualSizeOfBitmap;
88
89 private int scanLineSize;
90
91 private int actualColorsUsed;
92
93
94
95 private byte r[], g[], b[]; // color palette
96
97 private int noOfEntries;
98
99
100
101 private byte[] byteData; // Unpacked data
102
103 private int[] intData; // Unpacked data
104
105
106
107 public BuBmpLoader()
108
109 {
110
111 }
112
113
114
115 public Image getBmpImage(InputStream stream) throws Exception
116
117 {
118
119 read(stream);
120
121 return Toolkit.getDefaultToolkit().createImage(getImageSource());
122
123 }
124
125
126
127 private int readInt() throws IOException {
128
129 int b1 = is.read();
130
131 int b2 = is.read();
132
133 int b3 = is.read();
134
135 int b4 = is.read();
136
137 curPos += 4;
138
139 return ((b4 << 24) + (b3 << 16) + (b2 << 8) + (b1 << 0));
140
141 }
142
143
144
145
146
147 private short readShort() throws IOException {
148
149 int b1 = is.read();
150
151 int b2 = is.read();
152
153 curPos += 4;
154
155 return (short)((b2 << 8) + b1);
156
157 }
158
159
160
161
162
163 void getFileHeader() throws Exception {
164
165 // Actual contents (14 bytes):
166
167 short fileType = 0x4d42;// always "BM"
168
169 int fileSize; // size of file in bytes
170
171 short reserved1 = 0; // always 0
172
173 short reserved2 = 0; // always 0
174
175
176
177 fileType = readShort();
178
179 if (fileType != 0x4d42)
180
181 throw new Exception("Not a BMP file"); // wrong file type
182
183 fileSize = readInt();
184
185 reserved1 = readShort();
186
187 reserved2 = readShort();
188
189 bitmapOffset = readInt();
190
191 }
192
193
194
195 void getBitmapHeader() throws IOException {
196
197
198
199 // Actual contents (40 bytes):
200
201 int size; // size of this header in bytes
202
203 short planes; // no. of color planes: always 1
204
205 int sizeOfBitmap; // size of bitmap in bytes (may be 0: if so, calculate)
206
207 int horzResolution; // horizontal resolution, pixels/meter (may be 0)
208
209 int vertResolution; // vertical resolution, pixels/meter (may be 0)
210
211 int colorsUsed; // no. of colors in palette (if 0, calculate)
212
213 int colorsImportant; // no. of important colors (appear first in palette) (0 means all are important)
214
215 boolean topDown;
216
217 int noOfPixels;
218
219
220
221 size = readInt();
222
223 width = readInt();
224
225 height = readInt();
226
227 planes = readShort();
228
229 bitsPerPixel = readShort();
230
231 compression = readInt();
232
233 sizeOfBitmap = readInt();
234
235 horzResolution = readInt();
236
237 vertResolution = readInt();
238
239 colorsUsed = readInt();
240
241 colorsImportant = readInt();
242
243
244
245 topDown = (height < 0);
246
247 noOfPixels = width * height;
248
249
250
251 // Scan line is padded with zeroes to be a multiple of four bytes
252
253 scanLineSize = ((width * bitsPerPixel + 31) / 32) * 4;
254
255
256
257 if (sizeOfBitmap != 0)
258
259 actualSizeOfBitmap = sizeOfBitmap;
260
261 else
262
263 // a value of 0 doesn't mean zero - it means we have to calculate it
264
265 actualSizeOfBitmap = scanLineSize * height;
266
267
268
269 if (colorsUsed != 0)
270
271 actualColorsUsed = colorsUsed;
272
273 else
274
275 // a value of 0 means we determine this based on the bits per pixel
276
277 if (bitsPerPixel < 16)
278
279 actualColorsUsed = 1 << bitsPerPixel;
280
281 else
282
283 actualColorsUsed = 0; // no palette
284
285 }
286
287
288
289
290
291 void getPalette() throws IOException {
292
293 noOfEntries = actualColorsUsed;
294
295 //IJ.write("noOfEntries: " + noOfEntries);
296
297 if (noOfEntries>0) {
298
299 r = new byte[noOfEntries];
300
301 g = new byte[noOfEntries];
302
303 b = new byte[noOfEntries];
304
305
306
307 int reserved;
308
309 for (int i = 0; i < noOfEntries; i++) {
310
311 b[i] = (byte)is.read();
312
313 g[i] = (byte)is.read();
314
315 r[i] = (byte)is.read();
316
317 reserved = is.read();
318
319 curPos += 4;
320
321 }
322
323 }
324
325 }
326
327
328
329 void unpack(byte[] rawData, int rawOffset, int[] intData, int intOffset, int w) {
330
331 int j = intOffset;
332
333 int k = rawOffset;
334
335 int mask = 0xff;
336
337 for (int i = 0; i < w; i++) {
338
339 int b0 = (((int)(rawData[k++])) & mask);
340
341 int b1 = (((int)(rawData[k++])) & mask) << 8;
342
343 int b2 = (((int)(rawData[k++])) & mask) << 16;
344
345 intData[j] = 0xff000000 | b0 | b1 | b2;
346
347 j++;
348
349 }
350
351 }
352
353
354
355
356
357 void unpack(byte[] rawData, int rawOffset, int bpp,
358
359 byte[] byteData, int byteOffset, int w) throws Exception {
360
361 int j = byteOffset;
362
363 int k = rawOffset;
364
365 byte mask;
366
367 int pixPerByte;
368
369
370
371 switch (bpp) {
372
373 case 1: mask = (byte)0x01; pixPerByte = 8; break;
374
375 case 4: mask = (byte)0x0f; pixPerByte = 2; break;
376
377 case 8: mask = (byte)0xff; pixPerByte = 1; break;
378
379 default:
380
381 throw new Exception("Unsupported bits-per-pixel value");
382
383 }
384
385
386
387 for (int i = 0;;) {
388
389 int shift = 8 - bpp;
390
391 for (int ii = 0; ii < pixPerByte; ii++) {
392
393 byte br = rawData[k];
394
395 br >>= shift;
396
397 byteData[j] = (byte)(br & mask);
398
399 //System.out.println("Setting byteData[" + j + "]=" + Test.byteToHex(byteData[j]));
400
401 j++;
402
403 i++;
404
405 if (i == w) return;
406
407 shift -= bpp;
408
409 }
410
411 k++;
412
413 }
414
415 }
416
417
418
419
420
421 void getPixelData() throws Exception {
422
423 byte[] rawData; // the raw unpacked data
424
425
426
427 // Skip to the start of the bitmap data (if we are not already there)
428
429 long skip = bitmapOffset - curPos;
430
431 if (skip > 0) {
432
433 is.skip(skip);
434
435 curPos += skip;
436
437 }
438
439
440
441 int len = scanLineSize;
442
443 if (bitsPerPixel > 8)
444
445 intData = new int[width * height];
446
447 else
448
449 byteData = new byte[width * height];
450
451 rawData = new byte[actualSizeOfBitmap];
452
453 int rawOffset = 0;
454
455 int offset = (height - 1) * width;
456
457 for (int i = height - 1; i >= 0; i--) {
458
459 int n = is.read(rawData, rawOffset, len);
460
461 if (n < len) throw new Exception("Scan line ended prematurely after "
462
463 + n + " bytes");
464
465 if (bitsPerPixel > 8) {
466
467 // Unpack and create one int per pixel
468
469 unpack(rawData, rawOffset, intData, offset, width);
470
471 }
472
473 else {
474
475 // Unpack and create one byte per pixel
476
477 unpack(rawData, rawOffset, bitsPerPixel,
478
479 byteData, offset, width);
480
481 }
482
483 rawOffset += len;
484
485 offset -= width;
486
487 }
488
489 }
490
491
492
493 public void read(InputStream is) throws Exception {
494
495 this.is = is;
496
497 getFileHeader();
498
499 getBitmapHeader();
500
501 if (compression!=0)
502
503 throw new Exception(" BMP Compression not supported");
504
505 getPalette();
506
507 getPixelData();
508
509 }
510
511
512
513
514
515 public MemoryImageSource getImageSource() {
516
517 ColorModel cm;
518
519 MemoryImageSource mis;
520
521
522
523 if (noOfEntries > 0) {
524
525 // There is a color palette; create an IndexColorModel
526
527 cm = new IndexColorModel(bitsPerPixel,
528
529 noOfEntries, r, g, b);
530
531 } else {
532
533 // There is no palette; use the default RGB color model
534
535 cm = ColorModel.getRGBdefault();
536
537 }
538
539
540
541 // Create MemoryImageSource
542
543
544
545 if (bitsPerPixel > 8) {
546
547 // use one int per pixel
548
549 mis = new MemoryImageSource(width,
550
551 height, cm, intData, 0, width);
552
553 } else {
554
555 // use one byte per pixel
556
557 mis = new MemoryImageSource(width,
558
559 height, cm, byteData, 0, width);
560
561 }
562
563
564
565 return mis; // this can be used by Component.createImage()
566
567 }
568
569 }
570