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