00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 typedef PNMFileTypeSGI::Reader::TabEntry TabEntry;
00049
00050 typedef short ScanElem;
00051 typedef ScanElem * ScanLine;
00052
00053
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
00077
00078
00079 static bool eof_err = false;
00080
00081
00082
00083
00084
00085
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
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';
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
00149
00150
00151
00152 PNMFileTypeSGI::Reader::
00153 ~Reader() {
00154 if (table != NULL) {
00155 free(table);
00156 }
00157 }
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 bool PNMFileTypeSGI::Reader::
00170 supports_read_row() const {
00171 return true;
00172 }
00173
00174
00175
00176
00177
00178
00179
00180
00181
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
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
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;
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
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