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