00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 typedef PNMFileTypeSGI::Reader::TabEntry TabEntry;
00050
00051 typedef short ScanElem;
00052 typedef ScanElem * ScanLine;
00053
00054
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
00078
00079
00080 static bool eof_err = false;
00081
00082
00083
00084
00085
00086
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
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';
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
00150
00151
00152
00153 PNMFileTypeSGI::Reader::
00154 ~Reader() {
00155 if (table != NULL) {
00156 free(table);
00157 }
00158 }
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170 bool PNMFileTypeSGI::Reader::
00171 supports_read_row() const {
00172 return true;
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
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
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
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;
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
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