Panda3D
|
00001 // Filename: pnmFileTypeSGIWriter.cxx 00002 // Created by: drose (17Jun00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "pnmFileTypeSGI.h" 00016 00017 #ifdef HAVE_SGI_RGB 00018 00019 #include "config_pnmimagetypes.h" 00020 #include "sgi.h" 00021 00022 #include "pnmImage.h" 00023 #include "pnmWriter.h" 00024 00025 // Much code in this file originally came from Netpbm, specifically 00026 // pnmtosgi.c. It has since been fairly heavily modified. 00027 00028 /* pnmtosgi.c - convert portable anymap to SGI image 00029 ** 00030 ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) 00031 ** 00032 ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp) 00033 ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC 00034 ** 00035 ** Permission to use, copy, modify, and distribute this software and its 00036 ** documentation for any purpose and without fee is hereby granted, provided 00037 ** that the above copyright notice appear in all copies and that both that 00038 ** copyright notice and this permission notice appear in supporting 00039 ** documentation. This software is provided "as is" without express or 00040 ** implied warranty. 00041 ** 00042 ** 29Jan94: first version 00043 */ 00044 00045 00046 00047 #define WORSTCOMPR(x) (2*(x) + 2) 00048 00049 00050 #define MAXVAL_BYTE 255 00051 #define MAXVAL_WORD 65535 00052 00053 inline void 00054 put_byte(ostream *out_file, unsigned char b) { 00055 out_file->put(b); 00056 } 00057 00058 static void 00059 put_big_short(ostream *out_file, short s) { 00060 if ( pm_writebigshort( out_file, s ) == -1 ) 00061 pm_error( "write error" ); 00062 } 00063 00064 00065 static void 00066 put_big_long(ostream *out_file, long l) { 00067 if ( pm_writebiglong( out_file, l ) == -1 ) 00068 pm_error( "write error" ); 00069 } 00070 00071 00072 static void 00073 put_short_as_byte(ostream *out_file, short s) { 00074 put_byte(out_file, (unsigned char)s); 00075 } 00076 00077 00078 //////////////////////////////////////////////////////////////////// 00079 // Function: PNMFileTypeSGI::Writer::Constructor 00080 // Access: Public 00081 // Description: 00082 //////////////////////////////////////////////////////////////////// 00083 PNMFileTypeSGI::Writer:: 00084 Writer(PNMFileType *type, ostream *file, bool owns_file) : 00085 PNMWriter(type, file, owns_file) 00086 { 00087 } 00088 00089 //////////////////////////////////////////////////////////////////// 00090 // Function: PNMFileTypeSGI::Writer::Destructor 00091 // Access: Public, Virtual 00092 // Description: 00093 //////////////////////////////////////////////////////////////////// 00094 PNMFileTypeSGI::Writer:: 00095 ~Writer() { 00096 if (table!=NULL) { 00097 // Rewrite the table with the correct values in it. 00098 _file->seekp(table_start); 00099 write_table(); 00100 PANDA_FREE_ARRAY(table); 00101 } 00102 } 00103 00104 //////////////////////////////////////////////////////////////////// 00105 // Function: PNMFileTypeSGI::Writer::supports_write_row 00106 // Access: Public, Virtual 00107 // Description: Returns true if this particular PNMWriter supports a 00108 // streaming interface to writing the data: that is, it 00109 // is capable of writing the image one row at a time, 00110 // via repeated calls to write_row(). Returns false if 00111 // the only way to write from this file is all at once, 00112 // via write_data(). 00113 //////////////////////////////////////////////////////////////////// 00114 bool PNMFileTypeSGI::Writer:: 00115 supports_write_row() const { 00116 return true; 00117 } 00118 00119 //////////////////////////////////////////////////////////////////// 00120 // Function: PNMFileTypeSGI::Writer::write_header 00121 // Access: Public, Virtual 00122 // Description: If supports_write_row(), above, returns true, this 00123 // function may be called to write out the image header 00124 // in preparation to writing out the image data one row 00125 // at a time. Returns true if the header is 00126 // successfully written, false if there is an error. 00127 // 00128 // It is the user's responsibility to fill in the header 00129 // data via calls to set_x_size(), set_num_channels(), 00130 // etc., or copy_header_from(), before calling 00131 // write_header(). 00132 //////////////////////////////////////////////////////////////////// 00133 bool PNMFileTypeSGI::Writer:: 00134 write_header() { 00135 table = NULL; 00136 00137 switch (_num_channels) { 00138 case 1: 00139 dimensions = 2; 00140 break; 00141 00142 case 2: 00143 case 3: 00144 case 4: 00145 dimensions = 3; 00146 break; 00147 00148 default: 00149 nassertr(false, false); 00150 } 00151 00152 // For some reason, we have problems with SGI image files whose pixmax value 00153 // is not 255 or 65535. So, we'll round up when writing. 00154 if( _maxval <= MAXVAL_BYTE ) { 00155 bpc = 1; 00156 new_maxval = MAXVAL_BYTE; 00157 } else if( _maxval <= MAXVAL_WORD ) { 00158 bpc = 2; 00159 new_maxval = MAXVAL_WORD; 00160 } else { 00161 return false; 00162 } 00163 00164 if( sgi_storage_type != STORAGE_VERBATIM ) { 00165 table = (TabEntry *)PANDA_MALLOC_ARRAY(_num_channels * _y_size * sizeof(TabEntry)); 00166 memset(table, 0, _num_channels * _y_size * sizeof(TabEntry)); 00167 } 00168 00169 write_rgb_header(sgi_imagename.c_str()); 00170 00171 if (table!=NULL) { 00172 table_start = _file->tellp(); 00173 00174 // The first time we write the table, it has zeroes. We'll correct 00175 // this later. 00176 write_table(); 00177 } 00178 00179 current_row = _y_size - 1; 00180 return true; 00181 } 00182 00183 00184 //////////////////////////////////////////////////////////////////// 00185 // Function: PNMFileTypeSGI::Writer::write_row 00186 // Access: Public, Virtual 00187 // Description: If supports_write_row(), above, returns true, this 00188 // function may be called repeatedly to write the image, 00189 // one horizontal row at a time, beginning from the top. 00190 // Returns true if the row is successfully written, 00191 // false if there is an error. 00192 // 00193 // You must first call write_header() before writing the 00194 // individual rows. It is also important to delete the 00195 // PNMWriter class after successfully writing the last 00196 // row. Failing to do this may result in some data not 00197 // getting flushed! 00198 //////////////////////////////////////////////////////////////////// 00199 bool PNMFileTypeSGI::Writer:: 00200 write_row(xel *row_data, xelval *alpha_data) { 00201 ScanLine channel[4]; 00202 00203 build_scanline(channel, row_data, alpha_data); 00204 00205 if( bpc == 1 ) 00206 write_channels(channel, put_short_as_byte); 00207 else 00208 write_channels(channel, put_big_short); 00209 00210 for (int i = 0; i < _num_channels; i++) { 00211 PANDA_FREE_ARRAY(channel[i].data); 00212 } 00213 00214 current_row--; 00215 return true; 00216 } 00217 00218 00219 void PNMFileTypeSGI::Writer:: 00220 write_rgb_header(const char *imagename) { 00221 int i; 00222 00223 put_big_short(_file, SGI_MAGIC); 00224 put_byte(_file, sgi_storage_type); 00225 put_byte(_file, (char)bpc); 00226 put_big_short(_file, dimensions); 00227 put_big_short(_file, _x_size); 00228 put_big_short(_file, _y_size); 00229 put_big_short(_file, _num_channels); 00230 put_big_long(_file, 0); /* PIXMIN */ 00231 put_big_long(_file, new_maxval); /* PIXMAX */ 00232 for( i = 0; i < 4; i++ ) 00233 put_byte(_file, 0); 00234 for( i = 0; i < 79 && imagename[i] != '\0'; i++ ) 00235 put_byte(_file, imagename[i]); 00236 for(; i < 80; i++ ) 00237 put_byte(_file, 0); 00238 put_big_long(_file, CMAP_NORMAL); 00239 for( i = 0; i < 404; i++ ) 00240 put_byte(_file, 0); 00241 } 00242 00243 00244 void PNMFileTypeSGI::Writer:: 00245 write_table() { 00246 int i; 00247 int tabsize = _y_size*_num_channels; 00248 00249 for( i = 0; i < tabsize; i++ ) { 00250 put_big_long(_file, table[i].start); 00251 } 00252 for( i = 0; i < tabsize; i++ ) 00253 put_big_long(_file, table[i].length); 00254 } 00255 00256 00257 void PNMFileTypeSGI::Writer:: 00258 write_channels(ScanLine channel[], void (*put)(ostream *, short)) { 00259 int i, col; 00260 00261 for( i = 0; i < _num_channels; i++ ) { 00262 Table(i).start = _file->tellp(); 00263 Table(i).length = channel[i].length * bpc; 00264 00265 for( col = 0; col < channel[i].length; col++ ) { 00266 (*put)(_file, channel[i].data[col]); 00267 } 00268 } 00269 } 00270 00271 00272 void PNMFileTypeSGI::Writer:: 00273 build_scanline(ScanLine output[], xel *row_data, xelval *alpha_data) { 00274 int col; 00275 ScanElem *temp; 00276 00277 if( sgi_storage_type != STORAGE_VERBATIM ) { 00278 rletemp = (ScanElem *)alloca(WORSTCOMPR(_x_size) * sizeof(ScanElem)); 00279 } 00280 temp = (ScanElem *)PANDA_MALLOC_ARRAY(_x_size * sizeof(ScanElem)); 00281 00282 if( _num_channels <= 2 ) { 00283 for( col = 0; col < _x_size; col++ ) 00284 temp[col] = (ScanElem) 00285 (new_maxval * PPM_GETB(row_data[col]) / _maxval); 00286 temp = compress(temp, output[0]); 00287 00288 if (_num_channels == 2) { 00289 for( col = 0; col < _x_size; col++ ) 00290 temp[col] = (ScanElem) 00291 (new_maxval * alpha_data[col] / _maxval); 00292 temp = compress(temp, output[1]); 00293 } 00294 00295 } else { 00296 for( col = 0; col < _x_size; col++ ) 00297 temp[col] = (ScanElem) 00298 (new_maxval * PPM_GETR(row_data[col]) / _maxval); 00299 temp = compress(temp, output[0]); 00300 for( col = 0; col < _x_size; col++ ) 00301 temp[col] = (ScanElem) 00302 (new_maxval * PPM_GETG(row_data[col]) / _maxval); 00303 temp = compress(temp, output[1]); 00304 for( col = 0; col < _x_size; col++ ) 00305 temp[col] = (ScanElem) 00306 (new_maxval * PPM_GETB(row_data[col]) / _maxval); 00307 temp = compress(temp, output[2]); 00308 if (_num_channels == 4) { 00309 for( col = 0; col < _x_size; col++ ) 00310 temp[col] = (ScanElem) 00311 (new_maxval * alpha_data[col] / _maxval); 00312 temp = compress(temp, output[3]); 00313 } 00314 } 00315 00316 PANDA_FREE_ARRAY(temp); 00317 } 00318 00319 00320 PNMFileTypeSGI::Writer::ScanElem *PNMFileTypeSGI::Writer:: 00321 compress(ScanElem *temp, ScanLine &output) { 00322 int len; 00323 00324 switch( sgi_storage_type ) { 00325 case STORAGE_VERBATIM: 00326 output.length = _x_size; 00327 output.data = temp; 00328 temp = (ScanElem *)PANDA_MALLOC_ARRAY(_x_size * sizeof(ScanElem)); 00329 break; 00330 case STORAGE_RLE: 00331 len = rle_compress(temp, _x_size); /* writes result into rletemp */ 00332 output.length = len; 00333 output.data = (ScanElem *)PANDA_MALLOC_ARRAY(len * sizeof(ScanElem)); 00334 memcpy(output.data, rletemp, len * sizeof(ScanElem)); 00335 break; 00336 default: 00337 pm_error("unknown storage type - can\'t happen"); 00338 } 00339 return temp; 00340 } 00341 00342 00343 /* 00344 slightly modified RLE algorithm from ppmtoilbm.c 00345 written by Robert A. Knop (rknop@mop.caltech.edu) 00346 */ 00347 int PNMFileTypeSGI::Writer:: 00348 rle_compress(ScanElem *inbuf, int size) { 00349 int in, out, hold, count; 00350 ScanElem *outbuf = rletemp; 00351 00352 in=out=0; 00353 while( in<size ) { 00354 if( (in<size-1) && (inbuf[in]==inbuf[in+1]) ) { /*Begin replicate run*/ 00355 for( count=0,hold=in; in<size && inbuf[in]==inbuf[hold] && count<127; in++,count++) 00356 ; 00357 outbuf[out++]=(ScanElem)(count); 00358 outbuf[out++]=inbuf[hold]; 00359 } 00360 else { /*Do a literal run*/ 00361 hold=out; out++; count=0; 00362 while( ((in>=size-2)&&(in<size)) || ((in<size-2) && ((inbuf[in]!=inbuf[in+1])||(inbuf[in]!=inbuf[in+2]))) ) { 00363 outbuf[out++]=inbuf[in++]; 00364 if( ++count>=127 ) 00365 break; 00366 } 00367 outbuf[hold]=(ScanElem)(count | 0x80); 00368 } 00369 } 00370 outbuf[out++] = (ScanElem)0; /* terminator */ 00371 return(out); 00372 } 00373 00374 #endif // HAVE_SGI_RGB