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