Panda3D
 All Classes Functions Variables Enumerations
pnmFileTypeSGIReader.cxx
1 // Filename: pnmFileTypeSGIReader.cxx
2 // Created by: drose (17Jun00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pnmFileTypeSGI.h"
16 
17 #ifdef HAVE_SGI_RGB
18 
19 #include "config_pnmimagetypes.h"
20 #include "sgi.h"
21 
22 #include "pnmImage.h"
23 #include "pnmReader.h"
24 
25 #include "pnotify.h"
26 
27 // Much code in this file is borrowed from Netpbm, specifically sgitopnm.c.
28 
29 /* sgitopnm.c - read an SGI image and and produce a portable anymap
30 **
31 ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
32 **
33 ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
34 ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
35 **
36 ** Permission to use, copy, modify, and distribute this software and its
37 ** documentation for any purpose and without fee is hereby granted, provided
38 ** that the above copyright notice appear in all copies and that both that
39 ** copyright notice and this permission notice appear in supporting
40 ** documentation. This software is provided "as is" without express or
41 ** implied warranty.
42 **
43 ** 29Jan94: first version
44 ** 08Feb94: minor bugfix
45 */
46 
47 /* entry in RLE offset table */
48 typedef PNMFileTypeSGI::Reader::TabEntry TabEntry;
49 
50 typedef short ScanElem;
51 typedef ScanElem * ScanLine;
52 
53 /* prototypes */
54 static unsigned char get_byte ( istream* f );
55 static long get_big_long (istream *f);
56 static short get_big_short (istream *f);
57 static short get_byte_as_short (istream *f);
58 static int readerr (istream *f);
59 static void * xmalloc (int bytes);
60 #define MALLOC(n, type) (type *)xmalloc((n) * sizeof(type))
61 static const char * compression_name (char compr);
62 static void read_bytes (istream *ifp, int n, char *buf);
63 static bool read_header(istream *ifp, Header *head, const string &magic_number);
64 static TabEntry * read_table (istream *ifp, int tablen);
65 static void read_channel (istream *ifp, int xsize, int ysize,
66  int zsize, int bpc, TabEntry *table,
67  ScanElem *channel_data, long table_start,
68  int channel, int row);
69 static void rle_decompress (ScanElem *src, long srclen, ScanElem *dest, long destlen);
70 
71 #define WORSTCOMPR(x) (2*(x) + 2)
72 
73 #define MAXVAL_BYTE 255
74 #define MAXVAL_WORD 65535
75 
76 // This flag shouldn't really be a static global, but it's a little
77 // tricky to fix and it doesn't do any harm, since it only controls
78 // whether an error message is repeated.
79 static bool eof_err = false;
80 
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: PNMFileTypeSGI::Reader::Constructor
84 // Access: Public
85 // Description:
86 ////////////////////////////////////////////////////////////////////
87 PNMFileTypeSGI::Reader::
88 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
89  PNMReader(type, file, owns_file)
90 {
91  eof_err = false;
92  table = NULL;
93 
94  if (!read_magic_number(_file, magic_number, 4)) {
95  // No magic number. No image.
96  if (pnmimage_sgi_cat.is_debug()) {
97  pnmimage_sgi_cat.debug()
98  << "RGB file appears to be empty.\n";
99  }
100  _is_valid = false;
101  return;
102  }
103 
104  Header head;
105 
106  if (!::read_header(file, &head, magic_number)) {
107  _is_valid = false;
108  }
109 
110  long pixmax = (head.bpc == 1) ? MAXVAL_BYTE : MAXVAL_WORD;
111  if( pixmax > PNM_MAXMAXVAL ) {
112  pnmimage_sgi_cat.error()
113  << "Cannot read RGB image with maxval of " << pixmax
114  << "--largest allowable maxval is currently " << PNM_MAXMAXVAL << "\n";
115  _is_valid = false;
116  return;
117  }
118 
119  _maxval = (xelval)pixmax;
120 
121  table_start = file->tellg();
122  if( head.storage != STORAGE_VERBATIM )
123  table = read_table(file, head.ysize * head.zsize);
124 
125  _x_size = head.xsize;
126  _y_size = head.ysize;
127  _num_channels = min((int)head.zsize, 4);
128  bpc = head.bpc;
129 
130  current_row = _y_size - 1;
131 
132  if (_is_valid && pnmimage_sgi_cat.is_debug()) {
133  head.name[79] = '\0'; /* just to be safe */
134  pnmimage_sgi_cat.debug()
135  << "Read RGB image:\n"
136  << " raster size " << head.xsize << " x " << head.ysize
137  << ", " << head.zsize << " channels\n"
138  << " compression: " << (int)head.storage << " = "
139  << compression_name(head.storage) << "\n"
140  << " image name: " << head.name << "\n"
141  << " bpc: " << (int)head.bpc << " dimension: " << head.dimension << "\n"
142  << " pixmin: " << head.pixmin << " pixmax: " << head.pixmax
143  << " colormap: " << head.colormap << "\n";
144  }
145 }
146 
147 ////////////////////////////////////////////////////////////////////
148 // Function: PNMFileTypeSGI::Reader::Destructor
149 // Access: Public, Virtual
150 // Description:
151 ////////////////////////////////////////////////////////////////////
152 PNMFileTypeSGI::Reader::
153 ~Reader() {
154  if (table != NULL) {
155  free(table);
156  }
157 }
158 
159 ////////////////////////////////////////////////////////////////////
160 // Function: PNMFileTypeSGI::Reader::supports_read_row
161 // Access: Public, Virtual
162 // Description: Returns true if this particular PNMReader supports a
163 // streaming interface to reading the data: that is, it
164 // is capable of returning the data one row at a time,
165 // via repeated calls to read_row(). Returns false if
166 // the only way to read from this file is all at once,
167 // via read_data().
168 ////////////////////////////////////////////////////////////////////
169 bool PNMFileTypeSGI::Reader::
170 supports_read_row() const {
171  return true;
172 }
173 
174 ////////////////////////////////////////////////////////////////////
175 // Function: PNMFileTypeSGI::Reader::read_row
176 // Access: Public, Virtual
177 // Description: If supports_read_row(), above, returns true, this
178 // function may be called repeatedly to read the image,
179 // one horizontal row at a time, beginning from the top.
180 // Returns true if the row is successfully read, false
181 // if there is an error or end of file.
182 ////////////////////////////////////////////////////////////////////
183 bool PNMFileTypeSGI::Reader::
184 read_row(xel *row_data, xelval *alpha_data, int x_size, int y_size) {
185  if (!is_valid()) {
186  return false;
187  }
188  nassertr(current_row >= 0, false);
189 
190  ScanElem *red = (ScanElem *)alloca(x_size * sizeof(ScanElem));
191  ScanElem *grn = (ScanElem *)alloca(x_size * sizeof(ScanElem));
192  ScanElem *blu = (ScanElem *)alloca(x_size * sizeof(ScanElem));
193  ScanElem *alpha = (ScanElem *)alloca(x_size * sizeof(ScanElem));
194 
195  read_channel(_file, x_size, y_size, _num_channels, bpc, table, red,
196  table_start, 0, current_row);
197 
198  if (!is_grayscale()) {
199  read_channel(_file, x_size, y_size, _num_channels, bpc, table, grn,
200  table_start, 1, current_row);
201  read_channel(_file, x_size, y_size, _num_channels, bpc, table, blu,
202  table_start, 2, current_row);
203  }
204 
205  if (has_alpha()) {
206  read_channel(_file, x_size, y_size, _num_channels, bpc, table, alpha,
207  table_start, _num_channels - 1, current_row);
208  }
209 
210  for (int x = 0; x < x_size; x++) {
211  if (is_grayscale()) {
212  PPM_PUTB(row_data[x], (xelval)red[x]);
213  } else {
214  xelval r, g, b;
215  r = (xelval)red[x];
216  g = (xelval)grn[x];
217  b = (xelval)blu[x];
218  PPM_ASSIGN(row_data[x], r, g, b);
219  }
220 
221  if (has_alpha()) {
222  alpha_data[x] = (xelval)alpha[x];
223  }
224  }
225  current_row--;
226  return true;
227 }
228 
229 
230 
231 static bool
232 read_header(istream *ifp, Header *head, const string &magic_number) {
233  nassertr(magic_number.size() == 4, false);
234  head->magic =
235  ((unsigned char)magic_number[0] << 8) |
236  ((unsigned char)magic_number[1]);
237  head->storage = (unsigned char)magic_number[2];
238  head->bpc = (unsigned char)magic_number[3];
239  head->dimension = get_big_short(ifp);
240  head->xsize = get_big_short(ifp);
241  head->ysize = get_big_short(ifp);
242  head->zsize = get_big_short(ifp);
243  head->pixmin = get_big_long(ifp);
244  head->pixmax = get_big_long(ifp);
245  read_bytes(ifp, 4, head->dummy1);
246  read_bytes(ifp, 80, head->name);
247  head->colormap = get_big_long(ifp);
248  read_bytes(ifp, 404, head->dummy2);
249 
250  if (head->magic != SGI_MAGIC) {
251  pnmimage_sgi_cat.error()
252  << "Invalid magic number: not an SGI image file.\n";
253  return false;
254  }
255 
256  if (head->storage != 0 && head->storage != 1) {
257  pnmimage_sgi_cat.error()
258  << "Unknown compression type.\n";
259  return false;
260  }
261 
262  if (head->bpc < 1 || head->bpc > 2) {
263  pnmimage_sgi_cat.error()
264  << "Illegal precision value " << head->bpc << " (only 1-2 allowed)\n";
265  return false;
266  }
267 
268  // Actually, some old broken SGI image writers put garbage in this
269  // field, so just ignore it.
270  /*
271  if (head->colormap != CMAP_NORMAL) {
272  pnmimage_sgi_cat.error()
273  << "Unsupported non-normal pixel data (" << head->colormap << ")\n";
274  return false;
275  }
276  */
277 
278  /* adjust ysize/zsize to dimension, just to be sure */
279 
280  // On reflection, this is a bad idea. Ignore the number of
281  // dimensions, and take the xsize/ysize/zsize at face value. The
282  // table was written based on these numbers, after all; you can't
283  // just change them arbitrarily.
284 
285  /*
286  switch( head->dimension ) {
287  case 1:
288  head->ysize = 1;
289  break;
290  case 2:
291  head->zsize = 1;
292  break;
293  case 3:
294  switch( head->zsize ) {
295  case 1:
296  case 2:
297  head->dimension = 2;
298  break;
299  case 3:
300  case 4:
301  break;
302 
303  default:
304  pnmimage_sgi_cat.warning()
305  << "Using only first 4 channels of " << head->zsize
306  << "-channel image.\n";
307  head->zsize = 4;
308  break;
309  }
310  break;
311  default:
312  pnmimage_sgi_cat.error()
313  << "Illegal dimension value " << head->dimension
314  << " (only 1-3 allowed)\n";
315  return false;
316  }
317  */
318 
319  return true;
320 }
321 
322 
323 static TabEntry *
324 read_table(istream *ifp, int tablen) {
325  TabEntry *table;
326  int i;
327 
328  table = MALLOC(tablen, TabEntry);
329 
330  for( i = 0; i < tablen; i++ ) {
331  table[i].start = get_big_long(ifp);
332  }
333  for( i = 0; i < tablen; i++ ) {
334  table[i].length = get_big_long(ifp);
335  }
336 
337  return table;
338 }
339 
340 
341 
342 static void
343 read_channel(istream *ifp,
344  int xsize, int ysize, int, int bpc,
345  TabEntry *table,
346  ScanElem *channel_data, long table_start,
347  int channel, int row) {
348  ScanElem *temp = NULL;
349  int sgi_index, i;
350  long offset, length;
351 
352  short (*func)(istream *);
353  func = (bpc==1) ? get_byte_as_short : get_big_short;
354 
355  if ( table ) {
356  temp = (ScanElem *)alloca(WORSTCOMPR(xsize) * sizeof(ScanElem));
357  }
358 
359  sgi_index = channel * ysize + row;
360  if( table ) {
361  offset = table[sgi_index].start;
362  length = table[sgi_index].length;
363  if( bpc == 2 )
364  length /= 2; /* doc says length is in bytes, we are reading words */
365  if(!ifp->seekg(offset))
366  pm_error("seek error for offset %ld", offset);
367 
368  nassertv(length <= WORSTCOMPR(xsize));
369  for( i = 0; i < length; i++ )
370  temp[i] = (*func)(ifp);
371 
372  rle_decompress(temp, length, channel_data, xsize);
373  }
374  else {
375  offset = sgi_index * xsize + table_start;
376  if(!ifp->seekg(offset))
377  pm_error("seek error for offset %ld", offset);
378  for( i = 0; i < xsize; i++ )
379  channel_data[i] = (*func)(ifp);
380  }
381 }
382 
383 
384 
385 static void
386 rle_decompress(ScanElem *src,
387  long srcleft,
388  ScanElem *dest,
389  long destleft) {
390  int count;
391  unsigned char el;
392 
393  while( srcleft ) {
394  el = (unsigned char)(*src++ & 0xff);
395  --srcleft;
396  count = (int)(el & 0x7f);
397 
398  if( count == 0 )
399  return;
400  if( destleft < count )
401  pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count);
402  destleft -= count;
403  if( el & 0x80 ) {
404  if( srcleft < count )
405  pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count);
406  srcleft -= count;
407  while( count-- )
408  *dest++ = *src++;
409  }
410  else {
411  if( srcleft == 0 )
412  pm_error("RLE error: not enough data for replicate run");
413  while( count-- )
414  *dest++ = *src;
415  ++src;
416  --srcleft;
417  }
418  }
419  pm_error("RLE error: no terminating 0-byte");
420 }
421 
422 
423 /* basic I/O functions, taken from ilbmtoppm.c */
424 
425 static short
426 get_big_short(istream *ifp) {
427  short s;
428 
429  if( pm_readbigshort(ifp, &s) == -1 )
430  s = readerr(ifp);
431 
432  return s;
433 }
434 
435 static long
436 get_big_long(istream *ifp) {
437  long l;
438 
439  if( pm_readbiglong(ifp, &l) == -1 )
440  l = readerr(ifp);
441 
442  return l;
443 }
444 
445 static unsigned char
446 get_byte(istream *ifp) {
447  int i;
448 
449  i = ifp->get();
450  if( i == EOF )
451  i = readerr(ifp);
452 
453  return (unsigned char) i;
454 }
455 
456 
457 static int
458 readerr(istream *f) {
459  if (!eof_err) {
460  if (!f->eof()) {
461  pnmimage_sgi_cat.warning()
462  << "Read error on file.\n";
463  } else {
464  pnmimage_sgi_cat.warning()
465  << "Premature EOF on file.\n";
466  }
467  eof_err = true;
468  }
469 
470  return 0;
471 }
472 
473 
474 static void
475 read_bytes(istream *ifp,
476  int n,
477  char *buf) {
478  int r;
479 
480  ifp->read(buf, n);
481  r = ifp->gcount();
482  if( r != n ) {
483  readerr(ifp);
484  memset(buf+r, 0, n-r);
485  }
487 }
488 
489 
490 static short
491 get_byte_as_short(istream *ifp) {
492  return (short)get_byte(ifp);
493 }
494 
495 
496 static void *
497 xmalloc(int bytes) {
498  void *mem;
499 
500  if( bytes == 0 )
501  return NULL;
502 
503  mem = malloc(bytes);
504  if( mem == NULL )
505  pm_error("out of memory allocating %d bytes", bytes);
506  return mem;
507 }
508 
509 static const char *
510 compression_name(char compr) {
511  switch( compr ) {
512  case STORAGE_VERBATIM:
513  return "none";
514  case STORAGE_RLE:
515  return "RLE";
516  default:
517  return "unknown";
518  }
519 }
520 
521 #endif // HAVE_SGI_RGB
Definition: sgi.h:18
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:35
static bool read_magic_number(istream *file, string &magic_number, int num_bytes)
Ensures that the first n bytes of the file are read into magic_number.
bool read_header(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true)
Opens up the image file and tries to read its header information to determine its size...
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:263
This is an abstract base class that defines the interface for reading image files of various types...
Definition: pnmReader.h:31