Panda3D
pnmFileTypeBMPReader.cxx
1 // Filename: pnmFileTypeBMPReader.cxx
2 // Created by: drose (19Jun00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pnmFileTypeBMP.h"
16 
17 #ifdef HAVE_BMP
18 
19 #include "config_pnmimagetypes.h"
20 #include "bmp.h"
21 #include "pnmbitio.h"
22 
23 // Much code in this file is borrowed from Netpbm, specifically bmptoppm.c.
24 /*
25  * bmptoppm.c - Converts from a Microsoft Windows or OS/2 .BMP file to a
26  * PPM file.
27  *
28  * The current implementation is probably not complete, but it works for
29  * all the BMP files I have. I welcome feedback.
30  *
31  * Copyright (C) 1992 by David W. Sanderson.
32  *
33  * Permission to use, copy, modify, and distribute this software and its
34  * documentation for any purpose and without fee is hereby granted,
35  * provided that the above copyright notice appear in all copies and
36  * that both that copyright notice and this permission notice appear
37  * in supporting documentation. This software is provided "as is"
38  * without express or implied warranty.
39  */
40 
41 /*
42  * Utilities
43  */
44 
45 static int GetByte (istream * fp);
46 static short GetShort (istream * fp);
47 static long GetLong (istream * fp);
48 static void readto (istream *fp, unsigned long *ppos, unsigned long dst);
49 static void BMPreadfileheader (istream *fp, unsigned long *ppos,
50  unsigned long *poffBits);
51 static void BMPreadinfoheader (istream *fp, unsigned long *ppos,
52  unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount,
53  int *pclassv);
54 static int BMPreadrgbtable (istream *fp, unsigned long *ppos,
55  unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B);
56 
57 static const char *ifname = "BMP";
58 static char er_read[] = "%s: read error";
59 //static char er_seek[] = "%s: seek error";
60 
61 static int
62 GetByte(istream *fp)
63 {
64  int v;
65 
66  if ((v = fp->get()) == EOF)
67  {
68  pm_error(er_read, ifname);
69  }
70 
71  return v;
72 }
73 
74 static short
75 GetShort(istream *fp)
76 {
77  short v;
78 
79  if (pm_readlittleshort(fp, &v) == -1)
80  {
81  pm_error(er_read, ifname);
82  }
83 
84  return v;
85 }
86 
87 static long
88 GetLong(istream *fp)
89 {
90  long v;
91 
92  if (pm_readlittlelong(fp, &v) == -1)
93  {
94  pm_error(er_read, ifname);
95  }
96 
97  return v;
98 }
99 
100 /*
101  * readto - read as many bytes as necessary to position the
102  * file at the desired offset.
103  */
104 
105 static void
106 readto(istream *fp,
107  unsigned long *ppos, /* pointer to number of bytes read from fp */
108  unsigned long dst)
109 {
110  unsigned long pos;
111 
112  if(!fp || !ppos)
113  return;
114 
115  pos = *ppos;
116 
117  if(pos > dst)
118  pm_error("%s: internal error in readto()", ifname);
119 
120  for(; pos < dst; pos++)
121  {
122  if (fp->get() == EOF)
123  {
124  pm_error(er_read, ifname);
125  }
126  }
127 
128  *ppos = pos;
129 }
130 
131 
132 /*
133  * BMP reading routines
134  */
135 
136 static void
137 BMPreadfileheader(
138  istream *fp,
139  unsigned long *ppos, /* number of bytes read from fp */
140  unsigned long *poffBits)
141 {
142  /*
143  unsigned long cbSize;
144  unsigned short xHotSpot;
145  unsigned short yHotSpot;
146  */
147  unsigned long offBits;
148 
149  /*
150  We've already read the magic number.
151  if (GetByte(fp) != 'B')
152  {
153  pm_error("%s is not a BMP file", ifname);
154  }
155  if (GetByte(fp) != 'M')
156  {
157  pm_error("%s is not a BMP file", ifname);
158  }
159  */
160 
161  /* cbSize = */ GetLong(fp);
162  /* xHotSpot = */ GetShort(fp);
163  /* yHotSpot = */ GetShort(fp);
164  offBits = GetLong(fp);
165 
166  *poffBits = offBits;
167 
168  *ppos += 14;
169 }
170 
171 static void
172 BMPreadinfoheader(
173  istream *fp,
174  unsigned long *ppos, /* number of bytes read from fp */
175  unsigned long *pcx,
176  unsigned long *pcy,
177  unsigned short *pcBitCount,
178  int *pclassv)
179 {
180  unsigned long cbFix;
181  unsigned short cPlanes = 0;
182 
183  unsigned long cx = 0;
184  unsigned long cy = 0;
185  unsigned short cBitCount = 0;
186  int classv = 0;
187 
188  cbFix = GetLong(fp);
189 
190  switch (cbFix)
191  {
192  case 12:
193  classv = C_OS2;
194  break;
195  case 40: // BITMAPINFOHEADER
196  classv = C_WIN;
197  break;
198  case 52: // BITMAPV2INFOHEADER
199  classv = C_WINV2;
200  break;
201  case 56: // BITMAPV3INFOHEADER
202  classv = C_WINV3;
203  break;
204  case 108: // BITMAPV4HEADER
205  classv = C_WINV4;
206  break;
207  case 124: // BITMAPV5HEADER
208  classv = C_WINV5;
209  break;
210  default:
211  pm_error("%s: unknown cbFix: %d", ifname, cbFix);
212  break;
213  }
214 
215  if (classv == C_OS2) {
216  cx = GetShort(fp);
217  cy = GetShort(fp);
218  } else {
219  cx = GetLong(fp);
220  cy = GetLong(fp);
221  }
222  cPlanes = GetShort(fp);
223  cBitCount = GetShort(fp);
224 
225  /*
226  * We've read 16 bytes so far, need to read more
227  * for the required total.
228  */
229  if (classv != C_OS2) {
230  for (int i = 0; i < (int)cbFix - 16; i += 4) {
231  GetLong(fp);
232  }
233  }
234 
235  if (cPlanes != 1)
236  {
237  pm_error("%s: don't know how to handle cPlanes = %d"
238  ,ifname
239  ,cPlanes);
240  }
241 
242  switch (classv)
243  {
244  case C_WIN:
245  pm_message("Windows BMP, %dx%dx%d"
246  ,cx
247  ,cy
248  ,cBitCount);
249  break;
250  case C_WINV2:
251  case C_WINV3:
252  case C_WINV4:
253  case C_WINV5:
254  pm_message("Windows BMP V%d, %dx%dx%d"
255  ,(classv - C_WINV2 + 2)
256  ,cx
257  ,cy
258  ,cBitCount);
259  break;
260  case C_OS2:
261  pm_message("OS/2 BMP, %dx%dx%d"
262  ,cx
263  ,cy
264  ,cBitCount);
265  break;
266  }
267 
268 #ifdef DEBUG
269  pm_message("cbFix: %d", cbFix);
270  pm_message("cx: %d", cx);
271  pm_message("cy: %d", cy);
272  pm_message("cPlanes: %d", cPlanes);
273  pm_message("cBitCount: %d", cBitCount);
274 #endif
275 
276  *pcx = cx;
277  *pcy = cy;
278  *pcBitCount = cBitCount;
279  *pclassv = classv;
280 
281  *ppos += cbFix;
282 }
283 
284 /*
285  * returns the number of bytes read, or -1 on error.
286  */
287 static int
288 BMPreadrgbtable(
289  istream *fp,
290  unsigned long *ppos, /* number of bytes read from fp */
291  unsigned short cBitCount,
292  int classv,
293  pixval *R,
294  pixval *G,
295  pixval *B)
296 {
297  int i;
298  int nbyte = 0;
299 
300  long ncolors = (1 << cBitCount);
301 
302  for (i = 0; i < ncolors; i++)
303  {
304  B[i] = (pixval) GetByte(fp);
305  G[i] = (pixval) GetByte(fp);
306  R[i] = (pixval) GetByte(fp);
307  nbyte += 3;
308 
309  if (classv != C_OS2)
310  {
311  (void) GetByte(fp);
312  nbyte++;
313  }
314  }
315 
316  *ppos += nbyte;
317  return nbyte;
318 }
319 
320 /*
321  * returns the number of bytes read, or -1 on error.
322  */
323 static int
324 BMPreadrow(
325  istream *fp,
326  unsigned long *ppos, /* number of bytes read from fp */
327  pixel *row,
328  xelval *alpha_row,
329  unsigned long cx,
330  unsigned short cBitCount,
331  int indexed,
332  pixval *R,
333  pixval *G,
334  pixval *B)
335 {
336  BITSTREAM b = NULL;
337  unsigned nbyte = 0;
338  int rc;
339  unsigned x;
340 
341  if (indexed) {
342  if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0)
343  {
344  return -1;
345  }
346  }
347 
348  for (x = 0; x < cx; x++, row++)
349  {
350  unsigned long v;
351 
352  if (!indexed) {
353  int r, g, b;
354  b = GetByte(fp);
355  g = GetByte(fp);
356  r = GetByte(fp);
357  if (cBitCount > 24) {
358  *(alpha_row++) = GetByte(fp);
359  ++nbyte;
360  }
361  nbyte += 3;
362  PPM_ASSIGN(*row, r, g, b);
363  } else {
364  if ((rc = pm_bitread(b, cBitCount, &v)) == -1)
365  {
366  return -1;
367  }
368  nbyte += rc;
369 
370  PPM_ASSIGN(*row, R[v], G[v], B[v]);
371  }
372  }
373 
374  if (indexed) {
375  if ((rc = pm_bitfini(b)) != 0)
376  {
377  return -1;
378  }
379  }
380 
381  /*
382  * Make sure we read a multiple of 4 bytes.
383  */
384  while (nbyte % 4)
385  {
386  GetByte(fp);
387  nbyte++;
388  }
389 
390  *ppos += nbyte;
391  return nbyte;
392 }
393 
394 static void
395 BMPreadbits(xel *array, xelval *alpha_array,
396  istream *fp,
397  unsigned long *ppos, /* number of bytes read from fp */
398  unsigned long offBits,
399  unsigned long cx,
400  unsigned long cy,
401  unsigned short cBitCount,
402  int /* classv */,
403  int indexed,
404  pixval *R,
405  pixval *G,
406  pixval *B)
407 {
408  long y;
409 
410  readto(fp, ppos, offBits);
411 
412  if(cBitCount > 24 && cBitCount != 32)
413  {
414  pm_error("%s: cannot handle cBitCount: %d"
415  ,ifname
416  ,cBitCount);
417  }
418 
419  /*
420  * The picture is stored bottom line first, top line last
421  */
422 
423  for (y = (long)cy - 1; y >= 0; y--)
424  {
425  int rc;
426  rc = BMPreadrow(fp, ppos, array + y*cx, alpha_array + y*cx, cx, cBitCount, indexed, R, G, B);
427  if(rc == -1)
428  {
429  pm_error("%s: couldn't read row %d"
430  ,ifname
431  ,y);
432  }
433  if(rc%4)
434  {
435  pm_error("%s: row had bad number of bytes: %d"
436  ,ifname
437  ,rc);
438  }
439  }
440 
441 }
442 
443 ////////////////////////////////////////////////////////////////////
444 // Function: PNMFileTypeBMP::Reader::Constructor
445 // Access: Public
446 // Description:
447 ////////////////////////////////////////////////////////////////////
448 PNMFileTypeBMP::Reader::
449 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
450  PNMReader(type, file, owns_file)
451 {
452  if (!read_magic_number(_file, magic_number, 2)) {
453  // No magic number, no image.
454  if (pnmimage_bmp_cat.is_debug()) {
455  pnmimage_bmp_cat.debug()
456  << "BMP image file appears to be empty.\n";
457  }
458  _is_valid = false;
459  return;
460  }
461 
462  if (magic_number != string("BM")) {
463  pnmimage_bmp_cat.error()
464  << "File is not a valid BMP file.\n";
465  _is_valid = false;
466  return;
467  }
468 
469  int rc;
470 
471  unsigned long cx;
472  unsigned long cy;
473 
474  pos = 0;
475 
476  BMPreadfileheader(file, &pos, &offBits);
477  BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &classv);
478 
479  if (offBits != BMPoffbits(classv, cBitCount)) {
480  pnmimage_bmp_cat.warning()
481  << "offBits is " << offBits << ", expected "
482  << BMPoffbits(classv, cBitCount) << "\n";
483  }
484 
485  indexed = false;
486 
487  if (cBitCount <= 8) {
488  indexed = true;
489  rc = BMPreadrgbtable(file, &pos, cBitCount, classv, R, G, B);
490 
491  if (rc != (int)BMPlenrgbtable(classv, cBitCount)) {
492  pnmimage_bmp_cat.warning()
493  << rc << "-byte RGB table, expected "
494  << BMPlenrgbtable(classv, cBitCount) << " bytes\n";
495  }
496  }
497 
498  if (cBitCount > 24) {
499  _num_channels = 4;
500  } else {
501  _num_channels = 3;
502  }
503  _x_size = (int)cx;
504  _y_size = (int)cy;
505  _maxval = 255;
506 
507  if (pnmimage_bmp_cat.is_debug()) {
508  pnmimage_bmp_cat.debug()
509  << "Reading BMP " << *this << "\n";
510  }
511 }
512 
513 
514 ////////////////////////////////////////////////////////////////////
515 // Function: PNMFileTypeBMP::Reader::read_data
516 // Access: Public, Virtual
517 // Description: Reads in an entire image all at once, storing it in
518 // the pre-allocated _x_size * _y_size array and alpha
519 // pointers. (If the image type has no alpha channel,
520 // alpha is ignored.) Returns the number of rows
521 // correctly read.
522 //
523 // Derived classes need not override this if they
524 // instead provide supports_read_row() and read_row(),
525 // below.
526 ////////////////////////////////////////////////////////////////////
527 int PNMFileTypeBMP::Reader::
528 read_data(xel *array, xelval *alpha_array) {
529  BMPreadbits(array, alpha_array, _file, &pos, offBits, _x_size, _y_size,
530  cBitCount, classv, indexed, R, G, B);
531 
532  if (pos != BMPlenfile(classv, cBitCount, _x_size, _y_size)) {
533  pnmimage_bmp_cat.warning()
534  << "Read " << pos << " bytes, expected to read "
535  << BMPlenfile(classv, cBitCount, _x_size, _y_size) << " bytes\n";
536  }
537 
538  return _y_size;
539 }
540 
541 #endif // HAVE_BMP
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:35
This is an abstract base class that defines the interface for reading image files of various types...
Definition: pnmReader.h:31