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