1 /* input-bmp.c: reads any bitmap I could get for testing
2
3 Copyright (C) 1999, 2000, 2001 Martin Weber.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License
7 as published by the Free Software Foundation; either version 2.1 of
8 the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 USA. */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif /* Def: HAVE_CONFIG_H */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "types.h"
29 #include "bitmap.h"
30 #include "message.h"
31 #include "xstd.h"
32 #include "input-bmp.h"
33
34
35 #define MAXCOLORS 256
36 #define Image long
37
38 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
39
40 #define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
41
42 struct Bitmap_File_Head_Struct
43 {
44 char zzMagic[2]; /* 00 "BM" */
45 unsigned long bfSize; /* 02 */
46 unsigned short zzHotX; /* 06 */
47 unsigned short zzHotY; /* 08 */
48 unsigned long bfOffs; /* 0A */
49 unsigned long biSize; /* 0E */
50 } Bitmap_File_Head;
51
52 struct Bitmap_Head_Struct
53 {
54 unsigned long biWidth; /* 12 */
55 unsigned long biHeight; /* 16 */
56 unsigned short biPlanes; /* 1A */
57 unsigned short biBitCnt; /* 1C */
58 unsigned long biCompr; /* 1E */
59 unsigned long biSizeIm; /* 22 */
60 unsigned long biXPels; /* 26 */
61 unsigned long biYPels; /* 2A */
62 unsigned long biClrUsed; /* 2E */
63 unsigned long biClrImp; /* 32 */
64 /* 36 */
65 } Bitmap_Head;
66
67 static long ToL (unsigned char *);
68 static short ToS (unsigned char *);
69 static int ReadColorMap (FILE *,
70 unsigned char[256][3],
71 int,
72 int,
73 int *,
74 at_exception_type *);
75 static unsigned char *ReadImage (FILE *,
76 int,
77 int,
78 unsigned char[256][3],
79 int,
80 int,
81 int,
82 int);
83
84 at_bitmap_type
input_bmp_reader(at_string filename,at_input_opts_type * opts,at_msg_func msg_func,at_address msg_data)85 input_bmp_reader (at_string filename,
86 at_input_opts_type * opts,
87 at_msg_func msg_func,
88 at_address msg_data)
89 {
90 FILE *fd;
91 unsigned char buffer[64];
92 int ColormapSize, rowbytes, Maps, Grey;
93 unsigned char ColorMap[256][3];
94 at_bitmap_type image = at_bitmap_init(0, 0, 0, 1);
95 unsigned char * image_storage;
96 at_exception_type exp = at_exception_new(msg_func, msg_data);
97
98 fd = fopen (filename, "rb");
99
100 if (!fd)
101 {
102 LOG1 ("Can't open \"%s\"\n", filename);
103 at_exception_fatal(&exp, "bmp: cannot open input file");
104 return image;
105 }
106
107
108 /* It is a File. Now is it a Bitmap? Read the shortest possible header.*/
109
110 if (!ReadOK(fd, buffer, 18) || (strncmp((const char *)buffer,"BM",2)))
111 {
112 LOG1 ("Not a valid BMP file %s\n", filename);
113 at_exception_fatal(&exp, "bmp: invalid input file");
114 goto cleanup;
115 }
116
117 /* bring them to the right byteorder. Not too nice, but it should work */
118
119 Bitmap_File_Head.bfSize = ToL (&buffer[0x02]);
120 Bitmap_File_Head.zzHotX = ToS (&buffer[0x06]);
121 Bitmap_File_Head.zzHotY = ToS (&buffer[0x08]);
122 Bitmap_File_Head.bfOffs = ToL (&buffer[0x0a]);
123 Bitmap_File_Head.biSize = ToL (&buffer[0x0e]);
124
125 /* What kind of bitmap is it? */
126
127 if (Bitmap_File_Head.biSize == 12) /* OS/2 1.x ? */
128 {
129 if (!ReadOK (fd, buffer, 8))
130 {
131 LOG ("Error reading BMP file header\n");
132 at_exception_fatal(&exp, "Error reading BMP file header");
133 goto cleanup;
134 }
135
136 Bitmap_Head.biWidth = ToS (&buffer[0x00]); /* 12 */
137 Bitmap_Head.biHeight = ToS (&buffer[0x02]); /* 14 */
138 Bitmap_Head.biPlanes = ToS (&buffer[0x04]); /* 16 */
139 Bitmap_Head.biBitCnt = ToS (&buffer[0x06]); /* 18 */
140 Bitmap_Head.biCompr = 0;
141 Bitmap_Head.biSizeIm = 0;
142 Bitmap_Head.biXPels = Bitmap_Head.biYPels = 0;
143 Bitmap_Head.biClrUsed = 0;
144 Maps = 3;
145 }
146 else if (Bitmap_File_Head.biSize == 40) /* Windows 3.x */
147 {
148 if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4))
149 {
150 LOG ("Error reading BMP file header\n");
151 at_exception_fatal(&exp, "Error reading BMP file header");
152 goto cleanup;
153 }
154
155
156 Bitmap_Head.biWidth =ToL (&buffer[0x00]); /* 12 */
157 Bitmap_Head.biHeight =ToL (&buffer[0x04]); /* 16 */
158 Bitmap_Head.biPlanes =ToS (&buffer[0x08]); /* 1A */
159 Bitmap_Head.biBitCnt =ToS (&buffer[0x0A]); /* 1C */
160 Bitmap_Head.biCompr =ToL (&buffer[0x0C]); /* 1E */
161 Bitmap_Head.biSizeIm =ToL (&buffer[0x10]); /* 22 */
162 Bitmap_Head.biXPels =ToL (&buffer[0x14]); /* 26 */
163 Bitmap_Head.biYPels =ToL (&buffer[0x18]); /* 2A */
164 Bitmap_Head.biClrUsed =ToL (&buffer[0x1C]); /* 2E */
165 Bitmap_Head.biClrImp =ToL (&buffer[0x20]); /* 32 */
166 /* 36 */
167 Maps = 4;
168 }
169 else if (Bitmap_File_Head.biSize <= 64) /* Probably OS/2 2.x */
170 {
171 if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4))
172 {
173 LOG ("Error reading BMP file header\n");
174 at_exception_fatal(&exp, "Error reading BMP file header");
175 goto cleanup;
176 }
177
178 Bitmap_Head.biWidth =ToL (&buffer[0x00]); /* 12 */
179 Bitmap_Head.biHeight =ToL (&buffer[0x04]); /* 16 */
180 Bitmap_Head.biPlanes =ToS (&buffer[0x08]); /* 1A */
181 Bitmap_Head.biBitCnt =ToS (&buffer[0x0A]); /* 1C */
182 Bitmap_Head.biCompr =ToL (&buffer[0x0C]); /* 1E */
183 Bitmap_Head.biSizeIm =ToL (&buffer[0x10]); /* 22 */
184 Bitmap_Head.biXPels =ToL (&buffer[0x14]); /* 26 */
185 Bitmap_Head.biYPels =ToL (&buffer[0x18]); /* 2A */
186 Bitmap_Head.biClrUsed =ToL (&buffer[0x1C]); /* 2E */
187 Bitmap_Head.biClrImp =ToL (&buffer[0x20]); /* 32 */
188 /* 36 */
189 Maps = 3;
190 }
191 else
192 {
193 LOG ("Error reading BMP file header\n");
194 at_exception_fatal(&exp, "Error reading BMP file header");
195 goto cleanup;
196 }
197
198 /* Valid options 1, 4, 8, 16, 24, 32 */
199 /* 16 is awful, we should probably shoot whoever invented it */
200
201 /* There should be some colors used! */
202
203 ColormapSize = (Bitmap_File_Head.bfOffs - Bitmap_File_Head.biSize - 14) / Maps;
204
205 if ((Bitmap_Head.biClrUsed == 0) && (Bitmap_Head.biBitCnt <= 8))
206 Bitmap_Head.biClrUsed = ColormapSize;
207
208 /* Sanity checks */
209
210 if ((Bitmap_Head.biHeight == 0 || Bitmap_Head.biWidth == 0)
211 || (Bitmap_Head.biPlanes != 1)
212 || (ColormapSize > 256 || Bitmap_Head.biClrUsed > 256))
213 {
214 LOG ("Error reading BMP file header\n");
215 at_exception_fatal(&exp, "Error reading BMP file header");
216 goto cleanup;
217 }
218
219 /* Windows and OS/2 declare filler so that rows are a multiple of
220 * word length (32 bits == 4 bytes)
221 */
222
223 rowbytes= ( (Bitmap_Head.biWidth * Bitmap_Head.biBitCnt - 1) / 32) * 4 + 4;
224
225 #ifdef DEBUG
226 printf("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n",
227 Bitmap_File_Head.bfSize,Bitmap_Head.biClrUsed,Bitmap_Head.biBitCnt,Bitmap_Head.biWidth,
228 Bitmap_Head.biHeight, Bitmap_Head.biCompr, rowbytes);
229 #endif
230
231 /* Get the Colormap */
232 ReadColorMap (fd, ColorMap, ColormapSize, Maps, &Grey, &exp);
233 if (at_exception_got_fatal(&exp))
234 goto cleanup;
235
236 #ifdef DEBUG
237 printf("Colormap read\n");
238 #endif
239
240 /* Get the Image and return the ID or -1 on error*/
241 image_storage = ReadImage (fd,
242 Bitmap_Head.biWidth, Bitmap_Head.biHeight,
243 ColorMap,
244 Bitmap_Head.biBitCnt,
245 Bitmap_Head.biCompr,
246 rowbytes,
247 Grey);
248 image = at_bitmap_init(image_storage,
249 (unsigned short) Bitmap_Head.biWidth,
250 (unsigned short) Bitmap_Head.biHeight,
251 Grey ? 1 : 3);
252 cleanup:
253 fclose (fd);
254 return (image);
255 }
256
257 static int
ReadColorMap(FILE * fd,unsigned char buffer[256][3],int number,int size,int * grey,at_exception_type * exp)258 ReadColorMap (FILE *fd,
259 unsigned char buffer[256][3],
260 int number,
261 int size,
262 int *grey,
263 at_exception_type * exp)
264 {
265 int i;
266 unsigned char rgb[4];
267
268 *grey=(number>2);
269 for (i = 0; i < number ; i++)
270 {
271 if (!ReadOK (fd, rgb, size))
272 {
273 LOG ("Bad colormap\n");
274 at_exception_fatal (exp, "Bad colormap");
275 goto cleanup;
276 }
277
278 /* Bitmap save the colors in another order! But change only once! */
279
280 buffer[i][0] = rgb[2];
281 buffer[i][1] = rgb[1];
282 buffer[i][2] = rgb[0];
283 *grey = ((*grey) && (rgb[0]==rgb[1]) && (rgb[1]==rgb[2]));
284 }
285 cleanup:
286 return 0;
287 }
288
289 static unsigned char*
ReadImage(FILE * fd,int width,int height,unsigned char cmap[256][3],int bpp,int compression,int rowbytes,int grey)290 ReadImage (FILE *fd,
291 int width,
292 int height,
293 unsigned char cmap[256][3],
294 int bpp,
295 int compression,
296 int rowbytes,
297 int grey)
298 {
299 unsigned char v,howmuch;
300 int xpos = 0, ypos = 0;
301 unsigned char *image;
302 unsigned char *temp, *buffer;
303 long rowstride, channels;
304 unsigned short rgb;
305 int i, j, notused;
306
307 if (bpp >= 16) /* color image */
308 {
309 XMALLOC (image, width * height * 3 * sizeof (unsigned char));
310 channels = 3;
311 }
312 else if (grey) /* grey image */
313 {
314 XMALLOC (image, width * height * 1 * sizeof (unsigned char));
315 channels = 1;
316 }
317 else /* indexed image */
318 {
319 XMALLOC (image, width * height * 1 * sizeof (unsigned char));
320 channels = 1;
321 }
322
323 XMALLOC (buffer, rowbytes);
324 rowstride = width * channels;
325
326 ypos = height - 1; /* Bitmaps begin in the lower left corner */
327
328 switch (bpp) {
329
330 case 32:
331 {
332 while (ReadOK (fd, buffer, rowbytes))
333 {
334 temp = image + (ypos * rowstride);
335 for (xpos= 0; xpos < width; ++xpos)
336 {
337 *(temp++)= buffer[xpos * 4 + 2];
338 *(temp++)= buffer[xpos * 4 + 1];
339 *(temp++)= buffer[xpos * 4];
340 }
341 --ypos; /* next line */
342 }
343 }
344 break;
345
346 case 24:
347 {
348 while (ReadOK (fd, buffer, rowbytes))
349 {
350 temp = image + (ypos * rowstride);
351 for (xpos= 0; xpos < width; ++xpos)
352 {
353 *(temp++)= buffer[xpos * 3 + 2];
354 *(temp++)= buffer[xpos * 3 + 1];
355 *(temp++)= buffer[xpos * 3];
356 }
357 --ypos; /* next line */
358 }
359 }
360 break;
361
362 case 16:
363 {
364 while (ReadOK (fd, buffer, rowbytes))
365 {
366 temp = image + (ypos * rowstride);
367 for (xpos= 0; xpos < width; ++xpos)
368 {
369 rgb= ToS(&buffer[xpos * 2]);
370 *(temp++)= (unsigned char)(((rgb >> 10) & 0x1f) * 8);
371 *(temp++)= (unsigned char)(((rgb >> 5) & 0x1f) * 8);
372 *(temp++)= (unsigned char)(((rgb) & 0x1f) * 8);
373 }
374 --ypos; /* next line */
375 }
376 }
377 break;
378
379 case 8:
380 case 4:
381 case 1:
382 {
383 if (compression == 0)
384 {
385 while (ReadOK (fd, &v, 1))
386 {
387 for (i = 1; (i <= (8 / bpp)) && (xpos < width); i++, xpos++)
388 {
389 temp = (unsigned char*) (image + (ypos * rowstride) + (xpos * channels));
390 *temp= (unsigned char)(( v & ( ((1<<bpp)-1) << (8-(i*bpp)) ) ) >> (8-(i*bpp)));
391 }
392 if (xpos == width)
393 {
394 notused = ReadOK (fd, buffer, rowbytes - 1 -
395 (width * bpp - 1) / 8);
396 ypos--;
397 xpos = 0;
398
399 }
400 if (ypos < 0)
401 break;
402 }
403 break;
404 }
405 else
406 {
407 while (ypos >= 0 && xpos <= width)
408 {
409 notused = ReadOK (fd, buffer, 2);
410 if ((unsigned char) buffer[0] != 0)
411 /* Count + Color - record */
412 {
413 for (j = 0; ((unsigned char) j < (unsigned char) buffer[0]) && (xpos < width);)
414 {
415 #ifdef DEBUG2
416 printf("%u %u | ",xpos,width);
417 #endif
418 for (i = 1;
419 ((i <= (8 / bpp)) &&
420 (xpos < width) &&
421 ((unsigned char) j < (unsigned char) buffer[0]));
422 i++, xpos++, j++)
423 {
424 temp = image + (ypos * rowstride) + (xpos * channels);
425 *temp = (unsigned char) ((buffer[1] & (((1<<bpp)-1) << (8 - (i * bpp)))) >> (8 - (i * bpp)));
426 }
427 }
428 }
429 if (((unsigned char) buffer[0] == 0) && ((unsigned char) buffer[1] > 2))
430 /* uncompressed record */
431 {
432 howmuch = buffer[1];
433 for (j = 0; j < howmuch; j += (8 / bpp))
434 {
435 notused = ReadOK (fd, &v, 1);
436 i = 1;
437 while ((i <= (8 / bpp)) && (xpos < width))
438 {
439 temp = image + (ypos * rowstride) + (xpos * channels);
440 *temp = (unsigned char) ((v & (((1<<bpp)-1) << (8-(i*bpp)))) >> (8-(i*bpp)));
441 i++;
442 xpos++;
443 }
444 }
445
446 if ((howmuch % 2) && (bpp==4))
447 howmuch++;
448
449 if ((howmuch / (8 / bpp)) % 2)
450 notused = ReadOK (fd, &v, 1);
451 /*if odd(x div (8 div bpp )) then blockread(f,z^,1);*/
452 }
453 if (((unsigned char) buffer[0] == 0) && ((unsigned char) buffer[1]==0))
454 /* Line end */
455 {
456 ypos--;
457 xpos = 0;
458 }
459 if (((unsigned char) buffer[0]==0) && ((unsigned char) buffer[1]==1))
460 /* Bitmap end */
461 {
462 break;
463 }
464 if (((unsigned char) buffer[0]==0) && ((unsigned char) buffer[1]==2))
465 /* Deltarecord */
466 {
467 notused = ReadOK (fd, buffer, 2);
468 xpos += (unsigned char) buffer[0];
469 ypos -= (unsigned char) buffer[1];
470 }
471 }
472 break;
473 }
474 }
475 break;
476 default:
477 /* This is very bad, we should not be here */
478 ;
479 }
480
481 if (bpp <= 8)
482 {
483 unsigned char *temp2, *temp3;
484 unsigned char index;
485 temp2 = temp = image;
486 XMALLOC (image, width * height * 3 * sizeof (unsigned char));
487 temp3 = image;
488 for (ypos = 0; ypos < height; ypos++)
489 {
490 for (xpos = 0; xpos < width; xpos++)
491 {
492 index = *temp2++;
493 *temp3++ = cmap[index][0];
494 if (!grey)
495 {
496 *temp3++ = cmap[index][1];
497 *temp3++ = cmap[index][2];
498 }
499 }
500 }
501 free (temp);
502 }
503
504 free (buffer);
505 return image;
506 }
507
508 FILE *errorfile;
509 char *prog_name = "bmp";
510 char *filename;
511 int interactive_bmp;
512
513 static long
ToL(unsigned char * puffer)514 ToL (unsigned char *puffer)
515 {
516 return (puffer[0] | puffer[1]<<8 | puffer[2]<<16 | puffer[3]<<24);
517 }
518
519 static short
ToS(unsigned char * puffer)520 ToS (unsigned char *puffer)
521 {
522 return ((short)(puffer[0] | puffer[1]<<8));
523 }
524
525