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

panda/src/pnmimagetypes/pnmFileTypeSGIReader.cxx

Go to the documentation of this file.
00001 // Filename: pnmFileTypeSGIReader.cxx
00002 // Created by:  drose (17Jun00)
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 "pnmFileTypeSGI.h"
00020 #include "config_pnmimagetypes.h"
00021 #include "sgi.h"
00022 
00023 #include "pnmImage.h"
00024 #include "pnmReader.h"
00025 
00026 #include "notify.h"
00027 
00028 // Much code in this file is borrowed from Netpbm, specifically sgitopnm.c.
00029 
00030 /* sgitopnm.c - read an SGI image and and produce a portable anymap
00031 **
00032 ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
00033 **
00034 ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
00035 ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
00036 **
00037 ** Permission to use, copy, modify, and distribute this software and its
00038 ** documentation for any purpose and without fee is hereby granted, provided
00039 ** that the above copyright notice appear in all copies and that both that
00040 ** copyright notice and this permission notice appear in supporting
00041 ** documentation.  This software is provided "as is" without express or
00042 ** implied warranty.
00043 **
00044 ** 29Jan94: first version
00045 ** 08Feb94: minor bugfix
00046 */
00047 
00048 /* entry in RLE offset table */
00049 typedef PNMFileTypeSGI::Reader::TabEntry TabEntry;
00050 
00051 typedef short       ScanElem;
00052 typedef ScanElem *  ScanLine;
00053 
00054 /* prototypes */
00055 static unsigned char get_byte ( istream* f );
00056 static long get_big_long (istream *f);
00057 static short get_big_short (istream *f);
00058 static short get_byte_as_short (istream *f);
00059 static int readerr (istream *f);
00060 static void * xmalloc (int bytes);
00061 #define MALLOC(n, type)     (type *)xmalloc((n) * sizeof(type))
00062 static char * compression_name (char compr);
00063 static void       read_bytes (istream *ifp, int n, char *buf);
00064 static bool read_header(istream *ifp, Header *head, const string &magic_number);
00065 static TabEntry * read_table (istream *ifp, int tablen);
00066 static void       read_channel (istream *ifp, int xsize, int ysize,
00067                                      int zsize, int bpc, TabEntry *table,
00068                                      ScanElem *channel_data, long table_start,
00069                                      int channel, int row);
00070 static void       rle_decompress (ScanElem *src, long srclen, ScanElem *dest, long destlen);
00071 
00072 #define WORSTCOMPR(x)   (2*(x) + 2)
00073 
00074 #define MAXVAL_BYTE     255
00075 #define MAXVAL_WORD     65535
00076 
00077 // This flag shouldn't really be a static global, but it's a little
00078 // tricky to fix and it doesn't do any harm, since it only controls
00079 // whether an error message is repeated.
00080 static bool eof_err = false;
00081 
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: PNMFileTypeSGI::Reader::Constructor
00085 //       Access: Public
00086 //  Description:
00087 ////////////////////////////////////////////////////////////////////
00088 PNMFileTypeSGI::Reader::
00089 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
00090   PNMReader(type, file, owns_file)
00091 {
00092   eof_err = false;
00093   table = NULL;
00094 
00095   if (!read_magic_number(_file, magic_number, 4)) {
00096     // No magic number.  No image.
00097     if (pnmimage_sgi_cat.is_debug()) {
00098       pnmimage_sgi_cat.debug()
00099         << "RGB file appears to be empty.\n";
00100     }
00101     _is_valid = false;
00102     return;
00103   }
00104 
00105   Header head;
00106 
00107   if (!::read_header(file, &head, magic_number)) {
00108     _is_valid = false;
00109   }
00110 
00111   long pixmax = (head.bpc == 1) ? MAXVAL_BYTE : MAXVAL_WORD;
00112   if( pixmax > PNM_MAXMAXVAL ) {
00113     pnmimage_sgi_cat.error()
00114       << "Cannot read RGB image with maxval of " << pixmax
00115       << "--largest allowable maxval is currently " << PNM_MAXMAXVAL << "\n";
00116     _is_valid = false;
00117     return;
00118   }
00119 
00120   _maxval = (xelval)pixmax;
00121 
00122   table_start = file->tellg();
00123   if( head.storage != STORAGE_VERBATIM )
00124     table = read_table(file, head.ysize * head.zsize);
00125 
00126   _x_size = head.xsize;
00127   _y_size = head.ysize;
00128   _num_channels = min((int)head.zsize, 4);
00129   bpc = head.bpc;
00130 
00131   current_row = _y_size - 1;
00132 
00133   if (_is_valid && pnmimage_sgi_cat.is_debug()) {
00134     head.name[79] = '\0';  /* just to be safe */
00135     pnmimage_sgi_cat.debug()
00136       << "Read RGB image:\n"
00137       << "  raster size " << head.xsize << " x " << head.ysize
00138       << ", " << head.zsize << " channels\n"
00139       << "  compression: " << (int)head.storage << " = "
00140       << compression_name(head.storage) << "\n"
00141       << "  image name: " << head.name << "\n"
00142       << "  bpc: " << (int)head.bpc << " dimension: " << head.dimension << "\n"
00143       << "  pixmin: " << head.pixmin << " pixmax: " << head.pixmax
00144       << "  colormap: " << head.colormap << "\n";
00145   }
00146 }
00147 
00148 ////////////////////////////////////////////////////////////////////
00149 //     Function: PNMFileTypeSGI::Reader::Destructor
00150 //       Access: Public, Virtual
00151 //  Description:
00152 ////////////////////////////////////////////////////////////////////
00153 PNMFileTypeSGI::Reader::
00154 ~Reader() {
00155   if (table != NULL) {
00156     free(table);
00157   }
00158 }
00159 
00160 ////////////////////////////////////////////////////////////////////
00161 //     Function: PNMFileTypeSGI::Reader::supports_read_row
00162 //       Access: Public, Virtual
00163 //  Description: Returns true if this particular PNMReader supports a
00164 //               streaming interface to reading the data: that is, it
00165 //               is capable of returning the data one row at a time,
00166 //               via repeated calls to read_row().  Returns false if
00167 //               the only way to read from this file is all at once,
00168 //               via read_data().
00169 ////////////////////////////////////////////////////////////////////
00170 bool PNMFileTypeSGI::Reader::
00171 supports_read_row() const {
00172   return true;
00173 }
00174 
00175 ////////////////////////////////////////////////////////////////////
00176 //     Function: PNMFileTypeSGI::Reader::read_row
00177 //       Access: Public, Virtual
00178 //  Description: If supports_read_row(), above, returns true, this
00179 //               function may be called repeatedly to read the image,
00180 //               one horizontal row at a time, beginning from the top.
00181 //               Returns true if the row is successfully read, false
00182 //               if there is an error or end of file.
00183 ////////////////////////////////////////////////////////////////////
00184 bool PNMFileTypeSGI::Reader::
00185 read_row(xel *row_data, xelval *alpha_data) {
00186   if (!is_valid()) {
00187     return false;
00188   }
00189   nassertr(current_row >= 0, false);
00190 
00191   ScanElem *red = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
00192   ScanElem *grn = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
00193   ScanElem *blu = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
00194   ScanElem *alpha = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
00195 
00196   read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, red,
00197                table_start, 0, current_row);
00198 
00199   if (!is_grayscale()) {
00200     read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, grn,
00201                  table_start, 1, current_row);
00202     read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, blu,
00203                  table_start, 2, current_row);
00204   }
00205 
00206   if (has_alpha()) {
00207     read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, alpha,
00208                  table_start, _num_channels - 1, current_row);
00209   }
00210 
00211   for (int x = 0; x < _x_size; x++) {
00212     if (is_grayscale()) {
00213       PPM_PUTB(row_data[x], (xelval)red[x]);
00214     } else {
00215       xelval r, g, b;
00216       r = (xelval)red[x];
00217       g = (xelval)grn[x];
00218       b = (xelval)blu[x];
00219       PPM_ASSIGN(row_data[x], r, g, b);
00220     }
00221 
00222     if (has_alpha()) {
00223       alpha_data[x] = (xelval)alpha[x];
00224     }
00225   }
00226   current_row--;
00227   return true;
00228 }
00229 
00230 
00231 
00232 static bool
00233 read_header(istream *ifp, Header *head, const string &magic_number) {
00234     nassertr(magic_number.size() == 4, false);
00235     head->magic =
00236       ((unsigned char)magic_number[0] << 8) |
00237       ((unsigned char)magic_number[1]);
00238     head->storage   = (unsigned char)magic_number[2];
00239     head->bpc       = (unsigned char)magic_number[3];
00240     head->dimension = get_big_short(ifp);
00241     head->xsize     = get_big_short(ifp);
00242     head->ysize     = get_big_short(ifp);
00243     head->zsize     = get_big_short(ifp);
00244     head->pixmin    = get_big_long(ifp);
00245     head->pixmax    = get_big_long(ifp);
00246     read_bytes(ifp, 4, head->dummy1);
00247     read_bytes(ifp, 80, head->name);
00248     head->colormap  = get_big_long(ifp);
00249     read_bytes(ifp, 404, head->dummy2);
00250 
00251     if (head->magic != SGI_MAGIC) {
00252       pnmimage_sgi_cat.error()
00253         << "Invalid magic number: not an SGI image file.\n";
00254       return false;
00255     }
00256 
00257     if (head->storage != 0 && head->storage != 1) {
00258       pnmimage_sgi_cat.error()
00259         << "Unknown compression type.\n";
00260       return false;
00261     }
00262 
00263     if (head->bpc < 1 || head->bpc > 2) {
00264       pnmimage_sgi_cat.error()
00265         << "Illegal precision value " << head->bpc << " (only 1-2 allowed)\n";
00266       return false;
00267     }
00268 
00269     // Actually, some old broken SGI image writers put garbage in this
00270     // field, so just ignore it.
00271     /*
00272     if (head->colormap != CMAP_NORMAL) {
00273       pnmimage_sgi_cat.error()
00274         << "Unsupported non-normal pixel data (" << head->colormap << ")\n";
00275       return false;
00276     }
00277     */
00278 
00279     /* adjust ysize/zsize to dimension, just to be sure */
00280 
00281     // On reflection, this is a bad idea.  Ignore the number of
00282     // dimensions, and take the xsize/ysize/zsize at face value.  The
00283     // table was written based on these numbers, after all; you can't
00284     // just change them arbitrarily.
00285 
00286     /*
00287     switch( head->dimension ) {
00288     case 1:
00289       head->ysize = 1;
00290       break;
00291     case 2:
00292       head->zsize = 1;
00293       break;
00294     case 3:
00295       switch( head->zsize ) {
00296       case 1:
00297       case 2:
00298         head->dimension = 2;
00299         break;
00300       case 3:
00301       case 4:
00302         break;
00303 
00304       default:
00305         pnmimage_sgi_cat.warning()
00306           << "Using only first 4 channels of " << head->zsize
00307           << "-channel image.\n";
00308         head->zsize = 4;
00309         break;
00310       }
00311       break;
00312     default:
00313       pnmimage_sgi_cat.error()
00314         << "Illegal dimension value " << head->dimension
00315         << " (only 1-3 allowed)\n";
00316       return false;
00317     }
00318     */
00319 
00320     return true;
00321 }
00322 
00323 
00324 static TabEntry *
00325 read_table(istream *ifp, int tablen) {
00326     TabEntry *table;
00327     int i;
00328 
00329     table = MALLOC(tablen, TabEntry);
00330 
00331     for( i = 0; i < tablen; i++ ) {
00332         table[i].start = get_big_long(ifp);
00333     }
00334     for( i = 0; i < tablen; i++ ) {
00335         table[i].length = get_big_long(ifp);
00336     }
00337 
00338     return table;
00339 }
00340 
00341 
00342 
00343 static void
00344 read_channel(istream *ifp,
00345              int xsize, int ysize, int, int bpc,
00346              TabEntry *table,
00347              ScanElem *channel_data, long table_start,
00348              int channel, int row) {
00349     ScanElem *temp;
00350     int sgi_index, i;
00351     long offset, length;
00352 
00353     short (*func)(istream *);
00354     func = (bpc==1) ? get_byte_as_short : get_big_short;
00355 
00356     if ( table ) {
00357       temp = (ScanElem *)alloca(WORSTCOMPR(xsize) * sizeof(ScanElem));
00358     }
00359 
00360     sgi_index = channel * ysize + row;
00361     if( table ) {
00362       offset = table[sgi_index].start;
00363       length = table[sgi_index].length;
00364       if( bpc == 2 )
00365         length /= 2;   /* doc says length is in bytes, we are reading words */
00366       if(!ifp->seekg(offset))
00367         pm_error("seek error for offset %ld", offset);
00368 
00369       nassertv(length <= WORSTCOMPR(xsize));
00370       for( i = 0; i < length; i++ )
00371         temp[i] = (*func)(ifp);
00372 
00373       rle_decompress(temp, length, channel_data, xsize);
00374     }
00375     else {
00376       offset = sgi_index * xsize + table_start;
00377       if(!ifp->seekg(offset))
00378         pm_error("seek error for offset %ld", offset);
00379       for( i = 0; i < xsize; i++ )
00380         channel_data[i] = (*func)(ifp);
00381     }
00382 }
00383 
00384 
00385 
00386 static void
00387 rle_decompress(ScanElem *src,
00388                long srcleft,
00389                ScanElem *dest,
00390                long destleft) {
00391     int count;
00392     unsigned char el;
00393 
00394     while( srcleft ) {
00395         el = (unsigned char)(*src++ & 0xff);
00396         --srcleft;
00397         count = (int)(el & 0x7f);
00398 
00399         if( count == 0 )
00400             return;
00401         if( destleft < count )
00402             pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count);
00403         destleft -= count;
00404         if( el & 0x80 ) {
00405             if( srcleft < count )
00406                 pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count);
00407             srcleft -= count;
00408             while( count-- )
00409                 *dest++ = *src++;
00410         }
00411         else {
00412             if( srcleft == 0 )
00413                 pm_error("RLE error: not enough data for replicate run");
00414             while( count-- )
00415                 *dest++ = *src;
00416             ++src;
00417             --srcleft;
00418         }
00419     }
00420     pm_error("RLE error: no terminating 0-byte");
00421 }
00422 
00423 
00424 /* basic I/O functions, taken from ilbmtoppm.c */
00425 
00426 static short
00427 get_big_short(istream *ifp) {
00428     short s;
00429 
00430     if( pm_readbigshort(ifp, &s) == -1 )
00431         s = readerr(ifp);
00432 
00433     return s;
00434 }
00435 
00436 static long
00437 get_big_long(istream *ifp) {
00438     long l;
00439 
00440     if( pm_readbiglong(ifp, &l) == -1 )
00441         l = readerr(ifp);
00442 
00443     return l;
00444 }
00445 
00446 static unsigned char
00447 get_byte(istream *ifp) {
00448     int i;
00449 
00450     i = ifp->get();
00451     if( i == EOF )
00452         i = readerr(ifp);
00453 
00454     return (unsigned char) i;
00455 }
00456 
00457 
00458 static int
00459 readerr(istream *f) {
00460   if (!eof_err) {
00461     if (!f->eof()) {
00462       pnmimage_sgi_cat.warning()
00463         << "Read error on file.\n";
00464     } else {
00465       pnmimage_sgi_cat.warning()
00466         << "Premature EOF on file.\n";
00467     }
00468     eof_err = true;
00469   }
00470 
00471   return 0;
00472 }
00473 
00474 
00475 static void
00476 read_bytes(istream *ifp,
00477            int n,
00478            char *buf) {
00479     int r;
00480 
00481     ifp->read(buf, n);
00482     r = ifp->gcount();
00483     if( r != n ) {
00484       readerr(ifp);
00485       memset(buf+r, 0, n-r);
00486     }
00487 }
00488 
00489 
00490 static short
00491 get_byte_as_short(istream *ifp) {
00492     return (short)get_byte(ifp);
00493 }
00494 
00495 
00496 static void *
00497 xmalloc(int bytes) {
00498     void *mem;
00499 
00500     if( bytes == 0 )
00501         return NULL;
00502 
00503     mem = malloc(bytes);
00504     if( mem == NULL )
00505         pm_error("out of memory allocating %d bytes", bytes);
00506     return mem;
00507 }
00508 
00509 static char *
00510 compression_name(char compr) {
00511     switch( compr ) {
00512         case STORAGE_VERBATIM:
00513             return "none";
00514         case STORAGE_RLE:
00515             return "RLE";
00516         default:
00517             return "unknown";
00518     }
00519 }
00520 

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