Panda3D
|
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