Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/pnmimagetypes/pnmFileTypeBMPReader.cxx

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

Generated on Fri May 2 00:43:15 2003 for Panda by doxygen1.3