Panda3D
pnmFileTypeSGIWriter.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 pnmFileTypeSGIWriter.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 "pnmWriter.h"
23 
24 // Much code in this file originally came from Netpbm, specifically
25 // pnmtosgi.c. It has since been fairly heavily modified.
26 
27 /* pnmtosgi.c - convert portable anymap to SGI image
28 **
29 ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
30 **
31 ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
32 ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
33 **
34 ** Permission to use, copy, modify, and distribute this software and its
35 ** documentation for any purpose and without fee is hereby granted, provided
36 ** that the above copyright notice appear in all copies and that both that
37 ** copyright notice and this permission notice appear in supporting
38 ** documentation. This software is provided "as is" without express or
39 ** implied warranty.
40 **
41 ** 29Jan94: first version
42 */
43 
44 
45 
46 #define WORSTCOMPR(x) (2*(x) + 2)
47 
48 
49 #define MAXVAL_BYTE 255
50 #define MAXVAL_WORD 65535
51 
52 using std::ostream;
53 
54 inline void
55 put_byte(ostream *out_file, unsigned char b) {
56  out_file->put(b);
57 }
58 
59 static void
60 put_big_short(ostream *out_file, short s) {
61  if ( pm_writebigshort( out_file, s ) == -1 )
62  pm_error( "write error" );
63 }
64 
65 
66 static void
67 put_big_long(ostream *out_file, long l) {
68  if ( pm_writebiglong( out_file, l ) == -1 )
69  pm_error( "write error" );
70 }
71 
72 
73 static void
74 put_short_as_byte(ostream *out_file, short s) {
75  put_byte(out_file, (unsigned char)s);
76 }
77 
78 
79 /**
80  *
81  */
82 PNMFileTypeSGI::Writer::
83 Writer(PNMFileType *type, ostream *file, bool owns_file) :
84  PNMWriter(type, file, owns_file)
85 {
86 }
87 
88 /**
89  *
90  */
91 PNMFileTypeSGI::Writer::
92 ~Writer() {
93  if (table!=nullptr) {
94  // Rewrite the table with the correct values in it.
95  _file->seekp(table_start);
96  write_table();
97  PANDA_FREE_ARRAY(table);
98  }
99 }
100 
101 /**
102  * Returns true if this particular PNMWriter supports a streaming interface to
103  * writing the data: that is, it is capable of writing the image one row at a
104  * time, via repeated calls to write_row(). Returns false if the only way to
105  * write from this file is all at once, via write_data().
106  */
107 bool PNMFileTypeSGI::Writer::
108 supports_write_row() const {
109  return true;
110 }
111 
112 /**
113  * If supports_write_row(), above, returns true, this function may be called
114  * to write out the image header in preparation to writing out the image data
115  * one row at a time. Returns true if the header is successfully written,
116  * false if there is an error.
117  *
118  * It is the user's responsibility to fill in the header data via calls to
119  * set_x_size(), set_num_channels(), etc., or copy_header_from(), before
120  * calling write_header().
121  */
122 bool PNMFileTypeSGI::Writer::
123 write_header() {
124  table = nullptr;
125 
126  switch (_num_channels) {
127  case 1:
128  dimensions = 2;
129  break;
130 
131  case 2:
132  case 3:
133  case 4:
134  dimensions = 3;
135  break;
136 
137  default:
138  nassert_raise("unexpected channel count");
139  return false;
140  }
141 
142  // For some reason, we have problems with SGI image files whose pixmax value
143  // is not 255 or 65535. So, we'll round up when writing.
144  if( _maxval <= MAXVAL_BYTE ) {
145  bpc = 1;
146  new_maxval = MAXVAL_BYTE;
147  } else if( _maxval <= MAXVAL_WORD ) {
148  bpc = 2;
149  new_maxval = MAXVAL_WORD;
150  } else {
151  return false;
152  }
153 
154  if( sgi_storage_type != STORAGE_VERBATIM ) {
155  table = (TabEntry *)PANDA_MALLOC_ARRAY(_num_channels * _y_size * sizeof(TabEntry));
156  memset(table, 0, _num_channels * _y_size * sizeof(TabEntry));
157  }
158 
159  write_rgb_header(sgi_imagename.c_str());
160 
161  if (table!=nullptr) {
162  table_start = _file->tellp();
163 
164  // The first time we write the table, it has zeroes. We'll correct this
165  // later.
166  write_table();
167  }
168 
169  current_row = _y_size - 1;
170  return true;
171 }
172 
173 
174 /**
175  * If supports_write_row(), above, returns true, this function may be called
176  * repeatedly to write the image, one horizontal row at a time, beginning from
177  * the top. Returns true if the row is successfully written, false if there
178  * is an error.
179  *
180  * You must first call write_header() before writing the individual rows. It
181  * is also important to delete the PNMWriter class after successfully writing
182  * the last row. Failing to do this may result in some data not getting
183  * flushed!
184  */
185 bool PNMFileTypeSGI::Writer::
186 write_row(xel *row_data, xelval *alpha_data) {
187  ScanLine channel[4];
188 
189  build_scanline(channel, row_data, alpha_data);
190 
191  if( bpc == 1 )
192  write_channels(channel, put_short_as_byte);
193  else
194  write_channels(channel, put_big_short);
195 
196  for (int i = 0; i < _num_channels; i++) {
197  PANDA_FREE_ARRAY(channel[i].data);
198  }
199 
200  current_row--;
201  return true;
202 }
203 
204 
205 void PNMFileTypeSGI::Writer::
206 write_rgb_header(const char *imagename) {
207  int i;
208 
209  put_big_short(_file, SGI_MAGIC);
210  put_byte(_file, sgi_storage_type);
211  put_byte(_file, (char)bpc);
212  put_big_short(_file, dimensions);
213  put_big_short(_file, _x_size);
214  put_big_short(_file, _y_size);
215  put_big_short(_file, _num_channels);
216  put_big_long(_file, 0); /* PIXMIN */
217  put_big_long(_file, new_maxval); /* PIXMAX */
218  for( i = 0; i < 4; i++ )
219  put_byte(_file, 0);
220  for( i = 0; i < 79 && imagename[i] != '\0'; i++ )
221  put_byte(_file, imagename[i]);
222  for(; i < 80; i++ )
223  put_byte(_file, 0);
224  put_big_long(_file, CMAP_NORMAL);
225  for( i = 0; i < 404; i++ )
226  put_byte(_file, 0);
227 }
228 
229 
230 void PNMFileTypeSGI::Writer::
231 write_table() {
232  int i;
233  int tabsize = _y_size*_num_channels;
234 
235  for( i = 0; i < tabsize; i++ ) {
236  put_big_long(_file, table[i].start);
237  }
238  for( i = 0; i < tabsize; i++ )
239  put_big_long(_file, table[i].length);
240 }
241 
242 
243 void PNMFileTypeSGI::Writer::
244 write_channels(ScanLine channel[], void (*put)(ostream *, short)) {
245  int i, col;
246 
247  for( i = 0; i < _num_channels; i++ ) {
248  Table(i).start = _file->tellp();
249  Table(i).length = channel[i].length * bpc;
250 
251  for( col = 0; col < channel[i].length; col++ ) {
252  (*put)(_file, channel[i].data[col]);
253  }
254  }
255 }
256 
257 
258 void PNMFileTypeSGI::Writer::
259 build_scanline(ScanLine output[], xel *row_data, xelval *alpha_data) {
260  int col;
261  ScanElem *temp;
262 
263  if( sgi_storage_type != STORAGE_VERBATIM ) {
264  rletemp = (ScanElem *)alloca(WORSTCOMPR(_x_size) * sizeof(ScanElem));
265  }
266  temp = (ScanElem *)PANDA_MALLOC_ARRAY(_x_size * sizeof(ScanElem));
267 
268  if( _num_channels <= 2 ) {
269  for( col = 0; col < _x_size; col++ )
270  temp[col] = (ScanElem)
271  (new_maxval * PPM_GETB(row_data[col]) / _maxval);
272  temp = compress(temp, output[0]);
273 
274  if (_num_channels == 2) {
275  for( col = 0; col < _x_size; col++ )
276  temp[col] = (ScanElem)
277  (new_maxval * alpha_data[col] / _maxval);
278  temp = compress(temp, output[1]);
279  }
280 
281  } else {
282  for( col = 0; col < _x_size; col++ )
283  temp[col] = (ScanElem)
284  (new_maxval * PPM_GETR(row_data[col]) / _maxval);
285  temp = compress(temp, output[0]);
286  for( col = 0; col < _x_size; col++ )
287  temp[col] = (ScanElem)
288  (new_maxval * PPM_GETG(row_data[col]) / _maxval);
289  temp = compress(temp, output[1]);
290  for( col = 0; col < _x_size; col++ )
291  temp[col] = (ScanElem)
292  (new_maxval * PPM_GETB(row_data[col]) / _maxval);
293  temp = compress(temp, output[2]);
294  if (_num_channels == 4) {
295  for( col = 0; col < _x_size; col++ )
296  temp[col] = (ScanElem)
297  (new_maxval * alpha_data[col] / _maxval);
298  temp = compress(temp, output[3]);
299  }
300  }
301 
302  PANDA_FREE_ARRAY(temp);
303 }
304 
305 
306 PNMFileTypeSGI::Writer::ScanElem *PNMFileTypeSGI::Writer::
307 compress(ScanElem *temp, ScanLine &output) {
308  int len;
309 
310  switch( sgi_storage_type ) {
311  case STORAGE_VERBATIM:
312  output.length = _x_size;
313  output.data = temp;
314  temp = (ScanElem *)PANDA_MALLOC_ARRAY(_x_size * sizeof(ScanElem));
315  break;
316  case STORAGE_RLE:
317  len = rle_compress(temp, _x_size); /* writes result into rletemp */
318  output.length = len;
319  output.data = (ScanElem *)PANDA_MALLOC_ARRAY(len * sizeof(ScanElem));
320  memcpy(output.data, rletemp, len * sizeof(ScanElem));
321  break;
322  default:
323  pm_error("unknown storage type - can\'t happen");
324  }
325  return temp;
326 }
327 
328 
329 /*
330 slightly modified RLE algorithm from ppmtoilbm.c
331 written by Robert A. Knop (rknop@mop.caltech.edu)
332 */
333 int PNMFileTypeSGI::Writer::
334 rle_compress(ScanElem *inbuf, int size) {
335  int in, out, hold, count;
336  ScanElem *outbuf = rletemp;
337 
338  in=out=0;
339  while( in<size ) {
340  if( (in<size-1) && (inbuf[in]==inbuf[in+1]) ) { /*Begin replicate run*/
341  for( count=0,hold=in; in<size && inbuf[in]==inbuf[hold] && count<127; in++,count++)
342  ;
343  outbuf[out++]=(ScanElem)(count);
344  outbuf[out++]=inbuf[hold];
345  }
346  else { /*Do a literal run*/
347  hold=out; out++; count=0;
348  while( ((in>=size-2)&&(in<size)) || ((in<size-2) && ((inbuf[in]!=inbuf[in+1])||(inbuf[in]!=inbuf[in+2]))) ) {
349  outbuf[out++]=inbuf[in++];
350  if( ++count>=127 )
351  break;
352  }
353  outbuf[hold]=(ScanElem)(count | 0x80);
354  }
355  }
356  outbuf[out++] = (ScanElem)0; /* terminator */
357  return(out);
358 }
359 
360 #endif // HAVE_SGI_RGB
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
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 writing image files of various types.
Definition: pnmWriter.h:27
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.