rgbe.cpp
Go to the documentation of this file.00001 /**<!--------------------------------------------------------------------> 00002 @file rgbe.cpp 00003 @author Bruce Walter (http://www.graphics.cornell.edu/~bjw/) 00004 @author Travis Fischer (fisch0920@gmail.com) 00005 @date January 2009 00006 00007 @brief 00008 Utilities for reading and writing Ward's rgbe image format. 00009 00010 @note 00011 This code is from http://www.graphics.cornell.edu/~bjw/rgbe/ 00012 00013 @ee also http://www.graphics.cornell.edu/online/formats/rgbe/ 00014 <!-------------------------------------------------------------------->**/ 00015 00016 /* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE. 00017 * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY, 00018 * IT IS STRICTLY USE AT YOUR OWN RISK. */ 00019 00020 #include "rgbe.h" 00021 #include <math.h> 00022 #include <stdlib.h> 00023 #include <string.h> 00024 #include <ctype.h> 00025 00026 /* This file contains code to read and write four byte rgbe file format 00027 developed by Greg Ward. It handles the conversions between rgbe and 00028 pixels consisting of floats. The data is assumed to be an array of floats. 00029 By default there are three floats per pixel in the order red, green, blue. 00030 (RGBE_DATA_??? values control this.) Only the mimimal header reading and 00031 writing is implemented. Each routine does error checking and will return 00032 a status value as defined below. This code is intended as a skeleton so 00033 feel free to modify it to suit your needs. 00034 00035 (Place notice here if you modified the code.) 00036 posted to http://www.graphics.cornell.edu/~bjw/ 00037 written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95 00038 based on code written by Greg Ward 00039 */ 00040 00041 #ifdef _CPLUSPLUS 00042 /* define if your compiler understands inline commands */ 00043 #define INLINE inline 00044 #else 00045 #define INLINE 00046 #endif 00047 00048 /* offsets to red, green, and blue components in a data (float) pixel */ 00049 #define RGBE_DATA_RED 0 00050 #define RGBE_DATA_GREEN 1 00051 #define RGBE_DATA_BLUE 2 00052 /* number of floats per pixel */ 00053 #define RGBE_DATA_SIZE 3 00054 00055 enum rgbe_error_codes { 00056 rgbe_read_error, 00057 rgbe_write_error, 00058 rgbe_format_error, 00059 rgbe_memory_error, 00060 }; 00061 00062 /* default error routine. change this to change error handling */ 00063 static int rgbe_error(int rgbe_error_code, char *msg) 00064 { 00065 switch (rgbe_error_code) { 00066 case rgbe_read_error: 00067 perror("RGBE read error"); 00068 break; 00069 case rgbe_write_error: 00070 perror("RGBE write error"); 00071 break; 00072 case rgbe_format_error: 00073 fprintf(stderr,"RGBE bad file format: %s\n",msg); 00074 break; 00075 default: 00076 case rgbe_memory_error: 00077 fprintf(stderr,"RGBE error: %s\n",msg); 00078 } 00079 return RGBE_RETURN_FAILURE; 00080 } 00081 00082 /* standard conversion from float pixels to rgbe pixels */ 00083 /* note: you can remove the "inline"s if your compiler complains about it */ 00084 static INLINE void 00085 float2rgbe(unsigned char rgbe[4], float red, float green, float blue) 00086 { 00087 float v; 00088 int e; 00089 00090 v = red; 00091 if (green > v) v = green; 00092 if (blue > v) v = blue; 00093 if (v < 1e-32) { 00094 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 00095 } 00096 else { 00097 v = frexp(v,&e) * 256.0/v; 00098 rgbe[0] = (unsigned char) (red * v); 00099 rgbe[1] = (unsigned char) (green * v); 00100 rgbe[2] = (unsigned char) (blue * v); 00101 rgbe[3] = (unsigned char) (e + 128); 00102 } 00103 } 00104 00105 /* standard conversion from rgbe to float pixels */ 00106 /* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */ 00107 /* in the range [0,1] to map back into the range [0,1]. */ 00108 static INLINE void 00109 rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4]) 00110 { 00111 float f; 00112 00113 if (rgbe[3]) { /*nonzero pixel*/ 00114 f = ldexp(1.0,rgbe[3]-(int)(128+8)); 00115 *red = rgbe[0] * f; 00116 *green = rgbe[1] * f; 00117 *blue = rgbe[2] * f; 00118 } 00119 else 00120 *red = *green = *blue = 0.0; 00121 } 00122 00123 /* default minimal header. modify if you want more information in header */ 00124 int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info) 00125 { 00126 const char *programtype = "RGBE"; 00127 00128 if (info && (info->valid & RGBE_VALID_PROGRAMTYPE)) 00129 programtype = info->programtype; 00130 if (fprintf(fp,"#?%s\n",programtype) < 0) 00131 return rgbe_error(rgbe_write_error,NULL); 00132 /* The #? is to identify file type, the programtype is optional. */ 00133 if (info && (info->valid & RGBE_VALID_GAMMA)) { 00134 if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0) 00135 return rgbe_error(rgbe_write_error,NULL); 00136 } 00137 if (info && (info->valid & RGBE_VALID_EXPOSURE)) { 00138 if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0) 00139 return rgbe_error(rgbe_write_error,NULL); 00140 } 00141 if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0) 00142 return rgbe_error(rgbe_write_error,NULL); 00143 if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0) 00144 return rgbe_error(rgbe_write_error,NULL); 00145 return RGBE_RETURN_SUCCESS; 00146 } 00147 00148 /* minimal header reading. modify if you want to parse more information */ 00149 int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info) 00150 { 00151 char buf[128]; 00152 int found_format; 00153 float tempf; 00154 int i; 00155 00156 found_format = 0; 00157 if (info) { 00158 info->valid = 0; 00159 info->programtype[0] = 0; 00160 info->gamma = info->exposure = 1.0; 00161 } 00162 if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL) 00163 return rgbe_error(rgbe_read_error,NULL); 00164 if ((buf[0] != '#')||(buf[1] != '?')) { 00165 /* if you want to require the magic token then uncomment the next line */ 00166 /*return rgbe_error(rgbe_format_error,"bad initial token"); */ 00167 } 00168 else if (info) { 00169 info->valid |= RGBE_VALID_PROGRAMTYPE; 00170 for(i=0;i<(int)sizeof(info->programtype)-1;i++) { 00171 if ((buf[i+2] == 0) || isspace(buf[i+2])) 00172 break; 00173 info->programtype[i] = buf[i+2]; 00174 } 00175 info->programtype[i] = 0; 00176 if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) 00177 return rgbe_error(rgbe_read_error,NULL); 00178 } 00179 int formatFound = 0; 00180 for(;;) { 00181 if ((buf[0] == 0)||(buf[0] == '\n')){ 00182 if (!formatFound) 00183 return rgbe_error(rgbe_format_error, 00184 (char*)"no FORMAT specifier found"); 00185 else 00186 break; 00187 } 00188 else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0) 00189 { formatFound=1; } /* format found so break out of loop */ 00190 else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) { 00191 info->gamma = tempf; 00192 info->valid |= RGBE_VALID_GAMMA; 00193 } 00194 else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) { 00195 info->exposure = tempf; 00196 info->valid |= RGBE_VALID_EXPOSURE; 00197 } 00198 if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) 00199 return rgbe_error(rgbe_read_error,NULL); 00200 } 00201 /* if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) */ 00202 /* return rgbe_error(rgbe_read_error,NULL); */ 00203 if (strcmp(buf,"\n") != 0) 00204 return rgbe_error(rgbe_format_error, 00205 (char*)"missing blank line after FORMAT specifier"); 00206 if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) 00207 return rgbe_error(rgbe_read_error,NULL); 00208 if (sscanf(buf,"-Y %d +X %d",height,width) < 2) 00209 return rgbe_error(rgbe_format_error, 00210 (char*)"missing image size specifier"); 00211 return RGBE_RETURN_SUCCESS; 00212 } 00213 00214 /* simple write routine that does not use run length encoding */ 00215 /* These routines can be made faster by allocating a larger buffer and 00216 fread-ing and fwrite-ing the data in larger chunks */ 00217 int RGBE_WritePixels(FILE *fp, const float *data, int numpixels) 00218 { 00219 unsigned char rgbe[4]; 00220 00221 while (numpixels-- > 0) { 00222 float2rgbe(rgbe,data[RGBE_DATA_RED], 00223 data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]); 00224 data += RGBE_DATA_SIZE; 00225 if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) 00226 return rgbe_error(rgbe_write_error,NULL); 00227 } 00228 return RGBE_RETURN_SUCCESS; 00229 } 00230 00231 /* simple read routine. will not correctly handle run length encoding */ 00232 int RGBE_ReadPixels(FILE *fp, float *data, int numpixels) 00233 { 00234 unsigned char rgbe[4]; 00235 00236 while(numpixels-- > 0) { 00237 if (fread(rgbe, sizeof(rgbe), 1, fp) < 1) 00238 return rgbe_error(rgbe_read_error,NULL); 00239 rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN], 00240 &data[RGBE_DATA_BLUE],rgbe); 00241 data += RGBE_DATA_SIZE; 00242 } 00243 return RGBE_RETURN_SUCCESS; 00244 } 00245 00246 /* The code below is only needed for the run-length encoded files. */ 00247 /* Run length encoding adds considerable complexity but does */ 00248 /* save some space. For each scanline, each channel (r,g,b,e) is */ 00249 /* encoded separately for better compression. */ 00250 00251 static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes) 00252 { 00253 #define MINRUNLENGTH 4 00254 int cur, beg_run, run_count, old_run_count, nonrun_count; 00255 unsigned char buf[2]; 00256 00257 cur = 0; 00258 while(cur < numbytes) { 00259 beg_run = cur; 00260 /* find next run of length at least 4 if one exists */ 00261 run_count = old_run_count = 0; 00262 while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) { 00263 beg_run += run_count; 00264 old_run_count = run_count; 00265 run_count = 1; 00266 while( (beg_run + run_count < numbytes) && (run_count < 127) 00267 && (data[beg_run] == data[beg_run + run_count])) 00268 run_count++; 00269 } 00270 /* if data before next big run is a short run then write it as such */ 00271 if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) { 00272 buf[0] = 128 + old_run_count; /*write short run*/ 00273 buf[1] = data[cur]; 00274 if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1) 00275 return rgbe_error(rgbe_write_error,NULL); 00276 cur = beg_run; 00277 } 00278 /* write out bytes until we reach the start of the next run */ 00279 while(cur < beg_run) { 00280 nonrun_count = beg_run - cur; 00281 if (nonrun_count > 128) 00282 nonrun_count = 128; 00283 buf[0] = nonrun_count; 00284 if (fwrite(buf,sizeof(buf[0]),1,fp) < 1) 00285 return rgbe_error(rgbe_write_error,NULL); 00286 if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1) 00287 return rgbe_error(rgbe_write_error,NULL); 00288 cur += nonrun_count; 00289 } 00290 /* write out next run if one was found */ 00291 if (run_count >= MINRUNLENGTH) { 00292 buf[0] = 128 + run_count; 00293 buf[1] = data[beg_run]; 00294 if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1) 00295 return rgbe_error(rgbe_write_error,NULL); 00296 cur += run_count; 00297 } 00298 } 00299 return RGBE_RETURN_SUCCESS; 00300 #undef MINRUNLENGTH 00301 } 00302 00303 int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width, 00304 int num_scanlines) 00305 { 00306 unsigned char rgbe[4]; 00307 unsigned char *buffer; 00308 int i, err; 00309 00310 if ((scanline_width < 8)||(scanline_width > 0x7fff)) 00311 /* run length encoding is not allowed so write flat*/ 00312 return RGBE_WritePixels(fp,data,scanline_width*num_scanlines); 00313 buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width); 00314 if (buffer == NULL) 00315 /* no buffer space so write flat */ 00316 return RGBE_WritePixels(fp,data,scanline_width*num_scanlines); 00317 while(num_scanlines-- > 0) { 00318 rgbe[0] = 2; 00319 rgbe[1] = 2; 00320 rgbe[2] = scanline_width >> 8; 00321 rgbe[3] = scanline_width & 0xFF; 00322 if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) { 00323 free(buffer); 00324 return rgbe_error(rgbe_write_error,NULL); 00325 } 00326 for(i=0;i<scanline_width;i++) { 00327 float2rgbe(rgbe,data[RGBE_DATA_RED], 00328 data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]); 00329 buffer[i] = rgbe[0]; 00330 buffer[i+scanline_width] = rgbe[1]; 00331 buffer[i+2*scanline_width] = rgbe[2]; 00332 buffer[i+3*scanline_width] = rgbe[3]; 00333 data += RGBE_DATA_SIZE; 00334 } 00335 /* write out each of the four channels separately run length encoded */ 00336 /* first red, then green, then blue, then exponent */ 00337 for(i=0;i<4;i++) { 00338 if ((err = RGBE_WriteBytes_RLE(fp,&buffer[i*scanline_width], 00339 scanline_width)) != RGBE_RETURN_SUCCESS) { 00340 free(buffer); 00341 return err; 00342 } 00343 } 00344 } 00345 free(buffer); 00346 return RGBE_RETURN_SUCCESS; 00347 } 00348 00349 int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width, 00350 int num_scanlines) 00351 { 00352 unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end; 00353 int i, count; 00354 unsigned char buf[2]; 00355 00356 if ((scanline_width < 8)||(scanline_width > 0x7fff)) 00357 /* run length encoding is not allowed so read flat*/ 00358 return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines); 00359 scanline_buffer = NULL; 00360 /* read in each successive scanline */ 00361 while(num_scanlines > 0) { 00362 if (fread(rgbe,sizeof(rgbe),1,fp) < 1) { 00363 free(scanline_buffer); 00364 return rgbe_error(rgbe_read_error,NULL); 00365 } 00366 if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) { 00367 /* this file is not run length encoded */ 00368 rgbe2float(&data[0],&data[1],&data[2],rgbe); 00369 data += RGBE_DATA_SIZE; 00370 free(scanline_buffer); 00371 return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1); 00372 } 00373 if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) { 00374 free(scanline_buffer); 00375 return rgbe_error(rgbe_format_error, 00376 (char*)"wrong scanline width"); 00377 } 00378 if (scanline_buffer == NULL) 00379 scanline_buffer = (unsigned char *) 00380 malloc(sizeof(unsigned char)*4*scanline_width); 00381 if (scanline_buffer == NULL) 00382 return rgbe_error(rgbe_memory_error, 00383 (char*)"unable to allocate buffer space"); 00384 00385 ptr = &scanline_buffer[0]; 00386 /* read each of the four channels for the scanline into the buffer */ 00387 for(i=0;i<4;i++) { 00388 ptr_end = &scanline_buffer[(i+1)*scanline_width]; 00389 while(ptr < ptr_end) { 00390 if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) { 00391 free(scanline_buffer); 00392 return rgbe_error(rgbe_read_error,NULL); 00393 } 00394 if (buf[0] > 128) { 00395 /* a run of the same value */ 00396 count = buf[0]-128; 00397 if ((count == 0)||(count > ptr_end - ptr)) { 00398 free(scanline_buffer); 00399 return rgbe_error(rgbe_format_error, 00400 (char*)"bad scanline data"); 00401 } 00402 while(count-- > 0) 00403 *ptr++ = buf[1]; 00404 } 00405 else { 00406 /* a non-run */ 00407 count = buf[0]; 00408 if ((count == 0)||(count > ptr_end - ptr)) { 00409 free(scanline_buffer); 00410 return rgbe_error(rgbe_format_error, 00411 (char*)"bad scanline data"); 00412 } 00413 *ptr++ = buf[1]; 00414 if (--count > 0) { 00415 if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) { 00416 free(scanline_buffer); 00417 return rgbe_error(rgbe_read_error,NULL); 00418 } 00419 ptr += count; 00420 } 00421 } 00422 } 00423 } 00424 /* now convert data from buffer into floats */ 00425 for(i=0;i<scanline_width;i++) { 00426 rgbe[0] = scanline_buffer[i]; 00427 rgbe[1] = scanline_buffer[i+scanline_width]; 00428 rgbe[2] = scanline_buffer[i+2*scanline_width]; 00429 rgbe[3] = scanline_buffer[i+3*scanline_width]; 00430 rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN], 00431 &data[RGBE_DATA_BLUE],rgbe); 00432 data += RGBE_DATA_SIZE; 00433 } 00434 num_scanlines--; 00435 } 00436 free(scanline_buffer); 00437 return RGBE_RETURN_SUCCESS; 00438 } 00439
Generated on 28 Feb 2009 for Milton by
1.5.6