gKit2 light
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 
43 enum 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 */
52 static 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 */
75 static INLINE void
76 float2rgbe( 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]. */
101 static INLINE void
102 rgbe2float( 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 */
118 int 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 */
145 int 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 */
245 int 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 */
261 int 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 
281 static 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 
338 int 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 
388 int 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