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