Panda3D
 All Classes Functions Variables Enumerations
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 
195  cx = GetShort(fp);
196  cy = GetShort(fp);
197  cPlanes = GetShort(fp);
198  cBitCount = GetShort(fp);
199 
200  break;
201  case 40:
202  classv = C_WIN;
203 
204  cx = GetLong(fp);
205  cy = GetLong(fp);
206  cPlanes = GetShort(fp);
207  cBitCount = GetShort(fp);
208 
209  /*
210  * We've read 16 bytes so far, need to read 24 more
211  * for the required total of 40.
212  */
213 
214  GetLong(fp);
215  GetLong(fp);
216  GetLong(fp);
217  GetLong(fp);
218  GetLong(fp);
219  GetLong(fp);
220 
221  break;
222  default:
223  pm_error("%s: unknown cbFix: %d", ifname, cbFix);
224  break;
225  }
226 
227  if (cPlanes != 1)
228  {
229  pm_error("%s: don't know how to handle cPlanes = %d"
230  ,ifname
231  ,cPlanes);
232  }
233 
234  switch (classv)
235  {
236  case C_WIN:
237  pm_message("Windows BMP, %dx%dx%d"
238  ,cx
239  ,cy
240  ,cBitCount);
241  break;
242  case C_OS2:
243  pm_message("OS/2 BMP, %dx%dx%d"
244  ,cx
245  ,cy
246  ,cBitCount);
247  break;
248  }
249 
250 #ifdef DEBUG
251  pm_message("cbFix: %d", cbFix);
252  pm_message("cx: %d", cx);
253  pm_message("cy: %d", cy);
254  pm_message("cPlanes: %d", cPlanes);
255  pm_message("cBitCount: %d", cBitCount);
256 #endif
257 
258  *pcx = cx;
259  *pcy = cy;
260  *pcBitCount = cBitCount;
261  *pclassv = classv;
262 
263  *ppos += cbFix;
264 }
265 
266 /*
267  * returns the number of bytes read, or -1 on error.
268  */
269 static int
270 BMPreadrgbtable(
271  istream *fp,
272  unsigned long *ppos, /* number of bytes read from fp */
273  unsigned short cBitCount,
274  int classv,
275  pixval *R,
276  pixval *G,
277  pixval *B)
278 {
279  int i;
280  int nbyte = 0;
281 
282  long ncolors = (1 << cBitCount);
283 
284  for (i = 0; i < ncolors; i++)
285  {
286  B[i] = (pixval) GetByte(fp);
287  G[i] = (pixval) GetByte(fp);
288  R[i] = (pixval) GetByte(fp);
289  nbyte += 3;
290 
291  if (classv == C_WIN)
292  {
293  (void) GetByte(fp);
294  nbyte++;
295  }
296  }
297 
298  *ppos += nbyte;
299  return nbyte;
300 }
301 
302 /*
303  * returns the number of bytes read, or -1 on error.
304  */
305 static int
306 BMPreadrow(
307  istream *fp,
308  unsigned long *ppos, /* number of bytes read from fp */
309  pixel *row,
310  unsigned long cx,
311  unsigned short cBitCount,
312  int indexed,
313  pixval *R,
314  pixval *G,
315  pixval *B)
316 {
317  BITSTREAM b = NULL;
318  unsigned nbyte = 0;
319  int rc;
320  unsigned x;
321 
322  if (indexed) {
323  if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0)
324  {
325  return -1;
326  }
327  }
328 
329  for (x = 0; x < cx; x++, row++)
330  {
331  unsigned long v;
332 
333  if (!indexed) {
334  int r, g, b;
335  b = GetByte(fp);
336  g = GetByte(fp);
337  r = GetByte(fp);
338  nbyte += 3;
339  PPM_ASSIGN(*row, r, g, b);
340  } else {
341  if ((rc = pm_bitread(b, cBitCount, &v)) == -1)
342  {
343  return -1;
344  }
345  nbyte += rc;
346 
347  PPM_ASSIGN(*row, R[v], G[v], B[v]);
348  }
349  }
350 
351  if (indexed) {
352  if ((rc = pm_bitfini(b)) != 0)
353  {
354  return -1;
355  }
356  }
357 
358  /*
359  * Make sure we read a multiple of 4 bytes.
360  */
361  while (nbyte % 4)
362  {
363  GetByte(fp);
364  nbyte++;
365  }
366 
367  *ppos += nbyte;
368  return nbyte;
369 }
370 
371 static void
372 BMPreadbits(xel *array,
373  istream *fp,
374  unsigned long *ppos, /* number of bytes read from fp */
375  unsigned long offBits,
376  unsigned long cx,
377  unsigned long cy,
378  unsigned short cBitCount,
379  int /* classv */,
380  int indexed,
381  pixval *R,
382  pixval *G,
383  pixval *B)
384 {
385  long y;
386 
387  readto(fp, ppos, offBits);
388 
389  if(cBitCount > 24)
390  {
391  pm_error("%s: cannot handle cBitCount: %d"
392  ,ifname
393  ,cBitCount);
394  }
395 
396  /*
397  * The picture is stored bottom line first, top line last
398  */
399 
400  for (y = (long)cy - 1; y >= 0; y--)
401  {
402  int rc;
403  rc = BMPreadrow(fp, ppos, array + y*cx, cx, cBitCount, indexed, R, G, B);
404  if(rc == -1)
405  {
406  pm_error("%s: couldn't read row %d"
407  ,ifname
408  ,y);
409  }
410  if(rc%4)
411  {
412  pm_error("%s: row had bad number of bytes: %d"
413  ,ifname
414  ,rc);
415  }
416  }
417 
418 }
419 
420 ////////////////////////////////////////////////////////////////////
421 // Function: PNMFileTypeBMP::Reader::Constructor
422 // Access: Public
423 // Description:
424 ////////////////////////////////////////////////////////////////////
425 PNMFileTypeBMP::Reader::
426 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
427  PNMReader(type, file, owns_file)
428 {
429  if (!read_magic_number(_file, magic_number, 2)) {
430  // No magic number, no image.
431  if (pnmimage_bmp_cat.is_debug()) {
432  pnmimage_bmp_cat.debug()
433  << "BMP image file appears to be empty.\n";
434  }
435  _is_valid = false;
436  return;
437  }
438 
439  if (magic_number != string("BM")) {
440  pnmimage_bmp_cat.error()
441  << "File is not a valid BMP file.\n";
442  _is_valid = false;
443  return;
444  }
445 
446  int rc;
447 
448  unsigned long cx;
449  unsigned long cy;
450 
451  pos = 0;
452 
453  BMPreadfileheader(file, &pos, &offBits);
454  BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &classv);
455 
456  if (offBits != BMPoffbits(classv, cBitCount)) {
457  pnmimage_bmp_cat.warning()
458  << "offBits is " << offBits << ", expected "
459  << BMPoffbits(classv, cBitCount) << "\n";
460  }
461 
462  indexed = false;
463 
464  if (cBitCount <= 8) {
465  indexed = true;
466  rc = BMPreadrgbtable(file, &pos, cBitCount, classv, R, G, B);
467 
468  if (rc != (int)BMPlenrgbtable(classv, cBitCount)) {
469  pnmimage_bmp_cat.warning()
470  << rc << "-byte RGB table, expected "
471  << BMPlenrgbtable(classv, cBitCount) << " bytes\n";
472  }
473  }
474 
475  _num_channels = 3;
476  _x_size = (int)cx;
477  _y_size = (int)cy;
478  _maxval = 255;
479 
480  if (pnmimage_bmp_cat.is_debug()) {
481  pnmimage_bmp_cat.debug()
482  << "Reading BMP " << *this << "\n";
483  }
484 }
485 
486 
487 ////////////////////////////////////////////////////////////////////
488 // Function: PNMFileTypeBMP::Reader::read_data
489 // Access: Public, Virtual
490 // Description: Reads in an entire image all at once, storing it in
491 // the pre-allocated _x_size * _y_size array and alpha
492 // pointers. (If the image type has no alpha channel,
493 // alpha is ignored.) Returns the number of rows
494 // correctly read.
495 //
496 // Derived classes need not override this if they
497 // instead provide supports_read_row() and read_row(),
498 // below.
499 ////////////////////////////////////////////////////////////////////
500 int PNMFileTypeBMP::Reader::
501 read_data(xel *array, xelval *) {
502  BMPreadbits(array, _file, &pos, offBits, _x_size, _y_size,
503  cBitCount, classv, indexed, R, G, B);
504 
505  if (pos != BMPlenfile(classv, cBitCount, _x_size, _y_size)) {
506  pnmimage_bmp_cat.warning()
507  << "Read " << pos << " bytes, expected to read "
508  << BMPlenfile(classv, cBitCount, _x_size, _y_size) << " bytes\n";
509  }
510 
511  return _y_size;
512 }
513 
514 #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