gKit2 light
Loading...
Searching...
No Matches
rgbe.cpp
1/* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE.
2 * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY,
3 * IT IS STRICTLY USE AT YOUR OWN RISK. */
4
5#include <cmath>
6#include <cstdio>
7#include <cstdlib>
8#include <cstring>
9#include <ctype.h>
10
11#include "rgbe.h"
12
13
14/* This file contains code to read and write four byte rgbe file format
15 developed by Greg Ward. It handles the conversions between rgbe and
16 pixels consisting of floats. The data is assumed to be an array of floats.
17 By default there are three floats per pixel in the order red, green, blue.
18 (RGBE_DATA_??? values control this.) Only the mimimal header reading and
19 writing is implemented. Each routine does error checking and will return
20 a status value as defined below. This code is intended as a skeleton so
21 feel free to modify it to suit your needs.
22
23 (Place notice here if you modified the code.)
24 posted to http://www.graphics.cornell.edu/~bjw/
25 written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95
26 based on code written by Greg Ward
27*/
28
29#ifdef _CPLUSPLUS
30/* define if your compiler understands inline commands */
31#define INLINE inline
32#else
33#define INLINE
34#endif
35
36/* offsets to red, green, and blue components in a data (float) pixel */
37#define RGBE_DATA_RED 0
38#define RGBE_DATA_GREEN 1
39#define RGBE_DATA_BLUE 2
40/* number of floats per pixel */
41#define RGBE_DATA_SIZE 3
42
43enum rgbe_error_codes
44{
45 rgbe_read_error,
46 rgbe_write_error,
47 rgbe_format_error,
48 rgbe_memory_error
49};
50
51/* default error routine. change this to change error handling */
52static int rgbe_error( const int rgbe_error_code, const char *msg )
53{
54 switch ( rgbe_error_code )
55 {
56 case rgbe_read_error:
57 perror( "RGBE read error" );
58 break;
59 case rgbe_write_error:
60 perror( "RGBE write error" );
61 break;
62 case rgbe_format_error:
63 fprintf( stderr, "RGBE bad file format: %s\n", msg );
64 break;
65 default:
66 case rgbe_memory_error:
67 fprintf( stderr, "RGBE error: %s\n", msg );
68 }
69
70 return RGBE_RETURN_FAILURE;
71}
72
73/* standard conversion from float pixels to rgbe pixels */
74/* note: you can remove the "inline"s if your compiler complains about it */
75static INLINE void
76float2rgbe( unsigned char rgbe[4], float red, float green, float blue )
77{
78 float v;
79 int e;
80
81 v = red;
82 if ( green > v ) v = green;
83 if ( blue > v ) v = blue;
84 if ( v < 1e-32 )
85 {
86 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
87 }
88 else
89 {
90 v = std::frexp( v, &e ) * 256.0 / v;
91 rgbe[0] = ( unsigned char )( red * v );
92 rgbe[1] = ( unsigned char )( green * v );
93 rgbe[2] = ( unsigned char )( blue * v );
94 rgbe[3] = ( unsigned char )( e + 128 );
95 }
96}
97
98/* standard conversion from rgbe to float pixels */
99/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
100/* in the range [0,1] to map back into the range [0,1]. */
101static INLINE void
102rgbe2float( float *red, float *green, float *blue, unsigned char rgbe[4] )
103{
104 float f;
105
106 if ( rgbe[3] ) /*nonzero pixel*/
107 {
108 f = std::ldexp( 1.0, rgbe[3] - ( int )( 128 + 8 ) );
109 *red = rgbe[0] * f;
110 *green = rgbe[1] * f;
111 *blue = rgbe[2] * f;
112 }
113 else
114 *red = *green = *blue = 0.0;
115}
116
117/* default minimal header. modify if you want more information in header */
118int RGBE_WriteHeader( FILE *fp, const int width, const int height, const rgbe_header_info *info )
119{
120 const char *programtype = "RGBE";
121
122 if ( info && ( info->valid & RGBE_VALID_PROGRAMTYPE ) )
123 programtype = info->programtype;
124 if ( fprintf( fp, "#?%s\n", programtype ) < 0 )
125 return rgbe_error( rgbe_write_error, NULL );
126 /* The #? is to identify file type, the programtype is optional. */
127 if ( info && ( info->valid & RGBE_VALID_GAMMA ) )
128 {
129 if ( fprintf( fp, "GAMMA=%g\n", info->gamma ) < 0 )
130 return rgbe_error( rgbe_write_error, NULL );
131 }
132 if ( info && ( info->valid & RGBE_VALID_EXPOSURE ) )
133 {
134 if ( fprintf( fp, "EXPOSURE=%g\n", info->exposure ) < 0 )
135 return rgbe_error( rgbe_write_error, NULL );
136 }
137 if ( fprintf( fp, "FORMAT=32-bit_rle_rgbe\n\n" ) < 0 )
138 return rgbe_error( rgbe_write_error, NULL );
139 if ( fprintf( fp, "-Y %d +X %d\n", height, width ) < 0 )
140 return rgbe_error( rgbe_write_error, NULL );
141 return RGBE_RETURN_SUCCESS;
142}
143
144/* minimal header reading. modify if you want to parse more information */
145int RGBE_ReadHeader( FILE *fp, int *width, int *height, rgbe_header_info *info )
146{
147 char buf[128];
148 int found_format;
149 float tempf;
150 int i;
151 char sw[4], sh[4];
152
153 found_format = 0;
154
155 if ( info )
156 {
157 info->valid = 0;
158 info->programtype[0] = 0;
159 info->gamma = info->exposure = 1.0;
160 }
161 if ( fgets( buf, sizeof( buf ), fp ) == NULL )
162 return rgbe_error( rgbe_read_error, NULL );
163
164 if (( buf[0] != '#' ) || ( buf[1] != '?' ) )
165 {
166 /* if you want to require the magic token then uncomment the next line */
167 /*return rgbe_error(rgbe_format_error,"bad initial token"); */
168 }
169 else if ( info )
170 {
171 info->valid |= RGBE_VALID_PROGRAMTYPE;
172 for ( i = 0; i < (int) sizeof( info->programtype ) - 1; i++ )
173 {
174 if (( buf[i+2] == 0 ) || isspace( buf[i+2] ) )
175 break;
176 info->programtype[i] = buf[i+2];
177 }
178 info->programtype[i] = 0;
179
180 if ( fgets( buf, sizeof( buf ), fp ) == 0 )
181 return rgbe_error( rgbe_read_error, NULL );
182 }
183
184 for ( ;; )
185 {
186 if ( buf[0] == 0 || buf[0] == '\r' || buf[0] == '\n' )
187 break;
188 // return rgbe_error(rgbe_format_error, "no FORMAT specifier found");
189
190 else if ( strncmp( buf, "FORMAT=32-bit_rle_rgbe", strlen( "FORMAT=32-bit_rle_rgbe" ) ) == 0 )
191 found_format = 1;
192 // break;
193
194 else if ( info && ( sscanf( buf, "GAMMA=%g", &tempf ) == 1 ) )
195 {
196 info->gamma = tempf;
197 info->valid |= RGBE_VALID_GAMMA;
198 }
199
200 else if ( info && ( sscanf( buf, "EXPOSURE=%g", &tempf ) == 1 ) )
201 {
202 info->exposure = tempf;
203 info->valid |= RGBE_VALID_EXPOSURE;
204 }
205
206 if ( fgets( buf, sizeof( buf ), fp ) == 0 )
207 return rgbe_error( rgbe_read_error, NULL );
208 }
209
210 if ( found_format == 0 )
211 return rgbe_error( rgbe_format_error, "no FORMAT specifier found" );
212
213#if 0
214 //
215 if ( fgets( buf, sizeof( buf ) / sizeof( buf[0] ), fp ) == 0 )
216 return rgbe_error( rgbe_read_error, NULL );
217
218 if ( strcmp( buf, "\n" ) != 0 )
219 return rgbe_error( rgbe_format_error, "missing blank line after FORMAT specifier" );
220#endif
221
222 if ( fgets( buf, sizeof( buf ), fp ) == 0 )
223 return rgbe_error( rgbe_read_error, NULL );
224
225// printf("rgbe header: '%s'", buf);
226 if ( sscanf( buf, "%[-+]Y %d %[-+]X %d", sh, height, sw, width ) != 4
227 && sscanf( buf, "%[-+]X %d %[-+]Y %d", sh, height, sw, width ) != 4 )
228 return rgbe_error( rgbe_format_error, "missing image size specifier" );
229
230#if 0
231 if ( sw == '-' )
232 *width = - ( *width );
233 if ( sh == '-' )
234 *height = - ( *height );
235#endif
236
237// printf("rgbe header: %dx%d image\n", *width, *height);
238
239 return RGBE_RETURN_SUCCESS;
240}
241
242/* simple write routine that does not use run length encoding */
243/* These routines can be made faster by allocating a larger buffer and
244 fread-ing and fwrite-ing the data in larger chunks */
245int RGBE_WritePixels( FILE *fp, const float *data, const int n )
246{
247 unsigned char rgbe[4];
248 int numpixels= n;
249
250 while ( numpixels-- > 0 )
251 {
252 float2rgbe( rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE] );
253 data += RGBE_DATA_SIZE;
254 if ( fwrite( rgbe, sizeof( rgbe ), 1, fp ) < 1 )
255 return rgbe_error( rgbe_write_error, NULL );
256 }
257 return RGBE_RETURN_SUCCESS;
258}
259
260/* simple read routine. will not correctly handle run length encoding */
261int RGBE_ReadPixels( FILE *fp, float *data, const int n )
262{
263 unsigned char rgbe[4];
264 int numpixels= n;
265
266 while ( numpixels-- > 0 )
267 {
268 if ( fread( rgbe, sizeof( rgbe ), 1, fp ) < 1 )
269 return rgbe_error( rgbe_read_error, NULL );
270 rgbe2float( &data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe );
271 data += RGBE_DATA_SIZE;
272 }
273 return RGBE_RETURN_SUCCESS;
274}
275
276/* The code below is only needed for the run-length encoded files. */
277/* Run length encoding adds considerable complexity but does */
278/* save some space. For each scanline, each channel (r,g,b,e) is */
279/* encoded separately for better compression. */
280
281static int RGBE_WriteBytes_RLE( FILE *fp, const unsigned char *data, const int numbytes )
282{
283#define MINRUNLENGTH 4
284 int cur, beg_run, run_count, old_run_count, nonrun_count;
285 unsigned char buf[2];
286
287 cur = 0;
288 while ( cur < numbytes )
289 {
290 beg_run = cur;
291 /* find next run of length at least 4 if one exists */
292 run_count = old_run_count = 0;
293 while (( run_count < MINRUNLENGTH ) && ( beg_run < numbytes ) )
294 {
295 beg_run += run_count;
296 old_run_count = run_count;
297 run_count = 1;
298 while( (beg_run + run_count < numbytes) && (run_count < 127)
299 && (data[beg_run] == data[beg_run + run_count]) )
300 run_count++;
301 }
302 /* if data before next big run is a short run then write it as such */
303 if (( old_run_count > 1 ) && ( old_run_count == beg_run - cur ) )
304 {
305 buf[0] = 128 + old_run_count; /*write short run*/
306 buf[1] = data[cur];
307 if ( fwrite( buf, sizeof( buf[0] )*2, 1, fp ) < 1 )
308 return rgbe_error( rgbe_write_error, NULL );
309 cur = beg_run;
310 }
311 /* write out bytes until we reach the start of the next run */
312 while ( cur < beg_run )
313 {
314 nonrun_count = beg_run - cur;
315 if ( nonrun_count > 128 )
316 nonrun_count = 128;
317 buf[0] = nonrun_count;
318 if ( fwrite( buf, sizeof( buf[0] ), 1, fp ) < 1 )
319 return rgbe_error( rgbe_write_error, NULL );
320 if ( fwrite( &data[cur], sizeof( data[0] )*nonrun_count, 1, fp ) < 1 )
321 return rgbe_error( rgbe_write_error, NULL );
322 cur += nonrun_count;
323 }
324 /* write out next run if one was found */
325 if ( run_count >= MINRUNLENGTH )
326 {
327 buf[0] = 128 + run_count;
328 buf[1] = data[beg_run];
329 if ( fwrite( buf, sizeof( buf[0] )*2, 1, fp ) < 1 )
330 return rgbe_error( rgbe_write_error, NULL );
331 cur += run_count;
332 }
333 }
334 return RGBE_RETURN_SUCCESS;
335#undef MINRUNLENGTH
336}
337
338int RGBE_WritePixels_RLE( FILE *fp, const float *data, const int width, const int n )
339{
340 unsigned char rgbe[4];
341 unsigned char *buffer;
342 int i, err;
343
344 int scanline_width= width;
345 int num_scanlines= n;
346 if (( scanline_width < 8 ) || ( scanline_width > 0x7fff ) )
347 /* run length encoding is not allowed so write flat*/
348 return RGBE_WritePixels( fp, data, scanline_width*num_scanlines );
349 buffer = ( unsigned char * )malloc( sizeof( unsigned char ) * 4 * scanline_width );
350 if ( buffer == NULL )
351 /* no buffer space so write flat */
352 return RGBE_WritePixels( fp, data, scanline_width*num_scanlines );
353 while ( num_scanlines-- > 0 )
354 {
355 rgbe[0] = 2;
356 rgbe[1] = 2;
357 rgbe[2] = scanline_width >> 8;
358 rgbe[3] = scanline_width & 0xFF;
359 if ( fwrite( rgbe, sizeof( rgbe ), 1, fp ) < 1 )
360 {
361 free( buffer );
362 return rgbe_error( rgbe_write_error, NULL );
363 }
364 for ( i = 0;i < scanline_width;i++ )
365 {
366 float2rgbe( rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE] );
367 buffer[i] = rgbe[0];
368 buffer[i+scanline_width] = rgbe[1];
369 buffer[i+2*scanline_width] = rgbe[2];
370 buffer[i+3*scanline_width] = rgbe[3];
371 data += RGBE_DATA_SIZE;
372 }
373 /* write out each of the four channels separately run length encoded */
374 /* first red, then green, then blue, then exponent */
375 for ( i = 0;i < 4;i++ )
376 {
377 if (( err = RGBE_WriteBytes_RLE( fp, &buffer[i*scanline_width], scanline_width ) ) != RGBE_RETURN_SUCCESS )
378 {
379 free( buffer );
380 return err;
381 }
382 }
383 }
384 free( buffer );
385 return RGBE_RETURN_SUCCESS;
386}
387
388int RGBE_ReadPixels_RLE( FILE *fp, float *data, const int width, const int n )
389{
390 unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
391 int i, count;
392 unsigned char buf[2];
393
394 int scanline_width= width;
395 int num_scanlines= n;
396 if (( scanline_width < 8 ) || ( scanline_width > 0x7fff ) )
397 /* run length encoding is not allowed so read flat*/
398 return RGBE_ReadPixels( fp, data, scanline_width*num_scanlines );
399 scanline_buffer = NULL;
400 /* read in each successive scanline */
401 while ( num_scanlines > 0 )
402 {
403 if ( fread( rgbe, sizeof( rgbe ), 1, fp ) < 1 )
404 {
405 free( scanline_buffer );
406 return rgbe_error( rgbe_read_error, NULL );
407 }
408 if (( rgbe[0] != 2 ) || ( rgbe[1] != 2 ) || ( rgbe[2] & 0x80 ) )
409 {
410 /* this file is not run length encoded */
411 rgbe2float( &data[0], &data[1], &data[2], rgbe );
412 data += RGBE_DATA_SIZE;
413 free( scanline_buffer );
414 return RGBE_ReadPixels( fp, data, scanline_width*num_scanlines - 1 );
415 }
416 if (((( int )rgbe[2] ) << 8 | rgbe[3] ) != scanline_width )
417 {
418 free( scanline_buffer );
419 return rgbe_error( rgbe_format_error, "wrong scanline width" );
420 }
421 if ( scanline_buffer == NULL )
422 scanline_buffer = ( unsigned char * ) malloc( sizeof( unsigned char ) * 4 * scanline_width );
423 if ( scanline_buffer == NULL )
424 return rgbe_error( rgbe_memory_error, "unable to allocate buffer space" );
425
426 ptr = &scanline_buffer[0];
427 /* read each of the four channels for the scanline into the buffer */
428 for ( i = 0;i < 4;i++ )
429 {
430 ptr_end = &scanline_buffer[( i+1 )*scanline_width];
431 while ( ptr < ptr_end )
432 {
433 if ( fread( buf, sizeof(buf), 1, fp ) < 1 )
434 {
435 free( scanline_buffer );
436 return rgbe_error( rgbe_read_error, NULL );
437 }
438 if ( buf[0] > 128 )
439 {
440 /* a run of the same value */
441 count = buf[0] - 128;
442 if (( count == 0 ) || ( count > ptr_end - ptr ) )
443 {
444 free( scanline_buffer );
445 return rgbe_error( rgbe_format_error, "bad scanline data" );
446 }
447 while ( count-- > 0 )
448 *ptr++ = buf[1];
449 }
450 else
451 {
452 /* a non-run */
453 count = buf[0];
454 if (( count == 0 ) || ( count > ptr_end - ptr ) )
455 {
456 free( scanline_buffer );
457 return rgbe_error( rgbe_format_error, "bad scanline data" );
458 }
459 *ptr++ = buf[1];
460 if ( --count > 0 )
461 {
462 if ( fread( ptr, sizeof( unsigned char )*count, 1, fp ) < 1 )
463 {
464 free( scanline_buffer );
465 return rgbe_error( rgbe_read_error, NULL );
466 }
467 ptr += count;
468 }
469 }
470 }
471 }
472 /* now convert data from buffer into floats */
473 for ( i = 0;i < scanline_width;i++ )
474 {
475 rgbe[0] = scanline_buffer[i];
476 rgbe[1] = scanline_buffer[i+scanline_width];
477 rgbe[2] = scanline_buffer[i+2*scanline_width];
478 rgbe[3] = scanline_buffer[i+3*scanline_width];
479 rgbe2float( &data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe );
480 data += RGBE_DATA_SIZE;
481 }
482 num_scanlines--;
483 }
484 free( scanline_buffer );
485 return RGBE_RETURN_SUCCESS;
486}
487