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 doxygen 1.5.6