Panda3D
Loading...
Searching...
No Matches
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
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
52using std::ostream;
53
54inline void
55put_byte(ostream *out_file, unsigned char b) {
56 out_file->put(b);
57}
58
59static void
60put_big_short(ostream *out_file, short s) {
61 if ( pm_writebigshort( out_file, s ) == -1 )
62 pm_error( "write error" );
63}
64
65
66static void
67put_big_long(ostream *out_file, long l) {
68 if ( pm_writebiglong( out_file, l ) == -1 )
69 pm_error( "write error" );
70}
71
72
73static void
74put_short_as_byte(ostream *out_file, short s) {
75 put_byte(out_file, (unsigned char)s);
76}
77
78
79/**
80 *
81 */
82PNMFileTypeSGI::Writer::
83Writer(PNMFileType *type, ostream *file, bool owns_file) :
84 PNMWriter(type, file, owns_file)
85{
86}
87
88/**
89 *
90 */
91PNMFileTypeSGI::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 */
107bool PNMFileTypeSGI::Writer::
108supports_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 */
122bool PNMFileTypeSGI::Writer::
123write_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 */
185bool PNMFileTypeSGI::Writer::
186write_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
205void PNMFileTypeSGI::Writer::
206write_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
230void PNMFileTypeSGI::Writer::
231write_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
243void PNMFileTypeSGI::Writer::
244write_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
258void PNMFileTypeSGI::Writer::
259build_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
306PNMFileTypeSGI::Writer::ScanElem *PNMFileTypeSGI::Writer::
307compress(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/*
330slightly modified RLE algorithm from ppmtoilbm.c
331written by Robert A. Knop (rknop@mop.caltech.edu)
332*/
333int PNMFileTypeSGI::Writer::
334rle_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
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition pnmFileType.h:32
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.