2
* Read a Microsoft/IBM BMP file.
4
* Created for xli by Graeme Gill,
6
* Based on tga.c, and guided by the Microsoft file format
7
* description, and bmptoppm.c by David W. Sanderson.
12
#include "imagetypes.h"
15
#define GULONG4(bp) ((unsigned long)(bp)[0] + 256 * (unsigned long)(bp)[1] \
16
+ 65536 * (unsigned long)(bp)[2] + 16777216 * (unsigned long)(bp)[3])
17
#define GULONG2(bp) ((unsigned long)(bp)[0] + 256 * (unsigned long)(bp)[1])
18
#define GUINT2(bp) ((unsigned int)(bp)[0] + 256 * (unsigned int)(bp)[1])
20
/* Read the header of the file, and */
21
/* Return TRUE if this looks like a bmp file */
22
static boolean read_bmpHeader(ZFILE * zf, bmpHeader * hp, char *name)
24
unsigned char buf[WIN_INFOHEADER_LEN]; /* largest we'll need */
26
if (zread(zf, buf, BMP_FILEHEADER_LEN) != BMP_FILEHEADER_LEN)
29
if (buf[0] != 'B' || buf[1] != 'M')
30
return FALSE; /* bad magic number */
32
hp->bfSize = GULONG4(&buf[2]);
33
hp->bfxHotSpot = GUINT2(&buf[6]);
34
hp->bfyHotSpot = GUINT2(&buf[8]);
35
hp->bfOffBits = GULONG4(&buf[10]);
37
/* Read enough of the file info to figure the type out */
38
if (zread(zf, buf, 4) != 4)
41
hp->biSize = GULONG4(&buf[0]);
42
if (hp->biSize == WIN_INFOHEADER_LEN)
44
else if (hp->biSize == OS2_INFOHEADER_LEN)
49
if (hp->class == C_WIN) {
50
/* read in the rest of the info header */
51
if (zread(zf, buf + 4, WIN_INFOHEADER_LEN - 4)
52
!= WIN_INFOHEADER_LEN - 4)
55
hp->biWidth = GULONG4(&buf[4]);
56
hp->biHeight = GULONG4(&buf[8]);
57
hp->biPlanes = GUINT2(&buf[12]);;
58
hp->biBitCount = GUINT2(&buf[14]);;
59
hp->biCompression = GULONG4(&buf[16]);;
60
hp->biSizeImage = GULONG4(&buf[20]);;
61
hp->biXPelsPerMeter = GULONG4(&buf[24]);;
62
hp->biYPelsPerMeter = GULONG4(&buf[28]);;
63
hp->biClrUsed = GULONG4(&buf[32]);
64
hp->biClrImportant = GULONG4(&buf[36]);
66
/* read in the rest of the info header */
67
if (zread(zf, buf + 4, OS2_INFOHEADER_LEN - 4)
68
!= OS2_INFOHEADER_LEN - 4)
71
hp->biWidth = GULONG2(&buf[4]);
72
hp->biHeight = GULONG2(&buf[6]);
73
hp->biPlanes = GUINT2(&buf[8]);;
74
hp->biBitCount = GUINT2(&buf[10]);;
75
hp->biCompression = BI_RGB;
77
hp->biXPelsPerMeter = 0;
78
hp->biYPelsPerMeter = 0;
80
hp->biClrImportant = 0;
83
/* Check for file corruption */
85
if (hp->biBitCount != 1
86
&& hp->biBitCount != 4
87
&& hp->biBitCount != 8
88
&& hp->biBitCount != 24) {
89
fprintf(stderr, "bmpLoad: %s - Illegal image BitCount\n",
93
if ((hp->biCompression != BI_RGB
94
&& hp->biCompression != BI_RLE8
95
&& hp->biCompression != BI_RLE4)
96
|| (hp->biCompression == BI_RLE8 && hp->biBitCount != 8)
97
|| (hp->biCompression == BI_RLE4 && hp->biBitCount != 4)) {
99
"bmpLoad: %s - Illegal image compression type\n",
103
if (hp->biPlanes != 1) {
104
fprintf(stderr, "bmpLoad: %s - Illegal image Planes value\n",
108
/* Fix up a few things */
109
if (hp->biBitCount < 24) {
110
if (hp->biClrUsed == 0
111
|| hp->biClrUsed > (1 << hp->biBitCount))
112
hp->biClrUsed = (1 << hp->biBitCount);
120
/* Print a brief description of the image */
121
static void tell_about_bmp(bmpHeader * hp)
123
printf("%s is a %lux%lu %d bit deep, %s%s BMP image\n", hp->name,
124
hp->biWidth, hp->biHeight, hp->biBitCount,
125
hp->biCompression == BI_RGB ? "" : "Run length compressed, ",
126
hp->class == C_WIN ? "Windows" : "OS/2");
129
int bmpIdent(char *fullname, char *name)
134
if (!(zf = zopen(fullname))) {
138
if (!read_bmpHeader(zf, &hdr, name)) {
140
return 0; /* Nope, not a BMP file */
142
tell_about_bmp(&hdr);
147
Image *bmpLoad(char *fullname, ImageOptions * image_ops, boolean verbose)
152
boolean data_bounds = FALSE;
155
if (!(zf = zopen(fullname))) {
159
if (!read_bmpHeader(zf, &hdr, image_ops->name)) {
161
return NULL; /* Nope, not a BMP file */
164
tell_about_bmp(&hdr);
167
/* Create the appropriate image and colormap */
168
if (hdr.biBitCount < 24) {
169
/* must be 1, 4 or 8 bit mapped type */
172
/* maximum number of colors */
173
int used = (1 << hdr.biBitCount);
175
if (hdr.biBitCount == 1) /* bitmap */
176
image = newBitImage(hdr.biWidth, hdr.biHeight);
178
image = newRGBImage(hdr.biWidth, hdr.biHeight,
180
image->title = dupString(hdr.name);
182
if (hdr.class == C_WIN)
184
for (i = 0; i < hdr.biClrUsed; i++) {
185
if (zread(zf, buf, n) != n) {
186
fprintf(stderr, "bmpLoad: %s - Short read within Colormap\n", hdr.name);
191
image->rgb.red[i] = buf[2] << 8;
192
image->rgb.green[i] = buf[1] << 8;
193
image->rgb.blue[i] = buf[0] << 8;
196
/* init rest of colormap (if any) */
197
for (; i < used; i++) {
199
image->rgb.green[i] =
200
image->rgb.blue[i] = 0;
202
/* Don't know how many colors are actually used. */
203
/* (believing the header caould cause a fault) */
204
/* compress() will figure it out. */
205
image->rgb.used = used; /* so use max possible */
206
} else { /* else must be a true color image */
207
image = newTrueImage(hdr.biWidth, hdr.biHeight);
208
image->title = dupString(hdr.name);
211
/* Skip to offset specified in file header for image data */
212
if (hdr.class == C_WIN)
213
skip = hdr.bfOffBits - (BMP_FILEHEADER_LEN
214
+ WIN_INFOHEADER_LEN + 4 * hdr.biClrUsed);
216
skip = hdr.bfOffBits - (BMP_FILEHEADER_LEN
217
+ OS2_INFOHEADER_LEN + 3 * hdr.biClrUsed);
220
if (zgetc(zf) == EOF)
225
/* Read the pixel data */
226
if (hdr.biBitCount == 1) {
228
int illen, padlen, y;
230
/* round bits up to byte */
231
illen = (image->width + 7) / 8;
232
/* extra bytes to word boundary */
233
padlen = (((image->width + 31) / 32) * 4) - illen;
234
/* start at bottom */
235
data = image->data + (image->height - 1) * illen;
236
for (y = image->height; y > 0; y--, data -= illen) {
237
/* BMP files are left bit == ms bit,
238
* so read straight in.
240
if (zread(zf, data, illen) != illen
241
|| zread(zf, pad, padlen) != padlen)
244
} else if (hdr.biBitCount == 4) {
248
illen = image->width;
249
/* start at bottom */
250
data = image->data + (image->height - 1) * illen;
252
if (hdr.biCompression == BI_RLE4) {
254
bzero((char *) image->data,
255
image->width * image->height);
258
if ((d = zgetc(zf)) == EOF)
260
if (d != 0) { /* run of pixels */
262
if (x > image->width ||
264
/* don't run off buffer */
266
/* ignore this run */
268
if ((e = zgetc(zf)) == EOF)
272
if ((e = zgetc(zf)) == EOF)
276
for (i = d / 2; i > 0; i--) {
285
if ((d = zgetc(zf)) == EOF)
287
if (d == 0) { /* end of line */
294
if (d == 1) /* end of bitmap */
297
if (d == 2) { /* delta */
298
if ((d = zgetc(zf)) == EOF ||
299
(e = zgetc(zf)) == EOF)
307
/* else run of literals */
309
if (x > image->width || y > image->height) {
311
/* don't run off buffer */
313
x -= d; /* ignore this run */
314
btr = d / 2 + (d & 1)
315
+ (((d + 1) & 2) >> 1);
316
for (; btr > 0; btr--) {
317
if ((e = zgetc(zf)) == EOF)
322
for (i = d / 2; i > 0; i--) {
323
if ((e = zgetc(zf)) == EOF)
329
if ((e = zgetc(zf)) == EOF)
333
if ((d + 1) & 2) /* read pad byte */
334
if (zgetc(zf) == EOF)
337
} else { /* No 4 bit rle compression */
340
d = image->width / 2; /* double pixel count */
341
s = image->width & 1; /* single pixel */
342
p = (4 - (d + s)) & 0x3; /* byte pad */
343
for (y = image->height; y > 0; y--, data -= (2 * illen)) {
344
for (i = d; i > 0; i--) {
345
if ((e = zgetc(zf)) == EOF)
351
if ((e = zgetc(zf)) == EOF)
355
for (i = p; i > 0; i--)
356
if (zgetc(zf) == EOF)
360
} else if (hdr.biBitCount == 8) {
364
illen = image->width;
365
/* start at bottom */
366
data = image->data + (image->height - 1) * illen;
368
if (hdr.biCompression == BI_RLE8) {
370
bzero((char *) image->data,
371
image->width * image->height);
373
if ((d = zgetc(zf)) == EOF)
375
if (d != 0) { /* run of pixels */
377
if (x > image->width ||
379
/* don't run off buffer */
381
/* ignore this run */
383
if ((e = zgetc(zf)) == EOF)
387
if ((e = zgetc(zf)) == EOF)
394
if ((d = zgetc(zf)) == EOF)
396
if (d == 0) { /* end of line */
403
if (d == 1) /* end of bitmap */
406
if (d == 2) { /* delta */
407
if ((d = zgetc(zf)) == EOF ||
408
(e = zgetc(zf)) == EOF)
416
/* else run of literals */
418
if (x > image->width || y > image->height) {
420
/* don't run off buffer */
422
/* ignore this run */
425
for (; btr > 0; btr--) {
426
if ((e = zgetc(zf)) == EOF)
431
if (zread(zf, data, d) != d)
434
if (d & 1) /* read pad byte */
435
if (zgetc(zf) == EOF)
438
} else { /* No 8 bit rle compression */
442
/* extra bytes to word boundary */
443
padlen = ((image->width + 3) & ~3) - illen;
444
for (y = image->height; y > 0; y--, data -= illen) {
445
if (zread(zf, data, illen) != illen
446
|| zread(zf, pad, padlen) != padlen)
450
} else { /* hdr.biBitCount == 24 */
452
int illen, padlen, y;
453
illen = image->width * image->pixlen;
454
/* extra bytes to word boundary */
455
padlen = (((image->width * 3) + 3) & ~3) - illen;
456
/* start at bottom */
457
data = image->data + (image->height - 1) * illen;
458
for (y = image->height; y > 0; y--, data -= illen) {
461
/* BMP files are RGB, so read straight in. */
462
if (zread(zf, data, illen) != illen
463
|| zread(zf, pad, padlen) != padlen)
465
/* Oh, no they're not */
466
for (i = 3 * image->width - 1; i > 0; i -= 3) {
468
data[i] = data[i - 2];
475
fprintf(stderr, "bmpLoad: %s - Data outside image area\n",
481
fprintf(stderr, "bmpLoad: %s - Short read within Data\n", hdr.name);