Panda3D

pnmFileTypeBMPReader.cxx

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
 All Classes Functions Variables Enumerations