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