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
19#include "bmp.h"
20#include "pnmbitio.h"
21
22using std::istream;
23using 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
47static int GetByte (istream * fp);
48static short GetShort (istream * fp);
49static long GetLong (istream * fp);
50static void readto (istream *fp, unsigned long *ppos, unsigned long dst);
51static void BMPreadfileheader (istream *fp, unsigned long *ppos,
52 unsigned long *poffBits);
53static void BMPreadinfoheader (istream *fp, unsigned long *ppos,
54 unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount,
55 int *pclassv);
56static int BMPreadrgbtable (istream *fp, unsigned long *ppos,
57 unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B);
58
59static const char *ifname = "BMP";
60static char er_read[] = "%s: read error";
61// static char er_seek[] = "%s: seek error";
62
63static int
64GetByte(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
76static short
77GetShort(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
89static long
90GetLong(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
107static void
108readto(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
138static void
139BMPreadfileheader(
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
173static void
174BMPreadinfoheader(
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 */
289static int
290BMPreadrgbtable(
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 */
325static int
326BMPreadrow(
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
396static void
397BMPreadbits(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 */
448PNMFileTypeBMP::Reader::
449Reader(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 */
523int PNMFileTypeBMP::Reader::
524read_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.