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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
This is an abstract base class that defines the interface for reading image files of various types.
Definition: pnmReader.h:27
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
void pm_message(const char *format,...)
Outputs the given printf-style message to the user and returns.