Panda3D
|
00001 // Filename: pnmFileTypeBMPReader.cxx 00002 // Created by: drose (19Jun00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "pnmFileTypeBMP.h" 00016 00017 #ifdef HAVE_BMP 00018 00019 #include "config_pnmimagetypes.h" 00020 #include "bmp.h" 00021 #include "pnmbitio.h" 00022 00023 // Much code in this file is borrowed from Netpbm, specifically bmptoppm.c. 00024 /* 00025 * bmptoppm.c - Converts from a Microsoft Windows or OS/2 .BMP file to a 00026 * PPM file. 00027 * 00028 * The current implementation is probably not complete, but it works for 00029 * all the BMP files I have. I welcome feedback. 00030 * 00031 * Copyright (C) 1992 by David W. Sanderson. 00032 * 00033 * Permission to use, copy, modify, and distribute this software and its 00034 * documentation for any purpose and without fee is hereby granted, 00035 * provided that the above copyright notice appear in all copies and 00036 * that both that copyright notice and this permission notice appear 00037 * in supporting documentation. This software is provided "as is" 00038 * without express or implied warranty. 00039 */ 00040 00041 /* 00042 * Utilities 00043 */ 00044 00045 static int GetByte (istream * fp); 00046 static short GetShort (istream * fp); 00047 static long GetLong (istream * fp); 00048 static void readto (istream *fp, unsigned long *ppos, unsigned long dst); 00049 static void BMPreadfileheader (istream *fp, unsigned long *ppos, 00050 unsigned long *poffBits); 00051 static void BMPreadinfoheader (istream *fp, unsigned long *ppos, 00052 unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount, 00053 int *pclassv); 00054 static int BMPreadrgbtable (istream *fp, unsigned long *ppos, 00055 unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B); 00056 00057 static const char *ifname = "BMP"; 00058 static char er_read[] = "%s: read error"; 00059 //static char er_seek[] = "%s: seek error"; 00060 00061 static int 00062 GetByte(istream *fp) 00063 { 00064 int v; 00065 00066 if ((v = fp->get()) == EOF) 00067 { 00068 pm_error(er_read, ifname); 00069 } 00070 00071 return v; 00072 } 00073 00074 static short 00075 GetShort(istream *fp) 00076 { 00077 short v; 00078 00079 if (pm_readlittleshort(fp, &v) == -1) 00080 { 00081 pm_error(er_read, ifname); 00082 } 00083 00084 return v; 00085 } 00086 00087 static long 00088 GetLong(istream *fp) 00089 { 00090 long v; 00091 00092 if (pm_readlittlelong(fp, &v) == -1) 00093 { 00094 pm_error(er_read, ifname); 00095 } 00096 00097 return v; 00098 } 00099 00100 /* 00101 * readto - read as many bytes as necessary to position the 00102 * file at the desired offset. 00103 */ 00104 00105 static void 00106 readto(istream *fp, 00107 unsigned long *ppos, /* pointer to number of bytes read from fp */ 00108 unsigned long dst) 00109 { 00110 unsigned long pos; 00111 00112 if(!fp || !ppos) 00113 return; 00114 00115 pos = *ppos; 00116 00117 if(pos > dst) 00118 pm_error("%s: internal error in readto()", ifname); 00119 00120 for(; pos < dst; pos++) 00121 { 00122 if (fp->get() == EOF) 00123 { 00124 pm_error(er_read, ifname); 00125 } 00126 } 00127 00128 *ppos = pos; 00129 } 00130 00131 00132 /* 00133 * BMP reading routines 00134 */ 00135 00136 static void 00137 BMPreadfileheader( 00138 istream *fp, 00139 unsigned long *ppos, /* number of bytes read from fp */ 00140 unsigned long *poffBits) 00141 { 00142 /* 00143 unsigned long cbSize; 00144 unsigned short xHotSpot; 00145 unsigned short yHotSpot; 00146 */ 00147 unsigned long offBits; 00148 00149 /* 00150 We've already read the magic number. 00151 if (GetByte(fp) != 'B') 00152 { 00153 pm_error("%s is not a BMP file", ifname); 00154 } 00155 if (GetByte(fp) != 'M') 00156 { 00157 pm_error("%s is not a BMP file", ifname); 00158 } 00159 */ 00160 00161 /* cbSize = */ GetLong(fp); 00162 /* xHotSpot = */ GetShort(fp); 00163 /* yHotSpot = */ GetShort(fp); 00164 offBits = GetLong(fp); 00165 00166 *poffBits = offBits; 00167 00168 *ppos += 14; 00169 } 00170 00171 static void 00172 BMPreadinfoheader( 00173 istream *fp, 00174 unsigned long *ppos, /* number of bytes read from fp */ 00175 unsigned long *pcx, 00176 unsigned long *pcy, 00177 unsigned short *pcBitCount, 00178 int *pclassv) 00179 { 00180 unsigned long cbFix; 00181 unsigned short cPlanes = 0; 00182 00183 unsigned long cx = 0; 00184 unsigned long cy = 0; 00185 unsigned short cBitCount = 0; 00186 int classv = 0; 00187 00188 cbFix = GetLong(fp); 00189 00190 switch (cbFix) 00191 { 00192 case 12: 00193 classv = C_OS2; 00194 00195 cx = GetShort(fp); 00196 cy = GetShort(fp); 00197 cPlanes = GetShort(fp); 00198 cBitCount = GetShort(fp); 00199 00200 break; 00201 case 40: 00202 classv = C_WIN; 00203 00204 cx = GetLong(fp); 00205 cy = GetLong(fp); 00206 cPlanes = GetShort(fp); 00207 cBitCount = GetShort(fp); 00208 00209 /* 00210 * We've read 16 bytes so far, need to read 24 more 00211 * for the required total of 40. 00212 */ 00213 00214 GetLong(fp); 00215 GetLong(fp); 00216 GetLong(fp); 00217 GetLong(fp); 00218 GetLong(fp); 00219 GetLong(fp); 00220 00221 break; 00222 default: 00223 pm_error("%s: unknown cbFix: %d", ifname, cbFix); 00224 break; 00225 } 00226 00227 if (cPlanes != 1) 00228 { 00229 pm_error("%s: don't know how to handle cPlanes = %d" 00230 ,ifname 00231 ,cPlanes); 00232 } 00233 00234 switch (classv) 00235 { 00236 case C_WIN: 00237 pm_message("Windows BMP, %dx%dx%d" 00238 ,cx 00239 ,cy 00240 ,cBitCount); 00241 break; 00242 case C_OS2: 00243 pm_message("OS/2 BMP, %dx%dx%d" 00244 ,cx 00245 ,cy 00246 ,cBitCount); 00247 break; 00248 } 00249 00250 #ifdef DEBUG 00251 pm_message("cbFix: %d", cbFix); 00252 pm_message("cx: %d", cx); 00253 pm_message("cy: %d", cy); 00254 pm_message("cPlanes: %d", cPlanes); 00255 pm_message("cBitCount: %d", cBitCount); 00256 #endif 00257 00258 *pcx = cx; 00259 *pcy = cy; 00260 *pcBitCount = cBitCount; 00261 *pclassv = classv; 00262 00263 *ppos += cbFix; 00264 } 00265 00266 /* 00267 * returns the number of bytes read, or -1 on error. 00268 */ 00269 static int 00270 BMPreadrgbtable( 00271 istream *fp, 00272 unsigned long *ppos, /* number of bytes read from fp */ 00273 unsigned short cBitCount, 00274 int classv, 00275 pixval *R, 00276 pixval *G, 00277 pixval *B) 00278 { 00279 int i; 00280 int nbyte = 0; 00281 00282 long ncolors = (1 << cBitCount); 00283 00284 for (i = 0; i < ncolors; i++) 00285 { 00286 B[i] = (pixval) GetByte(fp); 00287 G[i] = (pixval) GetByte(fp); 00288 R[i] = (pixval) GetByte(fp); 00289 nbyte += 3; 00290 00291 if (classv == C_WIN) 00292 { 00293 (void) GetByte(fp); 00294 nbyte++; 00295 } 00296 } 00297 00298 *ppos += nbyte; 00299 return nbyte; 00300 } 00301 00302 /* 00303 * returns the number of bytes read, or -1 on error. 00304 */ 00305 static int 00306 BMPreadrow( 00307 istream *fp, 00308 unsigned long *ppos, /* number of bytes read from fp */ 00309 pixel *row, 00310 unsigned long cx, 00311 unsigned short cBitCount, 00312 int indexed, 00313 pixval *R, 00314 pixval *G, 00315 pixval *B) 00316 { 00317 BITSTREAM b = NULL; 00318 unsigned nbyte = 0; 00319 int rc; 00320 unsigned x; 00321 00322 if (indexed) { 00323 if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0) 00324 { 00325 return -1; 00326 } 00327 } 00328 00329 for (x = 0; x < cx; x++, row++) 00330 { 00331 unsigned long v; 00332 00333 if (!indexed) { 00334 int r, g, b; 00335 b = GetByte(fp); 00336 g = GetByte(fp); 00337 r = GetByte(fp); 00338 nbyte += 3; 00339 PPM_ASSIGN(*row, r, g, b); 00340 } else { 00341 if ((rc = pm_bitread(b, cBitCount, &v)) == -1) 00342 { 00343 return -1; 00344 } 00345 nbyte += rc; 00346 00347 PPM_ASSIGN(*row, R[v], G[v], B[v]); 00348 } 00349 } 00350 00351 if (indexed) { 00352 if ((rc = pm_bitfini(b)) != 0) 00353 { 00354 return -1; 00355 } 00356 } 00357 00358 /* 00359 * Make sure we read a multiple of 4 bytes. 00360 */ 00361 while (nbyte % 4) 00362 { 00363 GetByte(fp); 00364 nbyte++; 00365 } 00366 00367 *ppos += nbyte; 00368 return nbyte; 00369 } 00370 00371 static void 00372 BMPreadbits(xel *array, 00373 istream *fp, 00374 unsigned long *ppos, /* number of bytes read from fp */ 00375 unsigned long offBits, 00376 unsigned long cx, 00377 unsigned long cy, 00378 unsigned short cBitCount, 00379 int /* classv */, 00380 int indexed, 00381 pixval *R, 00382 pixval *G, 00383 pixval *B) 00384 { 00385 long y; 00386 00387 readto(fp, ppos, offBits); 00388 00389 if(cBitCount > 24) 00390 { 00391 pm_error("%s: cannot handle cBitCount: %d" 00392 ,ifname 00393 ,cBitCount); 00394 } 00395 00396 /* 00397 * The picture is stored bottom line first, top line last 00398 */ 00399 00400 for (y = (long)cy - 1; y >= 0; y--) 00401 { 00402 int rc; 00403 rc = BMPreadrow(fp, ppos, array + y*cx, cx, cBitCount, indexed, R, G, B); 00404 if(rc == -1) 00405 { 00406 pm_error("%s: couldn't read row %d" 00407 ,ifname 00408 ,y); 00409 } 00410 if(rc%4) 00411 { 00412 pm_error("%s: row had bad number of bytes: %d" 00413 ,ifname 00414 ,rc); 00415 } 00416 } 00417 00418 } 00419 00420 //////////////////////////////////////////////////////////////////// 00421 // Function: PNMFileTypeBMP::Reader::Constructor 00422 // Access: Public 00423 // Description: 00424 //////////////////////////////////////////////////////////////////// 00425 PNMFileTypeBMP::Reader:: 00426 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : 00427 PNMReader(type, file, owns_file) 00428 { 00429 if (!read_magic_number(_file, magic_number, 2)) { 00430 // No magic number, no image. 00431 if (pnmimage_bmp_cat.is_debug()) { 00432 pnmimage_bmp_cat.debug() 00433 << "BMP image file appears to be empty.\n"; 00434 } 00435 _is_valid = false; 00436 return; 00437 } 00438 00439 if (magic_number != string("BM")) { 00440 pnmimage_bmp_cat.error() 00441 << "File is not a valid BMP file.\n"; 00442 _is_valid = false; 00443 return; 00444 } 00445 00446 int rc; 00447 00448 unsigned long cx; 00449 unsigned long cy; 00450 00451 pos = 0; 00452 00453 BMPreadfileheader(file, &pos, &offBits); 00454 BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &classv); 00455 00456 if (offBits != BMPoffbits(classv, cBitCount)) { 00457 pnmimage_bmp_cat.warning() 00458 << "offBits is " << offBits << ", expected " 00459 << BMPoffbits(classv, cBitCount) << "\n"; 00460 } 00461 00462 indexed = false; 00463 00464 if (cBitCount <= 8) { 00465 indexed = true; 00466 rc = BMPreadrgbtable(file, &pos, cBitCount, classv, R, G, B); 00467 00468 if (rc != (int)BMPlenrgbtable(classv, cBitCount)) { 00469 pnmimage_bmp_cat.warning() 00470 << rc << "-byte RGB table, expected " 00471 << BMPlenrgbtable(classv, cBitCount) << " bytes\n"; 00472 } 00473 } 00474 00475 _num_channels = 3; 00476 _x_size = (int)cx; 00477 _y_size = (int)cy; 00478 _maxval = 255; 00479 00480 if (pnmimage_bmp_cat.is_debug()) { 00481 pnmimage_bmp_cat.debug() 00482 << "Reading BMP " << *this << "\n"; 00483 } 00484 } 00485 00486 00487 //////////////////////////////////////////////////////////////////// 00488 // Function: PNMFileTypeBMP::Reader::read_data 00489 // Access: Public, Virtual 00490 // Description: Reads in an entire image all at once, storing it in 00491 // the pre-allocated _x_size * _y_size array and alpha 00492 // pointers. (If the image type has no alpha channel, 00493 // alpha is ignored.) Returns the number of rows 00494 // correctly read. 00495 // 00496 // Derived classes need not override this if they 00497 // instead provide supports_read_row() and read_row(), 00498 // below. 00499 //////////////////////////////////////////////////////////////////// 00500 int PNMFileTypeBMP::Reader:: 00501 read_data(xel *array, xelval *) { 00502 BMPreadbits(array, _file, &pos, offBits, _x_size, _y_size, 00503 cBitCount, classv, indexed, R, G, B); 00504 00505 if (pos != BMPlenfile(classv, cBitCount, _x_size, _y_size)) { 00506 pnmimage_bmp_cat.warning() 00507 << "Read " << pos << " bytes, expected to read " 00508 << BMPlenfile(classv, cBitCount, _x_size, _y_size) << " bytes\n"; 00509 } 00510 00511 return _y_size; 00512 } 00513 00514 #endif // HAVE_BMP