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
pnmImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pm_error
void pm_error(const char *format,...)
Outputs the given printf-style message to the user and terminates messily.
Definition: pnmimage_base.cxx:54
pnmWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmFileTypeSGI.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
sgi.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_pnmimagetypes.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pixel
Definition: pnmimage_base.h:41
PNMWriter
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
PNMFileType
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32